Addresses the following review comments:
- "Isn't two_mask_3_claims_under_same_seed_are_rejected already checking
that there's a mechanism protecting against this exploit scenario?"
The doc block at nssa/src/state.rs:2488-2504 mixes three paragraphs,
one about reuse, one TODO about wallet side input, one exploit pin,
all attached to two_mask_3_claims_under_same_seed_are_rejected. The
reuse test below it had no doc at all. I split as follows: the
exploit-pin paragraph stays on two_mask_3_claims_..., the reuse
paragraph moves to a fresh docstring on
mask_3_reuse_across_txs_currently_unsupported.
- "I don't understand this. I think this should fail because ... the
input pre_state which is marked with is_authorized=true will make
things fail."
The reuse test's new docstring cites the actual reject site, the
post-loop private_pda_bound_positions assertion in
privacy_preserving_circuit.rs:185-192. At top level the Entry::Vacant
arm accepts is_authorized=true unconditionally, the rejection comes
from the bound-positions check firing because noop emits no Claim::Pda
and there is no caller ChainedCall.pda_seeds.
- "let's dont have this TODO as part of the doc"
The block is moved out into regular // comments immediately above
mask_3_reuse_across_txs_currently_unsupported.
- "let's not add implementation details to docs"
In caller_pda_seeds_authorize_mask_3_private_pda_for_callee's
docstring, I dropped the parenthetical "(Occupied branch)" and the
trailing sentence about which validate_and_sync_states code path gets
exercised.
- "what does \`Claim::Pda(seed)\` / \`pda_seeds\` mean?"
I rewrote the pda_family_binding docstring at
privacy_preserving_circuit.rs:33-39: replaced the ambiguous
"Claim::PrivatePda and ChainedCall's private seeds into plain
Claim::Pda(seed) / pda_seeds" phrase with "a Claim::Pda(seed) in a
program's post_state or a caller's ChainedCall.pda_seeds entry".
- Suggestion on nssa/src/validated_state_diff.rs:226 rewriting
"The public-execution path only sees mask-0 accounts" to
"The public-execution path only sees public accounts".
Applied: "The public-execution path only sees public accounts".
- Clarification requested on the private_pda_bound_positions field:
I expanded the docstring at privacy_preserving_circuit.rs:26-31 to
state that binding is an idempotent property, not an event, and to
enumerate the two proof paths that populate it (a Claim::Pda on a
mask-3 pre_state, or a caller's pda_seeds matching under the private
derivation).
Introduce the ATA program, which derives deterministic per-token holding
accounts from (owner, token_definition) via SHA256, eliminating the need
to manually create and track holding account IDs.
Program (programs/associated_token_account/):
- Create, Transfer, and Burn instructions with PDA-based authorization
- Deterministic address derivation: SHA256(owner || definition) → seed → AccountId
- Idempotent Create (no-op if ATA already exists)
Wallet CLI (`wallet ata`):
- `address` — derive ATA address locally (no network call)
- `create` — initialize an ATA on-chain
- `send` — transfer tokens from owner's ATA to a recipient
- `burn` — burn tokens from owner's ATA
- `list` — query ATAs across multiple token definitions
Usage:
wallet deploy-program artifacts/program_methods/associated_token_account.bin
wallet ata address --owner <ID> --token-definition <DEF_ID>
wallet ata create --owner Public/<ID> --token-definition <DEF_ID>
wallet ata send --from Public/<ID> --token-definition <DEF_ID> --to <RECIPIENT> --amount 100
wallet ata burn --holder Public/<ID> --token-definition <DEF_ID> --amount 50
wallet ata list --owner <ID> --token-definition <DEF1> <DEF2>
Includes tutorial: docs/LEZ testnet v0.1 tutorials/associated-token-accounts.md