Submitted proof set
Each initiation, acceptance, or rejection submits exactly two proofs:
The pool constructs both sets of public inputs. It supplies the same payment
effect hash, recipient VASP binding commitment, and payload commitment to both
proofs. Shielding and unshielding each use a single proof without a policy wrapper.
Payment identifiers
A shielded payment uses three derived values: nullifiers, input lock IDs, and the payment effect hash. The sender circuit derives the first two from the sender’s nullifier keynk and each input note’s leafIndex in the commitment
tree. This shared derivation binds both identifiers to the same input notes.
A nullifier permanently identifies a spent note:
Accept resolution.
An input lock ID prevents concurrent reservations of the same note:
Accept, unshield, or bridge withdrawal.
The payment effect hash commits the payment outcome to one value:
nk, and the output commitments contain
fresh secrets, so observers cannot reconstruct the payment effect from its
hash. The sender proof binds the sealed nullifiers to the input notes and
balances their value against the recipient note and optional change.
Onchain use
Payment request
The recipient provides a payment request signed by its VASP’s messaging key. The current request is version 2, with a fixed 433-byte binary encoding:
The sender checks the signature against the VASP’s admitted messaging key and
checks expiry offchain. Neither the pool nor the sender circuit verifies this
request signature or expiry. The request supplies the recipient address and
VASP identifier used to build the payment. There is no address-issuance proof
or onchain customer designation.
The sender circuit derives the recipient VASP binding commitment from the
request’s
fi_id and payment effect hash. At resolution, the recipient proof
and policy wrapper must reproduce that binding. A request naming the wrong
VASP can produce an unresolvable hold; expiry then permits reclaim. Record
thresholds guide payload preparation, while the VASP’s policy determines
whether that payload permits the payment.
Initiation
The sender proof establishes this statement:
I know the openings of one or two notes in the commitment tree and authorize
this payment with their spending key. The inputs share one asset and fund the
recipient note plus any change. My affilID is absent from the sanctioned
set. The payment effect, recipient VASP binding, and payload commitment match
the values fixed for this initiation.
Ownership and value
The proof witnesses each input note opening and its Merkle path. From(ak, nk), it derives the sender’s ivk and checks each note’s
address. A Schnorr signature under ak covers all eleven initiation public
inputs, including the payment effect hash, input lock IDs, nonce, deadline,
payload commitment, and sanctions root.
Input amounts equal the recipient amount plus change. Each amount is
range-checked to 128 bits and the recipient amount must be nonzero. Any change
address must derive from the sender’s ivk. Wallets should choose a fresh
change diversifier; the circuit enforces ownership but does not check prior
use. Both output notes inherit the minimum shieldTimestamp of the real inputs.
The pool checks input lock availability separately. The sender proof derives
the input lock IDs and nullifiers from the same (nk, leafIndex) pairs, so
the reserved notes are the notes acceptance spends.
Payload commitment
The witness includes the payload salt seed, optional Travel Rule record, and payment identifiers. The circuit recomputes the salted payload commitment, including a payment leaf derived from the public payment effect hash, recipient VASP binding commitment, nonce, and deadline. This binds the payload to the hold without publishing its plaintext. The main circuit does not verify an X.509 credential, an external screening verdict, or a standard beneficiary-alignment proof. Its sanctions check proves non-membership of the sender’saffilID. Additional requirements
belong in the VASP’s private policy and offchain checks; the payload’s evidence
leaf is reserved as zero.
Witness
Public inputs
The circuit receives these eleven field elements in order:
The registration root is an input to the accompanying policy wrapper, not the
main initiation circuit. After verifying both proofs, the pool records the
input locks and creates the hold.
Policy wrapper
The same wrapper circuit serves initiation, acceptance, and rejection. It witnesses the VASP’s registry leaf, messaging-key signature, private policy verification key, and policy proof. It checks that the witnessed key’s hash matches the leaf and verifies the policy proof recursively. It also checks a messaging-key signature over the chain, pool, payment effect hash, phase, and payload commitment. The wrapper has eight public inputs in order:
The inner policy exposes five values to the wrapper: payment effect hash,
phase, payload commitment, commitment root, and registry root. The wrapper
requires them to match its own public inputs. The private policy is responsible
for binding its own witness and checks to that context.
At initiation, the sender VASP signs the context. The initiation proof does
not bind the policy signer to the sender’s VASP. At resolution, the wrapper
binds the signing VASP to the hold’s recipient VASP. Customer affiliation is
maintained offchain. The registry contains VASPs, policy verification-key
hashes, and messaging public keys.
Encrypted payment envelope
Payment data travels in two encrypted parts with different recipients:
The memo is 666 bytes; the institutional payload is 2,230 bytes. Both use
ChaCha20-Poly1305 with the cleartext header authenticated as associated data.
Each encryption draws a fresh ephemeral secret, producing a one-time key.
Delivery and retrieval are the integrator’s responsibility.
Encryption
For the note memo, Grumpkin Diffie-Hellman uses the recipient address’s base point:epk = esk · G and
shared = esk · mpk = msk · epk, where mpk and msk are the recipient
VASP’s messaging public and secret keys. A separate domain tag derives its
symmetric key. Possessing ivk alone does not reveal the institutional record.
The recipient joins both plaintexts, recomputes the payload commitment, compares
it with the hold, and verifies the sender VASP’s payload signature against its
messaging key in the pool’s institution registry. These intake checks run
offchain. The sender’s payload signature is separate from the context signature
verified inside each policy wrapper.
Resolution
The recipient proof establishes this statement for either outcome:I know the recipient note and payment effect sealed by this hold. The note’s address derives from myivk, whoseaffilIDis absent from the sanctioned set. The recipient VASP identifier reproduces the hold’s binding. Acceptance discloses the sealed effect; rejection discloses an empty effect.
The eleven public inputs, in order, are:
The pool’s
acceptPayment and rejectPayment calls require both the recipient
proof and recipient VASP’s policy-wrapper proof. Acceptance records the
nullifiers, appends the recipient note and any change, and retains input locks.
Rejection releases locks without spending notes or appending commitments.
Both require an active hold at or before its deadline.
Rejection still requires the sealed payment effect and an unsanctioned
recipient. If the recipient cannot produce those witnesses, it cannot reject
the hold. Once the block timestamp exceeds the deadline, anyone can call
reclaimExpired to release the sender’s input locks without proofs.