On-Chain Components
Contracts and circuits that produce encrypted compliance payloads for anonymity revoking.
Overview
The anonymity revoking system is built around a simple idea: every on-chain operation that hides a piece of information must also attach that information encrypted under a compliance key, so that it can be decrypted later if required by law enforcement.
The holder of the compliance key — the anonymity revoker — is treated as a black box in this section. In practice, it is not a single entity but a Guardian Committee that holds a threshold-shared decryption key and follows a structured revoking process to handle decryption requests. From the perspective of contracts and circuits, all that matters is that a public key AR_PK exists on-chain and users encrypt against it.
For the Concealed Balances system, the modification is straightforward: each operation that hides an amount must include an additional ElGamal ciphertext encrypting that amount under AR_PK, and the circuit must prove this ciphertext is correctly formed. The Shielded Layer will require a more involved approach — the compliance payload is larger (it must include enough information to link transactions forward and backward for follow-up requests), but this is out of scope for the current document.
Anonymity Revoking Key
The contract stores the anonymity revoking public key (AR_PK in the Decryption Protocol):
Point public arPK;
This is a Grumpkin point. It is set during contract deployment (or via an admin function setArPK) and corresponds to the key held by the Guardian Committee. Key rotation is described in Decryption Protocol: Key Rotation.
Compliance Ciphertext
The compliance ciphertext for a given operation is an ElGamal encryption of the hidden amount under arPK:
c_ar = Enc(amount, arPK, r_ar) = (r_ar · G, amount · G + r_ar · arPK)
Where r_ar is a fresh random scalar chosen by the user. This ciphertext is stored on-chain alongside the transaction data, so that the anonymity revoker can later retrieve and decrypt it.
The compliance ciphertext uses the same exponential ElGamal scheme as user balances (it could use an arbitrary serialization scheme instead though because homomorphic addition is not required). Decryption yields amount · G, from which the plaintext amount is recovered via the discrete-log solver. Since compliance amounts are bounded to u64, this is always feasible.
Modified Operations
In the base Concealed Balances system, only transfer hides the transaction amount — it must be extended to include a compliance ciphertext. The conceal and reveal operations both have the amount visible on-chain (as a public ERC-20 burn and mint respectively), so no compliance ciphertext is needed for either.
Transfer
In the base system, the transfer circuit proves correct re-encryption of the sender's balance and encryption of the transfer amount under the recipient's key. With anonymity revoking, the circuit gains additional public inputs and constraints.
New public inputs:
arPK— the anonymity revoking public key (read from contract storage)c_ar— the compliance ciphertext(R_ar, C_ar), an encryption ofamountunderarPK
New private inputs (witness):
r_ar— the randomness used in the compliance ciphertext
New circuit constraints:
R_ar == r_ar · GC_ar == amount · G + r_ar · arPK
These constraints prove that c_ar is a correctly formed encryption of the same amount that appears in the rest of the transfer statement. The contract stores c_ar alongside the transaction.
Note on r_ar
The circuit does not constrain how r_ar is chosen — the user is free to pick any scalar. Choosing r_ar with full entropy is in the user's own interest: a predictable or low-entropy r_ar could allow an adversary to decrypt the compliance ciphertext independently. What the circuit does guarantee is that the user knows r_ar and has used it to form the ciphertext honestly, which prevents malleability attacks where a ciphertext encrypting a different value is substituted.
Contract Changes
For the transfer operation, the contract:
- Reads
arPKfrom storage. - Passes
arPKand the user-suppliedc_aras public inputs to the proof verifier. - Verifies the proof (which now includes the compliance encryption constraints).
- Emits
c_arin theConcealedTransferevent so it can be indexed and retrieved by the anonymity revoker.
The on-chain gas cost increase is modest: the contract emits two additional Grumpkin points (4 field elements) per transfer, and the verifier checks a slightly larger proof.
Future: Shielded Layer
In the Shielded Layer, the transaction graph is hidden — both sender and recipient are anonymous. The compliance payload must therefore include more than just the amount: it must contain enough information to allow the anonymity revoker to link transactions forward and backward through the UTXO pool when processing follow-up requests.
In the Concealed Balances system, the hidden payload is short — a single u64 amount — and fits naturally in one ElGamal ciphertext. In the Shielded Layer, the payload is much larger. Rather than encrypting everything with asymmetric encryption (ElGamal in this case), we use the standard approach of generating an ephemeral shared secret via Diffie-Hellman and using it as a key for SNARK-friendly symmetric encryption. For decryption, it is then sufficient to reveal the ephemeral key (see Decryption Protocol). The exact structure of the shielded compliance payload and its circuit constraints will be specified when the Shielded Layer functionality is added.