Skip to content

Commit b2d6376

Browse files
LHerskindrahul-kotharibenesjansklppy88
authored
docs: Yellow paper rollup circuits and state update (AztecProtocol#3558)
PR looking to fix AztecProtocol#3148, AztecProtocol#3149 and AztecProtocol#3150. --------- Co-authored-by: Rahul Kothari <rahul.kothari.201@gmail.com> Co-authored-by: Jan Beneš <janbenes1234@gmail.com> Co-authored-by: esau <152162806+sklppy88@users.noreply.github.com>
1 parent a60b71a commit b2d6376

File tree

7 files changed

+1532
-79
lines changed

7 files changed

+1532
-79
lines changed

yellow-paper/docs/contracts/index.md

+163-77
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,343 @@
1+
---
2+
title: Base Rollup
3+
sidebar_position: 2
4+
---
5+
6+
The base rollup circuit is the most complex of the rollup circuits, as it has to interpret the output data of a kernel proof and perform the state updates and transaction validation. While this makes the data structures complex to follow, the goal of the circuit is fairly straight forward:
7+
8+
Take `BaseRollupInputs` as an input value, and transform it to `BaseOrMergeRollupPublicInputs` as an output value while making sure that the validity conditions are met.
9+
10+
```mermaid
11+
graph LR
12+
A[BaseRollupInputs] --> C[BaseRollupCircuit] --> B[BaseOrMergeRollupPublicInputs]
13+
```
14+
15+
## Overview
16+
17+
Below is a subset of the figure from [earlier](./index.md) (granted, not much is removed). The figure shows the data structures related to the Base Rollup circuit.
18+
19+
```mermaid
20+
classDiagram
21+
direction TB
22+
23+
24+
class PartialStateReference {
25+
note_hash_tree: Snapshot
26+
nullifier_tree: Snapshot
27+
contract_tree: Snapshot
28+
public_data_tree: Snapshot
29+
}
30+
31+
class StateReference {
32+
l1_to_l2_message_tree: Snapshot
33+
partial: PartialStateReference
34+
}
35+
StateReference *-- PartialStateReference: partial
36+
37+
class GlobalVariables {
38+
block_number: Fr
39+
timestamp: Fr
40+
version: Fr
41+
chain_id: Fr
42+
coinbase: Address
43+
}
44+
45+
class Header {
46+
last_archive: Snapshot
47+
content_hash: Fr[2]
48+
state: StateReference
49+
global_variables: GlobalVariables
50+
}
51+
Header *.. Body : content_hash
52+
Header *-- StateReference : state
53+
Header *-- GlobalVariables : global_variables
54+
55+
class ContractData {
56+
leaf: Fr
57+
address: Address
58+
portal: EthAddress
59+
}
60+
61+
class Logs {
62+
private: EncryptedLogs
63+
public: UnencryptedLogs
64+
}
65+
66+
class PublicDataWrite {
67+
index: Fr
68+
value: Fr
69+
}
70+
71+
class TxEffect {
72+
note_hashes: List~Fr~
73+
nullifiers: List~Fr~
74+
l2_to_l1_msgs: List~Fr~
75+
contracts: List~ContractData~
76+
public_writes: List~PublicDataWrite~
77+
logs: Logs
78+
}
79+
TxEffect *-- "m" ContractData: contracts
80+
TxEffect *-- "m" PublicDataWrite: public_writes
81+
TxEffect *-- Logs : logs
82+
83+
class Body {
84+
l1_to_l2_messages: List~Fr~
85+
tx_effects: List~TxEffect~
86+
}
87+
Body *-- "m" TxEffect
88+
89+
class ProvenBlock {
90+
archive: Snapshot
91+
header: Header
92+
body: Body
93+
}
94+
95+
ProvenBlock *-- Header : header
96+
ProvenBlock *-- Body : body
97+
98+
class ConstantRollupData {
99+
last_archive: Snapshot
100+
base_rollup_vk_hash: Fr,
101+
merge_rollup_vk_hash: Fr,
102+
global_variables: GlobalVariables
103+
}
104+
ConstantRollupData *-- GlobalVariables : global_variables
105+
106+
class PublicDataUpdateRequest {
107+
index: Fr
108+
old_value: Fr
109+
new_value: Fr
110+
}
111+
112+
class PublicDataRead {
113+
index: Fr
114+
value: Fr
115+
}
116+
117+
class NewContractData {
118+
function_tree_root: Fr
119+
address: Address
120+
portal: EthAddress
121+
}
122+
123+
class CombinedAccumulatedData {
124+
aggregation_object: AggregationObject
125+
read_requests: List~Fr~
126+
pending_read_requests: List~Fr~
127+
note_hashes: List~Fr~
128+
nullifiers: List~Fr~
129+
nullified_note_hashes: List~Fr~
130+
131+
l2_to_l1_messages: List~Fr~
132+
contracts: List~NewContractData~
133+
public_update_requests: List~PublicDataUpdateRequest~
134+
public_reads: List~PublicDataRead~
135+
logs: Logs
136+
137+
private_call_stack: List~CallRequest~
138+
public_call_stack: List~CallRequest~
139+
start_public_data_root: Fr
140+
end_public_data_root: Fr
141+
}
142+
CombinedAccumulatedData *-- "m" NewContractData: contracts
143+
CombinedAccumulatedData *-- "m" PublicDataUpdateRequest: public_update_requests
144+
CombinedAccumulatedData *-- "m" PublicDataRead: public_reads
145+
CombinedAccumulatedData *-- Logs : logs
146+
147+
class ContractDeploymentData {
148+
deployer_public_key: Point
149+
constructor_vk_hash: Fr
150+
constructor_args_hash: Fr
151+
function_tree_root: Fr
152+
salt: Fr
153+
portal_address: Fr
154+
}
155+
156+
class TxContext {
157+
fee_context: FeeContext
158+
is_contract_deployment: bool
159+
chain_id: Fr
160+
version: Fr
161+
contract_deployment_data: ContractDeploymentData
162+
}
163+
TxContext *-- ContractDeploymentData: contract_deployment_data
164+
165+
class CombinedConstantData {
166+
historical_header: Header
167+
tx_context: TxContext
168+
}
169+
CombinedConstantData *-- Header : historical_header
170+
CombinedConstantData *-- TxContext : tx_context
171+
172+
class KernelPublicInputs {
173+
is_private: bool
174+
end: CombinedAccumulatedData
175+
constants: CombinedConstantData
176+
}
177+
KernelPublicInputs *-- CombinedAccumulatedData : end
178+
KernelPublicInputs *-- CombinedConstantData : constants
179+
180+
class KernelData {
181+
proof: Proof
182+
public_inputs: KernelPublicInputs
183+
}
184+
KernelData *-- KernelPublicInputs : public_inputs
185+
186+
class StateDiffHints {
187+
nullifier_predecessor_preimages: List~NullifierLeafPreimage~
188+
nullifier_predecessor_membership_witnesses: List~NullifierMembershipWitness~
189+
sorted_nullifiers: List~Fr~
190+
sorted_nullifier_indexes: List~Fr~
191+
note_hash_subtree_sibling_path: List~Fr~,
192+
nullifier_subtree_sibling_path: List~Fr~,
193+
contract_subtree_sibling_path: List~Fr~,
194+
public_data_sibling_path: List~Fr~,
195+
}
196+
197+
class BaseRollupInputs {
198+
historical_header_membership_witnesses: List~HeaderMembershipWitness~
199+
kernel_data: List~KernelData~
200+
partial: PartialStateReference
201+
state_diff_hints: StateDiffHints
202+
}
203+
BaseRollupInputs *-- "m" KernelData : kernelData
204+
BaseRollupInputs *-- PartialStateReference : partial
205+
BaseRollupInputs *-- StateDiffHints : state_diff_hints
206+
BaseRollupInputs *-- ConstantRollupData : constants
207+
208+
class BaseOrMergeRollupPublicInputs {
209+
type: Fr
210+
height_in_block_tree: Fr
211+
aggregation_object: AggregationObject
212+
txs_hash: Fr[2]
213+
out_hash: Fr[2]
214+
constants: ConstantRollupData
215+
start: PartialStateReference
216+
end: PartialStateReference
217+
}
218+
BaseOrMergeRollupPublicInputs *-- ConstantRollupData : constants
219+
BaseOrMergeRollupPublicInputs *-- PartialStateReference : start
220+
BaseOrMergeRollupPublicInputs *-- PartialStateReference : end
221+
```
222+
223+
:::warning TODO
224+
Fee structs and contract deployment structs will need to be revised, in line with newer ideas.
225+
:::
226+
227+
### Validity Conditions
228+
229+
```python
230+
def BaseRollupCircuit(
231+
state_diff_hints: StateDiffHints,
232+
historical_header_membership_witnesses: HeaderMembershipWitness[],
233+
kernel_data: KernelData[],
234+
partial: PartialStateReference,
235+
constants: ConstantRollupData,
236+
) -> BaseOrMergeRollupPublicInputs:
237+
238+
tx_hashes = Fr[][2]
239+
contracts = Fr[]
240+
public_data_tree_root = partial.public_data_tree
241+
for i in len(kernel_data):
242+
tx_hash, _c, public_data_tree_root = kernel_checks(
243+
kernel_data[i],
244+
constants,
245+
public_data_tree_root,
246+
historical_header_membership_witnesses[i],
247+
)
248+
tx_hashes.push(tx_hash)
249+
contracts.push_array(_c)
250+
251+
note_hash_subtree = MerkleTree(
252+
[...note_hashes for kernel_data.public_inputs.end.note_hashes in kernel_data]
253+
)
254+
note_hash_snapshot = merkle_insertion(
255+
partial.note_hash_tree.root,
256+
note_hash_subtree.root,
257+
state_diff_hints.note_hash_subtree_sibling_path,
258+
NOTE_HASH_SUBTREE_HEIGHT,
259+
NOTE_HASH_TREE_HEIGHT,
260+
)
261+
262+
# We can use the sorted nullifiers to simplify batch-insertion
263+
# The sorting can be checked with a permutation
264+
nullifier_snapshot = successor_merkle_batch_insertion(
265+
partial.nullifier_tree.root,
266+
[...nullifiers for kernel_data.public_inputs.end.nullifiers in kernel_data],
267+
state_diff_hints.sorted_nullifiers,
268+
state_diff_hints.sorted_nullifier_indexes,
269+
state_diff_hints.nullifier_subtree_sibling_path,
270+
state_diff.nullifier_predecessor_preimages,
271+
state_diff.nullifier_predecessor_membership_witnesses,
272+
NULLIFIER_SUBTREE_HEIGHT,
273+
NULLIFIER_TREE_HEIGHT,
274+
)
275+
276+
contract_sub_tree = MerkleTree(contracts)
277+
contract_snapshot = merkle_insertion(
278+
partial.note_hash_tree.root,
279+
note_hash_subtree.root,
280+
state_diff_hints.contract_subtree_sibling_path,
281+
CONTRACTS_SUBTREE_HEIGHT,
282+
CONTRACTS_TREE_HEIGHT,
283+
)
284+
285+
txs_hash = SHA256(tx_hashes)
286+
out_hash = SHA256(
287+
[...l2_to_l1_messages for kernel_data.public_inputs.end.l2_to_l1_messages in kernel_data]
288+
)
289+
290+
return BaseOrMergeRollupPublicInputs(
291+
type=0,
292+
height_in_block_tree=0,
293+
aggregation_object=
294+
txs_hash=txs_hash
295+
out_hash=out_hash
296+
start=partial,
297+
end=PartialStateReference(
298+
note_hash_tree=note_hash_snapshot,
299+
nullifier_tree=nullifier_snapshot,
300+
contract_tree=contract_snapshot,
301+
public_data_tree=public_data_tree_root,
302+
),
303+
)
304+
305+
def kernel_checks(
306+
kernel: KernelData,
307+
constants: ConstantRollupData,
308+
public_data_tree_root: Fr,
309+
historical_header_membership_witness: HeaderMembershipWitness
310+
) -> (Fr[2], Fr[], Fr):
311+
assert public_data_tree_root == kernel.public_inputs.end.start_public_data_root
312+
assert kernel.proof.verify(kernel.public_inputs)
313+
314+
tx_context = kernel.public_inputs.constants.tx_context
315+
assert tx_context.chainid == constants.globalVariables.chainid
316+
assert tx_context.version == constants.globalVariables.version
317+
318+
assert len(kernel.public_inputs.end.private_call_stack) == 0
319+
assert len(kernel.public_inputs.end.public_call_stack) == 0
320+
321+
assert merkle_inclusion(
322+
kernel.constants.historical_header.hash(),
323+
kernel.constants.historical_header.global_variables.block_number,
324+
historical_header_membership_witness,
325+
constants.last_archive
326+
)
327+
328+
contracts = []
329+
contract_datas = []
330+
for preimage in kernel.public_inputs.end.contracts:
331+
to_push = preimage.hash() if preimage.address == 0 else 0:
332+
contracts.push(to_push)
333+
contract_datas.push(ContractData(to_push, preimage.address, preimage.portal))
334+
335+
tx_hash = SHA256(
336+
kernel.public_inputs.end.note_hashes |
337+
kernel.public_inputs.end.nullifiers |
338+
contract_datas | 
339+
kernel.public_inputs.end.public_data_writes |
340+
kernel.public_inputs.end.l2_to_l1_messages
341+
)
342+
return (tx_hash, contracts, kernel.public_inputs.end.end_public_data_root)
343+
```

0 commit comments

Comments
 (0)