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:
-
NUM_BRIDGE_CALLS_PER_BLOCK
-
NUM_ASSETS
-
NUM_FIELDS
(number of inner-circuit public inputs propagated by rollup circuit) - rollup size (i.e. number of transaction proofs in a single rollup)
Public Inputs: Detail
There are public inputs, in three sections:
- Rollup Proof Data: elements from that define the rollup block information (described below)
- Rolled-Up Transactions Data: Inner-circuit public inputs (a total of inputs; inputs per rolled up transaction)1
- Recursive Proof Data: elements from , represented as elements from , whose values are ; see here for explanation.
All are field elements. The first public inputs are the following:
rollup_id
rollup_size
data_start_index
old_data_root
new_data_root
old_null_root
new_null_root
old_data_roots_root
new_data_roots_root
old_defi_root = 0
new_defi_root
defi_bridge_call_datas
(size: )defi_bridge_deposits
(size: )asset_ids
(size: )total_tx_fees
(size: )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:
proof_id
output_note_commitment_1
output_note_commitment_2
nullifier_1
nullifier_2
public_value
public_owner
public_asset_id
Private Inputs: Detail
The following inputs are private to reduce proof size:
- The recursive proof output of each inner proof (4 elements represented as 16 elements, see above)
- The remaining public inputs of each inner-circuit proof (see footnote 1)
old_data_path
linked_commitment_paths
linked_commitment_indices
new_null_roots
(except the latest one since that becomes a public input)old_null_paths
data_roots_paths
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 inputsAggregate
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 pairNonMembershipUpdate
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 updateBatchUpdate
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 corretness of the associated merkle root updateProcessDefiDeposit
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 datas to the rollup. Further, it also adds thedefi_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)
Let
Q_0 = [0, 0]
Validate
num_inputs == N
Let
previous_note_commitment_1 = 0; previous_note_commitment_2 = 0; previous_allow_chain = 0;
For
i = 1, ..., num_inputs
Let
pub_inputs = Extract(PI_i)
Let
vk = vks[proof_id_i]
Let
Q_i = Aggregate(PI_i, pub_inputs, vk, Q_{i-1}, (i > 1))
Let =
output_note_commitment_1_i
Let =
output_note_commitment_2_i
Validate
NonMembershipUpdate(
, ,nullifier_1_i)
Validate
NonMembershipUpdate(
,, nullifier_2_i)
Validate
Membership(old_data_roots_root, data_roots_indices[i], data_roots_pths[i], data_tree_root_i)
If
pub_inputs.PROOF_ID = DEFI_DEPOSIT
thenProcessDefiDeposit
:- Check
pub_inputs.ASSET_ID
matches only one (sayk
th) bridge call data inbridge_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
- Check
Validate
ProcessClaim(pub_inputs, new_defi_root)
Let
chaining = propagated_input_index != 0
Let
propagating_previous_output_1 = backward_link == previous_note_commitment_1
Let
propagating_previous_output_2 = backward_link == previous_note_commitment_2
Let
previous_tx_linked = propagating_previous_output_1 || propagating_previous_output_2
Let
start_of_subchain = chaining && !previous_tx_linked
Let
middle_of_chain = chaining && previous_tx_linked
If
start_of_subchain
then:- Validate
Membership(old_data_root, linked_commitment_indices[i], linked_commitment_paths[i], backward_link)
- Validate
Let
propagating_previous_output_index =
propagating_previous_output_1 ? 1 :
propagating_previous_output_2 ? 2 : 0- 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
.
Validate
[P1, P2] = Q_{num_inputs}
Validate
BatchUpdate(old_data_root, new_data_root, data_start_index, leaf_1, ..., leaf_{2 * num_inputs})
Validate
old_null_root = null_root_1
Validate
new_null_root = null_root_{2 * num_inputs + 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_id
✅output_note_1_commitment
✅output_note_2_commitment
✅input_note_1_nullifier
✅input_note_2_nullifier
✅public_value
✅public_owner
✅public_asset_id
❌merkle_root
❌tx_fee
❌asset_id
❌bridge_call_data
❌defi_deposit_value
❌defi_root
❌backward_link
❌allow_chain
↩