From dafa78938746b5f56e59b29c330e02eb55ad194e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Cauderlier?= Date: Mon, 4 Nov 2024 17:28:19 +0100 Subject: [PATCH 1/3] Etherlink/Kernel: Introduce block header --- etherlink/kernel_evm/kernel/src/block.rs | 29 ++++++++----- .../kernel/src/blueprint_storage.rs | 43 +++++++++++++++++++ 2 files changed, 62 insertions(+), 10 deletions(-) diff --git a/etherlink/kernel_evm/kernel/src/block.rs b/etherlink/kernel_evm/kernel/src/block.rs index 5318d34c8ff2..12bababd81e8 100644 --- a/etherlink/kernel_evm/kernel/src/block.rs +++ b/etherlink/kernel_evm/kernel/src/block.rs @@ -9,7 +9,9 @@ use crate::apply::{ apply_transaction, ExecutionInfo, ExecutionResult, Validity, WITHDRAWAL_OUTBOX_QUEUE, }; use crate::block_storage; -use crate::blueprint_storage::{drop_blueprint, read_blueprint}; +use crate::blueprint_storage::{ + drop_blueprint, read_blueprint, BlockHeader, BlueprintHeader, EVMBlockHeader, +}; use crate::configuration::ConfigurationMode; use crate::configuration::Limits; use crate::delayed_inbox::DelayedInbox; @@ -28,6 +30,7 @@ use evm_execution::precompiles; use evm_execution::precompiles::PrecompileBTreeMap; use evm_execution::trace::TracerInput; use primitive_types::{H160, H256, U256}; +use tezos_ethereum::block::BlockConstants; use tezos_ethereum::block::L2Block; use tezos_ethereum::transaction::TransactionHash; use tezos_evm_logging::{log, Level::*, Verbosity}; @@ -38,8 +41,6 @@ use tezos_smart_rollup::types::Timestamp; use tezos_smart_rollup_host::path::Path; use tick_model::estimate_remaining_ticks_for_transaction_execution; -use tezos_ethereum::block::BlockConstants; - pub const GENESIS_PARENT_HASH: H256 = H256([0xff; 32]); pub const GAS_LIMIT: u64 = 1 << 50; @@ -259,13 +260,21 @@ fn next_bip_from_blueprints( previous_timestamp, receipts_root, transactions_root, - ) = match block_storage::read_current(host) { - Ok(block) => ( - block.number + 1, - block.hash, - block.timestamp, - block.receipts_root, - block.transactions_root, + ) = match block_storage::read_current(host).map(|block| block.into()) { + Ok(BlockHeader { + blueprint_header: BlueprintHeader { number, timestamp }, + evm_block_header: + EVMBlockHeader { + hash, + receipts_root, + transactions_root, + }, + }) => ( + number + 1, + hash, + timestamp, + receipts_root, + transactions_root, ), Err(_) => ( U256::zero(), diff --git a/etherlink/kernel_evm/kernel/src/blueprint_storage.rs b/etherlink/kernel_evm/kernel/src/blueprint_storage.rs index 2f566a4095f3..3d691854af87 100644 --- a/etherlink/kernel_evm/kernel/src/blueprint_storage.rs +++ b/etherlink/kernel_evm/kernel/src/blueprint_storage.rs @@ -16,6 +16,8 @@ use crate::{delayed_inbox, DelayedInbox}; use primitive_types::{H256, U256}; use rlp::{Decodable, DecoderError, Encodable}; use sha3::{Digest, Keccak256}; +use tezos_ethereum::block::L2Block; +use tezos_ethereum::eth_gen::OwnedHash; use tezos_ethereum::rlp_helpers; use tezos_ethereum::tx_common::EthereumTransactionCommon; use tezos_evm_logging::{log, Level::*}; @@ -86,6 +88,47 @@ impl Decodable for StoreBlueprint { } } +// Part of the block header which is generic information because it is +// about the blueprint from which the block was build. This part is +// useful to validate the next blueprint. +#[derive(PartialEq, Debug, Clone)] +pub struct BlueprintHeader { + pub number: U256, + pub timestamp: Timestamp, +} + +// Part of the block header which is specific of the EVM chain. All +// fields are needed to build the next block. The hash is also needed +// to validate the next blueprint (which commits on this hash). +#[derive(PartialEq, Debug, Clone)] +pub struct EVMBlockHeader { + pub hash: H256, + pub receipts_root: OwnedHash, + pub transactions_root: OwnedHash, +} + +#[derive(PartialEq, Debug, Clone)] +pub struct BlockHeader { + pub blueprint_header: BlueprintHeader, + pub evm_block_header: EVMBlockHeader, +} + +impl From for BlockHeader { + fn from(block: L2Block) -> Self { + Self { + blueprint_header: BlueprintHeader { + number: block.number, + timestamp: block.timestamp, + }, + evm_block_header: EVMBlockHeader { + hash: block.hash, + receipts_root: block.receipts_root, + transactions_root: block.transactions_root, + }, + } + } +} + pub fn blueprint_path(number: U256) -> Result { let number_as_path: Vec = format!("/{}", number).into(); // The key being an integer value, it will always be valid as a path, -- GitLab From af4691b1193bcd286ff958b24f9b2bf930ef0169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Cauderlier?= Date: Tue, 18 Feb 2025 17:26:31 +0100 Subject: [PATCH 2/3] Etherlink/Kernel: serialization of block header --- .../kernel/src/blueprint_storage.rs | 68 ++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/etherlink/kernel_evm/kernel/src/blueprint_storage.rs b/etherlink/kernel_evm/kernel/src/blueprint_storage.rs index 3d691854af87..742316be4705 100644 --- a/etherlink/kernel_evm/kernel/src/blueprint_storage.rs +++ b/etherlink/kernel_evm/kernel/src/blueprint_storage.rs @@ -18,7 +18,9 @@ use rlp::{Decodable, DecoderError, Encodable}; use sha3::{Digest, Keccak256}; use tezos_ethereum::block::L2Block; use tezos_ethereum::eth_gen::OwnedHash; -use tezos_ethereum::rlp_helpers; +use tezos_ethereum::rlp_helpers::{ + self, append_timestamp, append_u256_le, decode_field_u256_le, decode_timestamp, +}; use tezos_ethereum::tx_common::EthereumTransactionCommon; use tezos_evm_logging::{log, Level::*}; use tezos_evm_runtime::runtime::Runtime; @@ -230,6 +232,70 @@ pub fn store_forced_blueprint( store_rlp(&store_blueprint, host, &chunk_path).map_err(Error::from) } +impl Encodable for EVMBlockHeader { + fn rlp_append(&self, stream: &mut rlp::RlpStream) { + let Self { + hash, + receipts_root, + transactions_root, + } = self; + stream.begin_list(3); + stream.append(hash); + stream.append(receipts_root); + stream.append(transactions_root); + } +} + +impl Decodable for EVMBlockHeader { + fn decode(decoder: &rlp::Rlp) -> Result { + rlp_helpers::check_list(decoder, 3)?; + let mut it = decoder.iter(); + let hash = rlp_helpers::decode_field(&rlp_helpers::next(&mut it)?, "hash")?; + let receipts_root = + rlp_helpers::decode_field(&rlp_helpers::next(&mut it)?, "receipts_root")?; + let transactions_root = + rlp_helpers::decode_field(&rlp_helpers::next(&mut it)?, "transactions_root")?; + Ok(Self { + hash, + receipts_root, + transactions_root, + }) + } +} + +impl Encodable for BlockHeader { + fn rlp_append(&self, stream: &mut rlp::RlpStream) { + let Self { + blueprint_header: BlueprintHeader { number, timestamp }, + evm_block_header, + } = self; + stream.begin_list(3); + append_u256_le(stream, number); + append_timestamp(stream, *timestamp); + stream.begin_list(1); // Nesting added for forward-compatibility with multichain + stream.append(evm_block_header); + } +} + +impl Decodable for BlockHeader { + fn decode(decoder: &rlp::Rlp) -> Result { + rlp_helpers::check_list(decoder, 3)?; + + let mut it = decoder.iter(); + let number = decode_field_u256_le(&rlp_helpers::next(&mut it)?, "number")?; + let timestamp = decode_timestamp(&rlp_helpers::next(&mut it)?)?; + let decoder = &rlp_helpers::next(&mut it)?; + rlp_helpers::check_list(decoder, 1)?; // Nesting added for forward-compatibility with multichain + let mut it = decoder.iter(); + let evm_block_header = + rlp_helpers::decode_field(&rlp_helpers::next(&mut it)?, "evm_block_header")?; + Ok(Self { + blueprint_header: BlueprintHeader { number, timestamp }, + evm_block_header, + }) + } +} + /// For the tick model we only accept blueprints where cumulative size of chunks /// less or equal than 512kB. A chunk weights 4kB, then (512 * 1024) / 4096 = /// 128. -- GitLab From 21adf1d8d096295c2a4f245deab265f2d509b637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Cauderlier?= Date: Tue, 4 Mar 2025 14:37:22 +0100 Subject: [PATCH 3/3] Etherlink/Kernel/Tests: test BlockHeader serialization roundtrip --- .../kernel/src/blueprint_storage.rs | 23 +++++++++++++++++++ .../kernel/src/sequencer_blueprint.rs | 18 +++++++-------- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/etherlink/kernel_evm/kernel/src/blueprint_storage.rs b/etherlink/kernel_evm/kernel/src/blueprint_storage.rs index 742316be4705..729109a78b32 100644 --- a/etherlink/kernel_evm/kernel/src/blueprint_storage.rs +++ b/etherlink/kernel_evm/kernel/src/blueprint_storage.rs @@ -622,6 +622,7 @@ mod tests { use crate::block::GENESIS_PARENT_HASH; use crate::configuration::{DalConfiguration, Limits, TezosContracts}; use crate::delayed_inbox::Hash; + use crate::sequencer_blueprint::rlp_roundtrip; use crate::storage::store_last_info_per_level_timestamp; use primitive_types::H256; use tezos_crypto_rs::hash::ContractKt1Hash; @@ -796,4 +797,26 @@ mod tests { fn test_invalid_sequencer_blueprint_is_removed_with_dal() { test_invalid_sequencer_blueprint_is_removed(true) } + + #[test] + fn test_block_header_roundtrip() { + let blueprint_header = BlueprintHeader { + number: 42.into(), + timestamp: Timestamp::from(10), + }; + let evm_block_header = EVMBlockHeader { + hash: H256::from([42u8; 32]), + receipts_root: vec![23; 5], + transactions_root: vec![18; 5], + }; + + rlp_roundtrip(evm_block_header.clone()); + + let block_header = BlockHeader { + blueprint_header, + evm_block_header, + }; + + rlp_roundtrip(block_header); + } } diff --git a/etherlink/kernel_evm/kernel/src/sequencer_blueprint.rs b/etherlink/kernel_evm/kernel/src/sequencer_blueprint.rs index 62c778988005..bba541f59d9a 100644 --- a/etherlink/kernel_evm/kernel/src/sequencer_blueprint.rs +++ b/etherlink/kernel_evm/kernel/src/sequencer_blueprint.rs @@ -242,27 +242,27 @@ impl Decodable for SequencerBlueprint { } } +#[cfg(test)] +pub fn rlp_roundtrip(v: S) { + let bytes = v.rlp_bytes(); + let v2: S = + rlp_helpers::FromRlpBytes::from_rlp_bytes(&bytes).expect("Should be decodable"); + assert_eq!(v, v2, "Roundtrip failed on {:?}", v) +} + #[cfg(test)] mod tests { - use super::{SequencerBlueprint, UnsignedSequencerBlueprint}; + use super::{rlp_roundtrip, SequencerBlueprint, UnsignedSequencerBlueprint}; use crate::blueprint::Blueprint; use crate::inbox::Transaction; use crate::inbox::TransactionContent::Ethereum; use primitive_types::{H160, U256}; - use rlp::{Decodable, Encodable}; use tezos_crypto_rs::hash::UnknownSignature; - use tezos_ethereum::rlp_helpers::FromRlpBytes; use tezos_ethereum::{ transaction::TRANSACTION_HASH_SIZE, tx_common::EthereumTransactionCommon, }; use tezos_smart_rollup_encoding::timestamp::Timestamp; - fn rlp_roundtrip(v: S) { - let bytes = v.rlp_bytes(); - let v2: S = FromRlpBytes::from_rlp_bytes(&bytes).expect("Should be decodable"); - assert_eq!(v, v2, "Roundtrip failed on {:?}", v) - } - fn address_from_str(s: &str) -> Option { let data = &hex::decode(s).unwrap(); Some(H160::from_slice(data)) -- GitLab