System overview
transfer_id. The canonical runtime never stores the
sender’s wallet address and the authority linkage in the same record. The operator holds the account
registry separately; it is accessible only under legal process.
List 1 / List 2 separation
The anonymity guarantee rests on a single structural property: no common key exists between List 1 and List 2.transfer_id is the hash of a random nonce. nullifier is the hash of the wallet
address concatenated with the transfer ID. These two values share no derivation path.
Joining them requires knowing the sender’s wallet address — which never leaves their device.
This is the same mechanism that separates public ballot direction from identity
in the voting and governance embodiments. The invariant is use-case-agnostic.
Nullifier: double-spend prevention
The nullifierH(wallet_address, transfer_id) serves the same function as the ZK
nullifier in the voting protocol: it prevents the same input from being used twice,
without revealing which account submitted a given transfer.
person_secret).
In the current shyware scaffold, the nullifier is a plain hash — the wallet address is
known to the sender, and the canonical runtime verifies only that the nullifier has not been used.
The production circuit will ZK-prove the nullifier derivation to hide the wallet address
from the canonical runtime entirely.
Value conservation
In addition to the two-list invariant, transfers must conserve value:state.ExecuteTransfer before every transfer is committed.
A transfer where sender.Balance < Amount is rejected with ErrorInsufficientBalance.
Current scaffold (plaintext amounts):
Amount and Balance are stored as int64. The canonical runtime reads balances to verify
conservation, meaning the runtime can observe individual account balances. This is the
TODO(circuit) state — acceptable for a licensed enterprise deployment where the
operator controls the runtime, not for a permissionless public deployment.
Production circuit (Pedersen commitments):
Amount becomes an AmountCommitment (Pedersen commitment over BN254). The ZK proof
proves Σ inputs == Σ outputs and sender_balance >= amount using range proofs, without
revealing either value to the canonical runtime. This is established cryptography (Confidential
Transactions, Monero RingCT) — an engineering addition to the existing circuit infrastructure.
Count-match invariant
At every committed block:state.ExecuteTransfer. A missing or extra record in either
list causes the transaction to be rejected. It is the transfer-domain equivalent of the
voting protocol’s count-match: len(voteDirections) == len(voterRegistry).
Supply invariant
For every asset:TxTypeMint increases TotalMinted and credits
the recipient account. Every TxTypeBurn increases TotalBurned and debits the source
account. The supply total is publicly queryable; individual balances are not.
Account commitments
Accounts are identified on-chain byaccount_commitment = H(wallet_address). The wallet
address itself is never stored on-chain. The commitment is a one-way function: the ABCI
node can store and look up balances by commitment without knowing the underlying address.
A TxTypeRegisterAccount transaction establishes the commitment on-chain, authenticated
by a WalletProof — an ECDSA signature proving ownership of the wallet address. After
registration, the wallet address is only needed locally to compute nullifiers.
Consensus layer
The current reference implementation uses CometBFT-style BFT consensus, aligned with the voting deployments. A 4-validator deployment tolerates 1 Byzantine fault. The canonical application implements:CheckTx— stateless validation (type, signature, required fields)FinalizeBlock— stateful execution (balance check, nullifier dedup, List 1/2 writes, count-match)Query— supply, transfer count, account balance (commitment → balance only)
What’s deferred (TODO circuit)
The following are markedTODO(circuit) in the source and represent the gap between the
current scaffold and a fully private permissionless deployment:
| Field | Current | Production |
|---|---|---|
TransferRecord.Amount | int64 plaintext | Pedersen commitment |
AccountRecord.Balance | int64 plaintext | Pedersen commitment |
TransferData.SenderProof | unused | Groth16 range proof |
| Nullifier derivation | plain H(wallet, tx_id) | ZK-proven (hide wallet from node) |