Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
240 changes: 163 additions & 77 deletions yellow-paper/docs/contracts/index.md

Large diffs are not rendered by default.

343 changes: 343 additions & 0 deletions yellow-paper/docs/rollup-circuits/base_rollup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,343 @@
---
title: Base Rollup
sidebar_position: 2
---

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:

Take `BaseRollupInputs` as an input value, and transform it to `BaseOrMergeRollupPublicInputs` as an output value while making sure that the validity conditions are met.

```mermaid
graph LR
A[BaseRollupInputs] --> C[BaseRollupCircuit] --> B[BaseOrMergeRollupPublicInputs]
```

## Overview

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.

```mermaid
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment for this diagram, about listing all class items inside the class, as well as your nice callouts to sub-classes.

classDiagram
direction TB


class PartialStateReference {
note_hash_tree: Snapshot
nullifier_tree: Snapshot
contract_tree: Snapshot
public_data_tree: Snapshot
}

class StateReference {
l1_to_l2_message_tree: Snapshot
partial: PartialStateReference
}
StateReference *-- PartialStateReference: partial

class GlobalVariables {
block_number: Fr
timestamp: Fr
version: Fr
chain_id: Fr
coinbase: Address
}

class Header {
last_archive: Snapshot
content_hash: Fr[2]
state: StateReference
global_variables: GlobalVariables
}
Header *.. Body : content_hash
Header *-- StateReference : state
Header *-- GlobalVariables : global_variables

class ContractData {
leaf: Fr
address: Address
portal: EthAddress
}

class Logs {
private: EncryptedLogs
public: UnencryptedLogs
}

class PublicDataWrite {
index: Fr
value: Fr
}

class TxEffect {
note_hashes: List~Fr~
nullifiers: List~Fr~
l2_to_l1_msgs: List~Fr~
contracts: List~ContractData~
public_writes: List~PublicDataWrite~
logs: Logs
}
TxEffect *-- "m" ContractData: contracts
TxEffect *-- "m" PublicDataWrite: public_writes
TxEffect *-- Logs : logs

class Body {
l1_to_l2_messages: List~Fr~
tx_effects: List~TxEffect~
}
Body *-- "m" TxEffect

class ProvenBlock {
archive: Snapshot
header: Header
body: Body
}

ProvenBlock *-- Header : header
ProvenBlock *-- Body : body

class ConstantRollupData {
last_archive: Snapshot
base_rollup_vk_hash: Fr,
merge_rollup_vk_hash: Fr,
global_variables: GlobalVariables
}
ConstantRollupData *-- GlobalVariables : global_variables

class PublicDataUpdateRequest {
index: Fr
old_value: Fr
new_value: Fr
}

class PublicDataRead {
index: Fr
value: Fr
}

class NewContractData {
function_tree_root: Fr
address: Address
portal: EthAddress
}

class CombinedAccumulatedData {
aggregation_object: AggregationObject
read_requests: List~Fr~
pending_read_requests: List~Fr~
note_hashes: List~Fr~
nullifiers: List~Fr~
nullified_note_hashes: List~Fr~

l2_to_l1_messages: List~Fr~
contracts: List~NewContractData~
public_update_requests: List~PublicDataUpdateRequest~
public_reads: List~PublicDataRead~
logs: Logs

private_call_stack: List~CallRequest~
public_call_stack: List~CallRequest~
start_public_data_root: Fr
end_public_data_root: Fr
}
CombinedAccumulatedData *-- "m" NewContractData: contracts
CombinedAccumulatedData *-- "m" PublicDataUpdateRequest: public_update_requests
CombinedAccumulatedData *-- "m" PublicDataRead: public_reads
CombinedAccumulatedData *-- Logs : logs

class ContractDeploymentData {
deployer_public_key: Point
constructor_vk_hash: Fr
constructor_args_hash: Fr
function_tree_root: Fr
salt: Fr
portal_address: Fr
}

class TxContext {
fee_context: FeeContext
is_contract_deployment: bool
chain_id: Fr
version: Fr
contract_deployment_data: ContractDeploymentData
}
TxContext *-- ContractDeploymentData: contract_deployment_data

class CombinedConstantData {
historical_header: Header
tx_context: TxContext
}
CombinedConstantData *-- Header : historical_header
CombinedConstantData *-- TxContext : tx_context

class KernelPublicInputs {
is_private: bool
end: CombinedAccumulatedData
constants: CombinedConstantData
}
KernelPublicInputs *-- CombinedAccumulatedData : end
KernelPublicInputs *-- CombinedConstantData : constants

class KernelData {
proof: Proof
public_inputs: KernelPublicInputs
}
KernelData *-- KernelPublicInputs : public_inputs

class StateDiffHints {
nullifier_predecessor_preimages: List~NullifierLeafPreimage~
nullifier_predecessor_membership_witnesses: List~NullifierMembershipWitness~
sorted_nullifiers: List~Fr~
sorted_nullifier_indexes: List~Fr~
note_hash_subtree_sibling_path: List~Fr~,
nullifier_subtree_sibling_path: List~Fr~,
contract_subtree_sibling_path: List~Fr~,
public_data_sibling_path: List~Fr~,
}

class BaseRollupInputs {
historical_header_membership_witnesses: List~HeaderMembershipWitness~
kernel_data: List~KernelData~
partial: PartialStateReference
state_diff_hints: StateDiffHints
}
BaseRollupInputs *-- "m" KernelData : kernelData
BaseRollupInputs *-- PartialStateReference : partial
BaseRollupInputs *-- StateDiffHints : state_diff_hints
BaseRollupInputs *-- ConstantRollupData : constants

class BaseOrMergeRollupPublicInputs {
type: Fr
height_in_block_tree: Fr
aggregation_object: AggregationObject
txs_hash: Fr[2]
out_hash: Fr[2]
constants: ConstantRollupData
start: PartialStateReference
end: PartialStateReference
}
BaseOrMergeRollupPublicInputs *-- ConstantRollupData : constants
BaseOrMergeRollupPublicInputs *-- PartialStateReference : start
BaseOrMergeRollupPublicInputs *-- PartialStateReference : end
```

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
:::warning TODO
Fee structs and contract deployment structs will need to be revised, in line with newer ideas.
:::

:::warning TODO
Fee structs and contract deployment structs will need to be revised, in line with newer ideas.
:::

### Validity Conditions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to this nice pseudocode, it'd be nice to have a succinct list of the things each circuit does, so the information can be consumed at a glance.
E.g.

  • Verifies a private or public kernel proof.
  • Ensures all of the tx's call stacks are empty (i.e. that all calls have been processed).
  • Checks that the purported historical header (that's been used by the input tx) is a valid historical header.
  • Sha256-hashes any tx data that must be broadcast, into a tx_hash:
    • new note hashes
    • new nullifiers
    • newly-deployed contract data
    • public data writes
    • l2 to l1 messages
  • Sha256-hashes L2 to L1 messages into an OutHash (preferably renamed to something clearer)
  • Ensures consistency between input and output data.
  • Computes a subtree containing the new note hashes.
    • Inserts this subtree into the note hash tree.
  • Inserts the new nullifiers into the nullifier tree (and updates corresponding pointer leaves).
  • Inserts new contracts into the contracts tree.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the pseudo is that list 🤷 why would we want to maintain two lists?


```python
def BaseRollupCircuit(
state_diff_hints: StateDiffHints,
historical_header_membership_witnesses: HeaderMembershipWitness[],
kernel_data: KernelData[],
partial: PartialStateReference,
constants: ConstantRollupData,
) -> BaseOrMergeRollupPublicInputs:

tx_hashes = Fr[][2]
contracts = Fr[]
public_data_tree_root = partial.public_data_tree
for i in len(kernel_data):
tx_hash, _c, public_data_tree_root = kernel_checks(
kernel_data[i],
constants,
public_data_tree_root,
historical_header_membership_witnesses[i],
)
tx_hashes.push(tx_hash)
contracts.push_array(_c)

note_hash_subtree = MerkleTree(
[...note_hashes for kernel_data.public_inputs.end.note_hashes in kernel_data]
)
note_hash_snapshot = merkle_insertion(
partial.note_hash_tree.root,
note_hash_subtree.root,
state_diff_hints.note_hash_subtree_sibling_path,
NOTE_HASH_SUBTREE_HEIGHT,
NOTE_HASH_TREE_HEIGHT,
)

# We can use the sorted nullifiers to simplify batch-insertion
# The sorting can be checked with a permutation
nullifier_snapshot = successor_merkle_batch_insertion(
partial.nullifier_tree.root,
[...nullifiers for kernel_data.public_inputs.end.nullifiers in kernel_data],
state_diff_hints.sorted_nullifiers,
state_diff_hints.sorted_nullifier_indexes,
state_diff_hints.nullifier_subtree_sibling_path,
state_diff.nullifier_predecessor_preimages,
state_diff.nullifier_predecessor_membership_witnesses,
NULLIFIER_SUBTREE_HEIGHT,
NULLIFIER_TREE_HEIGHT,
)

contract_sub_tree = MerkleTree(contracts)
contract_snapshot = merkle_insertion(
partial.note_hash_tree.root,
note_hash_subtree.root,
state_diff_hints.contract_subtree_sibling_path,
CONTRACTS_SUBTREE_HEIGHT,
CONTRACTS_TREE_HEIGHT,
)

txs_hash = SHA256(tx_hashes)
out_hash = SHA256(
[...l2_to_l1_messages for kernel_data.public_inputs.end.l2_to_l1_messages in kernel_data]
)

return BaseOrMergeRollupPublicInputs(
type=0,
height_in_block_tree=0,
aggregation_object=
txs_hash=txs_hash
out_hash=out_hash
start=partial,
end=PartialStateReference(
note_hash_tree=note_hash_snapshot,
nullifier_tree=nullifier_snapshot,
contract_tree=contract_snapshot,
public_data_tree=public_data_tree_root,
),
)

def kernel_checks(
kernel: KernelData,
constants: ConstantRollupData,
public_data_tree_root: Fr,
historical_header_membership_witness: HeaderMembershipWitness
) -> (Fr[2], Fr[], Fr):
assert public_data_tree_root == kernel.public_inputs.end.start_public_data_root
assert kernel.proof.verify(kernel.public_inputs)

tx_context = kernel.public_inputs.constants.tx_context
assert tx_context.chainid == constants.globalVariables.chainid
assert tx_context.version == constants.globalVariables.version

assert len(kernel.public_inputs.end.private_call_stack) == 0
assert len(kernel.public_inputs.end.public_call_stack) == 0

assert merkle_inclusion(
kernel.constants.historical_header.hash(),
kernel.constants.historical_header.global_variables.block_number,
historical_header_membership_witness,
constants.last_archive
)

contracts = []
contract_datas = []
for preimage in kernel.public_inputs.end.contracts:
to_push = preimage.hash() if preimage.address == 0 else 0:
contracts.push(to_push)
contract_datas.push(ContractData(to_push, preimage.address, preimage.portal))

tx_hash = SHA256(
kernel.public_inputs.end.note_hashes |
kernel.public_inputs.end.nullifiers |
contract_datas | 
kernel.public_inputs.end.public_data_writes |
kernel.public_inputs.end.l2_to_l1_messages
)
return (tx_hash, contracts, kernel.public_inputs.end.end_public_data_root)
```
Loading