Some checks are pending
ci / gate (push) Has started running
The spent check for wormhole deposits looked each nullifier up in Wormhole::UsedNullifiers by key. That map is Blake2_128Concat, so the key is blake2_128(n) ‖ n and every request handed the node the nullifier itself. An exit publishes its nullifier on chain, so whoever runs the node could match a later exit to the connection that asked, and that connection also asked for the wallet's ordinary balances. The accounts page ran it automatically for every open wallet with a phrase, so 0.1.0 and 0.2.0 sent unspent nullifiers to our mainnet endpoint. spent_among now does what quantus/extension does: it reads whole buckets of the map (keys sharing the first byte of blake2_128(n)) with state_getKeysPaged, the buckets it needs padded with random others to at least 16 and asked in shuffled order, and decides membership in Rust. The node learns which buckets were read, never which entry mattered. The per- key lookup is gone. Tests pin the rule: the plan covers ours, pads to 16 without repeats and does not lead with ours; no request carries a nullifier and each is the map's root plus one byte. Against mainnet, a real used nullifier read from a bucket hashes to that bucket and the bucket read reports it spent and a random one unspent. doc/threat-model.md gains the wormhole section: nullifiers and the wallet's leaves are never named to a node, and spending (#46, #47) must not request the Merkle path for the spent leaf alone. Closes #68 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014ftBXYuba8ARhQeF74oUgW