Skip to main content

Rollup circuit

Circuit Description

The rollup circuit aggregates proofs from a defined set of ‘inner’ circuits.

Each inner circuit has 16 public inputs. The rollup circuit will execute several defined subroutines on the public inputs.

Notation

We use the following definitions in this spec:

  • nB:n_B: NUM_BRIDGE_CALLS_PER_BLOCK
  • nA:n_A: NUM_ASSETS
  • nP:n_P: NUM_FIELDS (number of inner-circuit public inputs propagated by rollup circuit)
  • R:R: rollup size (i.e. number of transaction proofs in a single rollup)

Public Inputs: Detail

There are 28+2(nB+nA)+nPR28 + 2(n_B + n_A) + n_P \cdot R public inputs, in three sections:

  1. Rollup Proof Data: 12+2(nB+nA)12 + 2(n_B + n_A) elements from Fp\mathbb{F}_p that define the rollup block information (described below)
  2. Rolled-Up Transactions Data: Inner-circuit public inputs (a total of nP×Rn_P \times R inputs; nP=8n_P = 8 inputs per rolled up transaction)1
  3. Recursive Proof Data: 44 elements from Fq\mathbb{F}_q, represented as 1616 elements from Fp\mathbb{F}_p, whose values are <268<2^{68}; see here for explanation.

All are field elements. The first (12+2nB+2nA)(12 + 2n_B + 2n_A) public inputs are the following:

  1. rollup_id
  2. rollup_size
  3. data_start_index
  4. old_data_root
  5. new_data_root
  6. old_null_root
  7. new_null_root
  8. old_data_roots_root
  9. new_data_roots_root
  10. old_defi_root = 0
  11. new_defi_root
  12. defi_bridge_call_datas (size: nBn_B)
  13. defi_bridge_deposits (size: nBn_B)
  14. asset_ids (size: nAn_A)
  15. total_tx_fees (size: nAn_A)
  16. public_inputs_hash

The public_inputs_hash value is a SHA256 hash of the set of all join-split public inputs that will be broadcasted on-chain. These are:

  1. proof_id
  2. output_note_commitment_1
  3. output_note_commitment_2
  4. nullifier_1
  5. nullifier_2
  6. public_value
  7. public_owner
  8. public_asset_id

Private Inputs: Detail

The following inputs are private to reduce proof size:

  1. The recursive proof output of each inner proof (4 Fq\mathbb{F}_q elements represented as 16 Fp\mathbb{F}_p elements, see above)
  2. The remaining public inputs of each inner-circuit proof (see footnote 1)
  3. old_data_path
  4. linked_commitment_paths
  5. linked_commitment_indices
  6. new_null_roots (except the latest one since that becomes a public input)
  7. old_null_paths
  8. data_roots_paths
  9. data_roots_indices

Index of Functions

  • Extract Extraction Function extracts the public inputs from an inner proof, and validates the result matches the rollup’s inner public inputs
  • Aggregate Proof Aggregation Function for ultimate batch verification outside the circuit, given a verification key and (optional, defined by 4th input parameter) a previous output of Aggregate. Returns a BN254 point pair
  • NonMembershipUpdate Nullifier Update Function checks a nullifier is not in a nullifier set given its root, then inserts the nullifier and validates the correctness of the associated merkle root update
  • BatchUpdate Batch Update Function inserts a set of compressed note commitments into the note tree and validates the corretness of the associated merkle root update Update - inserts a single leaf into the root tree and validates the correctness of the associated merkle root update
  • ProcessDefiDeposit Processes Defi Deposit ensures that if a given inner proof is a defi deposit proof, it has a valid bridge call data that matches one of the input bridge call data to the rollup. Further, it also adds the defi_interaction_nonce in the encrypted claim note of a defi deposit proof.
  • ProcessClaim Process Claims checks if the claim proof is using the correct defi root.

Circuit Logic (Pseudocode)

  1. Let Q_0 = [0, 0]

  2. Validate num_inputs == N

  3. Let previous_note_commitment_1 = 0; previous_note_commitment_2 = 0; previous_allow_chain = 0;

  4. For i = 1, ..., num_inputs

    1. Let pub_inputs = Extract(PI_i)

    2. Let vk = vks[proof_id_i]

    3. Let Q_i = Aggregate(PI_i, pub_inputs, vk, Q_{i-1}, (i > 1))

    4. Let leaf2i\text{leaf}_{2i} = output_note_commitment_1_i

    5. Let leaf2i+1\text{leaf}_{2i+1} = output_note_commitment_2_i

    6. Validate NonMembershipUpdate(null root2i\text{null root}_{2i}, nullroot2i+1\text{nullroot}_{2i+1}, nullifier_1_i)

    7. Validate NonMembershipUpdate(null root2i+1\text{null root}_{2i + 1}, null root2i+2\text{null root}_{2i+2}, nullifier_2_i)

    8. Validate Membership(old_data_roots_root, data_roots_indices[i], data_roots_pths[i], data_tree_root_i)

    9. If pub_inputs.PROOF_ID = DEFI_DEPOSIT then ProcessDefiDeposit:

      • Check pub_inputs.ASSET_ID matches only one (say kth) bridge call data in bridge_call_datas
      • Update defi_bridge_deposits[k] += pub_inputs.PUBLIC_OUTPUT
      • Update encrypted_claim_note += (defi_interaction_nonce * rollup_id + k) * G_j, k ⋹ 0, 1, 2, 3
    10. Validate ProcessClaim(pub_inputs, new_defi_root)

    11. Let chaining = propagated_input_index != 0

    12. Let propagating_previous_output_1 = backward_link == previous_note_commitment_1

    13. Let propagating_previous_output_2 = backward_link == previous_note_commitment_2

    14. Let previous_tx_linked = propagating_previous_output_1 || propagating_previous_output_2

    15. Let start_of_subchain = chaining && !previous_tx_linked

    16. Let middle_of_chain = chaining && previous_tx_linked

    17. If start_of_subchain then:

      • Validate Membership(old_data_root, linked_commitment_indices[i], linked_commitment_paths[i], backward_link)
    18. Let

       propagating_previous_output_index =
    propagating_previous_output_1 ? 1 :
    propagating_previous_output_2 ? 2 : 0
    1. If middle_of_chain then:
      • require(previous_allow_chain == propagating_previous_output_index, "not permitted to propagate this note")
      • Set the inner proof value corresponding to the commitment being propagated to 0.
      • Set the inner proof value corresponding to the nullifier of the commitment being propagated to 0.
  5. Validate [P1, P2] = Q_{num_inputs}

  6. Validate BatchUpdate(old_data_root, new_data_root, data_start_index, leaf_1, ..., leaf_{2 * num_inputs})

  7. Validate old_null_root = null_root_1

  8. Validate new_null_root = null_root_{2 * num_inputs + 1}


  1. A transaction proof (i.e. inner proof) contains a total of 16 public inputs but the rollup circuit propagates only 8 of them as its public inputs. Those public inputs of the inner proof marked as ✅ are propagated: ✅ proof_idoutput_note_1_commitmentoutput_note_2_commitmentinput_note_1_nullifierinput_note_2_nullifierpublic_valuepublic_ownerpublic_asset_idmerkle_roottx_feeasset_idbridge_call_datadefi_deposit_valuedefi_rootbackward_linkallow_chain