From 295b8d79333f8c8f8c9ed260bf0aec8faa8ebbcd Mon Sep 17 00:00:00 2001 From: Arseniy Klempner Date: Thu, 21 May 2026 11:15:17 -0600 Subject: [PATCH] feat(chat): membership status watcher + bump nwaku for 2-phase gifter src/chat/delivery/waku_client.nim: - After the gifter-client returns its optimistic leaf_index, spawn a background watcher that polls the gifter's new membership-status RPC (short-lived libp2p streams on /logos/rln/membership/status/ 1.0.0) until the on-chain membership PDA materialises for our id. If the authoritative leaf differs from the optimistic snapshot, the watcher rewrites lezGm.membershipIndex. The OnchainLEZGroupManager's poll loop picks up the corrected proof on its next iteration; spam_protection's self-verify rejects bad proofs until then. vendor/nwaku bump pulls in: - New /logos/rln/membership/status/1.0.0 codec + handlers on the gifter server. - MembershipStatusRequest/Response RPC messages. - WakuRlnGifterClient.queryMembershipStatus. - Mix-node gifter-client watcher with the same pattern. --- src/chat/delivery/waku_client.nim | 46 +++++++++++++++++++++++++++++++ vendor/nwaku | 2 +- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/chat/delivery/waku_client.nim b/src/chat/delivery/waku_client.nim index 35422a0..8c1897e 100644 --- a/src/chat/delivery/waku_client.nim +++ b/src/chat/delivery/waku_client.nim @@ -451,6 +451,52 @@ proc start*(client: WakuClient) {.async.} = leafIndex = success.leafIndex, configAccount = configAccountId + # Background watcher: gifter returns a PRE-SUBMIT leaf_index snapshot, + # which can be wrong if a concurrent register tx claimed the slot + # first. Poll the gifter over a SEPARATE short-lived libp2p stream + # (membership status codec) until the on-chain membership PDA + # materialises for our id; if the authoritative leaf differs, + # rewrite lezGm.membershipIndex so subsequent proofs are valid. The + # pre-publish self-verify rejects bad proofs in the meantime. + let optimisticLeafChat = success.leafIndex + let watcherClient = gifterClient + let watcherPeer = gifterPeer + let watcherIdCommit = idCommitmentBytes + asyncSpawn (proc(): Future[void] {.async.} = + const pollEveryMs = 30_000 + const deadlineMs = 1_800_000 + let deadline = Moment.now() + chronos.milliseconds(deadlineMs) + while Moment.now() < deadline: + try: + await sleepAsync(chronos.milliseconds(pollEveryMs)) + except CancelledError: + return + let qr = + try: + await watcherClient.queryMembershipStatus( + watcherPeer, configAccountId, watcherIdCommit) + except CancelledError: + return + except CatchableError as e: + Result[rln_gifter_protocol.MembershipStatusResponse, string].err( + "queryMembershipStatus raised: " & e.msg) + if qr.isErr: continue + let resp = qr.get() + if resp.errorMessage.isSome: continue + if not resp.registered: continue + if resp.leafIndex.isSome: + let authLeaf = resp.leafIndex.get() + if authLeaf != optimisticLeafChat: + info "Chat-client membership leaf corrected from optimistic", + optimistic = optimisticLeafChat, authoritative = authLeaf + lezGm.membershipIndex = + some(onchain_group_manager.MembershipIndex(authLeaf)) + mix_lez_client.setRlnConfig(configAccountId, authLeaf.int) + return + warn "Chat-client membership confirmation timed out", + optimisticLeaf = optimisticLeafChat + )() + asyncSpawn taskKeepAlive(client) if client.cfg.mixEnabled and not client.node.wakuMix.isNil: diff --git a/vendor/nwaku b/vendor/nwaku index 33d9d89..ce6f5c2 160000 --- a/vendor/nwaku +++ b/vendor/nwaku @@ -1 +1 @@ -Subproject commit 33d9d89e00be774d33ceafd3d59755d5a1089bb4 +Subproject commit ce6f5c2d5e184b7c94a65068d4e5f29936259020