fix(pairing): subscribe to signer replies BEFORE the handshake publishes
The relay pushes events only to subscriptions that exist at delivery time. Both connect flows spawned the handshake task before run_demux registered the kind-24133 author subscription, so a fast signer reply (the live Sep 23 Amber scan: clean connect + secret echo, then get_public_key) landed in the gap and was silently dropped — the identity RPC timed out 30s later and the session died with 'The signer would not reveal its public key' after the connection was already stored, leaving the UI signed in with no profile. Both run_sign_task (bunker flow) and run_paired (QR pairing) now subscribe first via subscribe_to_signer and hand the stream + subscription to run_demux. futures-util promoted from dev-dependency to runtime. cargo test 209+3e2e green, clippy 0, fmt clean, release rebuilt.
This commit is contained in:
parent
13a66f28d8
commit
2e98e69ddf
2 changed files with 46 additions and 10 deletions
|
|
@ -20,6 +20,7 @@ rpassword = "7"
|
||||||
sha2 = "0.10"
|
sha2 = "0.10"
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
keyring = "4.2"
|
keyring = "4.2"
|
||||||
|
futures-util = "0.3"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
base64 = "0.22"
|
base64 = "0.22"
|
||||||
|
|
|
||||||
|
|
@ -870,6 +870,14 @@ impl Nip46ClientSigner {
|
||||||
conversation: ConversationKey,
|
conversation: ConversationKey,
|
||||||
client: Client,
|
client: Client,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
|
// Subscribe BEFORE the handshake publishes anything: relays only push
|
||||||
|
// events to subscriptions that exist at delivery time, and the
|
||||||
|
// identity RPC (`get_public_key`) fires within milliseconds of this
|
||||||
|
// point. When the spawn raced ahead of the subscription, Amber's fast
|
||||||
|
// reply landed in the gap and was lost — the live Sep 23 scan died
|
||||||
|
// exactly there ("signer would not reveal its public key" after a
|
||||||
|
// clean `connect` + secret echo).
|
||||||
|
let (notifications, subscription) = self.subscribe_to_signer(&client, uri.peer).await?;
|
||||||
{
|
{
|
||||||
let handshake = self.clone();
|
let handshake = self.clone();
|
||||||
let peer = uri.peer;
|
let peer = uri.peer;
|
||||||
|
|
@ -879,7 +887,31 @@ impl Nip46ClientSigner {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.run_demux(uri, keys, conversation, client).await
|
self.run_demux(uri, keys, conversation, client, notifications, subscription)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Open the notification stream and register the kind-24133 author
|
||||||
|
/// subscription for the signer. Kept separate so every connect path can
|
||||||
|
/// establish it BEFORE publishing its first RPC.
|
||||||
|
async fn subscribe_to_signer(
|
||||||
|
&self,
|
||||||
|
client: &Client,
|
||||||
|
peer: PublicKey,
|
||||||
|
) -> Result<
|
||||||
|
(
|
||||||
|
std::pin::Pin<Box<dyn futures_util::Stream<Item = ClientNotification> + Send>>,
|
||||||
|
Output<SubscriptionId>,
|
||||||
|
),
|
||||||
|
String,
|
||||||
|
> {
|
||||||
|
let filter = Filter::new().kind(Kind::NostrConnect).author(peer);
|
||||||
|
let notifications = client.notifications();
|
||||||
|
let subscription = client
|
||||||
|
.subscribe(filter)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Could not subscribe: {e}"))?;
|
||||||
|
Ok((notifications, subscription))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get current connection status.
|
/// Get current connection status.
|
||||||
|
|
@ -1177,6 +1209,12 @@ impl Nip46ClientSigner {
|
||||||
// Update connected relays
|
// Update connected relays
|
||||||
self.inner.lock().await.client = Some(client.clone());
|
self.inner.lock().await.client = Some(client.clone());
|
||||||
|
|
||||||
|
// Subscribe BEFORE the handshake publishes `connect`: relays only
|
||||||
|
// push the signer's replies to subscriptions that exist at delivery
|
||||||
|
// time, so registering after the first publish can silently drop the
|
||||||
|
// ack (see run_paired for the live incident this fixes).
|
||||||
|
let (notifications, subscription) = self.subscribe_to_signer(&client, uri.peer).await?;
|
||||||
|
|
||||||
// The connect handshake runs as its own task: it publishes `connect`
|
// The connect handshake runs as its own task: it publishes `connect`
|
||||||
// and then must AWAIT the signer's ack — which can wait on a human
|
// and then must AWAIT the signer's ack — which can wait on a human
|
||||||
// approving us in Amber — followed by `get_public_key` to learn the
|
// approving us in Amber — followed by `get_public_key` to learn the
|
||||||
|
|
@ -1195,7 +1233,8 @@ impl Nip46ClientSigner {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.run_demux(uri, keys, conversation, client).await
|
self.run_demux(uri, keys, conversation, client, notifications, subscription)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The long-lived request/response demux loop, shared by both connect
|
/// The long-lived request/response demux loop, shared by both connect
|
||||||
|
|
@ -1209,15 +1248,11 @@ impl Nip46ClientSigner {
|
||||||
keys: Keys,
|
keys: Keys,
|
||||||
conversation: ConversationKey,
|
conversation: ConversationKey,
|
||||||
client: Client,
|
client: Client,
|
||||||
|
mut notifications: std::pin::Pin<
|
||||||
|
Box<dyn futures_util::Stream<Item = ClientNotification> + Send>,
|
||||||
|
>,
|
||||||
|
subscription: Output<SubscriptionId>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
// Subscribe to kind 24133 from signer
|
|
||||||
let filter = Filter::new().kind(Kind::NostrConnect).author(uri.peer);
|
|
||||||
let mut notifications = client.notifications();
|
|
||||||
let subscription = client
|
|
||||||
.subscribe(filter)
|
|
||||||
.await
|
|
||||||
.map_err(|e| format!("Could not subscribe: {e}"))?;
|
|
||||||
|
|
||||||
// Handle incoming requests
|
// Handle incoming requests
|
||||||
loop {
|
loop {
|
||||||
let incoming =
|
let incoming =
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue