diff --git a/src/relays.rs b/src/relays.rs index 1ed4dde..9964b03 100644 --- a/src/relays.rs +++ b/src/relays.rs @@ -27,15 +27,18 @@ pub fn default_relays() -> Vec { /// connect event is thrown away, so it must never be in the pairing URI. /// - wss://relay.nostr.band currently hangs the WebSocket handshake; it is /// also pay-to-read. Dropped from the pairing set. -/// - wss://nos.lol and wss://relay.snort.social accept ephemeral 24133 and -/// serve it live (snort does not persist ephemeral kinds, which is fine — -/// pairing only needs the live push). +/// - wss://nos.lol and wss://relay.primal.net are the two relays with +/// PROVEN bidirectional ephemeral-24133 traffic in the live Sep 23 scans +/// (both stored Amber's connect reply AND our identity RPC). +/// - wss://relay.damus.io returned HTTP 503 to reads and answered connects +/// inconsistently during the same scans; while flapping it splits the +/// conversation across relays the signer never reads. +/// - wss://relay.snort.social accepts ephemeral 24133 publishes but does +/// not persist them; with damus out it adds no shared ground. pub fn pairing_relays() -> Vec { vec![ - "wss://relay.damus.io".to_string(), "wss://relay.primal.net".to_string(), "wss://nos.lol".to_string(), - "wss://relay.snort.social".to_string(), ] } diff --git a/src/signer/nip46_client.rs b/src/signer/nip46_client.rs index 0ea98b3..dd5e404 100644 --- a/src/signer/nip46_client.rs +++ b/src/signer/nip46_client.rs @@ -1714,10 +1714,42 @@ impl Nip46ClientSigner { }; // Learn the REAL signing identity from the signer itself. - let identity = self - .send_rpc("get_public_key", vec![], REQUEST_TIMEOUT, false) - .await - .map_err(|e| format!("The signer would not reveal its public key: {e}"))?; + // + // RETRIED with backoff on timeout. The live Sep 23 scans proved a + // structural race on the SIGNER side: our first `get_public_key` + // publishes the instant the connect echo is verified, but Amber's + // own subscription to our client key opens milliseconds later, so + // the request is already in the relays' past when its listener + // starts — relays never replay history to a fresh subscription and + // the answer never comes. A retry lands while the signer is + // listening. Non-timeout failures (refusal, bad payload) still fail + // fast. + let mut attempt = 0u32; + let identity = loop { + attempt += 1; + match self + .send_rpc("get_public_key", vec![], REQUEST_TIMEOUT, false) + .await + { + Ok(identity) => break identity, + Err(e) => { + if !matches!(e, SigningError::Timeout) || attempt >= 4 { + return Err(format!("The signer would not reveal its public key: {e}")); + } + if live_relays(&connection.relays) { + pairing_trace(&format!( + "identity attempt {attempt} timed out; retrying get_public_key" + )); + } + tokio::time::sleep(Duration::from_secs(match attempt { + 1 => 3, + 2 => 8, + _ => 15, + })) + .await; + } + } + }; let identity = PublicKey::from_hex(identity.trim()) .map_err(|e| format!("The signer returned an unreadable public key: {e}"))?; let identity_npub = identity