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
2 changes: 0 additions & 2 deletions l1-contracts/src/core/interfaces/IRollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ import {IERC20} from "@oz/token/ERC20/IERC20.sol";
struct PublicInputArgs {
bytes32 previousArchive;
bytes32 endArchive;
Timestamp endTimestamp;
bytes32 outHash;
address proverId;
}

Expand Down
2 changes: 1 addition & 1 deletion l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ library Constants {
uint256 internal constant BLOB_PUBLIC_INPUTS = 6;
uint256 internal constant BLOB_PUBLIC_INPUTS_BYTES = 112;
uint256 internal constant PROPOSED_BLOCK_HEADER_LENGTH_BYTES = 348;
uint256 internal constant ROOT_ROLLUP_PUBLIC_INPUTS_LENGTH = 1020;
uint256 internal constant ROOT_ROLLUP_PUBLIC_INPUTS_LENGTH = 1015;
uint256 internal constant NUM_MSGS_PER_BASE_PARITY = 4;
uint256 internal constant NUM_BASE_PARITY_PER_ROOT_PARITY = 4;
}
30 changes: 5 additions & 25 deletions l1-contracts/src/core/libraries/rollup/EpochProofLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,8 @@ library EpochProofLib {
// Structure of the root rollup public inputs we need to reassemble:
//
// struct RootRollupPublicInputs {
// previous_archive: AppendOnlyTreeSnapshot,
// end_archive: AppendOnlyTreeSnapshot,
// end_timestamp: u64,
// end_block_number: Field,
// out_hash: Field,
// previous_archive_root: Field,
// end_archive_root: Field,
// proposedBlockHeaderHashes: [Field; Constants.AZTEC_MAX_EPOCH_DURATION],
// fees: [FeeRecipient; Constants.AZTEC_MAX_EPOCH_DURATION],
// chain_id: Field,
Expand All @@ -161,34 +158,17 @@ library EpochProofLib {
// previous_archive.root: the previous archive tree root
publicInputs[0] = _args.previousArchive;

// previous_archive.next_available_leaf_index: the previous archive next available index
// normally this should be equal to the block number (since leaves are 0-indexed and blocks 1-indexed)
// but in yarn-project/merkle-tree/src/new_tree.ts we prefill the tree so that block N is in leaf N
publicInputs[1] = bytes32(_start);

// end_archive.root: the new archive tree root
publicInputs[2] = _args.endArchive;

// end_archive.next_available_leaf_index: the new archive next available index
publicInputs[3] = bytes32(_end + 1);

// end_timestamp: the timestamp of the last block in the epoch
publicInputs[4] = bytes32(Timestamp.unwrap(_args.endTimestamp));

// end_block_number: last block number in the epoch
publicInputs[5] = bytes32(_end);

// out_hash: root of this epoch's l2 to l1 message tree
publicInputs[6] = _args.outHash;
publicInputs[1] = _args.endArchive;
}

uint256 numBlocks = _end - _start + 1;

for (uint256 i = 0; i < numBlocks; i++) {
publicInputs[7 + i] = rollupStore.blocks[_start + i].headerHash;
publicInputs[2 + i] = rollupStore.blocks[_start + i].headerHash;
}
Comment on lines 165 to 169

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Without a start and end block number in the root I was wondering if its possible to submit an epoch proof with the 'wrong' start and end values - but I don't think it's possible.

Just to make sure I'm understanding, if someone tries to submit a proof for a shorter range of blocks then assertAcceptable will pass, but some headerHashes here will be set as 0s when they shouldn't be and fail verification. If it's a longer a range of blocks (and assertAcceptable somehow passes) then it's the reverse - some headerHashes will be non-0 when they are expected to be 0. I think they must be non-0 because L1 will not allow us to set end as a block not yet proposed - so it's all good?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Exactly :)


uint256 offset = 7 + Constants.AZTEC_MAX_EPOCH_DURATION;
uint256 offset = 2 + Constants.AZTEC_MAX_EPOCH_DURATION;

uint256 feesLength = Constants.AZTEC_MAX_EPOCH_DURATION * 2;
// fees[2n to 2n + 1]: a fee element, which contains of a recipient and a value
Expand Down
11 changes: 2 additions & 9 deletions l1-contracts/test/Rollup.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -588,8 +588,6 @@ contract RollupTest is RollupBase {
PublicInputArgs memory args = PublicInputArgs({
previousArchive: blockLog.archive,
endArchive: data.archive,
endTimestamp: Timestamp.wrap(0),
outHash: bytes32(0),
proverId: address(0)
});

Expand Down Expand Up @@ -781,13 +779,8 @@ contract RollupTest is RollupBase {
address _coinbase,
uint256 _fee
) internal {
PublicInputArgs memory args = PublicInputArgs({
previousArchive: _prevArchive,
endArchive: _archive,
endTimestamp: Timestamp.wrap(0),
outHash: bytes32(0),
proverId: _prover
});
PublicInputArgs memory args =
PublicInputArgs({previousArchive: _prevArchive, endArchive: _archive, proverId: _prover});

bytes32[] memory fees = new bytes32[](Constants.AZTEC_MAX_EPOCH_DURATION * 2);
fees[0] = bytes32(uint256(uint160(bytes20(_coinbase)))); // Need the address to be left padded within the bytes32
Expand Down
2 changes: 0 additions & 2 deletions l1-contracts/test/base/RollupBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ contract RollupBase is DecoderBase {
PublicInputArgs memory args = PublicInputArgs({
previousArchive: parentBlockLog.archive,
endArchive: endFull.block.archive,
endTimestamp: Timestamp.wrap(0), // WHAT ?
outHash: bytes32(0), // WHAT ?
proverId: _prover
});

Expand Down
2 changes: 0 additions & 2 deletions l1-contracts/test/benchmark/happy.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -348,8 +348,6 @@ contract BenchmarkRollupTest is FeeModelTestPoints, DecoderBase {
PublicInputArgs memory args = PublicInputArgs({
previousArchive: rollup.getBlock(start).archive,
endArchive: rollup.getBlock(start + epochSize - 1).archive,
endTimestamp: Timestamp.wrap(0),
outHash: bytes32(0),
proverId: address(0)
});

Expand Down
2 changes: 0 additions & 2 deletions l1-contracts/test/fees/FeeRollup.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,6 @@ contract FeeRollupTest is FeeModelTestPoints, DecoderBase {
PublicInputArgs memory args = PublicInputArgs({
previousArchive: rollup.getBlock(start).archive,
endArchive: rollup.getBlock(start + epochSize - 1).archive,
endTimestamp: Timestamp.wrap(0),
outHash: bytes32(0),
proverId: address(0)
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ impl BlockMergeRollupInputs {
// ^ Where instead of num_txs, use num_blocks = (end_global_variables.block_number - start_global_variables.block_number) + 1
components::assert_prev_block_rollups_follow_on_from_each_other(left, right);

let out_hash = components::compute_blocks_out_hash(self.previous_rollup_data);

let proposed_block_header_hashes =
components::accumulate_proposed_block_header_hashes(left, right);

Expand All @@ -67,7 +65,7 @@ impl BlockMergeRollupInputs {
new_archive: right.new_archive,
start_global_variables: left.start_global_variables,
end_global_variables: right.end_global_variables,
out_hash,
out_hash: 0, // Not needed for block merge. This is only required for block root since the value is inserted on L1 per block.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is it not still useful to see that it is accumulated correctly?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We don't accumulate the out_hash of all the blocks to build a tree anymore. When proposing a block, the out_hash of that block is set to the outbox. After this point it is pretty much irrelevant.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It is for example a bit confusing that we have header bytes, but it is not actually the header but only some subset of it, where the missing piece stateReference is passed separately as another blob of bytes. What is the reason that it was not kept inside that "header" blob?

The headerHash is also a bit weird as it is not really a hash of the header, but a hash of this subset of the header.

Agree that it's confusing. I struggled to find a good name for it, so kept the name header because it's a "proposed header". Maybe proposed "args" could be a better name? Then headerHash will become proposedArgsHash? Naming is hard...

I removed stateReference out of the proposed header as discussed because the values are committed to by archive. I know it seems stupid to have stateReference passed to L1 as separate bytes. But I'm hoping that it's just here temporary.

Whether stateReference is separated or included in the proposed header, we have to double check the values against those in world state: we build the trees from blob data, then get all the tree roots and archive and check that they all match the proposed values. Without stateReference onchain, it feels cleaner, we only need to check the archive.

But that also means that the archiver won't have the full header for proving. We could maybe have this condensed proposed header as the formal l2 block header. And the full header is only required upon proving, when world state is available and can reconstruct the stateReference.

But if you think a single type of block header is better, I will add the stateReference to the proposed header.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Re out_hash, we have one per block and insert it as the root of the outbox tree in propose. It's part of the content_commitment which is inside the new header hash, so if a proposed L1 out_hash does not match the one in the circuit, the final roof proof verification will fail. Nice to be able to remove it above block level!

proposed_block_header_hashes,
fees,
vk_tree_root: left.vk_tree_root,
Expand All @@ -83,7 +81,6 @@ mod tests {
use dep::types::constants::{
BLOCK_MERGE_ROLLUP_INDEX, BLOCK_ROOT_ROLLUP_INDEX, ROOT_PARITY_INDEX,
};
use dep::types::hash::accumulate_sha256;
use dep::types::tests::fixtures;
use types::merkle_tree::merkle_tree::MerkleTree;

Expand Down Expand Up @@ -122,15 +119,6 @@ mod tests {
let _output = inputs.block_merge_rollup_circuit();
}

#[test]
fn out_hash() {
let mut inputs = default_block_merge_rollup_inputs();
let expected_hash = accumulate_sha256([1, 2]);
let outputs = inputs.block_merge_rollup_circuit();

assert_eq(outputs.out_hash, expected_hash);
}

#[test]
fn block_fees_are_accumulated() {
let mut inputs = default_block_merge_rollup_inputs();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::abis::{
block_root_or_block_merge_public_inputs::{BlockRootOrBlockMergePublicInputs, FeeRecipient},
previous_rollup_block_data::PreviousRollupBlockData,
use crate::abis::block_root_or_block_merge_public_inputs::{
BlockRootOrBlockMergePublicInputs, FeeRecipient,
};
use super::abis::tx_effect::TxEffect;
use dep::types::{
Expand All @@ -18,7 +17,7 @@ use dep::types::{
PUBLIC_DATA_UPDATE_REQUESTS_PREFIX, PUBLIC_LOG_SIZE_IN_FIELDS, PUBLIC_LOGS_PREFIX,
REVERT_CODE_PREFIX, TX_FEE_PREFIX, TX_START_PREFIX,
},
hash::{accumulate_sha256, compute_contract_class_log_hash},
hash::compute_contract_class_log_hash,
merkle_tree::VariableMerkleTree,
traits::{Empty, ToField},
utils::arrays::{array_length, array_length_until, array_merge, array_padded_with},
Expand Down Expand Up @@ -82,12 +81,6 @@ pub fn accumulate_blocks_fees(
left: BlockRootOrBlockMergePublicInputs,
right: BlockRootOrBlockMergePublicInputs,
) -> [FeeRecipient; AZTEC_MAX_EPOCH_DURATION] {
let left_len = array_length(left.fees);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fees and blob_public_inputs and the above proposed_block_header_hashes are inserted 1 item per block in the block root. Instead of checking the array lengths for all of them here, we can just check the number of blocks in the root rollup.

let right_len = array_length(right.fees);
assert(
left_len + right_len <= AZTEC_MAX_EPOCH_DURATION,
"too many fee payment structs accumulated in rollup",
);
// TODO(Miranda): combine fees with same recipient depending on rollup structure
// Assuming that the final rollup tree (block root -> block merge -> root) has max 32 leaves (TODO: constrain in root), then
// in the worst case, we would be checking the left 16 values (left_len = 16) against the right 16 (right_len = 16).
Expand All @@ -102,11 +95,6 @@ pub fn accumulate_blob_public_inputs(
right: BlockRootOrBlockMergePublicInputs,
) -> [BlockBlobPublicInputs; AZTEC_MAX_EPOCH_DURATION] {
let left_len = array_length(left.blob_public_inputs);
let right_len = array_length(right.blob_public_inputs);
assert(
left_len + right_len <= AZTEC_MAX_EPOCH_DURATION,
"too many blob public input structs accumulated in rollup",
);
// NB: The below is cheaper than array_merge because assigning BlockBlobPublicInputs is cheaper than calling .equals
let mut add_from_left = true;
let mut result = [BlockBlobPublicInputs::empty(); AZTEC_MAX_EPOCH_DURATION];
Expand All @@ -121,17 +109,6 @@ pub fn accumulate_blob_public_inputs(
result
}

pub fn compute_blocks_out_hash(previous_rollup_data: [PreviousRollupBlockData; 2]) -> Field {
if previous_rollup_data[1].block_root_or_block_merge_public_inputs.is_padding() {
previous_rollup_data[0].block_root_or_block_merge_public_inputs.out_hash
} else {
accumulate_sha256([
previous_rollup_data[0].block_root_or_block_merge_public_inputs.out_hash,
previous_rollup_data[1].block_root_or_block_merge_public_inputs.out_hash,
])
}
}

pub fn compute_kernel_out_hash(l2_to_l1_msgs: [Field; MAX_L2_TO_L1_MSGS_PER_TX]) -> Field {
let non_empty_items = array_length(l2_to_l1_msgs);
let merkle_tree = VariableMerkleTree::new_sha(l2_to_l1_msgs, non_empty_items);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,45 @@ pub use root_rollup_public_inputs::RootRollupPublicInputs;

mod tests {
use crate::tests::root_rollup_inputs::default_root_rollup_inputs;
use dep::types::hash::accumulate_sha256;
use dep::types::tests::utils::assert_array_eq;

#[test]
fn check_block_hashes_empty_blocks() {
let expected_out_hash = accumulate_sha256([1, 2]);

fn root_rollup_end_state() {
let inputs = default_root_rollup_inputs();
let outputs = inputs.root_rollup_circuit();

// check out hash
assert_eq(outputs.out_hash, expected_out_hash);
}
let left = inputs.previous_rollup_data[0].block_root_or_block_merge_public_inputs;
let right = inputs.previous_rollup_data[1].block_root_or_block_merge_public_inputs;

#[test]
fn end_state() {
let inputs = default_root_rollup_inputs();
let outputs = inputs.root_rollup_circuit();
assert_eq(outputs.previous_archive_root, left.previous_archive.root);

assert_eq(outputs.end_archive_root, right.new_archive.root);

assert_array_eq(
outputs.proposed_block_header_hashes,
[left.proposed_block_header_hashes[0], right.proposed_block_header_hashes[0]],
);

assert_array_eq(outputs.fees, [left.fees[0], right.fees[0]]);

assert_eq(outputs.chain_id, left.start_global_variables.chain_id);
assert_eq(outputs.chain_id, right.end_global_variables.chain_id);

assert_eq(outputs.version, left.start_global_variables.version);
assert_eq(outputs.version, right.end_global_variables.version);

assert_eq(outputs.vk_tree_root, left.vk_tree_root);
assert_eq(outputs.vk_tree_root, right.vk_tree_root);

assert_eq(outputs.protocol_contract_tree_root, left.protocol_contract_tree_root);
assert_eq(outputs.protocol_contract_tree_root, right.protocol_contract_tree_root);

assert_eq(outputs.prover_id, left.prover_id);
assert_eq(outputs.prover_id, right.prover_id);

assert(outputs.previous_archive.eq(
inputs.previous_rollup_data[0]
.block_root_or_block_merge_public_inputs
.previous_archive,
));

assert(outputs.end_archive.eq(
inputs.previous_rollup_data[1].block_root_or_block_merge_public_inputs.new_archive,
));

assert(outputs.end_timestamp.eq(
inputs.previous_rollup_data[1]
.block_root_or_block_merge_public_inputs
.end_global_variables
.timestamp,
));
assert_array_eq(
outputs.blob_public_inputs,
[left.blob_public_inputs[0], right.blob_public_inputs[0]],
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use crate::{
};
use types::{
constants::{
BLOCK_MERGE_ROLLUP_INDEX, BLOCK_ROOT_ROLLUP_EMPTY_INDEX, BLOCK_ROOT_ROLLUP_INDEX,
BLOCK_ROOT_ROLLUP_SINGLE_TX_INDEX, PROOF_TYPE_ROOT_ROLLUP_HONK,
AZTEC_MAX_EPOCH_DURATION, BLOCK_MERGE_ROLLUP_INDEX, BLOCK_ROOT_ROLLUP_EMPTY_INDEX,
BLOCK_ROOT_ROLLUP_INDEX, BLOCK_ROOT_ROLLUP_SINGLE_TX_INDEX, PROOF_TYPE_ROOT_ROLLUP_HONK,
},
traits::Empty,
};
Expand Down Expand Up @@ -54,7 +54,12 @@ impl RootRollupInputs {
// ^ Where instead of num_txs, use num_blocks = (end_global_variables.block_number - start_global_variables.block_number) + 1
components::assert_prev_block_rollups_follow_on_from_each_other(left, right);

let out_hash = components::compute_blocks_out_hash(self.previous_rollup_data);
// Make sure that the total number of blocks in the epoch does not exceed the max, preventing the accumulated
// data below (proposed_block_header_hashes, fees, blob_public_inputs) from being truncated.
let num_blocks = (right.end_global_variables.block_number as u32)
- (left.start_global_variables.block_number as u32)
+ 1;
assert(num_blocks <= AZTEC_MAX_EPOCH_DURATION, "too many blocks in root rollup");

let proposed_block_header_hashes =
components::accumulate_proposed_block_header_hashes(left, right);
Expand All @@ -68,11 +73,8 @@ impl RootRollupInputs {
let blob_public_inputs = components::accumulate_blob_public_inputs(left, right);

RootRollupPublicInputs {
previous_archive: left.previous_archive,
end_archive: right.new_archive,
end_timestamp: right.end_global_variables.timestamp,
end_block_number: right.end_global_variables.block_number,
out_hash,
previous_archive_root: left.previous_archive.root,
end_archive_root: right.new_archive.root,
proposed_block_header_hashes,
fees,
chain_id: right.end_global_variables.chain_id,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
use crate::abis::block_root_or_block_merge_public_inputs::FeeRecipient;
use dep::types::abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot;
use dep::types::constants::AZTEC_MAX_EPOCH_DURATION;
use blob::blob_public_inputs::BlockBlobPublicInputs;

pub struct RootRollupPublicInputs {
// Snapshot of archive tree before/after this rollup has been processed
pub previous_archive: AppendOnlyTreeSnapshot,
pub end_archive: AppendOnlyTreeSnapshot,
pub end_timestamp: u64,
pub end_block_number: Field,
pub out_hash: Field,
pub previous_archive_root: Field,
pub end_archive_root: Field,
pub proposed_block_header_hashes: [Field; AZTEC_MAX_EPOCH_DURATION],
pub fees: [FeeRecipient; AZTEC_MAX_EPOCH_DURATION],
pub chain_id: Field,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ pub fn default_previous_rollup_block_data() -> [PreviousRollupBlockData; 2] {
previous_rollup_data[0].block_root_or_block_merge_public_inputs.out_hash = 1;
previous_rollup_data[1].block_root_or_block_merge_public_inputs.out_hash = 2;

previous_rollup_data[0].block_root_or_block_merge_public_inputs.proposed_block_header_hashes[0] =
21;
previous_rollup_data[1].block_root_or_block_merge_public_inputs.proposed_block_header_hashes[0] =
37;

previous_rollup_data[0].block_root_or_block_merge_public_inputs.fees[0].value = 10;
previous_rollup_data[1].block_root_or_block_merge_public_inputs.fees[0].value = 15;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -443,11 +443,8 @@ pub global BLOCK_ROOT_OR_BLOCK_MERGE_PUBLIC_INPUTS_LENGTH: u32 = 2
+ 1 /* protocol_contract_tree_root */
+ 1 /* prover_id */
+ AZTEC_MAX_EPOCH_DURATION * BLOB_PUBLIC_INPUTS * BLOBS_PER_BLOCK;
pub global ROOT_ROLLUP_PUBLIC_INPUTS_LENGTH: u32 = APPEND_ONLY_TREE_SNAPSHOT_LENGTH /* previous_archive */
+ APPEND_ONLY_TREE_SNAPSHOT_LENGTH /* end_archive */
+ 1 /* end_timestamp */
+ 1 /* end_block_number */
+ 1 /* out_hash */
pub global ROOT_ROLLUP_PUBLIC_INPUTS_LENGTH: u32 = 1 /* previous_archive_root */
+ 1 /* end_archive_root */
+ 1 /* chain_id */
+ 1 /* version */
+ 1 /* vk_tree_root */
Expand Down
Loading