Skip to main content
This page defines the proof boundaries for a MIP410 shielded payment. See Shielded Payments for the payment lifecycle and Proof and Verification for proof composition.

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 key nk 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:
Once the pool records a nullifier in the spent set, the note cannot be spent again. A payment’s nullifiers remain private until an Accept resolution. An input lock ID prevents concurrent reservations of the same note:
Two holds derived from the same input produce the same input lock ID and collide at initiation. The separate domain tag prevents observers from deriving the note’s nullifier from its input lock ID. Rejection or reclaim releases the lock. Acceptance retains it permanently, preventing a spent note from being reserved again while also recording its nullifier in the spent set. After rejection or reclaim, the note’s next hold, unshield, or bridge withdrawal presents the same input lock ID and links back to the failed attempt. The payment memo also carries the sender’s input nullifiers inside the payment effect. A recipient VASP that opens it can recognize the spend that eventually consumes those notes, even after rejecting the payment or letting it expire and be reclaimed. The nullifiers remain unchanged and become public at a later Accept, unshield, or bridge withdrawal. The payment effect hash commits the payment outcome to one value:
There are exactly two input slots. A payment spends one or two real notes; when there is only one, the second slot’s nullifier and input lock ID are zero. A zero change note commitment means no change note. The payment effect hash is a public input of initiation and serves as the hold ID. The nullifiers require the sender’s 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’s affilID. 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:
A domain-separated Poseidon2 derivation turns the shared point and ephemeral public key into the symmetric key. A one-byte view tag helps scanners discard unrelated memos before decryption. A VASP with delegated viewing access can open the memo without the customer’s spending key. The institutional payload instead uses 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 my ivk, whose affilID is 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.