Skip to main content

Redemption Circuit

The redemption circuit is the primary ZK circuit in Ghost Protocol. It proves that the prover knows the preimage of a commitment in the Merkle tree and correctly derives the nullifier, all without revealing which commitment is being redeemed.

Circuit definition

template Redemption(levels) {
// Public inputs (8)
signal input root;
signal input nullifier;
signal input withdrawAmount;
signal input recipient;
signal input changeCommitment;
signal input tokenId;
signal input policyId;
signal input policyParamsHash;

// Private inputs
signal input secret;
signal input nullifierSecret;
signal input amount;
signal input blinding;
signal input pathElements[levels];
signal input pathIndices[levels];
signal input newBlinding;
}

The circuit is instantiated with levels = 20 (Merkle tree depth).

What it proves

  1. Commitment preimage — The prover knows (secret, nullifierSecret, tokenId, amount, blinding) such that:

    commitment = Poseidon5(secret, nullifierSecret, tokenId, amount, blinding)
  2. Merkle membership — The commitment exists at some leaf position in the tree with the given root:

    MerkleProof(commitment, pathElements, pathIndices) == root
  3. Nullifier derivation — The nullifier is correctly computed:

    nullifier == Poseidon2(nullifierSecret, leafIndex)

    where leafIndex is derived from pathIndices.

  4. Amount conservation — The committed amount equals the sum of withdraw and change:

    amount == withdrawAmount + changeAmount

    where changeAmount is embedded in changeCommitment via newBlinding.

  5. Policy binding — If policyId != 0, verifies that the commitment includes the policy:

    commitment == Poseidon7(secret, nullifierSecret, tokenId, amount, blinding, policyId, policyParamsHash)

Partial reveals

The circuit supports partial reveals. If you committed 10 GHOST, you can:

  • Withdraw 3 GHOST (withdrawAmount = 3)
  • Create a change commitment for 7 GHOST (changeCommitment = Poseidon5(newSecret, newNullifierSecret, tokenId, 7, newBlinding))

The change commitment is inserted into the tree and can be revealed later.

For a full reveal (no change), set changeCommitment = 0 and withdrawAmount = amount.

Gas cost

On-chain verification costs approximately 200,000 gas via the ecPairing precompile at address 0x08.