SwapClaim Descriptions

Each swap claim contains a SwapClaimBody and a zk-SNARK swap claim proof.

Invariants

The invariants that the SwapClaim upholds are described below.

Local Invariants

  1. You cannot mint swap outputs to a different address than what was specified during the initial swap.

  2. You cannot mint swap outputs to different assets than those specified in the original swap.

  3. You cannot mint swap outputs to different amounts than those specified by the batch swap output data.

    3.1. You can only claim your contribution to the batch swap outputs.

    3.2. You can only claim the outputs using the batch swap output data for the block in which the swap was executed.

  4. You can only claim swap outputs for swaps that occurred.

  5. You can only claim swap outputs once.

    5.1. Each positioned swap has exactly one valid nullifier for it.

    5.2. No two swaps can have the same nullifier.

  6. The SwapClaim does not reveal the amounts or asset types of the swap output notes, nor does it reveal the identity of the claimant.

  7. The balance contribution of the value of the swap output notes is private.

Local Justification

  1. The Swap commits to the claim address. The SwapClaim circuit enforces that the same address used to derive the swap commitment is that used to derive the note commitments for the two output notes via the Swap Commitment Integrity and Output Note Commitment Integrity checks.

  2. The SwapClaim circuit checks that the trading pair on the original Swap is the same as that on the batch swap output data via the Trading Pair Consistency Check.

  3. You cannot mint outputs to different amounts via:

    3.1. The output amounts of each note is checked in-circuit to be only due to that user’s contribution of the batch swap output data via the Output Amounts Integrity check.

    3.2. The circuit checks the block height of the swap matches that of the batch swap output data via the Height Consistency Check. The ActionHandler for the SwapClaim checks that the batch swap output data provided by the SwapClaim matches that saved on-chain for that output height and trading pair.

  4. The SwapClaim circuit verifies that there is a valid Merkle authentication path to the swap in the global state commitment tree.

  5. You can only claim swap outputs once via:

    5.1. A swap’s transmission key binds to the nullifier key as described in the Nullifier Key Linking section, and all components of a positioned swap, along with this key, are hashed to derive the nullifier, in circuit as described below in the Nullifier Integrity section.

    5.2. In the ActionHandler for check_stateful we check that the nullifier is unspent.

  6. The revealed SwapClaim on the nullifier does not reveal the swap commitment, since the Nullifier Integrity check is done in zero-knowledge. The amount and asset type of each output note is hidden via the hiding property of the note commitments, which the claimer demonstrates an opening of via the Output Note Commitment Integrity check.

  7. The balance contribution of the two output notes is zero. The only contribution to the balance is the pre-paid SwapClaim fee.

Global Justification

1.1. This action mints the swap’s output notes, and is reflected in the balance by adding the value from the transaction value balance. Value is not created due to system level invariant 1, which ensures that transactions contribute a 0 value balance.

SwapClaim zk-SNARK Statements

The swap claim proof demonstrates the properties enumerated below for the private witnesses known by the prover:

  • Swap plaintext corresponding to the swap being claimed. This consists of:
    • Trading pair, which consists of two asset IDs
    • Fee value which consists of an amount interpreted as an constrained to fit in 128 bits and an asset ID
    • Input amount of the first asset interpreted as an constrained to fit in 128 bits
    • Input amount of the second asset interpreted as an constrained to fit in 128 bits
    • Rseed, interpreted as an
    • Diversified basepoint corresponding to the claim address
    • Transmission key corresponding to the claim address
    • Clue key corresponding to the claim address
  • Swap commitment
  • Merkle proof of inclusion for the swap commitment, consisting of a position pos constrained to fit in 48 bits and an authentication path consisting of 72 elements (3 siblings each per 24 levels)
  • Nullifier deriving key
  • Spend verification key
  • Output amount of the first asset interpreted as an constrained to fit in 128 bits
  • Output amount of the second asset interpreted as an constrained to fit in 128 bits
  • Note blinding factor used to blind the first output note commitment
  • Note blinding factor used to blind the second output note commitment

And the corresponding public inputs:

  • Merkle anchor of the state commitment tree
  • Nullifier corresponding to the swap
  • Fee to claim the outputs which consists of an amount interpreted as an constrained to fit in 128 bits and an asset ID
  • The batch swap output data, which consists of:
    • trading pair, which consists of two asset IDs
    • 128-bit fixed point values (represented in circuit as four 64-bit (Boolean constraint) limbs) for the batched inputs , outputs , and the unfilled quantities
    • block height constrained to fit in 64 bits
    • starting height of the epoch constrained to fit in 64 bits
  • Note commitment of the first output note
  • Note commitment of the second output note

Swap Commitment Integrity

The zk-SNARK certifies that the witnessed swap commitment was derived as:

.

using the above witnessed values and where ds is a constant domain separator:

ds = from_le_bytes(BLAKE2b-512(b"penumbra.swap")) mod q

Merkle auth path verification

The zk-SNARK certifies that the witnessed Merkle authentication path is a valid Merkle path of the swap commitment to the provided public anchor.

Nullifier Integrity

The zk-SNARK certifies that the nullifier was derived as:

using the witnessed values above and where ds is a constant domain separator:

ds = from_le_bytes(BLAKE2b-512(b"penumbra.nullifier")) mod q

as described in Nullifiers.

Nullifier Key Linking

The zk-SNARK certifies that the diversified address associated with the swap being claimed was derived as:

where is the witnessed diversified basepoint and is the incoming viewing key computed using a rate-2 Poseidon hash from the witnessed and as:

ivk = hash_2(from_le_bytes(b"penumbra.derive.ivk"), nk, decaf377_s(ak)) mod r

The zk-SNARK also certifies that:

Fee Consistency Check

The zk-SNARK certifies that the public claim fee is equal to the value witnessed as part of the swap plaintext.

Height Consistency Check

The zk-SNARK certifies that the swap commitment’s height is equal to the height of the batch swap output data (the clearing price height).

We compute the intra-epoch block height from the position of the swap commitment and check the following identity:

where are provided on the batch swap output data as a public input.

Trading Pair Consistency Check

The zk-SNARK certifies that the trading pair included in the swap plaintext corresponds to the trading pair included on the batch swap output data, i.e.:

Output Amounts Integrity

The zk-SNARK certifies that the claimed output amounts were computed correctly following the pro-rata output calculation performed using the correct batch swap output data.

Output Note Commitment Integrity

The zk-SNARK certifies that the note commitments and were derived as:

using the above witnessed values and where ds is a constant domain separator:

ds = from_le_bytes(BLAKE2b-512(b"penumbra.notecommit")) mod q