A wormhole address's System.Account balance is a receipt total. An exit
credits the exit account and records a nullifier, and nothing debits the
address. So what a wallet account can still spend is worked out deposit by
deposit, in pri(wallets.wormholeBalance):
- Transfer counts come from the chain's Wormhole.TransferCount, read from the
balance node.
- Deposits come from a blackbeard observer
(/v1/chains/{chain}/wormhole/deposits, blackbeard/observer#22), matched by
genesis, never by name. Anything the chain counts that the observer didn't
return is reported as missing, never folded into a smaller balance.
- Spent status comes from Wormhole.UsedNullifiers, read in whole buckets by
the first byte of blake2_128(nullifier), at least 16 buckets with random
decoys, in a shuffled order. A lookup by key would hand the node the
nullifier, and exits publish theirs.
- A deposit whose nullifier was never computed is reported as unchecked.
Nullifiers are precomputed while the password is available and stored per
wallet account: 128 transfer counts for each of the 40 addresses when a phrase
wallet or account is made. pri(wallets.wormholeUnlock) extends only the
addresses whose deposits outgrew what is stored, to their count plus 128.
Forgetting a wallet removes its nullifiers for every account index along with
the secret and the pairs. They're kept under their own prefix, and the
manifests ask for unlimitedStorage, since a busy mining address alone runs to
megabytes.
@quantus/crypto 0.3.0 provides wormholeNullifiers, pinned there to the chain's
circuit crate.
Specs: a wallet stores a full window whose bytes match a direct derivation; a
raw seed stores none; unlock extends only the addresses past their window and
keeps what was there; forget removes every account's nullifiers.
Live against mainnet and the deployed observer:
- a spent nullifier taken from a public ProofVerified event is found in its
bucket (240 entries, 158 ms);
- for a mining address the chain counts 41,849 transfers, the observer
returned 24,702, and 17,147 are reported missing. That is blackbeard/
observer#23's gap, now being rescanned, surfacing as intended.
Part of quantus/extension#14.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f