The context tree
The context is THE object policies judge. It is a fixed-shape set of
Poseidon2 binary trees whose leaves are Fields:
shared_root = merkle_64(shared leaves) (63 hashes)
payload_root = H(CTX_PAYLOAD, payload_schema_id, merkle_128(payload leaves))
sender_private_root = merkle_64(sender_private leaves) (63 hashes)
payment_root = H(CTX_PAYMENT, schema_id, shared_root, sender_private_root)
context_root = H(CTX_PHASE, payment_root, merkle_16(phase leaves))
payload_root is itself one of the shared leaves. Four regions, chosen so
each party can recompute exactly what it knows:
| Region | Who knows the leaves | Contents |
|---|---|---|
shared (64 leaves) | sender, recipient, recipient FI, sender FI | recipient note fields, refund_commitment, payload_root, recipient_fi_index, deadline_bucket, nonce, reserved |
payload (128 leaves, nested) | same as shared | travel-rule record, identifiers, remittance, purpose code, evidence hashes, reserved |
sender_private (64 leaves) | sender, sender FI | change note fields, the two input notes and their leaf indices, nk commitment, refund note preimage (sender pk_d, rho_refund), reserved |
phase (16 leaves) | pool-welded, per transition | phase, time_bucket, registry_root, chain_id, pool_address, attestation_in, fi_index (the proving FI), counterparty_fi_index, reserved |
Rules:
- Schema is data.
schema_idandpayload_schema_idname JSON schema files (protocol/schema/context-v1.json,payload-v1.json) that assign each leaf index a name, a type (field | u128 | bool | address | bytes31), and a region. The circuits know only the tree shapes and region boundaries. Changing a tree shape is a protocol version. - Additive evolution. A schema bump may only assign previously reserved
slots; existing indices never move or change type. Absent fields are
0. A policy circuit witnessesschema_idand assertsschema_id >= COMPILED_SCHEMA, so a policy compiled against schemaNproves against any payment at schema≥ Nwithout rebuilding. This is what Goal 4 means. - Multi-limb values (names, addresses) occupy consecutive leaves; the schema declares limb counts.
- The initiate circuit computes
payment_rootfrom plaintext it holds and publishes it. The hold stores it. The resolve and reclaim circuits take it as a public and recompute it from the opened envelope plus the opaquesender_private_rootcarried in the envelope. - The wrapper computes
context_rootin circuit frompayment_rootand the phase leaves it receives as publics or witnesses, so the pool never hashes. - A policy at phase 1/2 structurally cannot read
sender_privateleaves: it only receivessender_private_rootas an opaque field. This is a type-level guarantee (§5.2). refund_commitmentis insharedso the recipient side can publish it on reject, but its preimage is insender_private, so the recipient side never learns the sender's address. The initiate circuit welds the two.
Opening mode: v1 always opens a knowable region in full (all leaves witnessed, root recomputed once). Path openings are a v2 optimization, pending the benchmark in §12.