From d6a6517fd5c800ffc5b959d8fa6e7ba9048eb10b Mon Sep 17 00:00:00 2001 From: tiagofneto Date: Tue, 26 Mar 2024 11:25:01 +0100 Subject: [PATCH 01/48] updated deps and versioning --- .tool-versions | 3 ++- Scarb.lock | 20 ++++++++++++++++ Scarb.toml | 4 ++-- src/core/commitments_inbox.cairo | 2 +- src/core/evm_facts_registry.cairo | 32 +++++++------------------ src/core/headers_store.cairo | 20 +++++++--------- src/remappers/timestamp_remappers.cairo | 10 ++++---- 7 files changed, 46 insertions(+), 45 deletions(-) create mode 100644 Scarb.lock diff --git a/.tool-versions b/.tool-versions index 2175a33..780f6f9 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1,2 @@ -scarb 0.7.0 +scarb 2.6.4 +starknet-foundry 0.20.1 diff --git a/Scarb.lock b/Scarb.lock new file mode 100644 index 0000000..9c579e7 --- /dev/null +++ b/Scarb.lock @@ -0,0 +1,20 @@ +# Code generated by scarb DO NOT EDIT. +version = 1 + +[[package]] +name = "cairo_lib" +version = "0.2.0" +source = "git+https://github.com/HerodotusDev/cairo-lib.git?branch=update-cairo#c00222c907cbfd381d28749a2dc2fab0eac6beae" + +[[package]] +name = "herodotus_eth_starknet" +version = "0.1.0" +dependencies = [ + "cairo_lib", + "snforge_std", +] + +[[package]] +name = "snforge_std" +version = "0.20.1" +source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.20.1#fea2db8f2b20148cc15ee34b08de12028eb42942" diff --git a/Scarb.toml b/Scarb.toml index ee6ebc7..0868df8 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -11,8 +11,8 @@ casm = true [dependencies] starknet = "2.2.0" -cairo_lib = { git = "https://github.com/HerodotusDev/cairo-lib.git" } -snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.4.1" } +cairo_lib = { git = "https://github.com/HerodotusDev/cairo-lib.git", branch = "update-cairo" } +snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.20.1" } [tool.snforge] exit_first = true diff --git a/src/core/commitments_inbox.cairo b/src/core/commitments_inbox.cairo index 1c9ecf9..64ae53d 100644 --- a/src/core/commitments_inbox.cairo +++ b/src/core/commitments_inbox.cairo @@ -103,7 +103,7 @@ mod CommitmentsInbox { }; } - #[external(v0)] + #[abi(embed_v0)] impl CommitmentsInbox of super::ICommitmentsInbox { // @inheritdoc ICommitmentsInbox fn get_headers_store(self: @ContractState) -> ContractAddress { diff --git a/src/core/evm_facts_registry.cairo b/src/core/evm_facts_registry.cairo index dac33fa..5c88b90 100644 --- a/src/core/evm_facts_registry.cairo +++ b/src/core/evm_facts_registry.cairo @@ -167,7 +167,7 @@ mod EVMFactsRegistry { self.headers_store.write(headers_store); } - #[external(v0)] + #[abi(embed_v0)] impl EVMFactsRegistry of super::IEVMFactsRegistry { // @inheritdoc IEVMFactsRegistry fn get_headers_store(self: @ContractState) -> ContractAddress { @@ -309,15 +309,9 @@ mod EVMFactsRegistry { AccountField::StorageHash(_) => { self.storage_hash.write((account, block), value); }, - AccountField::CodeHash(_) => { - self.code_hash.write((account, block), value); - }, - AccountField::Balance(_) => { - self.balance.write((account, block), value); - }, - AccountField::Nonce(_) => { - self.nonce.write((account, block), value); - } + AccountField::CodeHash(_) => { self.code_hash.write((account, block), value); }, + AccountField::Balance(_) => { self.balance.write((account, block), value); }, + AccountField::Nonce(_) => { self.nonce.write((account, block), value); } }; i += 1; @@ -373,7 +367,7 @@ mod EVMFactsRegistry { RLPItem::Bytes(_) => panic_with_felt252('Invalid header rlp'), RLPItem::List(l) => { let (state_root_words, _) = *l.at(0); - state_root = state_root_words.as_u256_le(32).unwrap(); + state_root = state_root_words.as_u256_le().unwrap(); let (block_number_words, block_number_byte_len) = *l.at(1); assert(block_number_words.len() == 1, 'Invalid block number'); @@ -433,18 +427,10 @@ mod EVMFactsRegistry { let field = fields.at(i); let (field_value, field_value_len) = match field { - AccountField::StorageHash(_) => { - *l.at(2) - }, - AccountField::CodeHash(_) => { - *l.at(3) - }, - AccountField::Balance(_) => { - *l.at(1) - }, - AccountField::Nonce(_) => { - *l.at(0) - }, + AccountField::StorageHash(_) => { *l.at(2) }, + AccountField::CodeHash(_) => { *l.at(3) }, + AccountField::Balance(_) => { *l.at(1) }, + AccountField::Nonce(_) => { *l.at(0) }, }; account_fields.append(field_value.as_u256_be(field_value_len).unwrap()); diff --git a/src/core/headers_store.cairo b/src/core/headers_store.cairo index 153b516..29e772b 100644 --- a/src/core/headers_store.cairo +++ b/src/core/headers_store.cairo @@ -235,7 +235,7 @@ mod HeadersStore { } - #[external(v0)] + #[abi(embed_v0)] impl HeadersStore of super::IHeadersStore { // @inheritdoc IHeadersStore fn get_commitments_inbox(self: @ContractState) -> ContractAddress { @@ -309,9 +309,7 @@ mod HeadersStore { decoded_rlp = d; rlp_byte_len = d_l; }, - Result::Err(_) => { - panic_with_felt252('Invalid header rlp'); - } + Result::Err(_) => { panic_with_felt252('Invalid header rlp'); } }; let valid_proof = mmr @@ -344,9 +342,7 @@ mod HeadersStore { decoded_rlp = d; rlp_byte_len = d_l; }, - Result::Err(_) => { - panic_with_felt252('Invalid header rlp'); - } + Result::Err(_) => { panic_with_felt252('Invalid header rlp'); } }; let reference_block = reference_block.unwrap(); @@ -380,7 +376,7 @@ mod HeadersStore { RLPItem::List(l) => { let (words, words_byte_len) = *l.at(0); assert(words.len() == 4 && words_byte_len == 32, 'Invalid parent_hash rlp'); - words.as_u256_le(32).unwrap() + words.as_u256_le().unwrap() }, }; @@ -391,9 +387,7 @@ mod HeadersStore { decoded_rlp = d; rlp_byte_len = d_l; }, - Result::Err(_) => { - panic_with_felt252('Invalid header rlp'); - } + Result::Err(_) => { panic_with_felt252('Invalid header rlp'); } }; let mut last_word_byte_len = rlp_byte_len % 8; @@ -503,7 +497,9 @@ mod HeadersStore { ); let mut mmr: MMR = Default::default(); - mmr.append(initial_poseidon_blockhash, array![].span()); + mmr + .append(initial_poseidon_blockhash, array![].span()) + .expect('Failed to append to MMR'); let root = mmr.root; let last_pos = mmr.last_pos; diff --git a/src/remappers/timestamp_remappers.cairo b/src/remappers/timestamp_remappers.cairo index f276733..06fa3cb 100644 --- a/src/remappers/timestamp_remappers.cairo +++ b/src/remappers/timestamp_remappers.cairo @@ -83,7 +83,7 @@ mod TimestampRemappers { // External // - #[external(v0)] + #[abi(embed_v0)] impl TimestampRemappers of ITimestampRemappers { // Creates a new mapper and returns its ID. fn create_mapper(ref self: ContractState, start_block: u256) -> usize { @@ -246,9 +246,7 @@ mod TimestampRemappers { let ((block_number, block_number_byte_len), (timestamp, timestamp_byte_len)) = match decoded_rlp { RLPItem::Bytes(_) => panic_with_felt252('Invalid header rlp'), - RLPItem::List(l) => { - (*l.at(0), *l.at(1)) - }, + RLPItem::List(l) => { (*l.at(0), *l.at(1)) }, }; ( reverse_endianness_u64(*block_number.at(0), Option::Some(block_number_byte_len)) @@ -301,7 +299,7 @@ mod TimestampRemappers { mid = (left + right) / 2; let proof_element: @ProofElement = proofs.at(proof_idx); assert( - (*proof_element.index).into() == leaf_index_to_mmr_index(mid + 1), + (*proof_element.index).into() == leaf_index_to_mmr_index(mid.try_into().unwrap() + 1), 'Unexpected proof index' ); @@ -333,7 +331,7 @@ mod TimestampRemappers { // Verify the proof if it has not already been checked let tree_closest_low_val = tree.left_neighbor.unwrap(); assert( - tree_closest_low_val.index.into() == leaf_index_to_mmr_index(closest_idx + 1), + tree_closest_low_val.index.into() == leaf_index_to_mmr_index(closest_idx.try_into().unwrap() + 1), 'Unexpected proof index (c)' ); From 8736de12f8e8b44ecebc05154afc67c8de7f98ab Mon Sep 17 00:00:00 2001 From: tiagofneto Date: Tue, 26 Mar 2024 11:56:45 +0100 Subject: [PATCH 02/48] upgraded test --- .gitignore | 2 ++ src/core/tests/test_facts_registry.cairo | 26 +++++++------------ src/core/tests/test_headers_store.cairo | 23 +++++++--------- .../tests/test_timestamp_remappers.cairo | 24 ++++++----------- 4 files changed, 29 insertions(+), 46 deletions(-) diff --git a/.gitignore b/.gitignore index a529e00..a47ce4a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ target .DS_Store l1/broadcast/** + +.snfoundry_cache diff --git a/src/core/tests/test_facts_registry.cairo b/src/core/tests/test_facts_registry.cairo index a9199ff..d6fc698 100644 --- a/src/core/tests/test_facts_registry.cairo +++ b/src/core/tests/test_facts_registry.cairo @@ -1,4 +1,4 @@ -use snforge_std::{declare, PreparedContract, deploy, start_prank, stop_prank}; +use snforge_std::{declare, ContractClassTrait, start_prank, stop_prank, CheatTarget}; use herodotus_eth_starknet::core::headers_store::{ IHeadersStoreDispatcherTrait, IHeadersStoreDispatcher @@ -20,27 +20,21 @@ const TEST_BLOCK: u256 = 17000000; fn helper_create_facts_registry( mmr_root: felt252, mmr_size: usize ) -> (IEVMFactsRegistryDispatcher, ContractAddress) { - let class_hash = declare('HeadersStore'); - let prepared = PreparedContract { - class_hash: class_hash, constructor_calldata: @array![COMMITMENTS_INBOX_ADDRESS] - }; - let contract_address = deploy(prepared).unwrap(); + let contract = declare("HeadersStore"); + let contract_address = contract.deploy(@array![COMMITMENTS_INBOX_ADDRESS]).unwrap(); let mut headers_store = IHeadersStoreDispatcher { contract_address }; - start_prank(contract_address, COMMITMENTS_INBOX_ADDRESS.try_into().unwrap()); + start_prank(CheatTarget::One(contract_address), COMMITMENTS_INBOX_ADDRESS.try_into().unwrap()); headers_store.create_branch_from_message(mmr_root, mmr_size, 0); - stop_prank(contract_address); + stop_prank(CheatTarget::One(contract_address)); - let class_hash = declare('EVMFactsRegistry'); - let prepared = PreparedContract { - class_hash: class_hash, constructor_calldata: @array![contract_address.into()] - }; - let contract_address = deploy(prepared).unwrap(); + let contract = declare("EVMFactsRegistry"); + let contract_address = contract.deploy(@array![contract_address.into()]).unwrap(); (IEVMFactsRegistryDispatcher { contract_address }, contract_address) } #[test] fn test_prove_account() { - let (dispatcher, contract_address) = helper_create_facts_registry(TEST_MMR_ROOT, TEST_MMR_SIZE); + let (dispatcher, _) = helper_create_facts_registry(TEST_MMR_ROOT, TEST_MMR_SIZE); // Testing block 17000000 let block_header_rlp = *helper_get_headers_rlp().at(0); @@ -86,7 +80,7 @@ fn test_prove_account() { #[test] fn test_prove_storage() { // Expected key based on proof: 0x290decd | 39548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563 - let (dispatcher, contract_address) = helper_create_facts_registry(TEST_MMR_ROOT, TEST_MMR_SIZE); + let (dispatcher, _) = helper_create_facts_registry(TEST_MMR_ROOT, TEST_MMR_SIZE); // Testing block 17000000 let block_header_rlp = *helper_get_headers_rlp().at(0); @@ -121,7 +115,7 @@ fn test_prove_storage() { #[test] fn test_prove_storage_empty() { // Expected key based on proof: 0x290decd | 39548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563 - let (dispatcher, contract_address) = helper_create_facts_registry(TEST_MMR_ROOT, TEST_MMR_SIZE); + let (dispatcher, _) = helper_create_facts_registry(TEST_MMR_ROOT, TEST_MMR_SIZE); // Testing block 17000000 let block_header_rlp = *helper_get_headers_rlp().at(0); diff --git a/src/core/tests/test_headers_store.cairo b/src/core/tests/test_headers_store.cairo index b32bf39..38e9330 100644 --- a/src/core/tests/test_headers_store.cairo +++ b/src/core/tests/test_headers_store.cairo @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0 -use snforge_std::{declare, PreparedContract, deploy, start_prank, stop_prank}; +use snforge_std::{declare, ContractClassTrait, start_prank, stop_prank, CheatTarget}; use herodotus_eth_starknet::core::headers_store::{ IHeadersStoreDispatcherTrait, IHeadersStoreDispatcher, IHeadersStoreSafeDispatcherTrait, IHeadersStoreSafeDispatcher @@ -14,20 +14,14 @@ const MMR_INITIAL_ELEMENT: felt252 = const MMR_INITIAL_ROOT: felt252 = 0x6759138078831011e3bc0b4a135af21c008dda64586363531697207fb5a2bae; fn helper_create_headers_store() -> (IHeadersStoreDispatcher, ContractAddress) { - let class_hash = declare('HeadersStore'); - let prepared = PreparedContract { - class_hash: class_hash, constructor_calldata: @array![COMMITMENTS_INBOX_ADDRESS] - }; - let contract_address = deploy(prepared).unwrap(); + let contract = declare("HeadersStore"); + let contract_address = contract.deploy(@array![COMMITMENTS_INBOX_ADDRESS]).unwrap(); (IHeadersStoreDispatcher { contract_address }, contract_address) } fn helper_create_safe_headers_store() -> (IHeadersStoreSafeDispatcher, ContractAddress) { - let class_hash = declare('HeadersStore'); - let prepared = PreparedContract { - class_hash: class_hash, constructor_calldata: @array![COMMITMENTS_INBOX_ADDRESS] - }; - let contract_address = deploy(prepared).unwrap(); + let contract = declare("HeadersStore"); + let contract_address = contract.deploy(@array![COMMITMENTS_INBOX_ADDRESS]).unwrap(); (IHeadersStoreSafeDispatcher { contract_address }, contract_address) } @@ -37,12 +31,13 @@ fn helper_receive_hash( dispatcher: IHeadersStoreDispatcher, contract_address: ContractAddress ) { - start_prank(contract_address, COMMITMENTS_INBOX_ADDRESS.try_into().unwrap()); + start_prank(CheatTarget::One(contract_address), COMMITMENTS_INBOX_ADDRESS.try_into().unwrap()); dispatcher.receive_hash(blockhash, block_number); - stop_prank(contract_address); + stop_prank(CheatTarget::One(contract_address)); } #[test] +#[feature("safe_dispatcher")] fn test_receive_hash_wrong_address() { let (safe_dispatcher, _) = helper_create_safe_headers_store(); @@ -66,7 +61,7 @@ fn test_receive_hash() { #[test] fn test_initial_tree() { - let (dispatcher, contract_address) = helper_create_headers_store(); + let (dispatcher, _) = helper_create_headers_store(); let mmr_id = 0; diff --git a/src/remappers/tests/test_timestamp_remappers.cairo b/src/remappers/tests/test_timestamp_remappers.cairo index 1ded3a3..24b671c 100644 --- a/src/remappers/tests/test_timestamp_remappers.cairo +++ b/src/remappers/tests/test_timestamp_remappers.cairo @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0 -use snforge_std::{declare, PreparedContract, deploy, start_prank, stop_prank}; +use snforge_std::{declare, ContractClassTrait, start_prank, stop_prank, CheatTarget}; use starknet::ContractAddress; use cairo_lib::data_structures::mmr::mmr::MMR; use cairo_lib::data_structures::mmr::mmr::MMRTrait; @@ -15,27 +15,19 @@ use herodotus_eth_starknet::core::headers_store::{ }; fn deploy_headers_store() -> ContractAddress { - let class_hash = declare('HeadersStore'); + let contract = declare("HeadersStore"); let mut constructor_calldata = ArrayTrait::new(); constructor_calldata.append(0); - let prepared = PreparedContract { - class_hash: class_hash, constructor_calldata: @constructor_calldata - }; - - deploy(prepared).unwrap() + contract.deploy(@constructor_calldata).unwrap() } fn deploy_timestamp_remappers(headers_store: ContractAddress) -> ContractAddress { - let class_hash = declare('TimestampRemappers'); + let contract = declare("TimestampRemappers"); let mut constructor_calldata: Array = ArrayTrait::new(); constructor_calldata.append(headers_store.into()); - let prepared = PreparedContract { - class_hash: class_hash, constructor_calldata: @constructor_calldata - }; - - deploy(prepared).unwrap() + contract.deploy(@constructor_calldata).unwrap() } fn inner_test_proof(mmr: @MMR) { @@ -78,7 +70,7 @@ fn prepare_mmr() -> MMR { let mut peaks_2 = ArrayTrait::new(); peaks_2.append(0x5d44a3decb2b2e0cc71071f7b802f45dd792d064f0fc7316c46514f70f9891a); - mmr.append(4, peaks_2.span()); + mmr.append(4, peaks_2.span()).unwrap(); peaks_2.append(4); mmr.append(5, peaks_2.span()).unwrap(); @@ -376,12 +368,12 @@ fn test_remappers() { let headers_store_dispatcher = IHeadersStoreDispatcher { contract_address: headers_store }; let mmr_id = 1; // First MMR - start_prank(headers_store, 0.try_into().unwrap()); + start_prank(CheatTarget::One(headers_store), 0.try_into().unwrap()); headers_store_dispatcher .create_branch_from_message( 0x25aedbc0ddea804ce21d29a39f00358f68df0e462114f75b0576182d08db0, 4, 1 ); - stop_prank(headers_store); + stop_prank(CheatTarget::One(headers_store)); inner_test_reindex_batch(remapper_dispatcher, mapper_id, mmr_id); From e99ffc1df053d78a3d1459667f3f4feaac2adb57 Mon Sep 17 00:00:00 2001 From: tiagofneto Date: Tue, 26 Mar 2024 12:44:01 +0100 Subject: [PATCH 03/48] fixed multicall --- Scarb.toml | 2 +- multicall/deploy.toml | 2 +- snfoundry.toml | 4 ++++ src/remappers/timestamp_remappers.cairo | 7 +++++-- 4 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 snfoundry.toml diff --git a/Scarb.toml b/Scarb.toml index 0868df8..780c43d 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -21,4 +21,4 @@ exit_first = true [tool.sncast] account = "herodotus" network = "testnet" -url = "https://starknet-testnet.public.blastapi.io" +url = "https://starknet-sepolia.public.blastapi.io" diff --git a/multicall/deploy.toml b/multicall/deploy.toml index a0e6694..6bca8b5 100644 --- a/multicall/deploy.toml +++ b/multicall/deploy.toml @@ -5,7 +5,7 @@ inputs = [ "0x1", "0x18E4A8e2badB5f5950758F46f8108E2C5d357b07", "0x0", - "0x007327d012f432a9f940228ae6b01032ea4742d8bba8599d7a34e1d5c120e983", + "0x28bd4d8bf3a4091e42842fd45c008931113c391bd5d769b075c6e6e83fc6272", ] id = "commitments_inbox" unique = false diff --git a/snfoundry.toml b/snfoundry.toml new file mode 100644 index 0000000..2d23787 --- /dev/null +++ b/snfoundry.toml @@ -0,0 +1,4 @@ +# Default profile +[sncast.default] +account = "herodotus" +url = "https://free-rpc.nethermind.io/sepolia-juno" diff --git a/src/remappers/timestamp_remappers.cairo b/src/remappers/timestamp_remappers.cairo index 06fa3cb..851b11f 100644 --- a/src/remappers/timestamp_remappers.cairo +++ b/src/remappers/timestamp_remappers.cairo @@ -299,7 +299,8 @@ mod TimestampRemappers { mid = (left + right) / 2; let proof_element: @ProofElement = proofs.at(proof_idx); assert( - (*proof_element.index).into() == leaf_index_to_mmr_index(mid.try_into().unwrap() + 1), + (*proof_element.index) + .into() == leaf_index_to_mmr_index(mid.try_into().unwrap() + 1), 'Unexpected proof index' ); @@ -331,7 +332,9 @@ mod TimestampRemappers { // Verify the proof if it has not already been checked let tree_closest_low_val = tree.left_neighbor.unwrap(); assert( - tree_closest_low_val.index.into() == leaf_index_to_mmr_index(closest_idx.try_into().unwrap() + 1), + tree_closest_low_val + .index + .into() == leaf_index_to_mmr_index(closest_idx.try_into().unwrap() + 1), 'Unexpected proof index (c)' ); From 27ce28911fb9a152cc6edc3edf0d975630f32748 Mon Sep 17 00:00:00 2001 From: tiagofneto Date: Tue, 26 Mar 2024 15:28:05 +0100 Subject: [PATCH 04/48] profiling --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index a47ce4a..cdfe0f9 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ target l1/broadcast/** .snfoundry_cache +.snfoundry_trace +profile.pb.gz From 582bc86864ba5bfbf64b516c11e385a678b9b2ac Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Thu, 2 May 2024 12:33:19 +0200 Subject: [PATCH 05/48] Update deployment class_hashes --- .tool-versions | 2 +- multicall/deploy.toml | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.tool-versions b/.tool-versions index 780f6f9..8ed6949 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -scarb 2.6.4 +scarb nightly-2024-04-20 starknet-foundry 0.20.1 diff --git a/multicall/deploy.toml b/multicall/deploy.toml index 6bca8b5..f375bbe 100644 --- a/multicall/deploy.toml +++ b/multicall/deploy.toml @@ -1,25 +1,25 @@ [[call]] call_type = "deploy" -class_hash = "0x4618004201b8cc18d82ef9e909e8d653cd5ef0327f4b9f40d014eaeb93d8598" +class_hash = "0x54af96825d987ca89cf320f7c5a8031017815d884cff1592e8ff6da309f3ca6" inputs = [ "0x1", - "0x18E4A8e2badB5f5950758F46f8108E2C5d357b07", + "0x18e4a8e2badb5f5950758f46f8108e2c5d357b07", "0x0", - "0x28bd4d8bf3a4091e42842fd45c008931113c391bd5d769b075c6e6e83fc6272", + "0x5a6c0f84179d695f0b598cc5d0be50421c247da95cfe63e4cd66fc27f32dfe6", ] id = "commitments_inbox" unique = false [[call]] call_type = "deploy" -class_hash = "0x229789b99574e92c01f99e9d6f16a3cc6326085733504a8b7ca367606b40ab4" +class_hash = "0x1b0eeb6325cbb7874f5ee8d3f59df2617113073b52911cfca9077ec9d581555" inputs = ["commitments_inbox"] id = "headers_store" unique = false [[call]] call_type = "deploy" -class_hash = "0x4438ef289f10b9c687089b5ad218d8465a9266af74834356dbd78a35cfaf618" +class_hash = "0x3211460c883d24937aa616ec31d01a3f09fafb053439b9b1b7bedf72df00ce" inputs = ["headers_store"] id = "evm_facts_registry" unique = false @@ -29,3 +29,10 @@ call_type = "invoke" contract_address = "commitments_inbox" function = "set_headers_store" inputs = ["headers_store"] + +[[call]] +call_type = "deploy" +class_hash = "0xa5816245847010c159074cd8779a3fb38c1e0a887ad63f5b0f06ab9a9cb788" +inputs = ["headers_store"] +id = "timestamp_remappers" +unique = false \ No newline at end of file From e04a87924c2f856ebad3c0de9e907a61d2380d8e Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Mon, 13 May 2024 12:28:56 +0200 Subject: [PATCH 06/48] Add type aliases for MmrId and MmrSize --- src/core/headers_store.cairo | 108 ++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 52 deletions(-) diff --git a/src/core/headers_store.cairo b/src/core/headers_store.cairo index 29e772b..7a4c0aa 100644 --- a/src/core/headers_store.cairo +++ b/src/core/headers_store.cairo @@ -4,6 +4,9 @@ use cairo_lib::data_structures::mmr::proof::Proof; use cairo_lib::utils::types::words64::Words64; use cairo_lib::data_structures::mmr::mmr::MMR; +type MmrId = usize; +type MmrSize = usize; + #[starknet::interface] trait IHeadersStore { // @notice Returns the address of the CommitmentsInbox contract @@ -13,31 +16,31 @@ trait IHeadersStore { // @notice Returns the MMR with a given id // @param mmr_id The id of the MMR // @return The MMR with the given id - fn get_mmr(self: @TContractState, mmr_id: usize) -> MMR; + fn get_mmr(self: @TContractState, mmr_id: MmrId) -> MMR; // @notice Returns the root of the MMR with a given id // @param mmr_id The id of the MMR // @return The root of the MMR with the given id - fn get_mmr_root(self: @TContractState, mmr_id: usize) -> felt252; + fn get_mmr_root(self: @TContractState, mmr_id: MmrId) -> felt252; // @notice Returns the size of the MMR with a given id // @param mmr_id The id of the MMR // @return The size of the MMR with the given id - fn get_mmr_size(self: @TContractState, mmr_id: usize) -> usize; + fn get_mmr_size(self: @TContractState, mmr_id: MmrId) -> MmrSize; // @notice Returns the parent blockhash of a given block number, received from L1 and send throught the CommitmentsInbox fn get_received_block(self: @TContractState, block_number: u256) -> u256; // @notice Returns the latest MMR id // @dev MMR IDs are incremental - fn get_latest_mmr_id(self: @TContractState) -> usize; + fn get_latest_mmr_id(self: @TContractState) -> MmrId; // @notice Returns the root of the MMR with a given id and size // @dev The reason why we need to get historical roots is because we don't want MMR proofs to expire // @param mmr_id The id of the MMR // @param size The size of the MMR // @return The root of the MMR with the given id and size - fn get_historical_root(self: @TContractState, mmr_id: usize, size: usize) -> felt252; + fn get_historical_root(self: @TContractState, mmr_id: MmrId, size: MmrSize) -> felt252; // @notice Receives a parent blockhash and the corresponding block number from L1 and saves it // @dev This function can only be called by the CommitmentsInbox contract @@ -53,11 +56,11 @@ trait IHeadersStore { // @return True if the proof is valid and the element is present, false otherwise fn verify_mmr_inclusion( self: @TContractState, - index: usize, + index: MmrSize, poseidon_blockhash: felt252, peaks: Peaks, proof: Proof, - mmr_id: usize, + mmr_id: MmrId, ) -> bool; // @notice Verifies an inclusion proof in an MMR @@ -71,12 +74,12 @@ trait IHeadersStore { // @return True if the proof is valid and the element is present, false otherwise fn verify_historical_mmr_inclusion( self: @TContractState, - index: usize, + index: MmrSize, poseidon_blockhash: felt252, peaks: Peaks, proof: Proof, - mmr_id: usize, - last_pos: usize, + mmr_id: MmrId, + last_pos: MmrSize, ) -> bool; // @notice Appends a batch of block hashes to the MMR starting from a specific block, either from a hash received from L1 or from an MMR element @@ -92,9 +95,9 @@ trait IHeadersStore { ref self: TContractState, headers_rlp: Span, mmr_peaks: Peaks, - mmr_id: usize, + mmr_id: MmrId, reference_block: Option, - mmr_index: Option, + mmr_index: Option, mmr_proof: Option, ); @@ -104,7 +107,7 @@ trait IHeadersStore { // @param aggregator_id The id of the L1 aggregator // @dev This function can only be called by the CommitmentsInbox contract fn create_branch_from_message( - ref self: TContractState, root: felt252, last_pos: usize, aggregator_id: usize + ref self: TContractState, root: felt252, last_pos: MmrSize, aggregator_id: usize ); @@ -117,18 +120,18 @@ trait IHeadersStore { // @param last_pos last_pos of the given MMR fn create_branch_single_element( ref self: TContractState, - index: usize, + index: MmrSize, initial_poseidon_blockhash: felt252, peaks: Peaks, proof: Proof, - mmr_id: usize, - last_pos: usize + mmr_id: MmrId, + last_pos: MmrSize ); // @notice Creates a new MMR that is a clone of an already existing MMR // @param mmr_id The id of the MMR to clone // @param last_pos last_pos of the given MMR - fn create_branch_from(ref self: TContractState, mmr_id: usize, last_pos: usize); + fn create_branch_from(ref self: TContractState, mmr_id: MmrId, last_pos: MmrSize); } @@ -148,6 +151,7 @@ mod HeadersStore { use cairo_lib::hashing::poseidon::hash_words64; use cairo_lib::utils::bitwise::reverse_endianness_u256; use cairo_lib::encoding::rlp::{RLPItem, rlp_decode_list_lazy}; + use super::{MmrId, MmrSize}; const MMR_INITIAL_ROOT: felt252 = 0x6759138078831011e3bc0b4a135af21c008dda64586363531697207fb5a2bae; @@ -155,12 +159,11 @@ mod HeadersStore { #[storage] struct Storage { commitments_inbox: ContractAddress, - mmr: LegacyMap::, - // (id, size) => root - mmr_history: LegacyMap::<(usize, usize), felt252>, + mmr: LegacyMap::, + mmr_history: LegacyMap::<(MmrId, MmrSize), felt252>, // block_number => parent blockhash received_blocks: LegacyMap::, - latest_mmr_id: usize + latest_mmr_id: MmrId } #[event] @@ -184,8 +187,8 @@ mod HeadersStore { struct ProcessedBlock { block_number: u256, new_root: felt252, - new_size: usize, - mmr_id: usize + new_size: MmrSize, + mmr_id: MmrId } #[derive(Drop, starknet::Event)] @@ -193,33 +196,33 @@ mod HeadersStore { block_start: u256, block_end: u256, new_root: felt252, - new_size: usize, - mmr_id: usize + new_size: MmrSize, + mmr_id: MmrId } #[derive(Drop, starknet::Event)] struct BranchCreatedFromElement { - mmr_id: usize, + mmr_id: MmrId, root: felt252, - last_pos: usize, - detached_from_mmr_id: usize, - mmr_index: usize + last_pos: MmrSize, + detached_from_mmr_id: MmrId, + mmr_index: MmrSize } #[derive(Drop, starknet::Event)] struct BranchCreatedFromL1 { - mmr_id: usize, + mmr_id: MmrId, root: felt252, - last_pos: usize, + last_pos: MmrSize, aggregator_id: usize } #[derive(Drop, starknet::Event)] struct BranchCreatedClone { - mmr_id: usize, + mmr_id: MmrId, root: felt252, - last_pos: usize, - detached_from_mmr_id: usize + last_pos: MmrSize, + detached_from_mmr_id: MmrId } #[constructor] @@ -243,17 +246,17 @@ mod HeadersStore { } // @inheritdoc IHeadersStore - fn get_mmr(self: @ContractState, mmr_id: usize) -> MMR { + fn get_mmr(self: @ContractState, mmr_id: MmrId) -> MMR { self.mmr.read(mmr_id) } // @inheritdoc IHeadersStore - fn get_mmr_root(self: @ContractState, mmr_id: usize) -> felt252 { + fn get_mmr_root(self: @ContractState, mmr_id: MmrId) -> felt252 { self.mmr.read(mmr_id).root } // @inheritdoc IHeadersStore - fn get_mmr_size(self: @ContractState, mmr_id: usize) -> usize { + fn get_mmr_size(self: @ContractState, mmr_id: MmrId) -> MmrSize { self.mmr.read(mmr_id).last_pos } @@ -263,12 +266,12 @@ mod HeadersStore { } // @inheritdoc IHeadersStore - fn get_latest_mmr_id(self: @ContractState) -> usize { + fn get_latest_mmr_id(self: @ContractState) -> MmrId { self.latest_mmr_id.read() } // @inheritdoc IHeadersStore - fn get_historical_root(self: @ContractState, mmr_id: usize, size: usize) -> felt252 { + fn get_historical_root(self: @ContractState, mmr_id: MmrId, size: MmrSize) -> felt252 { self.mmr_history.read((mmr_id, size)) } @@ -287,9 +290,9 @@ mod HeadersStore { ref self: ContractState, headers_rlp: Span, mmr_peaks: Peaks, - mmr_id: usize, + mmr_id: MmrId, reference_block: Option, - mmr_index: Option, + mmr_index: Option, mmr_proof: Option, ) { let mut mmr = self.mmr.read(mmr_id); @@ -298,7 +301,8 @@ mod HeadersStore { let mut start_block = 0; let mut end_block = 0; - let (mut decoded_rlp, mut rlp_byte_len) = (RLPItem::Bytes((array![].span(), 0)), 0); + let mut decoded_rlp = RLPItem::Bytes((array![].span(), 0)); + let mut rlp_byte_len = 0; if mmr_proof.is_some() { assert(reference_block.is_none(), 'Cannot use proof AND ref block'); @@ -427,11 +431,11 @@ mod HeadersStore { // @inheritdoc IHeadersStore fn verify_mmr_inclusion( self: @ContractState, - index: usize, + index: MmrSize, poseidon_blockhash: felt252, peaks: Peaks, proof: Proof, - mmr_id: usize, + mmr_id: MmrId, ) -> bool { let mmr = self.mmr.read(mmr_id); @@ -443,12 +447,12 @@ mod HeadersStore { // @inheritdoc IHeadersStore fn verify_historical_mmr_inclusion( self: @ContractState, - index: usize, + index: MmrSize, poseidon_blockhash: felt252, peaks: Peaks, proof: Proof, - mmr_id: usize, - last_pos: usize, + mmr_id: MmrId, + last_pos: MmrSize, ) -> bool { let root = self.mmr_history.read((mmr_id, last_pos)); let mmr = MMRTrait::new(root, last_pos); @@ -460,7 +464,7 @@ mod HeadersStore { // @inheritdoc IHeadersStore fn create_branch_from_message( - ref self: ContractState, root: felt252, last_pos: usize, aggregator_id: usize + ref self: ContractState, root: felt252, last_pos: MmrSize, aggregator_id: usize ) { let caller = get_caller_address(); assert(caller == self.commitments_inbox.read(), 'Only CommitmentsInbox'); @@ -482,12 +486,12 @@ mod HeadersStore { // @inheritdoc IHeadersStore fn create_branch_single_element( ref self: ContractState, - index: usize, + index: MmrSize, initial_poseidon_blockhash: felt252, peaks: Peaks, proof: Proof, - mmr_id: usize, - last_pos: usize + mmr_id: MmrId, + last_pos: MmrSize ) { assert( HeadersStore::verify_historical_mmr_inclusion( @@ -524,7 +528,7 @@ mod HeadersStore { } // @inheritdoc IHeadersStore - fn create_branch_from(ref self: ContractState, mmr_id: usize, last_pos: usize) { + fn create_branch_from(ref self: ContractState, mmr_id: MmrId, last_pos: MmrSize) { let latest_mmr_id = self.latest_mmr_id.read() + 1; let root = self.mmr_history.read((mmr_id, last_pos)); From f940dee6d19faab25ebaf7df1008c4c610686210 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Mon, 13 May 2024 12:59:15 +0200 Subject: [PATCH 07/48] More type aliases --- src/core.cairo | 1 + src/core/commitments_inbox.cairo | 5 +-- src/core/common.cairo | 2 ++ src/core/evm_facts_registry.cairo | 32 ++++++++++--------- src/core/headers_store.cairo | 6 ++-- src/core/tests/test_facts_registry.cairo | 5 +-- src/remappers/interface.cairo | 20 ++++++------ .../tests/test_timestamp_remappers.cairo | 5 +-- src/remappers/timestamp_remappers.cairo | 26 +++++++-------- 9 files changed, 54 insertions(+), 48 deletions(-) create mode 100644 src/core/common.cairo diff --git a/src/core.cairo b/src/core.cairo index a2a21a1..db2f374 100644 --- a/src/core.cairo +++ b/src/core.cairo @@ -1,3 +1,4 @@ +mod common; mod commitments_inbox; mod headers_store; mod evm_facts_registry; diff --git a/src/core/commitments_inbox.cairo b/src/core/commitments_inbox.cairo index 64ae53d..40cf283 100644 --- a/src/core/commitments_inbox.cairo +++ b/src/core/commitments_inbox.cairo @@ -46,6 +46,7 @@ mod CommitmentsInbox { use herodotus_eth_starknet::core::headers_store::{ IHeadersStoreDispatcherTrait, IHeadersStoreDispatcher }; + use herodotus_eth_starknet::core::common::MmrSize; #[storage] struct Storage { @@ -83,7 +84,7 @@ mod CommitmentsInbox { #[derive(Drop, starknet::Event)] struct MMRReceived { root: felt252, - last_pos: usize, + last_pos: MmrSize, aggregator_id: usize } @@ -203,7 +204,7 @@ mod CommitmentsInbox { ref self: ContractState, from_address: felt252, root: felt252, - last_pos: usize, + last_pos: MmrSize, aggregator_id: usize ) { assert(from_address == self.l1_message_sender.read().into(), 'Invalid sender'); diff --git a/src/core/common.cairo b/src/core/common.cairo new file mode 100644 index 0000000..dd86926 --- /dev/null +++ b/src/core/common.cairo @@ -0,0 +1,2 @@ +type MmrId = u256; +type MmrSize = usize; diff --git a/src/core/evm_facts_registry.cairo b/src/core/evm_facts_registry.cairo index 5c88b90..9843cd5 100644 --- a/src/core/evm_facts_registry.cairo +++ b/src/core/evm_facts_registry.cairo @@ -2,6 +2,7 @@ use starknet::ContractAddress; use cairo_lib::data_structures::mmr::proof::Proof; use cairo_lib::data_structures::mmr::peaks::Peaks; use cairo_lib::utils::types::words64::Words64; +use herodotus_eth_starknet::core::common::{MmrId, MmrSize}; #[derive(Drop, Serde)] enum AccountField { @@ -52,11 +53,11 @@ trait IEVMFactsRegistry { block_header_rlp: Words64, account: felt252, mpt_proof: Span, - mmr_index: usize, + mmr_index: MmrSize, mmr_peaks: Peaks, mmr_proof: Proof, - mmr_id: usize, - last_pos: usize, + mmr_id: MmrId, + last_pos: MmrSize, ) -> Span; // @notice Gets a storage slot from a proven account @@ -85,11 +86,11 @@ trait IEVMFactsRegistry { block_header_rlp: Words64, account: felt252, mpt_proof: Span, - mmr_index: usize, + mmr_index: MmrSize, mmr_peaks: Peaks, mmr_proof: Proof, - mmr_id: usize, - last_pos: usize, + mmr_id: MmrId, + last_pos: MmrSize, ); // @notice Proves a storage slot at a given block @@ -121,6 +122,7 @@ mod EVMFactsRegistry { use cairo_lib::utils::types::words64::{ Words64, Words64Trait, reverse_endianness_u64, bytes_used_u64 }; + use herodotus_eth_starknet::core::common::{MmrId, MmrSize}; use herodotus_eth_starknet::core::headers_store::{ IHeadersStoreDispatcherTrait, IHeadersStoreDispatcher }; @@ -200,11 +202,11 @@ mod EVMFactsRegistry { block_header_rlp: Words64, account: felt252, mpt_proof: Span, - mmr_index: usize, + mmr_index: MmrSize, mmr_peaks: Peaks, mmr_proof: Proof, - mmr_id: usize, - last_pos: usize, + mmr_id: MmrId, + last_pos: MmrSize, ) -> Span { let (_, fields) = InternalFunctions::get_account( self, @@ -277,11 +279,11 @@ mod EVMFactsRegistry { block_header_rlp: Words64, account: felt252, mpt_proof: Span, - mmr_index: usize, + mmr_index: MmrSize, mmr_peaks: Peaks, mmr_proof: Proof, - mmr_id: usize, - last_pos: usize, + mmr_id: MmrId, + last_pos: MmrSize, ) { let (block, field_values) = InternalFunctions::get_account( @self, @@ -344,11 +346,11 @@ mod EVMFactsRegistry { block_header_rlp: Words64, account: felt252, mpt_proof: Span, - mmr_index: usize, + mmr_index: MmrSize, mmr_peaks: Peaks, mmr_proof: Proof, - mmr_id: usize, - last_pos: usize, + mmr_id: MmrId, + last_pos: MmrSize, ) -> (u256, Span) { let blockhash = hash_words64(block_header_rlp); diff --git a/src/core/headers_store.cairo b/src/core/headers_store.cairo index 7a4c0aa..20204a9 100644 --- a/src/core/headers_store.cairo +++ b/src/core/headers_store.cairo @@ -3,9 +3,7 @@ use cairo_lib::data_structures::mmr::peaks::Peaks; use cairo_lib::data_structures::mmr::proof::Proof; use cairo_lib::utils::types::words64::Words64; use cairo_lib::data_structures::mmr::mmr::MMR; - -type MmrId = usize; -type MmrSize = usize; +use herodotus_eth_starknet::core::common::{MmrId, MmrSize}; #[starknet::interface] trait IHeadersStore { @@ -151,7 +149,7 @@ mod HeadersStore { use cairo_lib::hashing::poseidon::hash_words64; use cairo_lib::utils::bitwise::reverse_endianness_u256; use cairo_lib::encoding::rlp::{RLPItem, rlp_decode_list_lazy}; - use super::{MmrId, MmrSize}; + use herodotus_eth_starknet::core::common::{MmrId, MmrSize}; const MMR_INITIAL_ROOT: felt252 = 0x6759138078831011e3bc0b4a135af21c008dda64586363531697207fb5a2bae; diff --git a/src/core/tests/test_facts_registry.cairo b/src/core/tests/test_facts_registry.cairo index d6fc698..32699f8 100644 --- a/src/core/tests/test_facts_registry.cairo +++ b/src/core/tests/test_facts_registry.cairo @@ -6,6 +6,7 @@ use herodotus_eth_starknet::core::headers_store::{ use herodotus_eth_starknet::core::evm_facts_registry::{ IEVMFactsRegistryDispatcherTrait, IEVMFactsRegistryDispatcher, AccountField }; +use herodotus_eth_starknet::core::common::MmrSize; use starknet::ContractAddress; use cairo_lib::utils::types::words64::Words64; use cairo_lib::hashing::poseidon::{hash_words64, PoseidonHasher}; @@ -13,12 +14,12 @@ use cairo_lib::data_structures::mmr::{proof::Proof, peaks::Peaks}; const COMMITMENTS_INBOX_ADDRESS: felt252 = 0x123; const TEST_MMR_ROOT: felt252 = 0x37a31db9c80c54ec632f04f7984155dc43591a3f8c891adfbf34e75331e0eec; -const TEST_MMR_SIZE: usize = 8; +const TEST_MMR_SIZE: MmrSize = 8; const TEST_ACCOUNT: felt252 = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; const TEST_BLOCK: u256 = 17000000; fn helper_create_facts_registry( - mmr_root: felt252, mmr_size: usize + mmr_root: felt252, mmr_size: MmrSize ) -> (IEVMFactsRegistryDispatcher, ContractAddress) { let contract = declare("HeadersStore"); let contract_address = contract.deploy(@array![COMMITMENTS_INBOX_ADDRESS]).unwrap(); diff --git a/src/remappers/interface.cairo b/src/remappers/interface.cairo index 33ed27b..b4404bf 100644 --- a/src/remappers/interface.cairo +++ b/src/remappers/interface.cairo @@ -7,14 +7,16 @@ use cairo_lib::data_structures::mmr::proof::Proof; use cairo_lib::data_structures::mmr::peaks::Peaks; use cairo_lib::utils::types::words64::Words64; +use herodotus_eth_starknet::core::common::{MmrId, MmrSize}; type Headers = Span; +type MapperId = usize; #[derive(Drop, Serde)] struct OriginElement { - tree_id: usize, - last_pos: usize, - leaf_idx: usize, + tree_id: MmrId, + last_pos: MmrSize, + leaf_idx: MmrSize, leaf_value: felt252, inclusion_proof: Proof, peaks: Peaks, @@ -23,15 +25,15 @@ struct OriginElement { #[derive(Drop, Serde)] struct ProofElement { - index: usize, + index: MmrSize, value: u256, proof: Proof, } #[derive(Drop, Serde)] struct BinarySearchTree { - mapper_id: usize, - last_pos: usize, // last_pos in mapper's MMR + mapper_id: MapperId, + last_pos: MmrSize, // last_pos in mapper's MMR peaks: Peaks, proofs: Span, // Midpoint elements inclusion proofs left_neighbor: Option, // Optional left neighbor inclusion proof @@ -44,12 +46,12 @@ struct BinarySearchTree { #[starknet::interface] trait ITimestampRemappers { // Creates a new mapper and returns its ID. - fn create_mapper(ref self: TContractState, start_block: u256) -> usize; + fn create_mapper(ref self: TContractState, start_block: u256) -> MapperId; // Adds elements from other trusted data sources to the given mapper. fn reindex_batch( ref self: TContractState, - mapper_id: usize, + mapper_id: MapperId, mapper_peaks: Peaks, origin_elements: Span ); @@ -60,5 +62,5 @@ trait ITimestampRemappers { ) -> Result, felt252>; // Getter for the last timestamp of a given mapper. - fn get_last_mapper_timestamp(self: @TContractState, mapper_id: usize) -> u256; + fn get_last_mapper_timestamp(self: @TContractState, mapper_id: MapperId) -> u256; } diff --git a/src/remappers/tests/test_timestamp_remappers.cairo b/src/remappers/tests/test_timestamp_remappers.cairo index 24b671c..e826f2f 100644 --- a/src/remappers/tests/test_timestamp_remappers.cairo +++ b/src/remappers/tests/test_timestamp_remappers.cairo @@ -8,11 +8,12 @@ use cairo_lib::utils::bitwise::bit_length; use cairo_lib::utils::math::pow; use herodotus_eth_starknet::remappers::interface::{ ITimestampRemappersDispatcherTrait, ITimestampRemappersDispatcher, BinarySearchTree, - ProofElement, OriginElement + ProofElement, OriginElement, MapperId }; use herodotus_eth_starknet::core::headers_store::{ IHeadersStoreDispatcherTrait, IHeadersStoreDispatcher }; +use herodotus_eth_starknet::core::common::MmrId; fn deploy_headers_store() -> ContractAddress { let contract = declare("HeadersStore"); @@ -83,7 +84,7 @@ fn prepare_mmr() -> MMR { } fn inner_test_reindex_batch( - remapper_dispatcher: ITimestampRemappersDispatcher, mapper_id: usize, mmr_id: usize + remapper_dispatcher: ITimestampRemappersDispatcher, mapper_id: MapperId, mmr_id: MmrId ) { let header = array![ 0x7323c119a02202f9, diff --git a/src/remappers/timestamp_remappers.cairo b/src/remappers/timestamp_remappers.cairo index 851b11f..26f0e55 100644 --- a/src/remappers/timestamp_remappers.cairo +++ b/src/remappers/timestamp_remappers.cairo @@ -6,9 +6,10 @@ #[starknet::contract] mod TimestampRemappers { + use herodotus_eth_starknet::core::common::{MmrId, MmrSize}; use herodotus_eth_starknet::remappers::interface::{ ITimestampRemappers, Headers, OriginElement, Proof, Peaks, Words64, ProofElement, - BinarySearchTree + BinarySearchTree, MapperId }; use starknet::ContractAddress; use cairo_lib::hashing::poseidon::{PoseidonHasher, hash_words64}; @@ -33,17 +34,17 @@ mod TimestampRemappers { #[derive(Drop, starknet::Event)] struct MapperCreated { - mapper_id: usize, + mapper_id: MapperId, start_block: u256 } #[derive(Drop, starknet::Event)] struct RemappedBlocks { - mapper_id: usize, + mapper_id: MapperId, start_block: u256, end_block: u256, mmr_root: felt252, - mmr_size: usize + mmr_size: MmrSize } // @@ -64,13 +65,10 @@ mod TimestampRemappers { #[storage] struct Storage { headers_store: ContractAddress, - // id => mapper - mappers: LegacyMap::, - mappers_count: usize, - // id => mmr - mappers_mmrs: LegacyMap::, - // (id, size) => root - mappers_mmrs_history: LegacyMap::<(usize, usize), felt252>, + mappers: LegacyMap::, + mappers_count: MapperId, + mappers_mmrs: LegacyMap::, + mappers_mmrs_history: LegacyMap::<(MapperId, MmrSize), felt252>, } #[constructor] @@ -86,7 +84,7 @@ mod TimestampRemappers { #[abi(embed_v0)] impl TimestampRemappers of ITimestampRemappers { // Creates a new mapper and returns its ID. - fn create_mapper(ref self: ContractState, start_block: u256) -> usize { + fn create_mapper(ref self: ContractState, start_block: u256) -> MapperId { let mmr: MMR = Default::default(); let mapper_id = self.mappers_count.read(); @@ -107,7 +105,7 @@ mod TimestampRemappers { // Adds elements from other trusted data sources to the given mapper. fn reindex_batch( ref self: ContractState, - mapper_id: usize, + mapper_id: MapperId, mapper_peaks: Peaks, origin_elements: Span ) { @@ -218,7 +216,7 @@ mod TimestampRemappers { } // Getter for the last timestamp of a given mapper. - fn get_last_mapper_timestamp(self: @ContractState, mapper_id: usize) -> u256 { + fn get_last_mapper_timestamp(self: @ContractState, mapper_id: MapperId) -> u256 { let mapper = self.mappers.read(mapper_id); mapper.last_timestamp } From 9e38bfdb86229ee7207b0af3de04a9da51fb58bb Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Mon, 13 May 2024 14:33:35 +0200 Subject: [PATCH 08/48] Update snfoundry --- .tool-versions | 2 +- Scarb.lock | 6 +++--- Scarb.toml | 4 ++-- multicall/deploy.toml | 6 +++--- src/core/tests/test_facts_registry.cairo | 14 ++++++++------ src/core/tests/test_headers_store.cairo | 8 ++++---- src/remappers/tests/test_timestamp_remappers.cairo | 13 ++++++++----- 7 files changed, 29 insertions(+), 24 deletions(-) diff --git a/.tool-versions b/.tool-versions index 8ed6949..c7435d1 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ scarb nightly-2024-04-20 -starknet-foundry 0.20.1 +starknet-foundry 0.23.0 diff --git a/Scarb.lock b/Scarb.lock index 9c579e7..611c08d 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -4,7 +4,7 @@ version = 1 [[package]] name = "cairo_lib" version = "0.2.0" -source = "git+https://github.com/HerodotusDev/cairo-lib.git?branch=update-cairo#c00222c907cbfd381d28749a2dc2fab0eac6beae" +source = "git+https://github.com/HerodotusDev/cairo-lib.git?branch=update-cairo#b0f93a15f09b5b9962d4dc45ddfdb05f9790291a" [[package]] name = "herodotus_eth_starknet" @@ -16,5 +16,5 @@ dependencies = [ [[package]] name = "snforge_std" -version = "0.20.1" -source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.20.1#fea2db8f2b20148cc15ee34b08de12028eb42942" +version = "0.23.0" +source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.23.0#f2bff8f796763ada77fe6033ec1b034ceee22abd" diff --git a/Scarb.toml b/Scarb.toml index 780c43d..95f5040 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -10,9 +10,9 @@ casm = true # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest [dependencies] -starknet = "2.2.0" +starknet = "2.6.3" cairo_lib = { git = "https://github.com/HerodotusDev/cairo-lib.git", branch = "update-cairo" } -snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.20.1" } +snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.23.0" } [tool.snforge] exit_first = true diff --git a/multicall/deploy.toml b/multicall/deploy.toml index f375bbe..a3fe0b3 100644 --- a/multicall/deploy.toml +++ b/multicall/deploy.toml @@ -12,14 +12,14 @@ unique = false [[call]] call_type = "deploy" -class_hash = "0x1b0eeb6325cbb7874f5ee8d3f59df2617113073b52911cfca9077ec9d581555" +class_hash = "0x2055a6a11f644201f229c1ecdc10912f20ae0f51ac8aa6ef921c58e52bae404" inputs = ["commitments_inbox"] id = "headers_store" unique = false [[call]] call_type = "deploy" -class_hash = "0x3211460c883d24937aa616ec31d01a3f09fafb053439b9b1b7bedf72df00ce" +class_hash = "0x74c929cfd9fd8d7577d8c7b8102010d925cbc1b1475a5873047099a4e00dab7" inputs = ["headers_store"] id = "evm_facts_registry" unique = false @@ -32,7 +32,7 @@ inputs = ["headers_store"] [[call]] call_type = "deploy" -class_hash = "0xa5816245847010c159074cd8779a3fb38c1e0a887ad63f5b0f06ab9a9cb788" +class_hash = "0x67ae27cd294fea085fc3b63cd8b872774cc1ba92bd54864c267eda6fa81a493" inputs = ["headers_store"] id = "timestamp_remappers" unique = false \ No newline at end of file diff --git a/src/core/tests/test_facts_registry.cairo b/src/core/tests/test_facts_registry.cairo index 32699f8..32cc012 100644 --- a/src/core/tests/test_facts_registry.cairo +++ b/src/core/tests/test_facts_registry.cairo @@ -21,15 +21,15 @@ const TEST_BLOCK: u256 = 17000000; fn helper_create_facts_registry( mmr_root: felt252, mmr_size: MmrSize ) -> (IEVMFactsRegistryDispatcher, ContractAddress) { - let contract = declare("HeadersStore"); - let contract_address = contract.deploy(@array![COMMITMENTS_INBOX_ADDRESS]).unwrap(); + let contract = declare("HeadersStore").unwrap(); + let (contract_address, _) = contract.deploy(@array![COMMITMENTS_INBOX_ADDRESS]).unwrap(); let mut headers_store = IHeadersStoreDispatcher { contract_address }; start_prank(CheatTarget::One(contract_address), COMMITMENTS_INBOX_ADDRESS.try_into().unwrap()); headers_store.create_branch_from_message(mmr_root, mmr_size, 0); stop_prank(CheatTarget::One(contract_address)); - let contract = declare("EVMFactsRegistry"); - let contract_address = contract.deploy(@array![contract_address.into()]).unwrap(); + let contract = declare("EVMFactsRegistry").unwrap(); + let (contract_address, _) = contract.deploy(@array![contract_address.into()]).unwrap(); (IEVMFactsRegistryDispatcher { contract_address }, contract_address) } @@ -80,7 +80,8 @@ fn test_prove_account() { #[test] fn test_prove_storage() { - // Expected key based on proof: 0x290decd | 39548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563 + // Expected key based on proof: 0x290decd | + // 39548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563 let (dispatcher, _) = helper_create_facts_registry(TEST_MMR_ROOT, TEST_MMR_SIZE); // Testing block 17000000 @@ -115,7 +116,8 @@ fn test_prove_storage() { #[test] fn test_prove_storage_empty() { - // Expected key based on proof: 0x290decd | 39548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563 + // Expected key based on proof: 0x290decd | + // 39548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563 let (dispatcher, _) = helper_create_facts_registry(TEST_MMR_ROOT, TEST_MMR_SIZE); // Testing block 17000000 diff --git a/src/core/tests/test_headers_store.cairo b/src/core/tests/test_headers_store.cairo index 38e9330..90560a9 100644 --- a/src/core/tests/test_headers_store.cairo +++ b/src/core/tests/test_headers_store.cairo @@ -14,14 +14,14 @@ const MMR_INITIAL_ELEMENT: felt252 = const MMR_INITIAL_ROOT: felt252 = 0x6759138078831011e3bc0b4a135af21c008dda64586363531697207fb5a2bae; fn helper_create_headers_store() -> (IHeadersStoreDispatcher, ContractAddress) { - let contract = declare("HeadersStore"); - let contract_address = contract.deploy(@array![COMMITMENTS_INBOX_ADDRESS]).unwrap(); + let contract = declare("HeadersStore").unwrap(); + let (contract_address, _) = contract.deploy(@array![COMMITMENTS_INBOX_ADDRESS]).unwrap(); (IHeadersStoreDispatcher { contract_address }, contract_address) } fn helper_create_safe_headers_store() -> (IHeadersStoreSafeDispatcher, ContractAddress) { - let contract = declare("HeadersStore"); - let contract_address = contract.deploy(@array![COMMITMENTS_INBOX_ADDRESS]).unwrap(); + let contract = declare("HeadersStore").unwrap(); + let (contract_address, _) = contract.deploy(@array![COMMITMENTS_INBOX_ADDRESS]).unwrap(); (IHeadersStoreSafeDispatcher { contract_address }, contract_address) } diff --git a/src/remappers/tests/test_timestamp_remappers.cairo b/src/remappers/tests/test_timestamp_remappers.cairo index e826f2f..cb8c22a 100644 --- a/src/remappers/tests/test_timestamp_remappers.cairo +++ b/src/remappers/tests/test_timestamp_remappers.cairo @@ -16,19 +16,21 @@ use herodotus_eth_starknet::core::headers_store::{ use herodotus_eth_starknet::core::common::MmrId; fn deploy_headers_store() -> ContractAddress { - let contract = declare("HeadersStore"); + let contract = declare("HeadersStore").unwrap(); let mut constructor_calldata = ArrayTrait::new(); constructor_calldata.append(0); - contract.deploy(@constructor_calldata).unwrap() + let (address, _) = contract.deploy(@constructor_calldata).unwrap(); + address } fn deploy_timestamp_remappers(headers_store: ContractAddress) -> ContractAddress { - let contract = declare("TimestampRemappers"); + let contract = declare("TimestampRemappers").unwrap(); let mut constructor_calldata: Array = ArrayTrait::new(); constructor_calldata.append(headers_store.into()); - contract.deploy(@constructor_calldata).unwrap() + let (address, _) = contract.deploy(@constructor_calldata).unwrap(); + address } fn inner_test_proof(mmr: @MMR) { @@ -347,7 +349,8 @@ fn inner_test_reindex_batch( }, ]; remapper_dispatcher.reindex_batch(mapper_id, ArrayTrait::new().span(), origin_elements.span()); -// From this point on, mapper_peaks has root 0x215ea4dbc30f0b14338d306f0035277c856c486126cd34966a82ead2a0a1c01 and 4 as elements count +// From this point on, mapper_peaks has root +// 0x215ea4dbc30f0b14338d306f0035277c856c486126cd34966a82ead2a0a1c01 and 4 as elements count } #[test] From 5bb9d153df5ce9055964bae4d7cc7434d2624e82 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Mon, 13 May 2024 16:08:08 +0200 Subject: [PATCH 09/48] Remove mmr with id 0 from constructor and add creating new MMR with create_branch_from --- src/core/common.cairo | 2 +- src/core/headers_store.cairo | 18 +++++++++--------- src/core/tests/test_headers_store.cairo | 7 +++++-- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/core/common.cairo b/src/core/common.cairo index dd86926..809225b 100644 --- a/src/core/common.cairo +++ b/src/core/common.cairo @@ -1,2 +1,2 @@ -type MmrId = u256; +type MmrId = usize; type MmrSize = usize; diff --git a/src/core/headers_store.cairo b/src/core/headers_store.cairo index 20204a9..11c78ab 100644 --- a/src/core/headers_store.cairo +++ b/src/core/headers_store.cairo @@ -127,6 +127,7 @@ trait IHeadersStore { ); // @notice Creates a new MMR that is a clone of an already existing MMR + // or an empty MMR if mmr_id is 0 (in that case last_pos is ignored) // @param mmr_id The id of the MMR to clone // @param last_pos last_pos of the given MMR fn create_branch_from(ref self: TContractState, mmr_id: MmrId, last_pos: MmrSize); @@ -226,13 +227,6 @@ mod HeadersStore { #[constructor] fn constructor(ref self: ContractState, commitments_inbox: ContractAddress) { self.commitments_inbox.write(commitments_inbox); - - let mmr: MMR = MMRTrait::new(MMR_INITIAL_ROOT, 1); - let root = mmr.root; - - self.mmr.write(0, mmr); - self.mmr_history.write((0, 1), root); - self.latest_mmr_id.write(0); } @@ -264,6 +258,7 @@ mod HeadersStore { } // @inheritdoc IHeadersStore + // @return Id of the latest created MMR or 0 if no MMRs exist fn get_latest_mmr_id(self: @ContractState) -> MmrId { self.latest_mmr_id.read() } @@ -526,9 +521,14 @@ mod HeadersStore { } // @inheritdoc IHeadersStore - fn create_branch_from(ref self: ContractState, mmr_id: MmrId, last_pos: MmrSize) { + fn create_branch_from(ref self: ContractState, mmr_id: MmrId, mut last_pos: MmrSize) { let latest_mmr_id = self.latest_mmr_id.read() + 1; - let root = self.mmr_history.read((mmr_id, last_pos)); + let root = if mmr_id == 0 { + last_pos = 1; + MMR_INITIAL_ROOT + } else { + self.mmr_history.read((mmr_id, last_pos)) + }; let mmr = MMRTrait::new(root, last_pos); diff --git a/src/core/tests/test_headers_store.cairo b/src/core/tests/test_headers_store.cairo index 90560a9..627f149 100644 --- a/src/core/tests/test_headers_store.cairo +++ b/src/core/tests/test_headers_store.cairo @@ -63,7 +63,9 @@ fn test_receive_hash() { fn test_initial_tree() { let (dispatcher, _) = helper_create_headers_store(); - let mmr_id = 0; + dispatcher.create_branch_from(0, 0); + let mmr_id = dispatcher.get_latest_mmr_id(); + assert(mmr_id == 1, 'new mmr invalid id'); let mmr = dispatcher.get_mmr(mmr_id); let expected_root = MMR_INITIAL_ROOT; @@ -84,7 +86,8 @@ fn test_process_batch_form_message() { let headers_rlp = helper_get_headers_rlp(); - let mmr_id = 0; + dispatcher.create_branch_from(0, 0); + let mmr_id = 1; dispatcher .process_batch( headers_rlp, From 66a6b30908da6c95035fd6c0119f0028fd7779f9 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Fri, 17 May 2024 13:06:10 +0200 Subject: [PATCH 10/48] Support custom Mmr IDs --- src/core/commitments_inbox.cairo | 26 +++-- src/core/common.cairo | 2 +- src/core/evm_facts_registry.cairo | 3 +- src/core/headers_store.cairo | 97 +++++++++++-------- src/core/tests/test_facts_registry.cairo | 2 +- src/core/tests/test_headers_store.cairo | 43 +++++++- .../tests/test_timestamp_remappers.cairo | 4 +- src/remappers/timestamp_remappers.cairo | 3 +- 8 files changed, 121 insertions(+), 59 deletions(-) diff --git a/src/core/commitments_inbox.cairo b/src/core/commitments_inbox.cairo index 40cf283..2f12f38 100644 --- a/src/core/commitments_inbox.cairo +++ b/src/core/commitments_inbox.cairo @@ -33,20 +33,24 @@ trait ICommitmentsInbox { // @dev This function is only callable by the owner fn renounce_ownership(ref self: TContractState); - // @notice receives a parent blockhash and the corresponding block number, simulating L1 messaging, and sends it to the HeadersStore - // @dev This function is only callable by the owner, ownership will be renounced in mainnet + // @notice receives a parent blockhash and the corresponding block number, simulating L1 + // messaging, and sends it to the HeadersStore + // @dev This function is only callable by the owner, + // ownership will be renounced in mainnet fn receive_commitment_owner(ref self: TContractState, parent_hash: u256, block_number: u256); } -// @notice The contract that receives the commitments from L1, both individual blocks and proven MMRs, sending them to the HeadersStore -// @dev The contract ownership will be renounced in mainnet, it is only used for testing purposes +// @notice The contract that receives the commitments from L1, both individual blocks and proven +// MMRs, sending them to the HeadersStore +// @dev The contract ownership will be renounced in mainnet, +// it is only used for testing purposes #[starknet::contract] mod CommitmentsInbox { use starknet::{ContractAddress, get_caller_address, EthAddress}; use herodotus_eth_starknet::core::headers_store::{ IHeadersStoreDispatcherTrait, IHeadersStoreDispatcher }; - use herodotus_eth_starknet::core::common::MmrSize; + use herodotus_eth_starknet::core::common::{MmrId, MmrSize}; #[storage] struct Storage { @@ -177,8 +181,10 @@ mod CommitmentsInbox { } } - // @notice receives a parent blockhash and the corresponding block number from L1, and sends it to the HeadersStore - // @param from_address The address of the sender, checking that it is the L1 contract + // @notice receives a parent blockhash and the corresponding block number from L1, and sends it + // to the HeadersStore + // @param from_address The address of the sender, checking that it is the L1 + // contract // @param blockhash The parent blockhash of the block // @param block_number The block number of the block #[l1_handler] @@ -199,19 +205,21 @@ mod CommitmentsInbox { // @param root The root of the MMR // @param last_pos The last position of the MMR // @param aggregator_id The aggregator id of the proven MMR + // @param mmr_id The id of the MMR #[l1_handler] fn receive_mmr( ref self: ContractState, from_address: felt252, root: felt252, last_pos: MmrSize, - aggregator_id: usize + aggregator_id: usize, + mmr_id: MmrId ) { assert(from_address == self.l1_message_sender.read().into(), 'Invalid sender'); let contract_address = self.headers_store.read(); IHeadersStoreDispatcher { contract_address } - .create_branch_from_message(root, last_pos, aggregator_id); + .create_branch_from_message(root, last_pos, aggregator_id, mmr_id); self.emit(Event::MMRReceived(MMRReceived { root, last_pos, aggregator_id })); } diff --git a/src/core/common.cairo b/src/core/common.cairo index 809225b..dd86926 100644 --- a/src/core/common.cairo +++ b/src/core/common.cairo @@ -1,2 +1,2 @@ -type MmrId = usize; +type MmrId = u256; type MmrSize = usize; diff --git a/src/core/evm_facts_registry.cairo b/src/core/evm_facts_registry.cairo index 9843cd5..dd10080 100644 --- a/src/core/evm_facts_registry.cairo +++ b/src/core/evm_facts_registry.cairo @@ -109,7 +109,8 @@ trait IEVMFactsRegistry { ); } -// @notice Contract that stores all the proven facts, entrypoint for applications using with Herodotus +// @notice Contract that stores all the proven facts, entrypoint for applications using with +// Herodotus #[starknet::contract] mod EVMFactsRegistry { use starknet::ContractAddress; diff --git a/src/core/headers_store.cairo b/src/core/headers_store.cairo index 11c78ab..bf93f76 100644 --- a/src/core/headers_store.cairo +++ b/src/core/headers_store.cairo @@ -26,15 +26,13 @@ trait IHeadersStore { // @return The size of the MMR with the given id fn get_mmr_size(self: @TContractState, mmr_id: MmrId) -> MmrSize; - // @notice Returns the parent blockhash of a given block number, received from L1 and send throught the CommitmentsInbox + // @notice Returns the parent blockhash of a given block number, received from L1 and send + // throught the CommitmentsInbox fn get_received_block(self: @TContractState, block_number: u256) -> u256; - // @notice Returns the latest MMR id - // @dev MMR IDs are incremental - fn get_latest_mmr_id(self: @TContractState) -> MmrId; - // @notice Returns the root of the MMR with a given id and size - // @dev The reason why we need to get historical roots is because we don't want MMR proofs to expire + // @dev The reason why we need to get historical roots is because we don't want MMR proofs to + // expire // @param mmr_id The id of the MMR // @param size The size of the MMR // @return The root of the MMR with the given id and size @@ -80,15 +78,18 @@ trait IHeadersStore { last_pos: MmrSize, ) -> bool; - // @notice Appends a batch of block hashes to the MMR starting from a specific block, either from a hash received from L1 or from an MMR element - // @param headers_rlp The RLP-encoded headers - // @param mmr_peaks The peaks of the MMR + // @notice Appends a batch of block hashes to the MMR starting from a specific block, either + // from a hash received from L1 or from an MMR element @param headers_rlp The RLP-encoded + // headers @param mmr_peaks The peaks of the MMR // @param mmr_id The id of the MMR - // @param reference_block A block whose hash was receiven from L1 (if starting from MMR element, None) + // @param reference_block A block whose hash was receiven from L1 (if starting from MMR element, + // None) // @param mmr_index The index of the starting blockhash in the MMR (if starting from L1, None) - // @param mmr_proof The MMR inclusion porrof of the starting blockhash (if starting from L1, None) - // @dev If the starting blockhash was received from L1, then reference_block must be provided, and mmr_index and mmr_proof must be None - // @dev If the starting blockhash is present in the MMR, then mmr_index and mmr_proof must be provided, and reference_block must be None + // @param mmr_proof The MMR inclusion porrof of the starting blockhash (if starting from L1, + // None) + // @dev If the starting blockhash was received from L1, then reference_block must be provided, + // and mmr_index and mmr_proof must be None @dev If the starting blockhash is present in the + // MMR, then mmr_index and mmr_proof must be provided, and reference_block must be None fn process_batch( ref self: TContractState, headers_rlp: Span, @@ -103,9 +104,14 @@ trait IHeadersStore { // @param root The root of the MMR // @param last_pos The size of the MMR // @param aggregator_id The id of the L1 aggregator + // @param mmr_id The id of the new MMR // @dev This function can only be called by the CommitmentsInbox contract fn create_branch_from_message( - ref self: TContractState, root: felt252, last_pos: MmrSize, aggregator_id: usize + ref self: TContractState, + root: felt252, + last_pos: MmrSize, + aggregator_id: usize, + mmr_id: MmrId ); @@ -123,20 +129,24 @@ trait IHeadersStore { peaks: Peaks, proof: Proof, mmr_id: MmrId, - last_pos: MmrSize + last_pos: MmrSize, + new_mmr_id: MmrId ); // @notice Creates a new MMR that is a clone of an already existing MMR // or an empty MMR if mmr_id is 0 (in that case last_pos is ignored) // @param mmr_id The id of the MMR to clone // @param last_pos last_pos of the given MMR - fn create_branch_from(ref self: TContractState, mmr_id: MmrId, last_pos: MmrSize); + // @param new_mmr_id The id of the new MMR + fn create_branch_from( + ref self: TContractState, mmr_id: MmrId, last_pos: MmrSize, new_mmr_id: MmrId + ); } // @notice Contract responsible for storing all the block hashes // @dev The contract keeps track of multiple MMRs (refered to as branches), each with a different id -// @dev The contract also keeps track of historical roots and corresponding sizes of every MMR, +// @dev The contract also keeps track of historical roots and corresponding sizes of every MMR, #[starknet::contract] mod HeadersStore { use starknet::{ContractAddress, get_caller_address}; @@ -162,7 +172,6 @@ mod HeadersStore { mmr_history: LegacyMap::<(MmrId, MmrSize), felt252>, // block_number => parent blockhash received_blocks: LegacyMap::, - latest_mmr_id: MmrId } #[event] @@ -257,12 +266,6 @@ mod HeadersStore { self.received_blocks.read(block_number) } - // @inheritdoc IHeadersStore - // @return Id of the latest created MMR or 0 if no MMRs exist - fn get_latest_mmr_id(self: @ContractState) -> MmrId { - self.latest_mmr_id.read() - } - // @inheritdoc IHeadersStore fn get_historical_root(self: @ContractState, mmr_id: MmrId, size: MmrSize) -> felt252 { self.mmr_history.read((mmr_id, size)) @@ -457,16 +460,24 @@ mod HeadersStore { // @inheritdoc IHeadersStore fn create_branch_from_message( - ref self: ContractState, root: felt252, last_pos: MmrSize, aggregator_id: usize + ref self: ContractState, + root: felt252, + last_pos: MmrSize, + aggregator_id: usize, + mmr_id: MmrId ) { + assert(mmr_id != 0, 'Cannot create mmr with id 0'); + assert(root != 0 || last_pos != 0, 'root=0 & last_pos=0 not allowed'); + let caller = get_caller_address(); assert(caller == self.commitments_inbox.read(), 'Only CommitmentsInbox'); - let mmr_id = self.latest_mmr_id.read() + 1; + let existing_mmr = self.mmr.read(mmr_id); + assert(existing_mmr.root == 0 && existing_mmr.last_pos == 0, 'MMR ID already exists'); + let mmr = MMRTrait::new(root, last_pos); self.mmr.write(mmr_id, mmr); self.mmr_history.write((mmr_id, last_pos), root); - self.latest_mmr_id.write(mmr_id); self .emit( @@ -484,8 +495,14 @@ mod HeadersStore { peaks: Peaks, proof: Proof, mmr_id: MmrId, - last_pos: MmrSize + last_pos: MmrSize, + new_mmr_id: MmrId ) { + assert(new_mmr_id != 0, 'Cannot create mmr with id 0'); + + let existing_mmr = self.mmr.read(mmr_id); + assert(existing_mmr.root == 0 && existing_mmr.last_pos == 0, 'MMR ID already exists'); + assert( HeadersStore::verify_historical_mmr_inclusion( @self, index, initial_poseidon_blockhash, peaks, proof, mmr_id, last_pos @@ -501,16 +518,14 @@ mod HeadersStore { let root = mmr.root; let last_pos = mmr.last_pos; - let latest_mmr_id = self.latest_mmr_id.read() + 1; - self.mmr.write(latest_mmr_id, mmr); - self.mmr_history.write((latest_mmr_id, last_pos), root); - self.latest_mmr_id.write(latest_mmr_id); + self.mmr.write(new_mmr_id, mmr); + self.mmr_history.write((new_mmr_id, last_pos), root); self .emit( Event::BranchCreatedFromElement( BranchCreatedFromElement { - mmr_id: latest_mmr_id, + mmr_id: new_mmr_id, root, last_pos, detached_from_mmr_id: mmr_id, @@ -521,8 +536,11 @@ mod HeadersStore { } // @inheritdoc IHeadersStore - fn create_branch_from(ref self: ContractState, mmr_id: MmrId, mut last_pos: MmrSize) { - let latest_mmr_id = self.latest_mmr_id.read() + 1; + fn create_branch_from( + ref self: ContractState, mmr_id: MmrId, mut last_pos: MmrSize, new_mmr_id: MmrId + ) { + assert(new_mmr_id != 0, 'Cannot create mmr with id 0'); + let root = if mmr_id == 0 { last_pos = 1; MMR_INITIAL_ROOT @@ -530,17 +548,16 @@ mod HeadersStore { self.mmr_history.read((mmr_id, last_pos)) }; - let mmr = MMRTrait::new(root, last_pos); + let new_mmr = MMRTrait::new(root, last_pos); - self.mmr.write(latest_mmr_id, mmr); - self.mmr_history.write((latest_mmr_id, last_pos), root); - self.latest_mmr_id.write(latest_mmr_id); + self.mmr.write(new_mmr_id, new_mmr); + self.mmr_history.write((new_mmr_id, last_pos), root); self .emit( Event::BranchCreatedClone( BranchCreatedClone { - mmr_id: latest_mmr_id, root, last_pos, detached_from_mmr_id: mmr_id + mmr_id: new_mmr_id, root, last_pos, detached_from_mmr_id: mmr_id } ) ); diff --git a/src/core/tests/test_facts_registry.cairo b/src/core/tests/test_facts_registry.cairo index 32cc012..45ce0ec 100644 --- a/src/core/tests/test_facts_registry.cairo +++ b/src/core/tests/test_facts_registry.cairo @@ -25,7 +25,7 @@ fn helper_create_facts_registry( let (contract_address, _) = contract.deploy(@array![COMMITMENTS_INBOX_ADDRESS]).unwrap(); let mut headers_store = IHeadersStoreDispatcher { contract_address }; start_prank(CheatTarget::One(contract_address), COMMITMENTS_INBOX_ADDRESS.try_into().unwrap()); - headers_store.create_branch_from_message(mmr_root, mmr_size, 0); + headers_store.create_branch_from_message(mmr_root, mmr_size, 0, 1); stop_prank(CheatTarget::One(contract_address)); let contract = declare("EVMFactsRegistry").unwrap(); diff --git a/src/core/tests/test_headers_store.cairo b/src/core/tests/test_headers_store.cairo index 627f149..805cab0 100644 --- a/src/core/tests/test_headers_store.cairo +++ b/src/core/tests/test_headers_store.cairo @@ -5,6 +5,7 @@ use herodotus_eth_starknet::core::headers_store::{ IHeadersStoreDispatcherTrait, IHeadersStoreDispatcher, IHeadersStoreSafeDispatcherTrait, IHeadersStoreSafeDispatcher }; +use herodotus_eth_starknet::core::common::{MmrSize, MmrId}; use starknet::ContractAddress; use cairo_lib::utils::types::words64::Words64; @@ -59,13 +60,47 @@ fn test_receive_hash() { assert(block_hash == real_block_hash, 'Block hash not set'); } +#[test] +fn test_create_branch_from_message() { + let (dispatcher, contract_address) = helper_create_safe_headers_store(); + + let mmr_id_1 = 0x5a93; + let mmr_id_2 = 0xcd82; + let root = 0x123123123; + let size = 10; + + start_prank(CheatTarget::One(contract_address), COMMITMENTS_INBOX_ADDRESS.try_into().unwrap()); + + // Create MMR. + assert(dispatcher.get_mmr_size(mmr_id_1).unwrap() == 0, 'Initial mmr size should be 0'); + dispatcher.create_branch_from_message(root, size, 0, mmr_id_1).unwrap(); + assert(dispatcher.get_mmr_size(mmr_id_1).unwrap() == size, 'Mmr size mismatch'); + assert(dispatcher.get_mmr_root(mmr_id_1).unwrap() == root, 'Mmr root mismatch'); + + // Creating MMR with ID 0 is not allowed. + assert(dispatcher.create_branch_from_message(root, size, 0, 0).is_err(), 'Mmr ID 0 should fail'); + + // Both root and size 0 is not allowed. + assert(dispatcher.create_branch_from_message(0, 0, 0, mmr_id_2).is_err(), 'Root and size 0 should fail'); + + // Creating MMR with the same ID should fail. + assert(dispatcher.create_branch_from_message(root, size, 0, mmr_id_1).is_err(), 'MMR already exists should fail'); + + // Create another MMR. + assert(dispatcher.get_mmr_size(mmr_id_2).unwrap() == 0, 'Initial mmr size should be 0'); + dispatcher.create_branch_from_message(root, size, 0, mmr_id_2).unwrap(); + assert(dispatcher.get_mmr_size(mmr_id_2).unwrap() == size, 'Mmr size mismatch'); + assert(dispatcher.get_mmr_root(mmr_id_2).unwrap() == root, 'Mmr root mismatch'); + + stop_prank(CheatTarget::One(contract_address)); +} + #[test] fn test_initial_tree() { let (dispatcher, _) = helper_create_headers_store(); - dispatcher.create_branch_from(0, 0); - let mmr_id = dispatcher.get_latest_mmr_id(); - assert(mmr_id == 1, 'new mmr invalid id'); + let mmr_id = 1; + dispatcher.create_branch_from(0, 0, mmr_id); let mmr = dispatcher.get_mmr(mmr_id); let expected_root = MMR_INITIAL_ROOT; @@ -86,8 +121,8 @@ fn test_process_batch_form_message() { let headers_rlp = helper_get_headers_rlp(); - dispatcher.create_branch_from(0, 0); let mmr_id = 1; + dispatcher.create_branch_from(0, 0, mmr_id); dispatcher .process_batch( headers_rlp, diff --git a/src/remappers/tests/test_timestamp_remappers.cairo b/src/remappers/tests/test_timestamp_remappers.cairo index cb8c22a..9736c49 100644 --- a/src/remappers/tests/test_timestamp_remappers.cairo +++ b/src/remappers/tests/test_timestamp_remappers.cairo @@ -370,12 +370,12 @@ fn test_remappers() { inner_test_proof(@mmr); let headers_store_dispatcher = IHeadersStoreDispatcher { contract_address: headers_store }; - let mmr_id = 1; // First MMR + let mmr_id = 0x123; // First MMR start_prank(CheatTarget::One(headers_store), 0.try_into().unwrap()); headers_store_dispatcher .create_branch_from_message( - 0x25aedbc0ddea804ce21d29a39f00358f68df0e462114f75b0576182d08db0, 4, 1 + 0x25aedbc0ddea804ce21d29a39f00358f68df0e462114f75b0576182d08db0, 4, 1, mmr_id ); stop_prank(CheatTarget::One(headers_store)); diff --git a/src/remappers/timestamp_remappers.cairo b/src/remappers/timestamp_remappers.cairo index 26f0e55..91d00a1 100644 --- a/src/remappers/timestamp_remappers.cairo +++ b/src/remappers/timestamp_remappers.cairo @@ -128,7 +128,8 @@ mod TimestampRemappers { break (); } - // 1. Verify that the block number is correct (i.e., matching with the expected block) + // 1. Verify that the block number is correct (i.e., matching with the expected + // block) let origin_element: @OriginElement = origin_elements.at(idx); let (origin_element_block_number, origin_element_timestamp) = InternalFunctions::extract_header_block_number_and_timestamp( From 2c0535e3fcd8fcd5b6c16a3f8b20a6ff47446ca1 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Tue, 21 May 2024 14:14:30 +0200 Subject: [PATCH 11/48] Add failing test for header of block 1 --- src/core/tests/test_headers_store.cairo | 164 ++++++++++++++++++++++++ 1 file changed, 164 insertions(+) diff --git a/src/core/tests/test_headers_store.cairo b/src/core/tests/test_headers_store.cairo index 627f149..47ab70e 100644 --- a/src/core/tests/test_headers_store.cairo +++ b/src/core/tests/test_headers_store.cairo @@ -7,6 +7,7 @@ use herodotus_eth_starknet::core::headers_store::{ }; use starknet::ContractAddress; use cairo_lib::utils::types::words64::Words64; +use cairo_lib::data_structures::mmr::mmr::MMRTrait; const COMMITMENTS_INBOX_ADDRESS: felt252 = 0x123; const MMR_INITIAL_ELEMENT: felt252 = @@ -36,6 +37,169 @@ fn helper_receive_hash( stop_prank(CheatTarget::One(contract_address)); } +#[test] +#[available_gas(99999999)] +fn test_header_block_1() { + let headers_rlp = array![ + array![ + 0x64dd60e4a0fc01f9, + 0x4c54f284013c491f, + 0xcf69fedcb4e3cf9b, + 0x0d1b29ed8a4f05a8, + 0x4dcc1da05e02a0dd, + 0xb585ab7a5dc7dee8, + 0x4512d31ad4ccb667, + 0x42a1f013748a941b, + 0x0042944793d440fd, + 0x0000000000000000, + 0x0000000000000000, + 0xedfc36fc32a01100, + 0x9319629f76c5000d, + 0x2a57fdbe486254cd, + 0x3dc181f13929b4f5, + 0xb5350f77a0c70890, + 0x58aff8b90f301ca1, + 0x2a7c97ac1a555144, + 0x27ab16def38f5914, + 0x82468fa0ce42063b, + 0x7bf6ce1c3a5b5782, + 0xb7e9521cac238426, + 0x697018cba3bda32c, + 0x0001b93b44a845dd, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x8280c3c901840280, + 0x80b0dbd6648429b7, + 0x5c67599946e432a0, + 0xa80937f4a9d1d3ee, + 0xf84428719d685d05, + 0x289ef4c9db34aa20, + 0x000000000000886c, + 0x555e4239840000 + ] + .span(), + array![ + 0xffe62d10a0fc01f9, + 0x48b5b8c90c4801b0, + 0xe46af4d44cc305fd, + 0xea90db93937591aa, + 0x4dcc1da07d880904, + 0xb585ab7a5dc7dee8, + 0x4512d31ad4ccb667, + 0x42a1f013748a941b, + 0x0042944793d440fd, + 0x0000000000000000, + 0x0000000000000000, + 0x8350600f4ca01100, + 0x56f3150a8a78d66f, + 0x73d2b84cef2d199b, + 0x42bed64a1ea39943, + 0xc494f04da0dcb31e, + 0xb948bfaaea99f413, + 0x5c0bb3233cd8d16f, + 0x2d6bc9f9efa595ec, + 0x5d713ca075e82e5d, + 0xde6fd4cc97256dd9, + 0x5b0a3eb1e4a56d04, + 0xc9e63e0cf62f0a7d, + 0x0001b9ea0096ee2f, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x8280c3c901840180, + 0x80aedbd66484f5f9, + 0x5c67599946e432a0, + 0xa80937f4a9d1d3ee, + 0xf84428719d685d05, + 0x289ef4c9db34aa20, + 0x000000000000886c, + 0x009d693a840000 + ] + .span() + ] + .span(); + + let mut mmr = MMRTrait::new( + root: 0x78ece8884698aadc91f067cf2d0d54a955e458ab6cd2ebc18fe815a3aafb43, last_pos: 263 + ); + let mmr_peaks = array![ + 0x735d9916958a088b58e320e015ba24e93ad034159fe0551c31cbb69d5be0a05, + 0x0e4829e42415b71f12d9d936cb22bc50cd97f4a9737852454deeca9b49c59a2, + 0x38aaa5bd29a41a3818b28eff66365d8ea7dd20380456f27b832f1091503a961 + ] + .span(); + let mmr_proof = array![].span(); + let mmr_id = 1; + + let (dispatcher, contract_address) = helper_create_headers_store(); + + start_prank(CheatTarget::One(contract_address), COMMITMENTS_INBOX_ADDRESS.try_into().unwrap()); + dispatcher.create_branch_from_message(mmr.root, mmr.last_pos, 0); + stop_prank(CheatTarget::One(contract_address)); + assert(dispatcher.get_mmr_root(mmr_id) == mmr.root, 'Root not set'); + + dispatcher.process_batch(headers_rlp, mmr_peaks, mmr_id, Option::None, Option::Some(mmr.last_pos), Option::Some(mmr_proof)); +} + #[test] #[feature("safe_dispatcher")] fn test_receive_hash_wrong_address() { From a037c623a6f1afc9da2b9de01fe71acb3b82ca9c Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Tue, 21 May 2024 14:24:54 +0200 Subject: [PATCH 12/48] Fix integer underflow panic --- src/core/headers_store.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/headers_store.cairo b/src/core/headers_store.cairo index 11c78ab..368fa3d 100644 --- a/src/core/headers_store.cairo +++ b/src/core/headers_store.cairo @@ -328,7 +328,7 @@ mod HeadersStore { .into() - 1; - end_block = start_block - headers_rlp.len().into() + 2; + end_block = (start_block + 2) - headers_rlp.len().into(); } }; } else { From 56406f6032dca9de73fa38699ba59be321c23a19 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Thu, 23 May 2024 10:42:55 +0200 Subject: [PATCH 13/48] Fix test --- src/core/tests/test_headers_store.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/tests/test_headers_store.cairo b/src/core/tests/test_headers_store.cairo index e3d3cc0..ddfe6c0 100644 --- a/src/core/tests/test_headers_store.cairo +++ b/src/core/tests/test_headers_store.cairo @@ -194,7 +194,7 @@ fn test_header_block_1() { let (dispatcher, contract_address) = helper_create_headers_store(); start_prank(CheatTarget::One(contract_address), COMMITMENTS_INBOX_ADDRESS.try_into().unwrap()); - dispatcher.create_branch_from_message(mmr.root, mmr.last_pos, 0); + dispatcher.create_branch_from_message(mmr.root, mmr.last_pos, 0, mmr_id); stop_prank(CheatTarget::One(contract_address)); assert(dispatcher.get_mmr_root(mmr_id) == mmr.root, 'Root not set'); From e297be347dc85c4b7d7e3c1f58e8874144014522 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Thu, 23 May 2024 10:45:10 +0200 Subject: [PATCH 14/48] Fmt --- src/core/tests/test_headers_store.cairo | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/core/tests/test_headers_store.cairo b/src/core/tests/test_headers_store.cairo index ddfe6c0..4faea62 100644 --- a/src/core/tests/test_headers_store.cairo +++ b/src/core/tests/test_headers_store.cairo @@ -198,7 +198,15 @@ fn test_header_block_1() { stop_prank(CheatTarget::One(contract_address)); assert(dispatcher.get_mmr_root(mmr_id) == mmr.root, 'Root not set'); - dispatcher.process_batch(headers_rlp, mmr_peaks, mmr_id, Option::None, Option::Some(mmr.last_pos), Option::Some(mmr_proof)); + dispatcher + .process_batch( + headers_rlp, + mmr_peaks, + mmr_id, + Option::None, + Option::Some(mmr.last_pos), + Option::Some(mmr_proof) + ); } #[test] @@ -242,13 +250,21 @@ fn test_create_branch_from_message() { assert(dispatcher.get_mmr_root(mmr_id_1).unwrap() == root, 'Mmr root mismatch'); // Creating MMR with ID 0 is not allowed. - assert(dispatcher.create_branch_from_message(root, size, 0, 0).is_err(), 'Mmr ID 0 should fail'); + assert( + dispatcher.create_branch_from_message(root, size, 0, 0).is_err(), 'Mmr ID 0 should fail' + ); // Both root and size 0 is not allowed. - assert(dispatcher.create_branch_from_message(0, 0, 0, mmr_id_2).is_err(), 'Root and size 0 should fail'); + assert( + dispatcher.create_branch_from_message(0, 0, 0, mmr_id_2).is_err(), + 'Root and size 0 should fail' + ); // Creating MMR with the same ID should fail. - assert(dispatcher.create_branch_from_message(root, size, 0, mmr_id_1).is_err(), 'MMR already exists should fail'); + assert( + dispatcher.create_branch_from_message(root, size, 0, mmr_id_1).is_err(), + 'MMR already exists should fail' + ); // Create another MMR. assert(dispatcher.get_mmr_size(mmr_id_2).unwrap() == 0, 'Initial mmr size should be 0'); From bf68b60d2a109d7490e7c750e82950a23746c363 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Thu, 23 May 2024 10:52:07 +0200 Subject: [PATCH 15/48] Add CI --- .github/workflows/ci.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..5071d2a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,13 @@ +name: CI +on: + push: + merge_group: + pull_request: + +jobs: + check: + runs-on: ubuntu-latest + steps: + uses: asdf-vm/actions/install@v2 + with: + command: "scarb fmt --check" \ No newline at end of file From ea0a8e067e0cc44581bf6db2f21cdbac5a3c4bf3 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Thu, 23 May 2024 11:13:18 +0200 Subject: [PATCH 16/48] Revert CI --- .github/workflows/ci.yml | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 5071d2a..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: CI -on: - push: - merge_group: - pull_request: - -jobs: - check: - runs-on: ubuntu-latest - steps: - uses: asdf-vm/actions/install@v2 - with: - command: "scarb fmt --check" \ No newline at end of file From f5afc1a274701698777cf32681a64b9b1293a53d Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Thu, 23 May 2024 13:33:31 +0200 Subject: [PATCH 17/48] Add tests --- src/core/headers_store.cairo | 8 ++- src/core/tests/test_headers_store.cairo | 68 ++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 4 deletions(-) diff --git a/src/core/headers_store.cairo b/src/core/headers_store.cairo index 310c8d8..15bc190 100644 --- a/src/core/headers_store.cairo +++ b/src/core/headers_store.cairo @@ -451,6 +451,9 @@ mod HeadersStore { last_pos: MmrSize, ) -> bool { let root = self.mmr_history.read((mmr_id, last_pos)); + + assert(root != 0, 'MMR doesn\'t exist'); + let mmr = MMRTrait::new(root, last_pos); mmr @@ -467,7 +470,7 @@ mod HeadersStore { mmr_id: MmrId ) { assert(mmr_id != 0, 'Cannot create mmr with id 0'); - assert(root != 0 || last_pos != 0, 'root=0 & last_pos=0 not allowed'); + assert(root != 0, 'root cannot be 0'); let caller = get_caller_address(); assert(caller == self.commitments_inbox.read(), 'Only CommitmentsInbox'); @@ -498,9 +501,10 @@ mod HeadersStore { last_pos: MmrSize, new_mmr_id: MmrId ) { + assert(mmr_id != 0, 'Invalid mmr id 0'); assert(new_mmr_id != 0, 'Cannot create mmr with id 0'); - let existing_mmr = self.mmr.read(mmr_id); + let existing_mmr = self.mmr.read(new_mmr_id); assert(existing_mmr.root == 0 && existing_mmr.last_pos == 0, 'MMR ID already exists'); assert( diff --git a/src/core/tests/test_headers_store.cairo b/src/core/tests/test_headers_store.cairo index 4faea62..9223516 100644 --- a/src/core/tests/test_headers_store.cairo +++ b/src/core/tests/test_headers_store.cairo @@ -8,7 +8,8 @@ use herodotus_eth_starknet::core::headers_store::{ use herodotus_eth_starknet::core::common::{MmrSize, MmrId}; use starknet::ContractAddress; use cairo_lib::utils::types::words64::Words64; -use cairo_lib::data_structures::mmr::mmr::MMRTrait; +use cairo_lib::data_structures::mmr::mmr::{MMR, MMRTrait}; +use debug::PrintTrait; const COMMITMENTS_INBOX_ADDRESS: felt252 = 0x123; const MMR_INITIAL_ELEMENT: felt252 = @@ -39,7 +40,6 @@ fn helper_receive_hash( } #[test] -#[available_gas(99999999)] fn test_header_block_1() { let headers_rlp = array![ array![ @@ -273,6 +273,70 @@ fn test_create_branch_from_message() { assert(dispatcher.get_mmr_root(mmr_id_2).unwrap() == root, 'Mmr root mismatch'); stop_prank(CheatTarget::One(contract_address)); + + let mmr_id_3 = 0x8124a; + + // Sender other than commitments inbox should fail. + assert(dispatcher.create_branch_from_message(root, size, 0, mmr_id_3).is_err(), 'only commitments inbox'); +} + +fn helper_create_mmr_with_items(mut items: Span) -> MMR { + let mut mmr: MMR = Default::default(); + let mut peaks = array![].span(); + loop { + match items.pop_front() { + Option::Some(item) => { + let (_root, new_peaks) = mmr.append(*item, peaks).unwrap(); + peaks = new_peaks; + }, + Option::None => { + break; + } + } + }; + mmr +} + +#[test] +fn test_create_branch_single_element() { + let (dispatcher, contract_address) = helper_create_safe_headers_store(); + + // Setup mmr with 7 elements + let mmr_id = 1; + let items = array![MMR_INITIAL_ELEMENT, 0x4AF3, 0xB1C2, 0x68D0, 0xE923, 0x0F4B, 0x37A8]; + let mmr = helper_create_mmr_with_items(items.span()); + start_prank(CheatTarget::One(contract_address), COMMITMENTS_INBOX_ADDRESS.try_into().unwrap()); + dispatcher.create_branch_from_message(mmr.root, mmr.last_pos, 0, mmr_id).unwrap(); + stop_prank(CheatTarget::One(contract_address)); + + // Create branch with 3rd element + let new_mmr_id = 10; + let index = 4; + let hash = 0xB1C2; + let proof = array![0x68D0, 0x5e58373c626c427a3d2b417634424a93ca5efa8cde09ac9747aa03f3afecb8d].span(); + let peaks = array![0x7cd5f93b55c504e2919127e13b025c86cbb135e14efb41a97c56a3f61bc48d8, 0x73cc8b5fc3ab909c2b7f33c34bd341df3b1d328f29c59bbe97dc53a17bbc33f, 0x37A8].span(); + + + // New MMR with ID 0 should fail + assert(dispatcher.create_branch_single_element(index, hash, peaks, proof, mmr_id, mmr.last_pos, 0).is_err(), 'new mmr id 0 should fail'); + + // Source MMR with ID 0 should fail + assert(dispatcher.create_branch_single_element(index, hash, peaks, proof, 0, mmr.last_pos, new_mmr_id).is_err(), 'src mmr id 0 should fail'); + + // Source MMR must exist + assert(dispatcher.create_branch_single_element(index, hash, peaks, proof, 2, mmr.last_pos, new_mmr_id).is_err(), 'no src mmr should fail'); + + // Invalid proof should fail + assert(dispatcher.create_branch_single_element(index, hash, peaks, array![0x68D0, 0x5e58373c626c427a3d2b417634424a93ca5efa8cde09ac9747aa03f3afecb8d, 0x1234].span(), mmr_id, mmr.last_pos, new_mmr_id).is_err(), 'invalid proof should fail'); + + // Valid proof should succeed + dispatcher.create_branch_single_element(index, hash, peaks, proof, mmr_id, mmr.last_pos, new_mmr_id).unwrap(); + let new_mmr = dispatcher.get_mmr(new_mmr_id).unwrap(); + assert(new_mmr.root == 0x2f9bb49a56c6119deabb24612297842a5ec873bd67e71e7b87b94c8e1b95d7a, 'new mmr root mismatch'); + assert(new_mmr.last_pos == 1, 'new mmr last_pos mismatch'); + + // New MMR that already exists should fail + assert(dispatcher.create_branch_single_element(index, hash, peaks, proof, mmr_id, mmr.last_pos, new_mmr_id).is_err(), 'mmr alrd exists should fail'); } #[test] From fb6962c565ad4cc4da779689f481f517815cd678 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Thu, 23 May 2024 13:39:39 +0200 Subject: [PATCH 18/48] Fmt --- src/core/tests/test_headers_store.cairo | 79 ++++++++++++++++++++----- 1 file changed, 65 insertions(+), 14 deletions(-) diff --git a/src/core/tests/test_headers_store.cairo b/src/core/tests/test_headers_store.cairo index 9223516..aa87360 100644 --- a/src/core/tests/test_headers_store.cairo +++ b/src/core/tests/test_headers_store.cairo @@ -277,7 +277,10 @@ fn test_create_branch_from_message() { let mmr_id_3 = 0x8124a; // Sender other than commitments inbox should fail. - assert(dispatcher.create_branch_from_message(root, size, 0, mmr_id_3).is_err(), 'only commitments inbox'); + assert( + dispatcher.create_branch_from_message(root, size, 0, mmr_id_3).is_err(), + 'only commitments inbox' + ); } fn helper_create_mmr_with_items(mut items: Span) -> MMR { @@ -289,9 +292,7 @@ fn helper_create_mmr_with_items(mut items: Span) -> MMR { let (_root, new_peaks) = mmr.append(*item, peaks).unwrap(); peaks = new_peaks; }, - Option::None => { - break; - } + Option::None => { break; } } }; mmr @@ -313,30 +314,80 @@ fn test_create_branch_single_element() { let new_mmr_id = 10; let index = 4; let hash = 0xB1C2; - let proof = array![0x68D0, 0x5e58373c626c427a3d2b417634424a93ca5efa8cde09ac9747aa03f3afecb8d].span(); - let peaks = array![0x7cd5f93b55c504e2919127e13b025c86cbb135e14efb41a97c56a3f61bc48d8, 0x73cc8b5fc3ab909c2b7f33c34bd341df3b1d328f29c59bbe97dc53a17bbc33f, 0x37A8].span(); - + let proof = array![0x68D0, 0x5e58373c626c427a3d2b417634424a93ca5efa8cde09ac9747aa03f3afecb8d] + .span(); + let peaks = array![ + 0x7cd5f93b55c504e2919127e13b025c86cbb135e14efb41a97c56a3f61bc48d8, + 0x73cc8b5fc3ab909c2b7f33c34bd341df3b1d328f29c59bbe97dc53a17bbc33f, + 0x37A8 + ] + .span(); // New MMR with ID 0 should fail - assert(dispatcher.create_branch_single_element(index, hash, peaks, proof, mmr_id, mmr.last_pos, 0).is_err(), 'new mmr id 0 should fail'); + assert( + dispatcher + .create_branch_single_element(index, hash, peaks, proof, mmr_id, mmr.last_pos, 0) + .is_err(), + 'new mmr id 0 should fail' + ); // Source MMR with ID 0 should fail - assert(dispatcher.create_branch_single_element(index, hash, peaks, proof, 0, mmr.last_pos, new_mmr_id).is_err(), 'src mmr id 0 should fail'); + assert( + dispatcher + .create_branch_single_element(index, hash, peaks, proof, 0, mmr.last_pos, new_mmr_id) + .is_err(), + 'src mmr id 0 should fail' + ); // Source MMR must exist - assert(dispatcher.create_branch_single_element(index, hash, peaks, proof, 2, mmr.last_pos, new_mmr_id).is_err(), 'no src mmr should fail'); + assert( + dispatcher + .create_branch_single_element(index, hash, peaks, proof, 2, mmr.last_pos, new_mmr_id) + .is_err(), + 'no src mmr should fail' + ); // Invalid proof should fail - assert(dispatcher.create_branch_single_element(index, hash, peaks, array![0x68D0, 0x5e58373c626c427a3d2b417634424a93ca5efa8cde09ac9747aa03f3afecb8d, 0x1234].span(), mmr_id, mmr.last_pos, new_mmr_id).is_err(), 'invalid proof should fail'); + assert( + dispatcher + .create_branch_single_element( + index, + hash, + peaks, + array![ + 0x68D0, + 0x5e58373c626c427a3d2b417634424a93ca5efa8cde09ac9747aa03f3afecb8d, + 0x1234 + ] + .span(), + mmr_id, + mmr.last_pos, + new_mmr_id + ) + .is_err(), + 'invalid proof should fail' + ); // Valid proof should succeed - dispatcher.create_branch_single_element(index, hash, peaks, proof, mmr_id, mmr.last_pos, new_mmr_id).unwrap(); + dispatcher + .create_branch_single_element(index, hash, peaks, proof, mmr_id, mmr.last_pos, new_mmr_id) + .unwrap(); let new_mmr = dispatcher.get_mmr(new_mmr_id).unwrap(); - assert(new_mmr.root == 0x2f9bb49a56c6119deabb24612297842a5ec873bd67e71e7b87b94c8e1b95d7a, 'new mmr root mismatch'); + assert( + new_mmr.root == 0x2f9bb49a56c6119deabb24612297842a5ec873bd67e71e7b87b94c8e1b95d7a, + 'new mmr root mismatch' + ); assert(new_mmr.last_pos == 1, 'new mmr last_pos mismatch'); // New MMR that already exists should fail - assert(dispatcher.create_branch_single_element(index, hash, peaks, proof, mmr_id, mmr.last_pos, new_mmr_id).is_err(), 'mmr alrd exists should fail'); + assert( + dispatcher + .create_branch_single_element( + index, hash, peaks, proof, mmr_id, mmr.last_pos, new_mmr_id + ) + .is_err(), + 'mmr alrd exists should fail' + ); } #[test] From 3e6ea6a74a6a7de462387d542d29c2586310147a Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Thu, 23 May 2024 13:56:32 +0200 Subject: [PATCH 19/48] More tests --- src/core/tests/test_headers_store.cairo | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/core/tests/test_headers_store.cairo b/src/core/tests/test_headers_store.cairo index aa87360..fdb1a9c 100644 --- a/src/core/tests/test_headers_store.cairo +++ b/src/core/tests/test_headers_store.cairo @@ -391,7 +391,7 @@ fn test_create_branch_single_element() { } #[test] -fn test_initial_tree() { +fn test_create_branch_new() { let (dispatcher, _) = helper_create_headers_store(); let mmr_id = 1; @@ -406,6 +406,25 @@ fn test_initial_tree() { assert(historical_root == expected_root, 'Wrong initial historical root'); } +#[test] +fn test_create_branch_from() { + let (dispatcher, contract_address) = helper_create_safe_headers_store(); + + // Setup mmr with 7 elements + let mmr_id = 0x4a02; + let items = array![MMR_INITIAL_ELEMENT, 0x4AF3, 0xB1C2, 0x68D0, 0xE923, 0x0F4B, 0x37A8]; + let mmr = helper_create_mmr_with_items(items.span()); + start_prank(CheatTarget::One(contract_address), COMMITMENTS_INBOX_ADDRESS.try_into().unwrap()); + dispatcher.create_branch_from_message(mmr.root, mmr.last_pos, 0, mmr_id).unwrap(); + stop_prank(CheatTarget::One(contract_address)); + + let new_mmr_id = 0x8cae; + dispatcher.create_branch_from(mmr_id, mmr.last_pos, new_mmr_id).unwrap(); + let new_mmr = dispatcher.get_mmr(new_mmr_id).unwrap(); + assert(new_mmr.root == mmr.root, 'new mmr root mismatch'); + assert(new_mmr.last_pos == mmr.last_pos, 'new mmr last_pos mismatch'); +} + #[test] fn test_process_batch_form_message() { let (dispatcher, contract_address) = helper_create_headers_store(); From f69233790e19901a194955ba8df1bd3706c8dc43 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Thu, 23 May 2024 16:34:26 +0200 Subject: [PATCH 20/48] Fix L1 tests --- l1/foundry.toml | 6 ++++-- l1/src/L1MessagesSender.sol | 10 ++++++---- l1/test/L1MessagesSender.t.sol | 12 +++++++----- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/l1/foundry.toml b/l1/foundry.toml index 03c33f8..64f69fe 100644 --- a/l1/foundry.toml +++ b/l1/foundry.toml @@ -5,11 +5,13 @@ libs = ["lib"] gas_reports = ["*"] optimizer = true optimizer_runs = 20000 +solc = "0.8.21" +evm_version = "shanghai" [rpc_endpoints] -goerli = "https://rpc.ankr.com/eth_goerli" +sepolia = "https://sepolia.drpc.org" [etherscan] -goerli = { key = "${ETHERSCAN_API_KEY}" } +sepolia = { key = "${ETHERSCAN_API_KEY}" } # See more config options https://github.com/foundry-rs/foundry/tree/master/config diff --git a/l1/src/L1MessagesSender.sol b/l1/src/L1MessagesSender.sol index f1ddcb0..67b6821 100644 --- a/l1/src/L1MessagesSender.sol +++ b/l1/src/L1MessagesSender.sol @@ -56,7 +56,7 @@ contract L1MessagesSender is Ownable { } /// @param aggregatorId The id of a tree previously created by the aggregators factory - function sendPoseidonMMRTreeToL2(uint256 aggregatorId) external payable { + function sendPoseidonMMRTreeToL2(uint256 aggregatorId, uint256 mmrId) external payable { address existingAggregatorAddr = aggregatorsFactory.aggregatorsById( aggregatorId ); @@ -70,7 +70,7 @@ contract L1MessagesSender is Ownable { require(mmrSize >= 1, "Invalid tree size"); require(poseidonMMRRoot != bytes32(0), "Invalid root (Poseidon)"); - _sendPoseidonMMRTreeToL2(poseidonMMRRoot, mmrSize, aggregatorId); + _sendPoseidonMMRTreeToL2(poseidonMMRRoot, mmrSize, aggregatorId, mmrId); } function _sendBlockHashToL2( @@ -97,13 +97,15 @@ contract L1MessagesSender is Ownable { function _sendPoseidonMMRTreeToL2( bytes32 poseidonMMRRoot, uint256 mmrSize, - uint256 aggregatorId + uint256 aggregatorId, + uint256 mmrId ) internal { - uint256[] memory message = new uint256[](3); + uint256[] memory message = new uint256[](4); message[0] = uint256(poseidonMMRRoot); message[1] = mmrSize; message[2] = aggregatorId; + message[3] = mmrId; // Pass along msg.value starknetCore.sendMessageToL2{value: msg.value}( diff --git a/l1/test/L1MessagesSender.t.sol b/l1/test/L1MessagesSender.t.sol index 3f51e87..14a8cf5 100644 --- a/l1/test/L1MessagesSender.t.sol +++ b/l1/test/L1MessagesSender.t.sol @@ -10,12 +10,12 @@ contract L1MessagesSenderTest is Test { L1MessagesSender public sender; function setUp() public { - vm.createSelectFork(vm.rpcUrl("goerli")); + vm.createSelectFork(vm.rpcUrl("sepolia")); sender = new L1MessagesSender( - IStarknetCore(0xde29d060D45901Fb19ED6C6e959EB22d8626708e), - 0x07bf6b32382276bFF5341f810A6811233A9591228642F60160129629448a21b6, - 0xB8Cb7707b5160eaE8931e0cf02B563a5CeA75F09 + IStarknetCore(0xE2Bb56ee936fd6433DC0F6e7e3b8365C906AA057), + 0x002e94a2344485429762F272c4Cf80F7378b30e1E9A34d662e0ba282135CC916, + 0x70C61dd17b7207B450Cb7DeDC92C1707A07a1213 ); } @@ -35,7 +35,9 @@ contract L1MessagesSenderTest is Test { // This aggregator id must exist in the factory uint256 aggregatorId = 1; + uint256 mmrId = 4; + // Value must be greater than 0 - sender.sendPoseidonMMRTreeToL2{value: 1}(aggregatorId); + sender.sendPoseidonMMRTreeToL2{value: 1}(aggregatorId, mmrId); } } From 528b7c55404e9219921305612284beaa7cc99400 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Fri, 24 May 2024 11:32:03 +0200 Subject: [PATCH 21/48] Change root=0 asserts --- src/core/headers_store.cairo | 12 ++++++------ src/core/tests/test_headers_store.cairo | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/core/headers_store.cairo b/src/core/headers_store.cairo index 15bc190..03b3dce 100644 --- a/src/core/headers_store.cairo +++ b/src/core/headers_store.cairo @@ -168,7 +168,9 @@ mod HeadersStore { #[storage] struct Storage { commitments_inbox: ContractAddress, + // MMR root = 0 means that MMR doesn't exist mmr: LegacyMap::, + // MMR root = 0 means that MMR doesn't exist mmr_history: LegacyMap::<(MmrId, MmrSize), felt252>, // block_number => parent blockhash received_blocks: LegacyMap::, @@ -434,6 +436,7 @@ mod HeadersStore { mmr_id: MmrId, ) -> bool { let mmr = self.mmr.read(mmr_id); + assert(mmr.root != 0, 'MMR does not exist'); mmr .verify_proof(index, poseidon_blockhash, peaks, proof) @@ -451,8 +454,7 @@ mod HeadersStore { last_pos: MmrSize, ) -> bool { let root = self.mmr_history.read((mmr_id, last_pos)); - - assert(root != 0, 'MMR doesn\'t exist'); + assert(root != 0, 'MMR does not exist'); let mmr = MMRTrait::new(root, last_pos); @@ -475,8 +477,7 @@ mod HeadersStore { let caller = get_caller_address(); assert(caller == self.commitments_inbox.read(), 'Only CommitmentsInbox'); - let existing_mmr = self.mmr.read(mmr_id); - assert(existing_mmr.root == 0 && existing_mmr.last_pos == 0, 'MMR ID already exists'); + assert(self.mmr.read(mmr_id).root == 0, 'MMR ID already exists'); let mmr = MMRTrait::new(root, last_pos); self.mmr.write(mmr_id, mmr); @@ -504,8 +505,7 @@ mod HeadersStore { assert(mmr_id != 0, 'Invalid mmr id 0'); assert(new_mmr_id != 0, 'Cannot create mmr with id 0'); - let existing_mmr = self.mmr.read(new_mmr_id); - assert(existing_mmr.root == 0 && existing_mmr.last_pos == 0, 'MMR ID already exists'); + assert(self.mmr.read(new_mmr_id).root == 0, 'MMR ID already exists'); assert( HeadersStore::verify_historical_mmr_inclusion( diff --git a/src/core/tests/test_headers_store.cairo b/src/core/tests/test_headers_store.cairo index fdb1a9c..d248c2d 100644 --- a/src/core/tests/test_headers_store.cairo +++ b/src/core/tests/test_headers_store.cairo @@ -254,10 +254,10 @@ fn test_create_branch_from_message() { dispatcher.create_branch_from_message(root, size, 0, 0).is_err(), 'Mmr ID 0 should fail' ); - // Both root and size 0 is not allowed. + // Root equal to 0 is not allowed. assert( - dispatcher.create_branch_from_message(0, 0, 0, mmr_id_2).is_err(), - 'Root and size 0 should fail' + dispatcher.create_branch_from_message(0, 1, 0, mmr_id_2).is_err(), + 'Root = 0 should fail' ); // Creating MMR with the same ID should fail. From 114f44989ac0d3492f2b44cd811be581e1b2bc06 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Fri, 24 May 2024 11:37:15 +0200 Subject: [PATCH 22/48] Add comment and format --- src/core/headers_store.cairo | 3 +++ src/core/tests/test_headers_store.cairo | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/headers_store.cairo b/src/core/headers_store.cairo index 03b3dce..389a50c 100644 --- a/src/core/headers_store.cairo +++ b/src/core/headers_store.cairo @@ -138,6 +138,9 @@ trait IHeadersStore { // @param mmr_id The id of the MMR to clone // @param last_pos last_pos of the given MMR // @param new_mmr_id The id of the new MMR + // @dev Notice that to prevent overwriting existing MMRs, a check if the MMR with the new id + // already exists is performed. Because of that MMR root = 0 is reserved for non-existing MMRs, + // so 0 cannot be used as a valid mmr root anywhere. fn create_branch_from( ref self: TContractState, mmr_id: MmrId, last_pos: MmrSize, new_mmr_id: MmrId ); diff --git a/src/core/tests/test_headers_store.cairo b/src/core/tests/test_headers_store.cairo index d248c2d..608e583 100644 --- a/src/core/tests/test_headers_store.cairo +++ b/src/core/tests/test_headers_store.cairo @@ -256,8 +256,7 @@ fn test_create_branch_from_message() { // Root equal to 0 is not allowed. assert( - dispatcher.create_branch_from_message(0, 1, 0, mmr_id_2).is_err(), - 'Root = 0 should fail' + dispatcher.create_branch_from_message(0, 1, 0, mmr_id_2).is_err(), 'Root = 0 should fail' ); // Creating MMR with the same ID should fail. From c72feeed7f6ac13b5d48170f89e5d8284b356c42 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Fri, 24 May 2024 12:09:52 +0200 Subject: [PATCH 23/48] Add root asserts for create_branch_from --- src/core/headers_store.cairo | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/headers_store.cairo b/src/core/headers_store.cairo index 389a50c..142cde9 100644 --- a/src/core/headers_store.cairo +++ b/src/core/headers_store.cairo @@ -548,6 +548,8 @@ mod HeadersStore { ) { assert(new_mmr_id != 0, 'Cannot create mmr with id 0'); + assert(self.mmr.read(new_mmr_id).root == 0, 'MMR ID already exists'); + let root = if mmr_id == 0 { last_pos = 1; MMR_INITIAL_ROOT @@ -555,6 +557,7 @@ mod HeadersStore { self.mmr_history.read((mmr_id, last_pos)) }; + assert(root != 0, 'MMR does not exist'); let new_mmr = MMRTrait::new(root, last_pos); self.mmr.write(new_mmr_id, new_mmr); From 6830254fd7e527ee035f8857cba7503bb60d7bce Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Fri, 24 May 2024 12:27:56 +0200 Subject: [PATCH 24/48] Rename and add asserts --- src/core/headers_store.cairo | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/core/headers_store.cairo b/src/core/headers_store.cairo index 142cde9..a7d326b 100644 --- a/src/core/headers_store.cairo +++ b/src/core/headers_store.cairo @@ -111,7 +111,7 @@ trait IHeadersStore { root: felt252, last_pos: MmrSize, aggregator_id: usize, - mmr_id: MmrId + new_mmr_id: MmrId ); @@ -297,6 +297,7 @@ mod HeadersStore { mmr_proof: Option, ) { let mut mmr = self.mmr.read(mmr_id); + assert(mmr.root != 0, 'MMR does not exist'); let poseidon_hash = hash_words64(*headers_rlp.at(0)); let mut peaks = mmr_peaks; let mut start_block = 0; @@ -472,24 +473,24 @@ mod HeadersStore { root: felt252, last_pos: MmrSize, aggregator_id: usize, - mmr_id: MmrId + new_mmr_id: MmrId ) { - assert(mmr_id != 0, 'Cannot create mmr with id 0'); + assert(new_mmr_id != 0, 'Cannot create mmr with id 0'); assert(root != 0, 'root cannot be 0'); let caller = get_caller_address(); assert(caller == self.commitments_inbox.read(), 'Only CommitmentsInbox'); - assert(self.mmr.read(mmr_id).root == 0, 'MMR ID already exists'); + assert(self.mmr.read(new_mmr_id).root == 0, 'MMR ID already exists'); let mmr = MMRTrait::new(root, last_pos); - self.mmr.write(mmr_id, mmr); - self.mmr_history.write((mmr_id, last_pos), root); + self.mmr.write(new_mmr_id, mmr); + self.mmr_history.write((new_mmr_id, last_pos), root); self .emit( Event::BranchCreatedFromL1( - BranchCreatedFromL1 { mmr_id, root, last_pos, aggregator_id } + BranchCreatedFromL1 { mmr_id: new_mmr_id, root, last_pos, aggregator_id } ) ); } From 4b05b1771ef03ba2f687ff920836530441663c3d Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Fri, 24 May 2024 12:51:27 +0200 Subject: [PATCH 25/48] Rename variables --- src/core/headers_store.cairo | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/headers_store.cairo b/src/core/headers_store.cairo index a7d326b..b47ad30 100644 --- a/src/core/headers_store.cairo +++ b/src/core/headers_store.cairo @@ -523,19 +523,19 @@ mod HeadersStore { .append(initial_poseidon_blockhash, array![].span()) .expect('Failed to append to MMR'); - let root = mmr.root; - let last_pos = mmr.last_pos; + let new_root = mmr.root; + let new_last_pos = mmr.last_pos; self.mmr.write(new_mmr_id, mmr); - self.mmr_history.write((new_mmr_id, last_pos), root); + self.mmr_history.write((new_mmr_id, new_last_pos), new_root); self .emit( Event::BranchCreatedFromElement( BranchCreatedFromElement { mmr_id: new_mmr_id, - root, - last_pos, + root: new_root, + last_pos: new_last_pos, detached_from_mmr_id: mmr_id, mmr_index: index } From d2a577877bcb2231382e85c20b1bc8f8710e6209 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Fri, 24 May 2024 12:52:43 +0200 Subject: [PATCH 26/48] Add explicit types to start and end_block --- src/core/headers_store.cairo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/headers_store.cairo b/src/core/headers_store.cairo index b47ad30..0dd9783 100644 --- a/src/core/headers_store.cairo +++ b/src/core/headers_store.cairo @@ -300,8 +300,8 @@ mod HeadersStore { assert(mmr.root != 0, 'MMR does not exist'); let poseidon_hash = hash_words64(*headers_rlp.at(0)); let mut peaks = mmr_peaks; - let mut start_block = 0; - let mut end_block = 0; + let mut start_block: u256 = 0; + let mut end_block: u256 = 0; let mut decoded_rlp = RLPItem::Bytes((array![].span(), 0)); let mut rlp_byte_len = 0; From 56c0524ad194e97c4d3b4f3b2e0350a7ba0bdcbc Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Fri, 24 May 2024 14:59:59 +0200 Subject: [PATCH 27/48] Rename assertion errors --- src/core/commitments_inbox.cairo | 14 ++--- src/core/evm_facts_registry.cairo | 24 ++++----- src/core/headers_store.cairo | 70 ++++++++++++------------- src/remappers/timestamp_remappers.cairo | 12 ++--- 4 files changed, 60 insertions(+), 60 deletions(-) diff --git a/src/core/commitments_inbox.cairo b/src/core/commitments_inbox.cairo index 2f12f38..a901054 100644 --- a/src/core/commitments_inbox.cairo +++ b/src/core/commitments_inbox.cairo @@ -128,21 +128,21 @@ mod CommitmentsInbox { // @inheritdoc ICommitmentsInbox fn set_headers_store(ref self: ContractState, headers_store: ContractAddress) { let caller = get_caller_address(); - assert(self.owner.read() == caller, 'Only owner'); + assert(self.owner.read() == caller, 'ONLY_OWNER'); self.headers_store.write(headers_store); } // @inheritdoc ICommitmentsInbox fn set_l1_message_sender(ref self: ContractState, l1_message_sender: EthAddress) { let caller = get_caller_address(); - assert(self.owner.read() == caller, 'Only owner'); + assert(self.owner.read() == caller, 'ONLY_OWNER'); self.l1_message_sender.write(l1_message_sender); } // @inheritdoc ICommitmentsInbox fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { let caller = get_caller_address(); - assert(self.owner.read() == caller, 'Only owner'); + assert(self.owner.read() == caller, 'ONLY_OWNER'); self.owner.write(new_owner); if new_owner.is_zero() { @@ -161,7 +161,7 @@ mod CommitmentsInbox { // @inheritdoc ICommitmentsInbox fn renounce_ownership(ref self: ContractState) { let caller = get_caller_address(); - assert(self.owner.read() == caller, 'Only owner'); + assert(self.owner.read() == caller, 'ONLY_OWNER'); self.owner.write(Zeroable::zero()); self.emit(Event::OwnershipRenounced(OwnershipRenounced { previous_owner: caller })); @@ -172,7 +172,7 @@ mod CommitmentsInbox { ref self: ContractState, parent_hash: u256, block_number: u256 ) { let caller = get_caller_address(); - assert(self.owner.read() == caller, 'Only owner'); + assert(self.owner.read() == caller, 'ONLY_OWNER'); let contract_address = self.headers_store.read(); IHeadersStoreDispatcher { contract_address }.receive_hash(parent_hash, block_number); @@ -191,7 +191,7 @@ mod CommitmentsInbox { fn receive_commitment( ref self: ContractState, from_address: felt252, parent_hash: u256, block_number: u256 ) { - assert(from_address == self.l1_message_sender.read().into(), 'Invalid sender'); + assert(from_address == self.l1_message_sender.read().into(), 'ONLY_L1_MESSAGE_SENDER'); let contract_address = self.headers_store.read(); IHeadersStoreDispatcher { contract_address }.receive_hash(parent_hash, block_number); @@ -215,7 +215,7 @@ mod CommitmentsInbox { aggregator_id: usize, mmr_id: MmrId ) { - assert(from_address == self.l1_message_sender.read().into(), 'Invalid sender'); + assert(from_address == self.l1_message_sender.read().into(), 'ONLY_L!_MESSAGE_SENDER'); let contract_address = self.headers_store.read(); IHeadersStoreDispatcher { contract_address } diff --git a/src/core/evm_facts_registry.cairo b/src/core/evm_facts_registry.cairo index dd10080..b0dbdd5 100644 --- a/src/core/evm_facts_registry.cairo +++ b/src/core/evm_facts_registry.cairo @@ -234,7 +234,7 @@ mod EVMFactsRegistry { mpt_proof: Span ) -> u256 { let storage_hash = reverse_endianness_u256( - self.storage_hash.read((account, block)).expect('Storage hash not proven') + self.storage_hash.read((account, block)).expect('STORAGE_HASH_NOT_PROVEN') ); // Split the slot into 4 64 bit words @@ -257,19 +257,19 @@ mod EVMFactsRegistry { let key = reverse_endianness_u256(keccak_cairo_words64(words, 8)); let mpt = MPTTrait::new(storage_hash); - let rlp_value = mpt.verify(key, 64, mpt_proof).expect('MPT verification failed'); + let rlp_value = mpt.verify(key, 64, mpt_proof).expect('MPT_VERIFICATION_FAILED'); if rlp_value.is_empty() { return 0; } - let (item, _) = rlp_decode(rlp_value).expect('Invalid RLP value'); + let (item, _) = rlp_decode(rlp_value).expect('INVALID_RLP_VALUE'); match item { RLPItem::Bytes((value, value_len)) => value .as_u256_be(value_len) - .expect('Invalid value'), - RLPItem::List(_) => panic_with_felt252('Invalid header rlp') + .expect('INVALID_RLP_VALUE'), + RLPItem::List(_) => panic_with_felt252('INVALID_HEADER_RLP') } } @@ -360,20 +360,20 @@ mod EVMFactsRegistry { .verify_historical_mmr_inclusion( mmr_index, blockhash, mmr_peaks, mmr_proof, mmr_id, last_pos ); - assert(mmr_inclusion, 'MMR inclusion not proven'); + assert(mmr_inclusion, 'INVALID_MMR_PROOF'); let (decoded_rlp, _) = rlp_decode_list_lazy(block_header_rlp, array![3, 8].span()) - .expect('Invalid header rlp'); + .expect('INVALID_HEADER_RLP'); let mut state_root: u256 = 0; let mut block_number: u256 = 0; match decoded_rlp { - RLPItem::Bytes(_) => panic_with_felt252('Invalid header rlp'), + RLPItem::Bytes(_) => panic_with_felt252('INVALID_HEADER_RLP'), RLPItem::List(l) => { let (state_root_words, _) = *l.at(0); state_root = state_root_words.as_u256_le().unwrap(); let (block_number_words, block_number_byte_len) = *l.at(1); - assert(block_number_words.len() == 1, 'Invalid block number'); + assert(block_number_words.len() == 1, 'INVALID_BLOCK_NUMBER'); let block_number_le = *block_number_words.at(0); block_number = @@ -403,7 +403,7 @@ mod EVMFactsRegistry { .span(); let key = reverse_endianness_u256(keccak_cairo_words64(words, 4)); - let rlp_account = mpt.verify(key, 64, mpt_proof).expect('MPT verification failed'); + let rlp_account = mpt.verify(key, 64, mpt_proof).expect('MPT_VERIFICATION_FAILED'); let mut account_fields = ArrayTrait::new(); if rlp_account.is_empty() { @@ -418,9 +418,9 @@ mod EVMFactsRegistry { i += 1; }; } else { - let (decoded_account, _) = rlp_decode(rlp_account).expect('Invalid account rlp'); + let (decoded_account, _) = rlp_decode(rlp_account).expect('INVALID_ACCOUNT_RLP'); match decoded_account { - RLPItem::Bytes(_) => panic_with_felt252('Invalid account rlp'), + RLPItem::Bytes(_) => panic_with_felt252('INVALID_ACCOUNT_RLP'), RLPItem::List(l) => { let mut i: usize = 0; loop { diff --git a/src/core/headers_store.cairo b/src/core/headers_store.cairo index 0dd9783..210a1ad 100644 --- a/src/core/headers_store.cairo +++ b/src/core/headers_store.cairo @@ -279,7 +279,7 @@ mod HeadersStore { // @inheritdoc IHeadersStore fn receive_hash(ref self: ContractState, parent_hash: u256, block_number: u256) { let caller = get_caller_address(); - assert(caller == self.commitments_inbox.read(), 'Only CommitmentsInbox'); + assert(caller == self.commitments_inbox.read(), 'ONLY_COMMITMENTS_INBOX'); self.received_blocks.write(block_number, parent_hash); @@ -297,7 +297,7 @@ mod HeadersStore { mmr_proof: Option, ) { let mut mmr = self.mmr.read(mmr_id); - assert(mmr.root != 0, 'MMR does not exist'); + assert(mmr.root != 0, 'SRC_MMR_NOT_FOUND'); let poseidon_hash = hash_words64(*headers_rlp.at(0)); let mut peaks = mmr_peaks; let mut start_block: u256 = 0; @@ -307,27 +307,27 @@ mod HeadersStore { let mut rlp_byte_len = 0; if mmr_proof.is_some() { - assert(reference_block.is_none(), 'Cannot use proof AND ref block'); - assert(headers_rlp.len() >= 2, 'Invalid headers rlp'); + assert(reference_block.is_none(), 'PROOF_AND_REF_BLOCK_NOT_ALLOWED'); + assert(headers_rlp.len() >= 2, 'INVALID_HEADER_RLP'); match rlp_decode_list_lazy(*headers_rlp.at(0), array![0, 8].span()) { Result::Ok((d, d_l)) => { decoded_rlp = d; rlp_byte_len = d_l; }, - Result::Err(_) => { panic_with_felt252('Invalid header rlp'); } + Result::Err(_) => { panic_with_felt252('INVALID_HEADER_RLP'); } }; let valid_proof = mmr .verify_proof(mmr_index.unwrap(), poseidon_hash, mmr_peaks, mmr_proof.unwrap()) - .expect('MMR proof verification failed'); - assert(valid_proof, 'Invalid proof'); + .expect('INVALID_MMR_PROOF'); + assert(valid_proof, 'INVALID_MMR_PROOF'); match @decoded_rlp { - RLPItem::Bytes(_) => panic_with_felt252('Invalid header rlp'), + RLPItem::Bytes(_) => panic_with_felt252('INVALID_HEADER_RLP'), RLPItem::List(l) => { let (start_block_words, start_block_byte_len) = *(*l).at(1); - assert(start_block_words.len() == 1, 'Invalid start_block'); + assert(start_block_words.len() == 1, 'INVALID_START_BLOCK'); let start_block_le = *start_block_words.at(0); start_block = @@ -341,14 +341,14 @@ mod HeadersStore { } }; } else { - assert(headers_rlp.len() >= 1, 'Invalid headers rlp'); + assert(headers_rlp.len() >= 1, 'INVALID_HEADER_RLP'); match rlp_decode_list_lazy(*headers_rlp.at(0), array![0].span()) { Result::Ok((d, d_l)) => { decoded_rlp = d; rlp_byte_len = d_l; }, - Result::Err(_) => { panic_with_felt252('Invalid header rlp'); } + Result::Err(_) => { panic_with_felt252('INVALID_HEADER_RLP'); } }; let reference_block = reference_block.unwrap(); @@ -356,7 +356,7 @@ mod HeadersStore { end_block = start_block - headers_rlp.len().into() + 1; let initial_blockhash = self.received_blocks.read(reference_block); - assert(initial_blockhash != Zeroable::zero(), 'Block not received'); + assert(initial_blockhash != Zeroable::zero(), 'BLOCK_NOT_RECEIVED'); let mut last_word_byte_len = rlp_byte_len % 8; if last_word_byte_len == 0 { @@ -365,9 +365,9 @@ mod HeadersStore { let rlp_hash = InternalFunctions::keccak_hash_rlp( *headers_rlp.at(0), last_word_byte_len, true ); - assert(rlp_hash == initial_blockhash, 'Invalid initial header rlp'); + assert(rlp_hash == initial_blockhash, 'INVALID_INITIAL_HEADER_RLP'); - let (_, p) = mmr.append(poseidon_hash, mmr_peaks).expect('Failed to append to MMR'); + let (_, p) = mmr.append(poseidon_hash, mmr_peaks).expect('MMR_APPEND_FAILED'); peaks = p; } @@ -378,10 +378,10 @@ mod HeadersStore { } let parent_hash: u256 = match decoded_rlp { - RLPItem::Bytes(_) => panic_with_felt252('Invalid header rlp'), + RLPItem::Bytes(_) => panic_with_felt252('INVALID_HEADER_RLP'), RLPItem::List(l) => { let (words, words_byte_len) = *l.at(0); - assert(words.len() == 4 && words_byte_len == 32, 'Invalid parent_hash rlp'); + assert(words.len() == 4 && words_byte_len == 32, 'INVALID_PARENT_HASH_RLP'); words.as_u256_le().unwrap() }, }; @@ -393,7 +393,7 @@ mod HeadersStore { decoded_rlp = d; rlp_byte_len = d_l; }, - Result::Err(_) => { panic_with_felt252('Invalid header rlp'); } + Result::Err(_) => { panic_with_felt252('INVALID_HEADER_RLP'); } }; let mut last_word_byte_len = rlp_byte_len % 8; @@ -403,11 +403,11 @@ mod HeadersStore { let current_hash = InternalFunctions::keccak_hash_rlp( current_rlp, last_word_byte_len, false ); - assert(current_hash == parent_hash, 'Invalid header rlp'); + assert(current_hash == parent_hash, 'INVALID_HEADER_RLP'); let poseidon_hash = hash_words64(current_rlp); - let (_, p) = mmr.append(poseidon_hash, peaks).expect('Failed to append to MMR'); + let (_, p) = mmr.append(poseidon_hash, peaks).expect('MMR_APPEND_FAILED'); peaks = p; i += 1; @@ -440,11 +440,11 @@ mod HeadersStore { mmr_id: MmrId, ) -> bool { let mmr = self.mmr.read(mmr_id); - assert(mmr.root != 0, 'MMR does not exist'); + assert(mmr.root != 0, 'MMR_NOT_FOUND'); mmr .verify_proof(index, poseidon_blockhash, peaks, proof) - .expect('MMR proof verification failed') + .expect('INVALID_MMR_PROOF') } // @inheritdoc IHeadersStore @@ -458,13 +458,13 @@ mod HeadersStore { last_pos: MmrSize, ) -> bool { let root = self.mmr_history.read((mmr_id, last_pos)); - assert(root != 0, 'MMR does not exist'); + assert(root != 0, 'MMR_NOT_FOUND'); let mmr = MMRTrait::new(root, last_pos); mmr .verify_proof(index, poseidon_blockhash, peaks, proof) - .expect('MMR proof verification failed') + .expect('INVALID_MMR_PROOF') } // @inheritdoc IHeadersStore @@ -475,13 +475,13 @@ mod HeadersStore { aggregator_id: usize, new_mmr_id: MmrId ) { - assert(new_mmr_id != 0, 'Cannot create mmr with id 0'); - assert(root != 0, 'root cannot be 0'); + assert(new_mmr_id != 0, 'NEW_MMR_ID_0_NOT_ALLOWED'); + assert(root != 0, 'ROOT_0_NOT_ALLOWED'); let caller = get_caller_address(); - assert(caller == self.commitments_inbox.read(), 'Only CommitmentsInbox'); + assert(caller == self.commitments_inbox.read(), 'ONLY_COMMITMENTS_INBOX'); - assert(self.mmr.read(new_mmr_id).root == 0, 'MMR ID already exists'); + assert(self.mmr.read(new_mmr_id).root == 0, 'NEW_MMR_ALREADY_EXISTS'); let mmr = MMRTrait::new(root, last_pos); self.mmr.write(new_mmr_id, mmr); @@ -506,22 +506,22 @@ mod HeadersStore { last_pos: MmrSize, new_mmr_id: MmrId ) { - assert(mmr_id != 0, 'Invalid mmr id 0'); - assert(new_mmr_id != 0, 'Cannot create mmr with id 0'); + assert(mmr_id != 0, 'SRC_MMR_ID_0_NOT_ALLOWED'); + assert(new_mmr_id != 0, 'NEW_MMR_ID_0_NOT_ALLOWED'); - assert(self.mmr.read(new_mmr_id).root == 0, 'MMR ID already exists'); + assert(self.mmr.read(new_mmr_id).root == 0, 'NEW_MMR_ALREADY_EXISTS'); assert( HeadersStore::verify_historical_mmr_inclusion( @self, index, initial_poseidon_blockhash, peaks, proof, mmr_id, last_pos ), - 'Invalid proof' + 'INVALID_MMR_PROOF' ); let mut mmr: MMR = Default::default(); mmr .append(initial_poseidon_blockhash, array![].span()) - .expect('Failed to append to MMR'); + .expect('MMR_APPEND_FAILED'); let new_root = mmr.root; let new_last_pos = mmr.last_pos; @@ -547,9 +547,9 @@ mod HeadersStore { fn create_branch_from( ref self: ContractState, mmr_id: MmrId, mut last_pos: MmrSize, new_mmr_id: MmrId ) { - assert(new_mmr_id != 0, 'Cannot create mmr with id 0'); + assert(new_mmr_id != 0, 'NEW_MMR_ID_0_NOT_ALLOWED'); - assert(self.mmr.read(new_mmr_id).root == 0, 'MMR ID already exists'); + assert(self.mmr.read(new_mmr_id).root == 0, 'NEW_MMR_ALREADY_EXISTS'); let root = if mmr_id == 0 { last_pos = 1; @@ -558,7 +558,7 @@ mod HeadersStore { self.mmr_history.read((mmr_id, last_pos)) }; - assert(root != 0, 'MMR does not exist'); + assert(root != 0, 'SRC_MMR_NOT_FOUND'); let new_mmr = MMRTrait::new(root, last_pos); self.mmr.write(new_mmr_id, new_mmr); diff --git a/src/remappers/timestamp_remappers.cairo b/src/remappers/timestamp_remappers.cairo index 91d00a1..4ab746f 100644 --- a/src/remappers/timestamp_remappers.cairo +++ b/src/remappers/timestamp_remappers.cairo @@ -110,7 +110,7 @@ mod TimestampRemappers { origin_elements: Span ) { let len = origin_elements.len(); // Count of elements in the batch to append - assert(len != 0, 'Empty batch'); + assert(len != 0, 'EMPTY_BATCH'); // Fetch from storage let headers_store_addr = self.headers_store.read(); @@ -135,11 +135,11 @@ mod TimestampRemappers { InternalFunctions::extract_header_block_number_and_timestamp( *origin_element.header ); - assert(origin_element_block_number == expected_block, 'Unexpected block number'); + assert(origin_element_block_number == expected_block, 'UNEXPECTED_BLOCK_NUMBER'); // 2. Verify that the header rlp is correct (i.e., matching with the leaf value) let current_hash = hash_words64(*origin_element.header); - assert(current_hash == *origin_element.leaf_value.into(), 'Invalid header rlp'); + assert(current_hash == *origin_element.leaf_value.into(), 'INVALID_HEADER_RLP'); // 3. Verify that the inclusion proof of the leaf is valid let is_valid_proof = IHeadersStoreDispatcher { @@ -153,7 +153,7 @@ mod TimestampRemappers { *origin_element.tree_id, *origin_element.last_pos ); - assert(is_valid_proof, 'Invalid proof'); + assert(is_valid_proof, 'INVALID_MMR_PROOF'); // Add the block timestamp to the mapper MMR so we can binary search it later let (_, p) = mapper_mmr @@ -312,7 +312,7 @@ mod TimestampRemappers { proof: *proof_element.proof, ) .unwrap(); - assert(is_valid_proof, 'Invalid proof'); + assert(is_valid_proof, 'INVALID_MMR_PROOF'); if x >= mid_val { left = mid + 1; @@ -346,7 +346,7 @@ mod TimestampRemappers { tree_closest_low_val.proof, ) .unwrap(); - assert(is_valid_low_proof, 'Invalid proof'); + assert(is_valid_low_proof, 'INVALID_MMR_PROOF'); } return Option::Some(closest_idx); From b6eee69e5d2800b2e2716c7db7a1546a19d6d949 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Fri, 24 May 2024 15:15:31 +0200 Subject: [PATCH 28/48] Fmt --- src/core/headers_store.cairo | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/core/headers_store.cairo b/src/core/headers_store.cairo index 210a1ad..eaf08fe 100644 --- a/src/core/headers_store.cairo +++ b/src/core/headers_store.cairo @@ -442,9 +442,7 @@ mod HeadersStore { let mmr = self.mmr.read(mmr_id); assert(mmr.root != 0, 'MMR_NOT_FOUND'); - mmr - .verify_proof(index, poseidon_blockhash, peaks, proof) - .expect('INVALID_MMR_PROOF') + mmr.verify_proof(index, poseidon_blockhash, peaks, proof).expect('INVALID_MMR_PROOF') } // @inheritdoc IHeadersStore @@ -462,9 +460,7 @@ mod HeadersStore { let mmr = MMRTrait::new(root, last_pos); - mmr - .verify_proof(index, poseidon_blockhash, peaks, proof) - .expect('INVALID_MMR_PROOF') + mmr.verify_proof(index, poseidon_blockhash, peaks, proof).expect('INVALID_MMR_PROOF') } // @inheritdoc IHeadersStore @@ -519,9 +515,7 @@ mod HeadersStore { ); let mut mmr: MMR = Default::default(); - mmr - .append(initial_poseidon_blockhash, array![].span()) - .expect('MMR_APPEND_FAILED'); + mmr.append(initial_poseidon_blockhash, array![].span()).expect('MMR_APPEND_FAILED'); let new_root = mmr.root; let new_last_pos = mmr.last_pos; From e29b22f3323c21ea29a01c2f715ccdea30054d32 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Fri, 31 May 2024 10:34:16 +0200 Subject: [PATCH 29/48] Add error codes to readme --- README.md | 54 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index dea72bf..04ca96f 100644 --- a/README.md +++ b/README.md @@ -4,18 +4,58 @@ This repository contains and implements smart contracts deployed on Starknet. This repository contains the following modules: -- Core - Implements the core logic behind Herodotus. -- Remappers - Implements a util allowing to map arbitrary timestamps to L1 block numbers. -- Turbo - Acts as a frontend to the Core contracts, provides great UX to developers and simplifies the integration. -- L1 - Smart contracts deployed on Ethereum L1 responsible for synchronizing with L1. +- Core - Implements the core logic behind Herodotus. +- Remappers - Implements a util allowing to map arbitrary timestamps to L1 block numbers. +- Turbo - Acts as a frontend to the Core contracts, provides great UX to developers and simplifies the integration. +- L1 - Smart contracts deployed on Ethereum L1 responsible for synchronizing with L1. # Core This module is responsible for: -- Processing new block headers and growing the MMR. -- Receiving and handling L1 messages containing blockhashes and Poseidon roots of the MMR which generation has been SHARP proven. -- Verifying state proofs and saving the proven values in the `FactsRegistry` +- Processing new block headers and growing the MMR. +- Receiving and handling L1 messages containing blockhashes and Poseidon roots of the MMR which generation has been SHARP proven. +- Verifying state proofs and saving the proven values in the `FactsRegistry` + +## Error codes + +### Headers Store + +- `ONLY_COMMITMENTS_INBOX` - Only commitments inbox (address saved in `commitments_inbox` variable) can send messages to this function. + +- `SRC_MMR_NOT_FOUND` - Source MMR (one from which the branch is created) with provided MMR ID does not exist in the store. + +- `SRC_MMR_ID_0_NOT_ALLOWED` - Source MMR (one from which the branch is created) with ID 0 is not allowed. + +- `NEW_MMR_ID_0_NOT_ALLOWED` - New MMR (one that is created from source branch) with ID 0 is not allowed. + +- `ROOT_0_NOT_ALLOWED` - Creating MMR with root 0 is not allowed. + +- `NEW_MMR_ALREADY_EXISTS` - New MMR (one that is created from source branch) with provided ID already exists in the store. + +- `MMR_NOT_FOUND` - MMR with provided ID does not exist in the store. + +- `PROOF_AND_REF_BLOCK_NOT_ALLOWED` - `process_batch` can't be called with both proof and reference block. Please select either one. + +- `INVALID_HEADER_RLP` - Provided header RLP is invalid. + +- `INVALID_MMR_PROOF` - Provided MMR proof (`proof` or `peaks` or both) is invalid. + +- `INVALID_START_BLOCK` - Cannot read block number from the first header RLP. + +- `BLOCK_NOT_RECEIVED` - Block which was referenced in `process_batch` was not written to the store with `receive_hash` function. + +- `INVALID_INITIAL_HEADER_RLP` - First header RLP didn't match the reference block. + +- `MMR_APPEND_FAILED` - Append to MMR function failed, most likely due to invalid peaks. + +- `INVALID_PARENT_HASH_RLP` - Could not read parent hash from the provided header RLP. + +### Commitments Inbox + +- `ONLY_OWNER` - Only owner can call this function. + +- `ONLY_L1_MESSAGE_SENDER` - Only L1 message sender can call this function. # Timestamps to block numbers mapper From 95688917a78a71b48efcd524b4fcf8777b101e06 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Fri, 31 May 2024 10:34:44 +0200 Subject: [PATCH 30/48] Fix l1 message sender test --- l1/test/L1MessagesSender.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1/test/L1MessagesSender.t.sol b/l1/test/L1MessagesSender.t.sol index 14a8cf5..5349dcd 100644 --- a/l1/test/L1MessagesSender.t.sol +++ b/l1/test/L1MessagesSender.t.sol @@ -14,7 +14,7 @@ contract L1MessagesSenderTest is Test { sender = new L1MessagesSender( IStarknetCore(0xE2Bb56ee936fd6433DC0F6e7e3b8365C906AA057), - 0x002e94a2344485429762F272c4Cf80F7378b30e1E9A34d662e0ba282135CC916, + 0x02d939c63f39760E6bC9120B2FA5b0E14e16a8f8FF22E1D8f0F088b2808F6637, 0x70C61dd17b7207B450Cb7DeDC92C1707A07a1213 ); } From 07927c5dbc345bca2d5b6a71003669c58d0205b7 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Mon, 3 Jun 2024 13:43:27 +0200 Subject: [PATCH 31/48] Change MmrId type to u128 --- src/core/common.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/common.cairo b/src/core/common.cairo index dd86926..b9825ad 100644 --- a/src/core/common.cairo +++ b/src/core/common.cairo @@ -1,2 +1,2 @@ -type MmrId = u256; +type MmrId = u128; type MmrSize = usize; From 57efad85b6aa94328be19eac66f5f4e6d0f30e5d Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Mon, 3 Jun 2024 13:50:46 +0200 Subject: [PATCH 32/48] Custom MapperIds --- src/remappers/interface.cairo | 4 ++-- src/remappers/timestamp_remappers.cairo | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/remappers/interface.cairo b/src/remappers/interface.cairo index b4404bf..ad59fbc 100644 --- a/src/remappers/interface.cairo +++ b/src/remappers/interface.cairo @@ -10,7 +10,7 @@ use cairo_lib::utils::types::words64::Words64; use herodotus_eth_starknet::core::common::{MmrId, MmrSize}; type Headers = Span; -type MapperId = usize; +type MapperId = u128; #[derive(Drop, Serde)] struct OriginElement { @@ -46,7 +46,7 @@ struct BinarySearchTree { #[starknet::interface] trait ITimestampRemappers { // Creates a new mapper and returns its ID. - fn create_mapper(ref self: TContractState, start_block: u256) -> MapperId; + fn create_mapper(ref self: TContractState, start_block: u256, mapper_id: MapperId) -> MapperId; // Adds elements from other trusted data sources to the given mapper. fn reindex_batch( diff --git a/src/remappers/timestamp_remappers.cairo b/src/remappers/timestamp_remappers.cairo index 4ab746f..b9839de 100644 --- a/src/remappers/timestamp_remappers.cairo +++ b/src/remappers/timestamp_remappers.cairo @@ -66,7 +66,6 @@ mod TimestampRemappers { struct Storage { headers_store: ContractAddress, mappers: LegacyMap::, - mappers_count: MapperId, mappers_mmrs: LegacyMap::, mappers_mmrs_history: LegacyMap::<(MapperId, MmrSize), felt252>, } @@ -74,7 +73,6 @@ mod TimestampRemappers { #[constructor] fn constructor(ref self: ContractState, headers_store: ContractAddress) { self.headers_store.write(headers_store); - self.mappers_count.write(0); } // @@ -84,10 +82,11 @@ mod TimestampRemappers { #[abi(embed_v0)] impl TimestampRemappers of ITimestampRemappers { // Creates a new mapper and returns its ID. - fn create_mapper(ref self: ContractState, start_block: u256) -> MapperId { + fn create_mapper(ref self: ContractState, start_block: u256, mapper_id: MapperId) -> MapperId { let mmr: MMR = Default::default(); - let mapper_id = self.mappers_count.read(); + assert(start_block != 0, 'START_BLOCK_0_NOT_ALLOWED'); + assert(self.mappers.read(mapper_id).start_block == 0, 'MAPPER_ID_ALREADY_EXISTS'); self.mappers_mmrs_history.write((mapper_id, 0), mmr.root); self.mappers_mmrs.write(mapper_id, mmr); @@ -95,8 +94,6 @@ mod TimestampRemappers { let mapper = Mapper { start_block, elements_count: 0, last_timestamp: 0 }; self.mappers.write(mapper_id, mapper); - self.mappers_count.write(mapper_id + 1); - self.emit(Event::MapperCreated(MapperCreated { mapper_id, start_block })); mapper_id @@ -115,6 +112,7 @@ mod TimestampRemappers { // Fetch from storage let headers_store_addr = self.headers_store.read(); let mut mapper = self.mappers.read(mapper_id); + assert(mapper.start_block != 0, 'MAPPER_DOES_NOT_EXIST') let mut mapper_mmr = self.mappers_mmrs.read(mapper_id); // Determine the expected block number of the first element in the batch From babd1d7c64dd3a43af3cbf0259c9e1aa4e881958 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Mon, 3 Jun 2024 14:02:40 +0200 Subject: [PATCH 33/48] Fix timestamp test --- src/remappers/tests/test_timestamp_remappers.cairo | 2 +- src/remappers/timestamp_remappers.cairo | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/remappers/tests/test_timestamp_remappers.cairo b/src/remappers/tests/test_timestamp_remappers.cairo index 9736c49..5e14d1a 100644 --- a/src/remappers/tests/test_timestamp_remappers.cairo +++ b/src/remappers/tests/test_timestamp_remappers.cairo @@ -362,7 +362,7 @@ fn test_remappers() { contract_address: timestamp_remappers }; let start_block: u256 = 9260751; // Mainnet block - let mapper_id = remapper_dispatcher.create_mapper(start_block); + let mapper_id = remapper_dispatcher.create_mapper(start_block, 0); assert(mapper_id == 0, 'Invalid mapper id'); // An MMR containing { 1, 2, 4, 5, 8 } as string with Starknet Poseidon as a hasher. diff --git a/src/remappers/timestamp_remappers.cairo b/src/remappers/timestamp_remappers.cairo index b9839de..abc424b 100644 --- a/src/remappers/timestamp_remappers.cairo +++ b/src/remappers/timestamp_remappers.cairo @@ -112,7 +112,7 @@ mod TimestampRemappers { // Fetch from storage let headers_store_addr = self.headers_store.read(); let mut mapper = self.mappers.read(mapper_id); - assert(mapper.start_block != 0, 'MAPPER_DOES_NOT_EXIST') + assert(mapper.start_block != 0, 'MAPPER_DOES_NOT_EXIST'); let mut mapper_mmr = self.mappers_mmrs.read(mapper_id); // Determine the expected block number of the first element in the batch From 9676e7293ff74710315fcb52187dec740e9931bc Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Mon, 3 Jun 2024 14:03:26 +0200 Subject: [PATCH 34/48] Fmt --- src/remappers/timestamp_remappers.cairo | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/remappers/timestamp_remappers.cairo b/src/remappers/timestamp_remappers.cairo index abc424b..fba7967 100644 --- a/src/remappers/timestamp_remappers.cairo +++ b/src/remappers/timestamp_remappers.cairo @@ -82,7 +82,9 @@ mod TimestampRemappers { #[abi(embed_v0)] impl TimestampRemappers of ITimestampRemappers { // Creates a new mapper and returns its ID. - fn create_mapper(ref self: ContractState, start_block: u256, mapper_id: MapperId) -> MapperId { + fn create_mapper( + ref self: ContractState, start_block: u256, mapper_id: MapperId + ) -> MapperId { let mmr: MMR = Default::default(); assert(start_block != 0, 'START_BLOCK_0_NOT_ALLOWED'); From 568df0c299f7164f312116a7b219fa969b4bc3ac Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Mon, 3 Jun 2024 14:37:56 +0200 Subject: [PATCH 35/48] Fix assert error typo --- src/core/commitments_inbox.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/commitments_inbox.cairo b/src/core/commitments_inbox.cairo index a901054..9156a8d 100644 --- a/src/core/commitments_inbox.cairo +++ b/src/core/commitments_inbox.cairo @@ -215,7 +215,7 @@ mod CommitmentsInbox { aggregator_id: usize, mmr_id: MmrId ) { - assert(from_address == self.l1_message_sender.read().into(), 'ONLY_L!_MESSAGE_SENDER'); + assert(from_address == self.l1_message_sender.read().into(), 'ONLY_L1_MESSAGE_SENDER'); let contract_address = self.headers_store.read(); IHeadersStoreDispatcher { contract_address } From ae4f46fa2bfd0ca72e0180ea39c49a4ea3de19cd Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Mon, 3 Jun 2024 16:05:30 +0200 Subject: [PATCH 36/48] Change aggregator id type --- src/core/commitments_inbox.cairo | 4 ++-- src/core/headers_store.cairo | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/commitments_inbox.cairo b/src/core/commitments_inbox.cairo index 9156a8d..ac2c79f 100644 --- a/src/core/commitments_inbox.cairo +++ b/src/core/commitments_inbox.cairo @@ -89,7 +89,7 @@ mod CommitmentsInbox { struct MMRReceived { root: felt252, last_pos: MmrSize, - aggregator_id: usize + aggregator_id: u256 } #[constructor] @@ -212,7 +212,7 @@ mod CommitmentsInbox { from_address: felt252, root: felt252, last_pos: MmrSize, - aggregator_id: usize, + aggregator_id: u256, mmr_id: MmrId ) { assert(from_address == self.l1_message_sender.read().into(), 'ONLY_L1_MESSAGE_SENDER'); diff --git a/src/core/headers_store.cairo b/src/core/headers_store.cairo index eaf08fe..95ccd45 100644 --- a/src/core/headers_store.cairo +++ b/src/core/headers_store.cairo @@ -110,7 +110,7 @@ trait IHeadersStore { ref self: TContractState, root: felt252, last_pos: MmrSize, - aggregator_id: usize, + aggregator_id: u256, new_mmr_id: MmrId ); @@ -227,7 +227,7 @@ mod HeadersStore { mmr_id: MmrId, root: felt252, last_pos: MmrSize, - aggregator_id: usize + aggregator_id: u256 } #[derive(Drop, starknet::Event)] @@ -468,7 +468,7 @@ mod HeadersStore { ref self: ContractState, root: felt252, last_pos: MmrSize, - aggregator_id: usize, + aggregator_id: u256, new_mmr_id: MmrId ) { assert(new_mmr_id != 0, 'NEW_MMR_ID_0_NOT_ALLOWED'); From a96d02f8c092ad43474e009510e21d9ceb150149 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Mon, 3 Jun 2024 16:06:24 +0200 Subject: [PATCH 37/48] Change Mmr Size type to u128 --- Scarb.lock | 2 +- src/core/common.cairo | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Scarb.lock b/Scarb.lock index 611c08d..68e7dec 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -4,7 +4,7 @@ version = 1 [[package]] name = "cairo_lib" version = "0.2.0" -source = "git+https://github.com/HerodotusDev/cairo-lib.git?branch=update-cairo#b0f93a15f09b5b9962d4dc45ddfdb05f9790291a" +source = "git+https://github.com/HerodotusDev/cairo-lib.git?branch=update-cairo#bc453f981c6ce2753543474a2492ecac03c36ba4" [[package]] name = "herodotus_eth_starknet" diff --git a/src/core/common.cairo b/src/core/common.cairo index b9825ad..2d4781b 100644 --- a/src/core/common.cairo +++ b/src/core/common.cairo @@ -1,2 +1,2 @@ type MmrId = u128; -type MmrSize = usize; +type MmrSize = u128; From 214a995d18b9788d8b0ecc025249477534451594 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Mon, 3 Jun 2024 16:31:27 +0200 Subject: [PATCH 38/48] Alias mmr element and aggregator id types --- src/core/commitments_inbox.cairo | 11 ++--- src/core/common.cairo | 2 +- src/core/evm_facts_registry.cairo | 6 ++- src/core/headers_store.cairo | 52 ++++++++++++------------ src/core/tests/test_facts_registry.cairo | 3 +- src/core/tests/test_headers_store.cairo | 11 ++--- src/remappers/interface.cairo | 5 ++- src/remappers/timestamp_remappers.cairo | 8 ++-- 8 files changed, 51 insertions(+), 47 deletions(-) diff --git a/src/core/commitments_inbox.cairo b/src/core/commitments_inbox.cairo index ac2c79f..fa7327a 100644 --- a/src/core/commitments_inbox.cairo +++ b/src/core/commitments_inbox.cairo @@ -50,7 +50,8 @@ mod CommitmentsInbox { use herodotus_eth_starknet::core::headers_store::{ IHeadersStoreDispatcherTrait, IHeadersStoreDispatcher }; - use herodotus_eth_starknet::core::common::{MmrId, MmrSize}; + use herodotus_eth_starknet::core::common::{MmrId, AggregatorId}; + use cairo_lib::data_structures::mmr::mmr::{MmrSize, MmrElement}; #[storage] struct Storage { @@ -87,9 +88,9 @@ mod CommitmentsInbox { #[derive(Drop, starknet::Event)] struct MMRReceived { - root: felt252, + root: MmrElement, last_pos: MmrSize, - aggregator_id: u256 + aggregator_id: AggregatorId } #[constructor] @@ -210,9 +211,9 @@ mod CommitmentsInbox { fn receive_mmr( ref self: ContractState, from_address: felt252, - root: felt252, + root: MmrElement, last_pos: MmrSize, - aggregator_id: u256, + aggregator_id: AggregatorId, mmr_id: MmrId ) { assert(from_address == self.l1_message_sender.read().into(), 'ONLY_L1_MESSAGE_SENDER'); diff --git a/src/core/common.cairo b/src/core/common.cairo index 2d4781b..da00ea0 100644 --- a/src/core/common.cairo +++ b/src/core/common.cairo @@ -1,2 +1,2 @@ type MmrId = u128; -type MmrSize = u128; +type AggregatorId = u256; diff --git a/src/core/evm_facts_registry.cairo b/src/core/evm_facts_registry.cairo index b0dbdd5..7f91eda 100644 --- a/src/core/evm_facts_registry.cairo +++ b/src/core/evm_facts_registry.cairo @@ -1,8 +1,9 @@ use starknet::ContractAddress; +use cairo_lib::data_structures::mmr::mmr::MmrSize; use cairo_lib::data_structures::mmr::proof::Proof; use cairo_lib::data_structures::mmr::peaks::Peaks; use cairo_lib::utils::types::words64::Words64; -use herodotus_eth_starknet::core::common::{MmrId, MmrSize}; +use herodotus_eth_starknet::core::common::MmrId; #[derive(Drop, Serde)] enum AccountField { @@ -115,6 +116,7 @@ trait IEVMFactsRegistry { mod EVMFactsRegistry { use starknet::ContractAddress; use super::AccountField; + use cairo_lib::data_structures::mmr::mmr::MmrSize; use cairo_lib::data_structures::mmr::proof::Proof; use cairo_lib::data_structures::mmr::peaks::Peaks; use cairo_lib::hashing::poseidon::hash_words64; @@ -123,7 +125,7 @@ mod EVMFactsRegistry { use cairo_lib::utils::types::words64::{ Words64, Words64Trait, reverse_endianness_u64, bytes_used_u64 }; - use herodotus_eth_starknet::core::common::{MmrId, MmrSize}; + use herodotus_eth_starknet::core::common::MmrId; use herodotus_eth_starknet::core::headers_store::{ IHeadersStoreDispatcherTrait, IHeadersStoreDispatcher }; diff --git a/src/core/headers_store.cairo b/src/core/headers_store.cairo index 95ccd45..7bc1a6d 100644 --- a/src/core/headers_store.cairo +++ b/src/core/headers_store.cairo @@ -2,8 +2,8 @@ use starknet::ContractAddress; use cairo_lib::data_structures::mmr::peaks::Peaks; use cairo_lib::data_structures::mmr::proof::Proof; use cairo_lib::utils::types::words64::Words64; -use cairo_lib::data_structures::mmr::mmr::MMR; -use herodotus_eth_starknet::core::common::{MmrId, MmrSize}; +use cairo_lib::data_structures::mmr::mmr::{MMR, MmrSize, MmrElement}; +use herodotus_eth_starknet::core::common::{MmrId, AggregatorId}; #[starknet::interface] trait IHeadersStore { @@ -19,7 +19,7 @@ trait IHeadersStore { // @notice Returns the root of the MMR with a given id // @param mmr_id The id of the MMR // @return The root of the MMR with the given id - fn get_mmr_root(self: @TContractState, mmr_id: MmrId) -> felt252; + fn get_mmr_root(self: @TContractState, mmr_id: MmrId) -> MmrElement; // @notice Returns the size of the MMR with a given id // @param mmr_id The id of the MMR @@ -36,7 +36,7 @@ trait IHeadersStore { // @param mmr_id The id of the MMR // @param size The size of the MMR // @return The root of the MMR with the given id and size - fn get_historical_root(self: @TContractState, mmr_id: MmrId, size: MmrSize) -> felt252; + fn get_historical_root(self: @TContractState, mmr_id: MmrId, size: MmrSize) -> MmrElement; // @notice Receives a parent blockhash and the corresponding block number from L1 and saves it // @dev This function can only be called by the CommitmentsInbox contract @@ -53,7 +53,7 @@ trait IHeadersStore { fn verify_mmr_inclusion( self: @TContractState, index: MmrSize, - poseidon_blockhash: felt252, + poseidon_blockhash: MmrElement, peaks: Peaks, proof: Proof, mmr_id: MmrId, @@ -71,7 +71,7 @@ trait IHeadersStore { fn verify_historical_mmr_inclusion( self: @TContractState, index: MmrSize, - poseidon_blockhash: felt252, + poseidon_blockhash: MmrElement, peaks: Peaks, proof: Proof, mmr_id: MmrId, @@ -108,9 +108,9 @@ trait IHeadersStore { // @dev This function can only be called by the CommitmentsInbox contract fn create_branch_from_message( ref self: TContractState, - root: felt252, + root: MmrElement, last_pos: MmrSize, - aggregator_id: u256, + aggregator_id: AggregatorId, new_mmr_id: MmrId ); @@ -125,7 +125,7 @@ trait IHeadersStore { fn create_branch_single_element( ref self: TContractState, index: MmrSize, - initial_poseidon_blockhash: felt252, + initial_poseidon_blockhash: MmrElement, peaks: Peaks, proof: Proof, mmr_id: MmrId, @@ -153,7 +153,7 @@ trait IHeadersStore { #[starknet::contract] mod HeadersStore { use starknet::{ContractAddress, get_caller_address}; - use cairo_lib::data_structures::mmr::mmr::{MMR, MMRTrait}; + use cairo_lib::data_structures::mmr::mmr::{MMR, MMRTrait, MmrSize, MmrElement}; use cairo_lib::data_structures::mmr::peaks::Peaks; use cairo_lib::data_structures::mmr::proof::Proof; use cairo_lib::utils::types::words64::{ @@ -163,9 +163,9 @@ mod HeadersStore { use cairo_lib::hashing::poseidon::hash_words64; use cairo_lib::utils::bitwise::reverse_endianness_u256; use cairo_lib::encoding::rlp::{RLPItem, rlp_decode_list_lazy}; - use herodotus_eth_starknet::core::common::{MmrId, MmrSize}; + use herodotus_eth_starknet::core::common::{MmrId, AggregatorId}; - const MMR_INITIAL_ROOT: felt252 = + const MMR_INITIAL_ROOT: MmrElement = 0x6759138078831011e3bc0b4a135af21c008dda64586363531697207fb5a2bae; #[storage] @@ -174,7 +174,7 @@ mod HeadersStore { // MMR root = 0 means that MMR doesn't exist mmr: LegacyMap::, // MMR root = 0 means that MMR doesn't exist - mmr_history: LegacyMap::<(MmrId, MmrSize), felt252>, + mmr_history: LegacyMap::<(MmrId, MmrSize), MmrElement>, // block_number => parent blockhash received_blocks: LegacyMap::, } @@ -199,7 +199,7 @@ mod HeadersStore { #[derive(Drop, starknet::Event)] struct ProcessedBlock { block_number: u256, - new_root: felt252, + new_root: MmrElement, new_size: MmrSize, mmr_id: MmrId } @@ -208,7 +208,7 @@ mod HeadersStore { struct ProcessedBatch { block_start: u256, block_end: u256, - new_root: felt252, + new_root: MmrElement, new_size: MmrSize, mmr_id: MmrId } @@ -216,7 +216,7 @@ mod HeadersStore { #[derive(Drop, starknet::Event)] struct BranchCreatedFromElement { mmr_id: MmrId, - root: felt252, + root: MmrElement, last_pos: MmrSize, detached_from_mmr_id: MmrId, mmr_index: MmrSize @@ -225,15 +225,15 @@ mod HeadersStore { #[derive(Drop, starknet::Event)] struct BranchCreatedFromL1 { mmr_id: MmrId, - root: felt252, + root: MmrElement, last_pos: MmrSize, - aggregator_id: u256 + aggregator_id: AggregatorId } #[derive(Drop, starknet::Event)] struct BranchCreatedClone { mmr_id: MmrId, - root: felt252, + root: MmrElement, last_pos: MmrSize, detached_from_mmr_id: MmrId } @@ -257,7 +257,7 @@ mod HeadersStore { } // @inheritdoc IHeadersStore - fn get_mmr_root(self: @ContractState, mmr_id: MmrId) -> felt252 { + fn get_mmr_root(self: @ContractState, mmr_id: MmrId) -> MmrElement { self.mmr.read(mmr_id).root } @@ -272,7 +272,7 @@ mod HeadersStore { } // @inheritdoc IHeadersStore - fn get_historical_root(self: @ContractState, mmr_id: MmrId, size: MmrSize) -> felt252 { + fn get_historical_root(self: @ContractState, mmr_id: MmrId, size: MmrSize) -> MmrElement { self.mmr_history.read((mmr_id, size)) } @@ -434,7 +434,7 @@ mod HeadersStore { fn verify_mmr_inclusion( self: @ContractState, index: MmrSize, - poseidon_blockhash: felt252, + poseidon_blockhash: MmrElement, peaks: Peaks, proof: Proof, mmr_id: MmrId, @@ -449,7 +449,7 @@ mod HeadersStore { fn verify_historical_mmr_inclusion( self: @ContractState, index: MmrSize, - poseidon_blockhash: felt252, + poseidon_blockhash: MmrElement, peaks: Peaks, proof: Proof, mmr_id: MmrId, @@ -466,9 +466,9 @@ mod HeadersStore { // @inheritdoc IHeadersStore fn create_branch_from_message( ref self: ContractState, - root: felt252, + root: MmrElement, last_pos: MmrSize, - aggregator_id: u256, + aggregator_id: AggregatorId, new_mmr_id: MmrId ) { assert(new_mmr_id != 0, 'NEW_MMR_ID_0_NOT_ALLOWED'); @@ -495,7 +495,7 @@ mod HeadersStore { fn create_branch_single_element( ref self: ContractState, index: MmrSize, - initial_poseidon_blockhash: felt252, + initial_poseidon_blockhash: MmrElement, peaks: Peaks, proof: Proof, mmr_id: MmrId, diff --git a/src/core/tests/test_facts_registry.cairo b/src/core/tests/test_facts_registry.cairo index 45ce0ec..0ed52e4 100644 --- a/src/core/tests/test_facts_registry.cairo +++ b/src/core/tests/test_facts_registry.cairo @@ -6,11 +6,10 @@ use herodotus_eth_starknet::core::headers_store::{ use herodotus_eth_starknet::core::evm_facts_registry::{ IEVMFactsRegistryDispatcherTrait, IEVMFactsRegistryDispatcher, AccountField }; -use herodotus_eth_starknet::core::common::MmrSize; use starknet::ContractAddress; use cairo_lib::utils::types::words64::Words64; use cairo_lib::hashing::poseidon::{hash_words64, PoseidonHasher}; -use cairo_lib::data_structures::mmr::{proof::Proof, peaks::Peaks}; +use cairo_lib::data_structures::mmr::{proof::Proof, peaks::Peaks, mmr::MmrSize}; const COMMITMENTS_INBOX_ADDRESS: felt252 = 0x123; const TEST_MMR_ROOT: felt252 = 0x37a31db9c80c54ec632f04f7984155dc43591a3f8c891adfbf34e75331e0eec; diff --git a/src/core/tests/test_headers_store.cairo b/src/core/tests/test_headers_store.cairo index 608e583..9921d6f 100644 --- a/src/core/tests/test_headers_store.cairo +++ b/src/core/tests/test_headers_store.cairo @@ -5,16 +5,17 @@ use herodotus_eth_starknet::core::headers_store::{ IHeadersStoreDispatcherTrait, IHeadersStoreDispatcher, IHeadersStoreSafeDispatcherTrait, IHeadersStoreSafeDispatcher }; -use herodotus_eth_starknet::core::common::{MmrSize, MmrId}; +use herodotus_eth_starknet::core::common::MmrId; use starknet::ContractAddress; use cairo_lib::utils::types::words64::Words64; -use cairo_lib::data_structures::mmr::mmr::{MMR, MMRTrait}; +use cairo_lib::data_structures::mmr::mmr::{MMR, MMRTrait, MmrSize, MmrElement}; use debug::PrintTrait; const COMMITMENTS_INBOX_ADDRESS: felt252 = 0x123; -const MMR_INITIAL_ELEMENT: felt252 = +const MMR_INITIAL_ELEMENT: MmrElement = 0x02241b3b7f1c4b9cf63e670785891de91f7237b1388f6635c1898ae397ad32dd; -const MMR_INITIAL_ROOT: felt252 = 0x6759138078831011e3bc0b4a135af21c008dda64586363531697207fb5a2bae; +const MMR_INITIAL_ROOT: MmrElement = + 0x6759138078831011e3bc0b4a135af21c008dda64586363531697207fb5a2bae; fn helper_create_headers_store() -> (IHeadersStoreDispatcher, ContractAddress) { let contract = declare("HeadersStore").unwrap(); @@ -282,7 +283,7 @@ fn test_create_branch_from_message() { ); } -fn helper_create_mmr_with_items(mut items: Span) -> MMR { +fn helper_create_mmr_with_items(mut items: Span) -> MMR { let mut mmr: MMR = Default::default(); let mut peaks = array![].span(); loop { diff --git a/src/remappers/interface.cairo b/src/remappers/interface.cairo index ad59fbc..c0b5c90 100644 --- a/src/remappers/interface.cairo +++ b/src/remappers/interface.cairo @@ -4,10 +4,11 @@ // Interface types // +use cairo_lib::data_structures::mmr::mmr::{MmrSize, MmrElement}; use cairo_lib::data_structures::mmr::proof::Proof; use cairo_lib::data_structures::mmr::peaks::Peaks; use cairo_lib::utils::types::words64::Words64; -use herodotus_eth_starknet::core::common::{MmrId, MmrSize}; +use herodotus_eth_starknet::core::common::MmrId; type Headers = Span; type MapperId = u128; @@ -17,7 +18,7 @@ struct OriginElement { tree_id: MmrId, last_pos: MmrSize, leaf_idx: MmrSize, - leaf_value: felt252, + leaf_value: MmrElement, inclusion_proof: Proof, peaks: Peaks, header: Words64 diff --git a/src/remappers/timestamp_remappers.cairo b/src/remappers/timestamp_remappers.cairo index fba7967..0b6c514 100644 --- a/src/remappers/timestamp_remappers.cairo +++ b/src/remappers/timestamp_remappers.cairo @@ -6,14 +6,14 @@ #[starknet::contract] mod TimestampRemappers { - use herodotus_eth_starknet::core::common::{MmrId, MmrSize}; + use herodotus_eth_starknet::core::common::MmrId; use herodotus_eth_starknet::remappers::interface::{ ITimestampRemappers, Headers, OriginElement, Proof, Peaks, Words64, ProofElement, BinarySearchTree, MapperId }; use starknet::ContractAddress; use cairo_lib::hashing::poseidon::{PoseidonHasher, hash_words64}; - use cairo_lib::data_structures::mmr::mmr::{MMR, MMRTrait}; + use cairo_lib::data_structures::mmr::mmr::{MMR, MMRTrait, MmrSize, MmrElement}; use cairo_lib::data_structures::mmr::utils::{leaf_index_to_mmr_index}; use cairo_lib::encoding::rlp::{RLPItem, rlp_decode_list_lazy}; use cairo_lib::utils::types::words64::{reverse_endianness_u64, bytes_used_u64}; @@ -43,7 +43,7 @@ mod TimestampRemappers { mapper_id: MapperId, start_block: u256, end_block: u256, - mmr_root: felt252, + mmr_root: MmrElement, mmr_size: MmrSize } @@ -67,7 +67,7 @@ mod TimestampRemappers { headers_store: ContractAddress, mappers: LegacyMap::, mappers_mmrs: LegacyMap::, - mappers_mmrs_history: LegacyMap::<(MapperId, MmrSize), felt252>, + mappers_mmrs_history: LegacyMap::<(MapperId, MmrSize), MmrElement>, } #[constructor] From 0dd027a43751570a2814d625d26489fe56cf6aeb Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Mon, 3 Jun 2024 16:42:35 +0200 Subject: [PATCH 39/48] Update deploy script to latest class hashes --- multicall/deploy.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/multicall/deploy.toml b/multicall/deploy.toml index a3fe0b3..963fde4 100644 --- a/multicall/deploy.toml +++ b/multicall/deploy.toml @@ -1,9 +1,9 @@ [[call]] call_type = "deploy" -class_hash = "0x54af96825d987ca89cf320f7c5a8031017815d884cff1592e8ff6da309f3ca6" +class_hash = "0x636debf6067e2fd953c4a2ceb6ca3b93d97474a725816a3ea438d3f79eb7512" inputs = [ "0x1", - "0x18e4a8e2badb5f5950758f46f8108e2c5d357b07", + "0xdF32BADD47BEef951a77Da5dbfA14E2D65dF3A20", "0x0", "0x5a6c0f84179d695f0b598cc5d0be50421c247da95cfe63e4cd66fc27f32dfe6", ] @@ -12,14 +12,14 @@ unique = false [[call]] call_type = "deploy" -class_hash = "0x2055a6a11f644201f229c1ecdc10912f20ae0f51ac8aa6ef921c58e52bae404" +class_hash = "0x5415fef5becee8d69aeaeaed8b12d2e0505c9f045b679ef2b40163cc54dae02" inputs = ["commitments_inbox"] id = "headers_store" unique = false [[call]] call_type = "deploy" -class_hash = "0x74c929cfd9fd8d7577d8c7b8102010d925cbc1b1475a5873047099a4e00dab7" +class_hash = "0x59bc294565250247b8d77921c84c96a312748496245fb11b3f1c37be6920a21" inputs = ["headers_store"] id = "evm_facts_registry" unique = false @@ -32,7 +32,7 @@ inputs = ["headers_store"] [[call]] call_type = "deploy" -class_hash = "0x67ae27cd294fea085fc3b63cd8b872774cc1ba92bd54864c267eda6fa81a493" +class_hash = "0x6b83039caf2042ee17d07853c0c24a021f4301f6c49a778fe7f690b45b2fc8f" inputs = ["headers_store"] id = "timestamp_remappers" unique = false \ No newline at end of file From a54202d010e9937825a083499f08ee0495fa8400 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Mon, 3 Jun 2024 16:47:35 +0200 Subject: [PATCH 40/48] Change Mmr Id type to uint128 in L1 msg sender --- l1/src/L1MessagesSender.sol | 4 ++-- l1/test/L1MessagesSender.t.sol | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/l1/src/L1MessagesSender.sol b/l1/src/L1MessagesSender.sol index 67b6821..9ea525d 100644 --- a/l1/src/L1MessagesSender.sol +++ b/l1/src/L1MessagesSender.sol @@ -56,7 +56,7 @@ contract L1MessagesSender is Ownable { } /// @param aggregatorId The id of a tree previously created by the aggregators factory - function sendPoseidonMMRTreeToL2(uint256 aggregatorId, uint256 mmrId) external payable { + function sendPoseidonMMRTreeToL2(uint256 aggregatorId, uint128 mmrId) external payable { address existingAggregatorAddr = aggregatorsFactory.aggregatorsById( aggregatorId ); @@ -98,7 +98,7 @@ contract L1MessagesSender is Ownable { bytes32 poseidonMMRRoot, uint256 mmrSize, uint256 aggregatorId, - uint256 mmrId + uint128 mmrId ) internal { uint256[] memory message = new uint256[](4); diff --git a/l1/test/L1MessagesSender.t.sol b/l1/test/L1MessagesSender.t.sol index 5349dcd..104555b 100644 --- a/l1/test/L1MessagesSender.t.sol +++ b/l1/test/L1MessagesSender.t.sol @@ -35,7 +35,7 @@ contract L1MessagesSenderTest is Test { // This aggregator id must exist in the factory uint256 aggregatorId = 1; - uint256 mmrId = 4; + uint128 mmrId = 4; // Value must be greater than 0 sender.sendPoseidonMMRTreeToL2{value: 1}(aggregatorId, mmrId); From 6e24e800aabce4057768e47d0cc0388bd03675d9 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Mon, 3 Jun 2024 16:50:54 +0200 Subject: [PATCH 41/48] Update L1 test --- l1/test/L1MessagesSender.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1/test/L1MessagesSender.t.sol b/l1/test/L1MessagesSender.t.sol index 104555b..fdab38f 100644 --- a/l1/test/L1MessagesSender.t.sol +++ b/l1/test/L1MessagesSender.t.sol @@ -14,7 +14,7 @@ contract L1MessagesSenderTest is Test { sender = new L1MessagesSender( IStarknetCore(0xE2Bb56ee936fd6433DC0F6e7e3b8365C906AA057), - 0x02d939c63f39760E6bC9120B2FA5b0E14e16a8f8FF22E1D8f0F088b2808F6637, + 0x07d15ab6a37642b1765a1be4ec90d793db9c4b8cd01d7bb694b20c0b9699c173, 0x70C61dd17b7207B450Cb7DeDC92C1707A07a1213 ); } From c63a678b8eee19e48b89a1527dde73eb29f6ccc1 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Tue, 4 Jun 2024 11:32:25 +0200 Subject: [PATCH 42/48] Fix invalid u256 handling on L1-L2 messaging --- l1/src/L1MessagesSender.sol | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/l1/src/L1MessagesSender.sol b/l1/src/L1MessagesSender.sol index 9ea525d..1d895b2 100644 --- a/l1/src/L1MessagesSender.sol +++ b/l1/src/L1MessagesSender.sol @@ -70,7 +70,7 @@ contract L1MessagesSender is Ownable { require(mmrSize >= 1, "Invalid tree size"); require(poseidonMMRRoot != bytes32(0), "Invalid root (Poseidon)"); - _sendPoseidonMMRTreeToL2(poseidonMMRRoot, mmrSize, aggregatorId, mmrId); + _sendPoseidonMMRTreeToL2(poseidonMMRRoot, uint128(mmrSize), aggregatorId, mmrId); } function _sendBlockHashToL2( @@ -96,16 +96,20 @@ contract L1MessagesSender is Ownable { function _sendPoseidonMMRTreeToL2( bytes32 poseidonMMRRoot, - uint256 mmrSize, + uint128 mmrSize, uint256 aggregatorId, uint128 mmrId ) internal { - uint256[] memory message = new uint256[](4); + uint128 aggregatorId_low = uint128(aggregatorId); + uint128 aggregatorId_high = uint128(aggregatorId >> 128); + uint256[] memory message = new uint256[](5); + message[0] = uint256(poseidonMMRRoot); message[1] = mmrSize; - message[2] = aggregatorId; - message[3] = mmrId; + message[2] = aggregatorId_low; + message[3] = aggregatorId_high; + message[4] = mmrId; // Pass along msg.value starknetCore.sendMessageToL2{value: msg.value}( From 81401913279c44d4118c9044823730d286adfd72 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Tue, 4 Jun 2024 16:48:02 +0200 Subject: [PATCH 43/48] Change mmr ID type to uint256 --- l1/src/L1MessagesSender.sol | 17 +++++++++-------- l1/test/L1MessagesSender.t.sol | 2 +- src/core/common.cairo | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/l1/src/L1MessagesSender.sol b/l1/src/L1MessagesSender.sol index 1d895b2..3821e14 100644 --- a/l1/src/L1MessagesSender.sol +++ b/l1/src/L1MessagesSender.sol @@ -56,7 +56,7 @@ contract L1MessagesSender is Ownable { } /// @param aggregatorId The id of a tree previously created by the aggregators factory - function sendPoseidonMMRTreeToL2(uint256 aggregatorId, uint128 mmrId) external payable { + function sendPoseidonMMRTreeToL2(uint256 aggregatorId, uint256 mmrId) external payable { address existingAggregatorAddr = aggregatorsFactory.aggregatorsById( aggregatorId ); @@ -98,18 +98,19 @@ contract L1MessagesSender is Ownable { bytes32 poseidonMMRRoot, uint128 mmrSize, uint256 aggregatorId, - uint128 mmrId + uint256 mmrId ) internal { - uint128 aggregatorId_low = uint128(aggregatorId); - uint128 aggregatorId_high = uint128(aggregatorId >> 128); + (uint256 aggregatorIdLow, uint256 aggregatorIdHigh) = aggregatorId.split128(); + (uint256 mmrIdLow, uint256 mmrIdHigh) = mmrId.split128(); - uint256[] memory message = new uint256[](5); + uint256[] memory message = new uint256[](6); message[0] = uint256(poseidonMMRRoot); message[1] = mmrSize; - message[2] = aggregatorId_low; - message[3] = aggregatorId_high; - message[4] = mmrId; + message[2] = aggregatorIdLow; + message[3] = aggregatorIdHigh; + message[4] = mmrIdLow; + message[5] = mmrIdHigh; // Pass along msg.value starknetCore.sendMessageToL2{value: msg.value}( diff --git a/l1/test/L1MessagesSender.t.sol b/l1/test/L1MessagesSender.t.sol index fdab38f..fbf5e36 100644 --- a/l1/test/L1MessagesSender.t.sol +++ b/l1/test/L1MessagesSender.t.sol @@ -35,7 +35,7 @@ contract L1MessagesSenderTest is Test { // This aggregator id must exist in the factory uint256 aggregatorId = 1; - uint128 mmrId = 4; + uint256 mmrId = 4; // Value must be greater than 0 sender.sendPoseidonMMRTreeToL2{value: 1}(aggregatorId, mmrId); diff --git a/src/core/common.cairo b/src/core/common.cairo index da00ea0..da94a28 100644 --- a/src/core/common.cairo +++ b/src/core/common.cairo @@ -1,2 +1,2 @@ -type MmrId = u128; +type MmrId = u256; type AggregatorId = u256; From 4387c5f57e9c487152a87384100aa7c2e2db4616 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Tue, 4 Jun 2024 17:03:51 +0200 Subject: [PATCH 44/48] Update deployment scripts --- multicall/deploy.toml | 10 +++++----- multicall/set-l1-msg-sender.toml | 5 +++++ 2 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 multicall/set-l1-msg-sender.toml diff --git a/multicall/deploy.toml b/multicall/deploy.toml index 963fde4..612c655 100644 --- a/multicall/deploy.toml +++ b/multicall/deploy.toml @@ -1,9 +1,9 @@ [[call]] call_type = "deploy" -class_hash = "0x636debf6067e2fd953c4a2ceb6ca3b93d97474a725816a3ea438d3f79eb7512" +class_hash = "0x3e9609ed86823420a71bf49659692e3dced77187db80c44c9835c1dd6971292" inputs = [ "0x1", - "0xdF32BADD47BEef951a77Da5dbfA14E2D65dF3A20", + "0x30DdE9bC96D800fF70383c74a95141E156Db1DF0", "0x0", "0x5a6c0f84179d695f0b598cc5d0be50421c247da95cfe63e4cd66fc27f32dfe6", ] @@ -12,14 +12,14 @@ unique = false [[call]] call_type = "deploy" -class_hash = "0x5415fef5becee8d69aeaeaed8b12d2e0505c9f045b679ef2b40163cc54dae02" +class_hash = "0x43b8bbd6d131703af8b288225caa29dc4d0cdd807b9be1ddcf9483d836a5684" inputs = ["commitments_inbox"] id = "headers_store" unique = false [[call]] call_type = "deploy" -class_hash = "0x59bc294565250247b8d77921c84c96a312748496245fb11b3f1c37be6920a21" +class_hash = "0x476dd358f684b29ef00299a8bcefa3cbde0f46d4df8716f2fde06555ece0c7b" inputs = ["headers_store"] id = "evm_facts_registry" unique = false @@ -32,7 +32,7 @@ inputs = ["headers_store"] [[call]] call_type = "deploy" -class_hash = "0x6b83039caf2042ee17d07853c0c24a021f4301f6c49a778fe7f690b45b2fc8f" +class_hash = "0x59547c81438fa787d10e30223990192ea29da848db9996491c805cd28d8b097" inputs = ["headers_store"] id = "timestamp_remappers" unique = false \ No newline at end of file diff --git a/multicall/set-l1-msg-sender.toml b/multicall/set-l1-msg-sender.toml new file mode 100644 index 0000000..b0ba0b4 --- /dev/null +++ b/multicall/set-l1-msg-sender.toml @@ -0,0 +1,5 @@ +[[call]] +call_type = "invoke" +contract_address = "0x0175a93a8663ad935bc0362ffdeb975a4a93a5eb6370ad027a0c48717dae925b" +function = "set_l1_message_sender" +inputs = ["0x30DdE9bC96D800fF70383c74a95141E156Db1DF0"] \ No newline at end of file From 0ed552bf1949ed343db7c75823452f102c2653af Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Wed, 5 Jun 2024 14:04:47 +0200 Subject: [PATCH 45/48] Change MapperId type --- multicall/deploy.toml | 2 +- src/remappers/interface.cairo | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/multicall/deploy.toml b/multicall/deploy.toml index 612c655..f8fe95d 100644 --- a/multicall/deploy.toml +++ b/multicall/deploy.toml @@ -32,7 +32,7 @@ inputs = ["headers_store"] [[call]] call_type = "deploy" -class_hash = "0x59547c81438fa787d10e30223990192ea29da848db9996491c805cd28d8b097" +class_hash = "0x73305243f4fa52b5a26b75bcb2ace081e28eb365239b58723ae643f63875078" inputs = ["headers_store"] id = "timestamp_remappers" unique = false \ No newline at end of file diff --git a/src/remappers/interface.cairo b/src/remappers/interface.cairo index c0b5c90..db5f5f1 100644 --- a/src/remappers/interface.cairo +++ b/src/remappers/interface.cairo @@ -11,7 +11,7 @@ use cairo_lib::utils::types::words64::Words64; use herodotus_eth_starknet::core::common::MmrId; type Headers = Span; -type MapperId = u128; +type MapperId = u256; #[derive(Drop, Serde)] struct OriginElement { From 8b5e411c2b9d5a8504eaf45a4af80bb1274acd60 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Fri, 7 Jun 2024 17:08:10 +0200 Subject: [PATCH 46/48] Update cairo-lib version --- Scarb.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scarb.lock b/Scarb.lock index 68e7dec..04fd00d 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -4,7 +4,7 @@ version = 1 [[package]] name = "cairo_lib" version = "0.2.0" -source = "git+https://github.com/HerodotusDev/cairo-lib.git?branch=update-cairo#bc453f981c6ce2753543474a2492ecac03c36ba4" +source = "git+https://github.com/HerodotusDev/cairo-lib.git?branch=update-cairo#0db686d55a4f52835c2127539953178bd01e19f3" [[package]] name = "herodotus_eth_starknet" From d22cb1c502dfc46845933e01e5471e561e9ef007 Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Fri, 7 Jun 2024 18:30:25 +0200 Subject: [PATCH 47/48] Remove L1 directory --- l1/.env.example | 6 - l1/.github/workflows/test.yml | 34 ------ l1/.gitignore | 16 --- l1/README.md | 57 --------- l1/foundry.toml | 17 --- l1/lib/forge-std | 1 - l1/lib/openzeppelin-contracts | 1 - l1/remappings.txt | 5 - l1/script/L1MessagesSender.s.sol | 25 ---- l1/src/L1MessagesSender.sol | 138 ---------------------- l1/src/interfaces/IAggregator.sol | 13 -- l1/src/interfaces/IAggregatorsFactory.sol | 8 -- l1/src/interfaces/IStarknetCore.sol | 55 --------- l1/src/lib/Uint256Splitter.sol | 28 ----- l1/test/L1MessagesSender.t.sol | 43 ------- multicall/deploy.toml | 8 +- multicall/set-l1-msg-sender.toml | 4 +- 17 files changed, 6 insertions(+), 453 deletions(-) delete mode 100644 l1/.env.example delete mode 100644 l1/.github/workflows/test.yml delete mode 100644 l1/.gitignore delete mode 100644 l1/README.md delete mode 100644 l1/foundry.toml delete mode 160000 l1/lib/forge-std delete mode 160000 l1/lib/openzeppelin-contracts delete mode 100644 l1/remappings.txt delete mode 100644 l1/script/L1MessagesSender.s.sol delete mode 100644 l1/src/L1MessagesSender.sol delete mode 100644 l1/src/interfaces/IAggregator.sol delete mode 100644 l1/src/interfaces/IAggregatorsFactory.sol delete mode 100644 l1/src/interfaces/IStarknetCore.sol delete mode 100644 l1/src/lib/Uint256Splitter.sol delete mode 100644 l1/test/L1MessagesSender.t.sol diff --git a/l1/.env.example b/l1/.env.example deleted file mode 100644 index cbf22bf..0000000 --- a/l1/.env.example +++ /dev/null @@ -1,6 +0,0 @@ -DEPLOY_RPC_URL= -STARKNET_CORE_ADDRESS= -L2_RECIPIENT_ADDRESS= -AGGREGATORS_FACTORY_ADDRESS= -ETHERSCAN_API_KEY= -PRIVATE_KEY= diff --git a/l1/.github/workflows/test.yml b/l1/.github/workflows/test.yml deleted file mode 100644 index 09880b1..0000000 --- a/l1/.github/workflows/test.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: test - -on: workflow_dispatch - -env: - FOUNDRY_PROFILE: ci - -jobs: - check: - strategy: - fail-fast: true - - name: Foundry project - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1 - with: - version: nightly - - - name: Run Forge build - run: | - forge --version - forge build --sizes - id: build - - - name: Run Forge tests - run: | - forge test -vvv - id: test diff --git a/l1/.gitignore b/l1/.gitignore deleted file mode 100644 index 742c440..0000000 --- a/l1/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -# Compiler files -cache/ -out/ - -# Ignores development broadcast logs -!/broadcast -/broadcast/*/31337/ -/broadcast/**/dry-run/ - -broadcast/ - -# Docs -docs/ - -# Dotenv file -.env diff --git a/l1/README.md b/l1/README.md deleted file mode 100644 index 8a7f999..0000000 --- a/l1/README.md +++ /dev/null @@ -1,57 +0,0 @@ -## L1MessagesSender - -## Introduction - -This is a simple contract that can send L1 block hashes, Poseidon Merkle Mountain Range (MMR) and Keccak MMR tree root hashes alongside tree sizes read from one of our aggregators contract. - -The recipient is sitting on the other side on L2 (Starknet). We are using the native messaging system to communicate between those two layers. - -### Build - -```shell -$ forge build -``` - -### Deploy - -```shell -$ source .env; forge script script/L1MessagesSender.s.sol:L1MessagesSenderDeployer --rpc-url $DEPLOY_RPC_URL --broadcast --verify -vvvv -``` - -### Test - -```shell -$ forge test -``` - -### Format - -```shell -$ forge fmt -``` - -### Gas Snapshots - -```shell -$ forge snapshot -``` - -### Anvil - -```shell -$ anvil -``` - -### Cast - -```shell -$ cast -``` - -### Help - -```shell -$ forge --help -$ anvil --help -$ cast --help -``` diff --git a/l1/foundry.toml b/l1/foundry.toml deleted file mode 100644 index 64f69fe..0000000 --- a/l1/foundry.toml +++ /dev/null @@ -1,17 +0,0 @@ -[profile.default] -src = "src" -out = "out" -libs = ["lib"] -gas_reports = ["*"] -optimizer = true -optimizer_runs = 20000 -solc = "0.8.21" -evm_version = "shanghai" - -[rpc_endpoints] -sepolia = "https://sepolia.drpc.org" - -[etherscan] -sepolia = { key = "${ETHERSCAN_API_KEY}" } - -# See more config options https://github.com/foundry-rs/foundry/tree/master/config diff --git a/l1/lib/forge-std b/l1/lib/forge-std deleted file mode 160000 index 74cfb77..0000000 --- a/l1/lib/forge-std +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 74cfb77e308dd188d2f58864aaf44963ae6b88b1 diff --git a/l1/lib/openzeppelin-contracts b/l1/lib/openzeppelin-contracts deleted file mode 160000 index fd81a96..0000000 --- a/l1/lib/openzeppelin-contracts +++ /dev/null @@ -1 +0,0 @@ -Subproject commit fd81a96f01cc42ef1c9a5399364968d0e07e9e90 diff --git a/l1/remappings.txt b/l1/remappings.txt deleted file mode 100644 index 3f2cd54..0000000 --- a/l1/remappings.txt +++ /dev/null @@ -1,5 +0,0 @@ -ds-test/=lib/forge-std/lib/ds-test/src/ -erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/ -forge-std/=lib/forge-std/src/ -openzeppelin-contracts/=lib/openzeppelin-contracts/ -openzeppelin/=lib/openzeppelin-contracts/contracts/ diff --git a/l1/script/L1MessagesSender.s.sol b/l1/script/L1MessagesSender.s.sol deleted file mode 100644 index 1a29ec4..0000000 --- a/l1/script/L1MessagesSender.s.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "forge-std/Script.sol"; -import "forge-std/console.sol"; - -import {L1MessagesSender} from "../src/L1MessagesSender.sol"; -import {IStarknetCore} from "../src/interfaces/IStarknetCore.sol"; - -contract L1MessagesSenderDeployer is Script { - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - vm.startBroadcast(deployerPrivateKey); - - L1MessagesSender l1MessagesSender = new L1MessagesSender( - IStarknetCore(vm.envAddress("STARKNET_CORE_ADDRESS")), - vm.envUint("L2_RECIPIENT_ADDRESS"), - vm.envAddress("AGGREGATORS_FACTORY_ADDRESS") - ); - - console.log("L1MessagesSender address: %s", address(l1MessagesSender)); - - vm.stopBroadcast(); - } -} diff --git a/l1/src/L1MessagesSender.sol b/l1/src/L1MessagesSender.sol deleted file mode 100644 index 3821e14..0000000 --- a/l1/src/L1MessagesSender.sol +++ /dev/null @@ -1,138 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.21; - -import {Ownable} from "openzeppelin/access/Ownable.sol"; - -import {IStarknetCore} from "./interfaces/IStarknetCore.sol"; - -import {IAggregatorsFactory} from "./interfaces/IAggregatorsFactory.sol"; -import {IAggregator} from "./interfaces/IAggregator.sol"; - -import {Uint256Splitter} from "./lib/Uint256Splitter.sol"; - -contract L1MessagesSender is Ownable { - using Uint256Splitter for uint256; - - IStarknetCore public immutable starknetCore; - - uint256 public l2RecipientAddr; - - IAggregatorsFactory public aggregatorsFactory; - - /// @dev L2 "receive_commitment" L1 handler selector - uint256 constant RECEIVE_COMMITMENT_L1_HANDLER_SELECTOR = - 0x3fa70707d0e831418fb142ca8fb7483611b84e89c0c42bf1fc2a7a5c40890ad; - - /// @dev L2 "receive_mmr" L1 handler selector - uint256 constant RECEIVE_MMR_L1_HANDLER_SELECTOR = - 0x36c76e67f1d589956059cbd9e734d42182d1f8a57d5876390bb0fcfe1090bb4; - - /// @param starknetCore_ a StarknetCore address to send and consume messages on/from L2 - /// @param l2RecipientAddr_ a L2 recipient address that is the recipient contract on L2. - /// @param aggregatorsFactoryAddr_ Herodotus aggregators factory address (where MMR trees are referenced) - constructor( - IStarknetCore starknetCore_, - uint256 l2RecipientAddr_, - address aggregatorsFactoryAddr_ - ) { - starknetCore = starknetCore_; - l2RecipientAddr = l2RecipientAddr_; - aggregatorsFactory = IAggregatorsFactory(aggregatorsFactoryAddr_); - } - - /// @notice Send an exact L1 parent hash to L2 - /// @param blockNumber_ the child block of the requested parent hash - function sendExactParentHashToL2(uint256 blockNumber_) external payable { - bytes32 parentHash = blockhash(blockNumber_ - 1); - require(parentHash != bytes32(0), "ERR_INVALID_BLOCK_NUMBER"); - - _sendBlockHashToL2(parentHash, blockNumber_); - } - - /// @notice Send the L1 latest parent hash to L2 - function sendLatestParentHashToL2() external payable { - bytes32 parentHash = blockhash(block.number - 1); - _sendBlockHashToL2(parentHash, block.number); - } - - /// @param aggregatorId The id of a tree previously created by the aggregators factory - function sendPoseidonMMRTreeToL2(uint256 aggregatorId, uint256 mmrId) external payable { - address existingAggregatorAddr = aggregatorsFactory.aggregatorsById( - aggregatorId - ); - - require(existingAggregatorAddr != address(0), "Unknown aggregator"); - - IAggregator aggregator = IAggregator(existingAggregatorAddr); - bytes32 poseidonMMRRoot = aggregator.getMMRPoseidonRoot(); - uint256 mmrSize = aggregator.getMMRSize(); - - require(mmrSize >= 1, "Invalid tree size"); - require(poseidonMMRRoot != bytes32(0), "Invalid root (Poseidon)"); - - _sendPoseidonMMRTreeToL2(poseidonMMRRoot, uint128(mmrSize), aggregatorId, mmrId); - } - - function _sendBlockHashToL2( - bytes32 parentHash_, - uint256 blockNumber_ - ) internal { - uint256[] memory message = new uint256[](4); - (uint256 parentHashLow, uint256 parentHashHigh) = uint256(parentHash_) - .split128(); - (uint256 blockNumberLow, uint256 blockNumberHigh) = blockNumber_ - .split128(); - message[0] = parentHashLow; - message[1] = parentHashHigh; - message[2] = blockNumberLow; - message[3] = blockNumberHigh; - - starknetCore.sendMessageToL2{value: msg.value}( - l2RecipientAddr, - RECEIVE_COMMITMENT_L1_HANDLER_SELECTOR, - message - ); - } - - function _sendPoseidonMMRTreeToL2( - bytes32 poseidonMMRRoot, - uint128 mmrSize, - uint256 aggregatorId, - uint256 mmrId - ) internal { - (uint256 aggregatorIdLow, uint256 aggregatorIdHigh) = aggregatorId.split128(); - (uint256 mmrIdLow, uint256 mmrIdHigh) = mmrId.split128(); - - uint256[] memory message = new uint256[](6); - - message[0] = uint256(poseidonMMRRoot); - message[1] = mmrSize; - message[2] = aggregatorIdLow; - message[3] = aggregatorIdHigh; - message[4] = mmrIdLow; - message[5] = mmrIdHigh; - - // Pass along msg.value - starknetCore.sendMessageToL2{value: msg.value}( - l2RecipientAddr, - RECEIVE_MMR_L1_HANDLER_SELECTOR, - message - ); - } - - /// @notice Set the L2 recipient address - /// @param newL2RecipientAddr_ The new L2 recipient address - function setL2RecipientAddr( - uint256 newL2RecipientAddr_ - ) external onlyOwner { - l2RecipientAddr = newL2RecipientAddr_; - } - - /// @notice Set the aggregators factory address - /// @param newAggregatorsFactoryAddr_ The new aggregators factory address - function setAggregatorsFactoryAddr( - address newAggregatorsFactoryAddr_ - ) external onlyOwner { - aggregatorsFactory = IAggregatorsFactory(newAggregatorsFactoryAddr_); - } -} diff --git a/l1/src/interfaces/IAggregator.sol b/l1/src/interfaces/IAggregator.sol deleted file mode 100644 index 48243c3..0000000 --- a/l1/src/interfaces/IAggregator.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.21; - -interface IAggregator { - /// @notice Returns the current root hash of the Keccak Merkle Mountain Range (MMR) tree - function getMMRKeccakRoot() external view returns (bytes32); - - /// @notice Returns the current root hash of the Poseidon Merkle Mountain Range (MMR) tree - function getMMRPoseidonRoot() external view returns (bytes32); - - /// @notice Returns the current size of the Merkle Mountain Range (MMR) trees - function getMMRSize() external view returns (uint256); -} diff --git a/l1/src/interfaces/IAggregatorsFactory.sol b/l1/src/interfaces/IAggregatorsFactory.sol deleted file mode 100644 index d474a2c..0000000 --- a/l1/src/interfaces/IAggregatorsFactory.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.21; - -import {IAggregator} from "./IAggregator.sol"; - -interface IAggregatorsFactory { - function aggregatorsById(uint256 id) external view returns (address); -} diff --git a/l1/src/interfaces/IStarknetCore.sol b/l1/src/interfaces/IStarknetCore.sol deleted file mode 100644 index a3f6240..0000000 --- a/l1/src/interfaces/IStarknetCore.sol +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.21; - -interface IStarknetCore { - /** - Sends a message to an L2 contract. - This function is payable, the payed amount is the message fee. - - Returns the hash of the message and the nonce of the message. - */ - function sendMessageToL2( - uint256 toAddress, - uint256 selector, - uint256[] calldata payload - ) external payable returns (bytes32, uint256); - - /** - Consumes a message that was sent from an L2 contract. - - Returns the hash of the message. - */ - function consumeMessageFromL2( - uint256 fromAddress, - uint256[] calldata payload - ) external returns (bytes32); - - /** - Starts the cancellation of an L1 to L2 message. - A message can be canceled messageCancellationDelay() seconds after this function is called. - - Note: This function may only be called for a message that is currently pending and the caller - must be the sender of the that message. - */ - function startL1ToL2MessageCancellation( - uint256 toAddress, - uint256 selector, - uint256[] calldata payload, - uint256 nonce - ) external returns (bytes32); - - /** - Cancels an L1 to L2 message, this function should be called at least - messageCancellationDelay() seconds after the call to startL1ToL2MessageCancellation(). - A message may only be cancelled by its sender. - If the message is missing, the call will revert. - - Note that the message fee is not refunded. - */ - function cancelL1ToL2Message( - uint256 toAddress, - uint256 selector, - uint256[] calldata payload, - uint256 nonce - ) external returns (bytes32); -} diff --git a/l1/src/lib/Uint256Splitter.sol b/l1/src/lib/Uint256Splitter.sol deleted file mode 100644 index c68730f..0000000 --- a/l1/src/lib/Uint256Splitter.sol +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.0; - -library Uint256Splitter { - uint256 constant _MASK = type(uint128).max; - - /// @notice Splits a uint256 into two uint128s (low, high) represented as uint256s. - /// @param a The uint256 to split. - function split128( - uint256 a - ) internal pure returns (uint256 lower, uint256 upper) { - return (a & _MASK, a >> 128); - } - - /// @notice Merges two uint128s (low, high) into one uint256. - /// @param lower The lower uint256. The caller is required to pass a value that is less than 2^128 - 1. - /// @param upper The upper uint256. - function merge128( - uint256 lower, - uint256 upper - ) internal pure returns (uint256 a) { - require(lower <= _MASK, "Uint256Splitter: lower exceeds uint128"); - // return (upper << 128) | lower; - assembly { - a := or(shl(128, upper), lower) - } - } -} diff --git a/l1/test/L1MessagesSender.t.sol b/l1/test/L1MessagesSender.t.sol deleted file mode 100644 index fbf5e36..0000000 --- a/l1/test/L1MessagesSender.t.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "forge-std/Test.sol"; - -import {L1MessagesSender} from "../src/L1MessagesSender.sol"; -import {IStarknetCore} from "../src/interfaces/IStarknetCore.sol"; - -contract L1MessagesSenderTest is Test { - L1MessagesSender public sender; - - function setUp() public { - vm.createSelectFork(vm.rpcUrl("sepolia")); - - sender = new L1MessagesSender( - IStarknetCore(0xE2Bb56ee936fd6433DC0F6e7e3b8365C906AA057), - 0x07d15ab6a37642b1765a1be4ec90d793db9c4b8cd01d7bb694b20c0b9699c173, - 0x70C61dd17b7207B450Cb7DeDC92C1707A07a1213 - ); - } - - function testSendExactParentHashToL2() public { - uint256 prevBlock = block.number - 1; - - // Value must be greater than 0 - sender.sendExactParentHashToL2{value: 1}(prevBlock); - } - - function testSendLatestParentHashToL2() public { - // Value must be greater than 0 - sender.sendLatestParentHashToL2{value: 1}(); - } - - function testSendPoseidonMMRTreeToL2() public { - // This aggregator id must exist in the factory - uint256 aggregatorId = 1; - - uint256 mmrId = 4; - - // Value must be greater than 0 - sender.sendPoseidonMMRTreeToL2{value: 1}(aggregatorId, mmrId); - } -} diff --git a/multicall/deploy.toml b/multicall/deploy.toml index f8fe95d..281bc17 100644 --- a/multicall/deploy.toml +++ b/multicall/deploy.toml @@ -1,6 +1,6 @@ [[call]] call_type = "deploy" -class_hash = "0x3e9609ed86823420a71bf49659692e3dced77187db80c44c9835c1dd6971292" +class_hash = "0x18c9ce7ffa88f15bd1fcda1350cb66cc5c369bc924e5dc108be1c9317298c99" inputs = [ "0x1", "0x30DdE9bC96D800fF70383c74a95141E156Db1DF0", @@ -12,14 +12,14 @@ unique = false [[call]] call_type = "deploy" -class_hash = "0x43b8bbd6d131703af8b288225caa29dc4d0cdd807b9be1ddcf9483d836a5684" +class_hash = "0x48d1f93626722872832416241a30c20bb77403b48249e65bebae67ab7a5329" inputs = ["commitments_inbox"] id = "headers_store" unique = false [[call]] call_type = "deploy" -class_hash = "0x476dd358f684b29ef00299a8bcefa3cbde0f46d4df8716f2fde06555ece0c7b" +class_hash = "0x5bae175b97cd8d6ce988ee862be7081397d36a8044a4d7e9b1bd810532daba9" inputs = ["headers_store"] id = "evm_facts_registry" unique = false @@ -32,7 +32,7 @@ inputs = ["headers_store"] [[call]] call_type = "deploy" -class_hash = "0x73305243f4fa52b5a26b75bcb2ace081e28eb365239b58723ae643f63875078" +class_hash = "0x2e7de0c6a2fd1759b02cc59998f6267e5d1b73852d1ec556594269c9bc48b63" inputs = ["headers_store"] id = "timestamp_remappers" unique = false \ No newline at end of file diff --git a/multicall/set-l1-msg-sender.toml b/multicall/set-l1-msg-sender.toml index b0ba0b4..1bdc4f1 100644 --- a/multicall/set-l1-msg-sender.toml +++ b/multicall/set-l1-msg-sender.toml @@ -1,5 +1,5 @@ [[call]] call_type = "invoke" -contract_address = "0x0175a93a8663ad935bc0362ffdeb975a4a93a5eb6370ad027a0c48717dae925b" +contract_address = "0x0763c1a0ec1d64afe2d8d0a2c0cab6fd494dcb26d08ef1020b27aa5695761e21" function = "set_l1_message_sender" -inputs = ["0x30DdE9bC96D800fF70383c74a95141E156Db1DF0"] \ No newline at end of file +inputs = ["0x423F7744017600727cE4789933E4648068835E28"] \ No newline at end of file From caf98340cb2557786c765a3fd80cdde3cbdba53e Mon Sep 17 00:00:00 2001 From: Filip Krawczyk Date: Fri, 19 Jul 2024 11:08:41 +0200 Subject: [PATCH 48/48] Fix underflow in process_batch --- src/core/headers_store.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/headers_store.cairo b/src/core/headers_store.cairo index 7bc1a6d..bb6b8d7 100644 --- a/src/core/headers_store.cairo +++ b/src/core/headers_store.cairo @@ -353,7 +353,7 @@ mod HeadersStore { let reference_block = reference_block.unwrap(); start_block = reference_block - 1; - end_block = start_block - headers_rlp.len().into() + 1; + end_block = (start_block + 1) - headers_rlp.len().into(); let initial_blockhash = self.received_blocks.read(reference_block); assert(initial_blockhash != Zeroable::zero(), 'BLOCK_NOT_RECEIVED');