From 2e65db308939c09f6f28108daa0f3b3423d449bf Mon Sep 17 00:00:00 2001 From: arnaud Date: Wed, 5 Mar 2025 14:23:53 +0100 Subject: [PATCH 1/8] Etherlink/Kernel/Tezlink: Introducing tezos block in etherlink kernel --- etherlink/kernel_latest/Cargo.lock | 11 +++ etherlink/kernel_latest/Cargo.toml | 1 + etherlink/kernel_latest/kernel/Cargo.toml | 1 + etherlink/kernel_latest/tezos/Cargo.toml | 17 ++++ etherlink/kernel_latest/tezos/src/block.rs | 99 ++++++++++++++++++++++ etherlink/kernel_latest/tezos/src/lib.rs | 5 ++ 6 files changed, 134 insertions(+) create mode 100644 etherlink/kernel_latest/tezos/Cargo.toml create mode 100644 etherlink/kernel_latest/tezos/src/block.rs create mode 100644 etherlink/kernel_latest/tezos/src/lib.rs diff --git a/etherlink/kernel_latest/Cargo.lock b/etherlink/kernel_latest/Cargo.lock index 591835502acd..fb24c1f98942 100644 --- a/etherlink/kernel_latest/Cargo.lock +++ b/etherlink/kernel_latest/Cargo.lock @@ -800,6 +800,7 @@ dependencies = [ "tezos_crypto_rs", "tezos_data_encoding", "tezos_ethereum_latest", + "tezos_tezlink_latest", "thiserror", ] @@ -2466,6 +2467,16 @@ dependencies = [ "thiserror", ] +[[package]] +name = "tezos_tezlink_latest" +version = "0.1.0" +dependencies = [ + "primitive-types", + "rlp", + "tezos-smart-rollup", + "tezos_ethereum_latest", +] + [[package]] name = "thiserror" version = "1.0.38" diff --git a/etherlink/kernel_latest/Cargo.toml b/etherlink/kernel_latest/Cargo.toml index 6fd0a9a287a9..be890c1c6e3f 100644 --- a/etherlink/kernel_latest/Cargo.toml +++ b/etherlink/kernel_latest/Cargo.toml @@ -61,6 +61,7 @@ libsecp256k1 = { version = "0.7", default-features = false, features = [ # kernel crates tezos_ethereum = { package = "tezos_ethereum_latest", path = "./ethereum" } +tezos_tezlink = { package = "tezos_tezlink_latest", path = "./tezos" } evm-execution = { package = "evm-execution-latest", path = "./evm_execution" } tezos-evm-logging = { package = "tezos-evm-logging-latest", path = "./logging" } tezos-evm-runtime = { package = "tezos-evm-runtime-latest", path = "./runtime" } diff --git a/etherlink/kernel_latest/kernel/Cargo.toml b/etherlink/kernel_latest/kernel/Cargo.toml index 6323ce6a8b03..b1c9ecfaa768 100644 --- a/etherlink/kernel_latest/kernel/Cargo.toml +++ b/etherlink/kernel_latest/kernel/Cargo.toml @@ -40,6 +40,7 @@ ethbloom.workspace = true evm.workspace = true evm-execution.workspace = true tezos_ethereum.workspace = true +tezos_tezlink.workspace = true tezos-evm-logging.workspace = true tezos-evm-runtime.workspace = true tezos-indexable-storage.workspace = true diff --git a/etherlink/kernel_latest/tezos/Cargo.toml b/etherlink/kernel_latest/tezos/Cargo.toml new file mode 100644 index 000000000000..82f253f4a49e --- /dev/null +++ b/etherlink/kernel_latest/tezos/Cargo.toml @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: 2025 Functori +# +# SPDX-License-Identifier: MIT + +[package] +name = "tezos_tezlink_latest" +version = "0.1.0" +edition = "2021" +license = "MIT" + +[dependencies] + +rlp.workspace = true + +tezos_ethereum.workspace = true +primitive-types.workspace = true +tezos-smart-rollup.workspace = true \ No newline at end of file diff --git a/etherlink/kernel_latest/tezos/src/block.rs b/etherlink/kernel_latest/tezos/src/block.rs new file mode 100644 index 000000000000..b1aebca921b9 --- /dev/null +++ b/etherlink/kernel_latest/tezos/src/block.rs @@ -0,0 +1,99 @@ +// SPDX-FileCopyrightText: 2025 Functori +// +// SPDX-License-Identifier: MIT + +use primitive_types::{H256, U256}; +use std::{array::TryFromSliceError, str::FromStr}; +use tezos_smart_rollup::types::Timestamp; + +// WIP: This structure will evolve to look like Tezos block +#[derive(PartialEq, Debug)] +pub struct TezBlock { + pub number: U256, + pub hash: H256, + pub timestamp: Timestamp, + pub previous_hash: H256, +} + +impl TezBlock { + pub fn genesis_block_hash() -> H256 { + // This H256 comes from this b58 hash 'BLockGenesisGenesisGenesisGenesisGenesis1db77eJNeJ9' + // That is the ghostnet genesis hash according to 'devtools/get_contracts/config.ml' + H256::from_str("8fcf233671b6a04fcf679d2a381c2544ea6c1ea29ba6157776ed8423e7c02934") + .unwrap() + } + + // Encoded size for parameter were taken from this command: + // `octez-codec describe block_header binary schema` + pub fn to_bytes(&self) -> Vec { + let Self { + hash: _, + number, + timestamp, + previous_hash, + } = self; + let mut data = vec![]; + + // Encode all block fields + let num_enc: [u8; 4] = number.as_u32().to_be_bytes(); + let predecessor: [u8; 32] = previous_hash.to_fixed_bytes(); + let time_enc: [u8; 8] = timestamp.i64().to_le_bytes(); + + // Append encoded fields to data + data.extend_from_slice(&num_enc); + data.extend_from_slice(&predecessor); + data.extend_from_slice(&time_enc); + + data + } + + pub fn try_from_bytes(bytes: &[u8]) -> Result { + let number = U256::from_big_endian(&bytes[0..4]); + + let previous_hash = H256::from_slice(&bytes[4..36]); + + // Decode the timestamp + let timestamp_array: [u8; 8] = bytes[36..44].try_into()?; + let timestamp = Timestamp::from(i64::from_le_bytes(timestamp_array)); + + // In a next MR, genesis_block_hash will be replaced by a true hash function + Ok(TezBlock { + number, + hash: TezBlock::genesis_block_hash(), + timestamp, + previous_hash, + }) + } +} + +#[cfg(test)] +mod tests { + use primitive_types::U256; + use tezos_smart_rollup::types::Timestamp; + + use super::TezBlock; + + pub fn block_roundtrip(block: TezBlock) { + let bytes = block.to_bytes(); + let decoded_block = + TezBlock::try_from_bytes(&bytes).expect("Block should be decodable"); + assert_eq!(block, decoded_block, "Roundtrip failed on {:?}", block) + } + + fn dummy_tezblock() -> TezBlock { + let number = U256::one(); + let timestamp = Timestamp::from(0); + let previous_hash = TezBlock::genesis_block_hash(); + TezBlock { + number, + hash: TezBlock::genesis_block_hash(), + timestamp, + previous_hash, + } + } + + #[test] + fn test_block_rlp_roundtrip() { + block_roundtrip(dummy_tezblock()); + } +} diff --git a/etherlink/kernel_latest/tezos/src/lib.rs b/etherlink/kernel_latest/tezos/src/lib.rs new file mode 100644 index 000000000000..72a147401ef7 --- /dev/null +++ b/etherlink/kernel_latest/tezos/src/lib.rs @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2025 Functori +// +// SPDX-License-Identifier: MIT + +pub mod block; -- GitLab From dd491d42e94c340ea19fe72e06ed573154b1f613 Mon Sep 17 00:00:00 2001 From: arnaud Date: Mon, 24 Mar 2025 11:15:47 +0100 Subject: [PATCH 2/8] Etherlink/Kernel/Renaming: Remove current notion in next_bip_from_blueprint --- etherlink/kernel_latest/kernel/src/block.rs | 12 ++++++------ .../kernel_latest/kernel/src/block_in_progress.rs | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/etherlink/kernel_latest/kernel/src/block.rs b/etherlink/kernel_latest/kernel/src/block.rs index acee6f1899b9..01ce97a2aee4 100644 --- a/etherlink/kernel_latest/kernel/src/block.rs +++ b/etherlink/kernel_latest/kernel/src/block.rs @@ -258,8 +258,8 @@ fn next_bip_from_blueprints( kernel_upgrade: &Option, ) -> Result { let ( - current_block_number, - current_block_parent_hash, + next_bip_number, + next_bip_parent_hash, previous_timestamp, receipts_root, transactions_root, @@ -290,8 +290,8 @@ fn next_bip_from_blueprints( let (blueprint, size) = read_blueprint( host, config, - current_block_number, - current_block_parent_hash, + next_bip_number, + next_bip_parent_hash, previous_timestamp, )?; log!(host, Benchmarking, "Size of blueprint: {}", size); @@ -315,8 +315,8 @@ fn next_bip_from_blueprints( let bip = block_in_progress::BlockInProgress::from_blueprint( blueprint, - current_block_number, - current_block_parent_hash, + next_bip_number, + next_bip_parent_hash, tick_counter.c, gas_price, receipts_root, diff --git a/etherlink/kernel_latest/kernel/src/block_in_progress.rs b/etherlink/kernel_latest/kernel/src/block_in_progress.rs index 126410a7c270..c8712a95c0be 100644 --- a/etherlink/kernel_latest/kernel/src/block_in_progress.rs +++ b/etherlink/kernel_latest/kernel/src/block_in_progress.rs @@ -277,7 +277,7 @@ impl BlockInProgress { pub fn from_blueprint( blueprint: crate::blueprint::Blueprint, - current_block_number: U256, + number: U256, parent_hash: H256, tick_counter: u64, base_fee_per_gas: U256, @@ -294,7 +294,7 @@ impl BlockInProgress { } }; BlockInProgress::new_with_ticks( - current_block_number, + number, parent_hash, ring, tick_counter, -- GitLab From 50f7797e19108c578c6630f48a416af530b3d948 Mon Sep 17 00:00:00 2001 From: arnaud Date: Thu, 6 Mar 2025 10:55:47 +0100 Subject: [PATCH 3/8] Etherlink/Kernel/Tezlink: BlockInProgress can be an EtherlinkBlockInProgress or a TezlinkBlockInProgress --- etherlink/kernel_latest/kernel/src/block.rs | 104 +++++++++++++----- .../kernel/src/block_in_progress.rs | 38 +++---- .../kernel_latest/kernel/src/gas_price.rs | 6 +- etherlink/kernel_latest/kernel/src/storage.rs | 8 +- 4 files changed, 103 insertions(+), 53 deletions(-) diff --git a/etherlink/kernel_latest/kernel/src/block.rs b/etherlink/kernel_latest/kernel/src/block.rs index 01ce97a2aee4..b0863a21aaf4 100644 --- a/etherlink/kernel_latest/kernel/src/block.rs +++ b/etherlink/kernel_latest/kernel/src/block.rs @@ -24,7 +24,7 @@ use crate::upgrade::KernelUpgrade; use crate::Configuration; use crate::{block_in_progress, tick_model}; use anyhow::Context; -use block_in_progress::BlockInProgress; +use block_in_progress::EthBlockInProgress; use evm::Config; use evm_execution::account_storage::{init_account_storage, EthereumAccountStorage}; use evm_execution::precompiles; @@ -95,7 +95,7 @@ enum BlockInProgressProvenance { fn on_invalid_transaction( host: &mut Host, transaction: &Transaction, - block_in_progress: &mut BlockInProgress, + block_in_progress: &mut EthBlockInProgress, data_size: u64, ) { if transaction.is_delayed() { @@ -115,7 +115,7 @@ fn on_invalid_transaction( fn compute( host: &mut Host, outbox_queue: &OutboxQueue<'_, impl Path>, - block_in_progress: &mut BlockInProgress, + block_in_progress: &mut EthBlockInProgress, block_constants: &BlockConstants, precompiles: &PrecompileBTreeMap, evm_account_storage: &mut EthereumAccountStorage, @@ -245,6 +245,21 @@ fn compute( }) } +#[allow(clippy::large_enum_variant)] +pub enum BlockInProgress { + Etherlink(EthBlockInProgress), + Tezlink(U256), +} + +impl BlockInProgress { + pub fn number(&self) -> U256 { + match self { + Self::Etherlink(bip) => bip.number, + Self::Tezlink(n) => *n, + } + } +} + enum BlueprintParsing { Next(Box), None, @@ -313,7 +328,7 @@ fn next_bip_from_blueprints( chain_config.limits.minimum_base_fee_per_gas, ); - let bip = block_in_progress::BlockInProgress::from_blueprint( + let bip = block_in_progress::EthBlockInProgress::from_blueprint( blueprint, next_bip_number, next_bip_parent_hash, @@ -328,9 +343,13 @@ fn next_bip_from_blueprints( tezos_evm_logging::Level::Debug, "bip: {bip:?}" ); - Ok(BlueprintParsing::Next(Box::new(bip))) + Ok(BlueprintParsing::Next(Box::new( + BlockInProgress::Etherlink(bip), + ))) } - ChainConfig::Michelson(_) => panic!("Implement Tezlink"), + ChainConfig::Michelson(_) => Ok(BlueprintParsing::Next(Box::new( + BlockInProgress::Tezlink(next_bip_number), + ))), } } None => Ok(BlueprintParsing::None), @@ -341,7 +360,7 @@ fn next_bip_from_blueprints( fn compute_bip( host: &mut Host, outbox_queue: &OutboxQueue<'_, impl Path>, - mut block_in_progress: BlockInProgress, + mut block_in_progress: EthBlockInProgress, precompiles: &PrecompileBTreeMap, tick_counter: &mut TickCounter, sequencer_pool_address: Option, @@ -507,7 +526,11 @@ pub fn produce( let (block_in_progress_provenance, block_in_progress) = match storage::read_block_in_progress(&safe_host)? { Some(block_in_progress) => { - (BlockInProgressProvenance::Storage, block_in_progress) + // We don't yet support saving Tez block in progress in the durable storage and restoring them. + ( + BlockInProgressProvenance::Storage, + BlockInProgress::Etherlink(block_in_progress), + ) } None => { // Using `safe_host.host` allows to escape from the failsafe storage, which is necessary @@ -539,24 +562,51 @@ pub fn produce( } }; - let processed_blueprint = block_in_progress.number; - let computation_result = match &config.chain_config { - ChainConfig::Evm(chain_config) => compute_bip( - &mut safe_host, - &outbox_queue, - block_in_progress, - &precompiles, - &mut tick_counter, - sequencer_pool_address, - &chain_config.limits, - config.maximum_allowed_ticks, - tracer_input, - chain_id, - da_fee_per_byte, - coinbase, - &chain_config.evm_config, - ), - ChainConfig::Michelson(_) => panic!("Implement Tezlink"), + let processed_blueprint = block_in_progress.number(); + let computation_result = match (&config.chain_config, block_in_progress) { + ( + ChainConfig::Evm(chain_config), + BlockInProgress::Etherlink(block_in_progress), + ) => { + log!( + safe_host, + Debug, + "Computing the BlockInProgress for Etherlink" + ); + compute_bip( + &mut safe_host, + &outbox_queue, + block_in_progress, + &precompiles, + &mut tick_counter, + sequencer_pool_address, + &chain_config.limits, + config.maximum_allowed_ticks, + tracer_input, + chain_id, + da_fee_per_byte, + coinbase, + &chain_config.evm_config, + ) + } + (ChainConfig::Michelson(_), BlockInProgress::Tezlink(number)) => { + log!( + safe_host, + Debug, + "Computing the BlockInProgress for Tezlink at level {}", + number + ); + return Ok(ComputationResult::Finished); + } + (_, _) => { + // This case should be correctly handled by this MR https://gitlab.com/tezos/tezos/-/merge_requests/17259 + log!( + safe_host, + Fatal, + "Incoherent BlockInProgress found with the Chain running in the kernel" + ); + return Ok(ComputationResult::Finished); + } }; match computation_result { Ok(BlockComputationResult::Finished { @@ -1327,7 +1377,7 @@ mod tests { let transactions = vec![valid_tx].into(); // init block in progress - let mut block_in_progress = BlockInProgress::new( + let mut block_in_progress = EthBlockInProgress::new( U256::from(1), transactions, block_constants.block_fees.base_fee_per_gas(), diff --git a/etherlink/kernel_latest/kernel/src/block_in_progress.rs b/etherlink/kernel_latest/kernel/src/block_in_progress.rs index c8712a95c0be..173a9e721d01 100644 --- a/etherlink/kernel_latest/kernel/src/block_in_progress.rs +++ b/etherlink/kernel_latest/kernel/src/block_in_progress.rs @@ -33,7 +33,7 @@ use tezos_smart_rollup_host::path::{concat, RefPath}; #[derive(Debug, PartialEq, Clone)] /// Container for all data needed during block computation -pub struct BlockInProgress { +pub struct EthBlockInProgress { /// block number pub number: U256, /// queue containing the transactions to execute @@ -66,9 +66,9 @@ pub struct BlockInProgress { pub previous_transactions_root: OwnedHash, } -impl Encodable for BlockInProgress { +impl Encodable for EthBlockInProgress { fn rlp_append(&self, stream: &mut rlp::RlpStream) { - let BlockInProgress { + let EthBlockInProgress { number, tx_queue, valid_txs, @@ -117,8 +117,8 @@ fn append_txs(stream: &mut rlp::RlpStream, valid_txs: &[[u8; TRANSACTION_HASH_SI }) } -impl Decodable for BlockInProgress { - fn decode(decoder: &rlp::Rlp<'_>) -> Result { +impl Decodable for EthBlockInProgress { + fn decode(decoder: &rlp::Rlp<'_>) -> Result { if !decoder.is_list() { return Err(DecoderError::RlpExpectedToBeList); } @@ -192,7 +192,7 @@ fn decode_queue(decoder: &rlp::Rlp<'_>) -> Result, Decoder Ok(queue) } -impl BlockInProgress { +impl EthBlockInProgress { pub fn queue_length(&self) -> usize { self.tx_queue.len() } @@ -235,7 +235,7 @@ impl BlockInProgress { base_fee_per_gas: U256, receipts_root: OwnedHash, transactions_root: OwnedHash, - ) -> BlockInProgress { + ) -> EthBlockInProgress { Self::new_with_ticks( number, H256::zero(), @@ -283,7 +283,7 @@ impl BlockInProgress { base_fee_per_gas: U256, receipts_root: OwnedHash, transactions_root: OwnedHash, - ) -> BlockInProgress { + ) -> EthBlockInProgress { // blueprint is turn into a ring to allow popping from the front let ring = match blueprint.transactions { EthTxs(transactions) => transactions.into(), @@ -293,7 +293,7 @@ impl BlockInProgress { VecDeque::new() } }; - BlockInProgress::new_with_ticks( + EthBlockInProgress::new_with_ticks( number, parent_hash, ring, @@ -582,7 +582,7 @@ impl BlockInProgress { #[cfg(test)] mod tests { - use super::BlockInProgress; + use super::EthBlockInProgress; use crate::bridge::Deposit; use crate::transaction::{Transaction, TransactionContent}; use primitive_types::{H160, H256, U256}; @@ -641,7 +641,7 @@ mod tests { #[test] fn test_encode_bip_ethereum() { - let bip = BlockInProgress { + let bip = EthBlockInProgress { number: U256::from(42), tx_queue: vec![dummy_tx_eth(1), dummy_tx_eth(8)].into(), valid_txs: vec![[2; TRANSACTION_HASH_SIZE], [9; TRANSACTION_HASH_SIZE]], @@ -667,10 +667,10 @@ mod tests { let bytes = hex::decode(expected).expect("Should be valid hex string"); let decoder = Rlp::new(&bytes); let decoded = - BlockInProgress::decode(&decoder).expect("Should have decoded data"); + EthBlockInProgress::decode(&decoder).expect("Should have decoded data"); // the estimated ticks in the current run are not stored - let fresh_bip = BlockInProgress { + let fresh_bip = EthBlockInProgress { estimated_ticks_in_run: 0, ..bip }; @@ -679,7 +679,7 @@ mod tests { #[test] fn test_encode_bip_deposit() { - let bip = BlockInProgress { + let bip = EthBlockInProgress { number: U256::from(42), tx_queue: vec![dummy_tx_deposit(1), dummy_tx_deposit(8)].into(), valid_txs: vec![[2; TRANSACTION_HASH_SIZE], [9; TRANSACTION_HASH_SIZE]], @@ -705,10 +705,10 @@ mod tests { let bytes = hex::decode(expected).expect("Should be valid hex string"); let decoder = Rlp::new(&bytes); let decoded = - BlockInProgress::decode(&decoder).expect("Should have decoded data"); + EthBlockInProgress::decode(&decoder).expect("Should have decoded data"); // the estimated ticks in the current run are not stored - let fresh_bip = BlockInProgress { + let fresh_bip = EthBlockInProgress { estimated_ticks_in_run: 0, ..bip }; @@ -717,7 +717,7 @@ mod tests { #[test] fn test_encode_bip_mixed() { - let bip = BlockInProgress { + let bip = EthBlockInProgress { number: U256::from(42), tx_queue: vec![dummy_tx_eth(1), dummy_tx_deposit(8)].into(), valid_txs: vec![[2; TRANSACTION_HASH_SIZE], [9; TRANSACTION_HASH_SIZE]], @@ -743,10 +743,10 @@ mod tests { let bytes = hex::decode(expected).expect("Should be valid hex string"); let decoder = Rlp::new(&bytes); let decoded = - BlockInProgress::decode(&decoder).expect("Should have decoded data"); + EthBlockInProgress::decode(&decoder).expect("Should have decoded data"); // the estimated ticks in the run are not stored - let fresh_bip = BlockInProgress { + let fresh_bip = EthBlockInProgress { estimated_ticks_in_run: 0, ..bip }; diff --git a/etherlink/kernel_latest/kernel/src/gas_price.rs b/etherlink/kernel_latest/kernel/src/gas_price.rs index 16618b3b16a5..0a7420b249ae 100644 --- a/etherlink/kernel_latest/kernel/src/gas_price.rs +++ b/etherlink/kernel_latest/kernel/src/gas_price.rs @@ -4,7 +4,7 @@ //! Adjustments of the gas price (a.k.a `base_fee_per_gas`), in response to load. -use crate::block_in_progress::BlockInProgress; +use crate::block_in_progress::EthBlockInProgress; use primitive_types::U256; use softfloat::F64; @@ -24,7 +24,7 @@ const ALPHA: F64 = softfloat::f64!(0.000_000_000_007); /// Register a completed block into the tick backlog pub fn register_block( host: &mut impl Runtime, - bip: &BlockInProgress, + bip: &EthBlockInProgress, ) -> anyhow::Result<()> { if bip.queue_length() > 0 { anyhow::bail!("update_gas_price on non-empty block"); @@ -196,7 +196,7 @@ mod test { H160::zero(), ); - let mut bip = BlockInProgress::new_with_ticks( + let mut bip = EthBlockInProgress::new_with_ticks( U256::zero(), Default::default(), VecDeque::new(), diff --git a/etherlink/kernel_latest/kernel/src/storage.rs b/etherlink/kernel_latest/kernel/src/storage.rs index 6105f2103804..0bc0dce49f9b 100644 --- a/etherlink/kernel_latest/kernel/src/storage.rs +++ b/etherlink/kernel_latest/kernel/src/storage.rs @@ -5,7 +5,7 @@ // // SPDX-License-Identifier: MIT -use crate::block_in_progress::BlockInProgress; +use crate::block_in_progress::EthBlockInProgress; use crate::event::Event; use crate::simulation::SimulationResult; use crate::tick_model::constants::MAXIMUM_GAS_LIMIT; @@ -701,7 +701,7 @@ pub fn store_kernel_version( #[cfg_attr(feature = "benchmark", inline(never))] pub fn store_block_in_progress( host: &mut Host, - bip: &BlockInProgress, + bip: &EthBlockInProgress, ) -> anyhow::Result<()> { let path = OwnedPath::from(EVM_BLOCK_IN_PROGRESS); let bytes = &bip.rlp_bytes(); @@ -721,7 +721,7 @@ pub fn store_block_in_progress( #[cfg_attr(feature = "benchmark", inline(never))] pub fn read_block_in_progress( host: &Host, -) -> anyhow::Result> { +) -> anyhow::Result> { let path = OwnedPath::from(EVM_BLOCK_IN_PROGRESS); if let Some(ValueType::Value) = host.store_has(&path)? { let bytes = host @@ -734,7 +734,7 @@ pub fn read_block_in_progress( bytes.len() ); let decoder = Rlp::new(bytes.as_slice()); - let bip = BlockInProgress::decode(&decoder) + let bip = EthBlockInProgress::decode(&decoder) .context("Failed to decode current block in progress")?; Ok(Some(bip)) } else { -- GitLab From 27dd56c2cf4c5c51a0889c4306f3f4e8cf87c0a9 Mon Sep 17 00:00:00 2001 From: arnaud Date: Thu, 6 Mar 2025 13:01:54 +0100 Subject: [PATCH 4/8] Etherlink/Kernel/Tezlink: Introduce ChainHeader that can be wether an Etherlink block header or a Tezlink one --- etherlink/kernel_latest/kernel/src/block.rs | 24 +-- .../kernel/src/blueprint_storage.rs | 145 +++++++++++++++--- etherlink/kernel_latest/kernel/src/event.rs | 28 +++- etherlink/kernel_latest/kernel/src/inbox.rs | 13 +- .../kernel/src/sequencer_blueprint.rs | 14 +- 5 files changed, 183 insertions(+), 41 deletions(-) diff --git a/etherlink/kernel_latest/kernel/src/block.rs b/etherlink/kernel_latest/kernel/src/block.rs index b0863a21aaf4..8b55bdb53673 100644 --- a/etherlink/kernel_latest/kernel/src/block.rs +++ b/etherlink/kernel_latest/kernel/src/block.rs @@ -9,8 +9,9 @@ use crate::apply::{ apply_transaction, ExecutionInfo, ExecutionResult, Validity, WITHDRAWAL_OUTBOX_QUEUE, }; use crate::blueprint_storage::{ - drop_blueprint, read_blueprint, read_current_block_header, - store_current_block_header, BlockHeader, BlueprintHeader, EVMBlockHeader, + drop_blueprint, read_blueprint, read_current_block_header_for_family, + store_current_block_header, BlockHeader, BlueprintHeader, ChainHeader, + EVMBlockHeader, }; use crate::chains::{ChainConfig, EvmLimits}; use crate::configuration::ConfigurationMode; @@ -272,21 +273,22 @@ fn next_bip_from_blueprints( config: &mut Configuration, kernel_upgrade: &Option, ) -> Result { + let chain_family = config.chain_config.get_chain_family(); let ( next_bip_number, next_bip_parent_hash, previous_timestamp, receipts_root, transactions_root, - ) = match read_current_block_header(host) { + ) = match read_current_block_header_for_family(host, &chain_family) { Ok(BlockHeader { blueprint_header: BlueprintHeader { number, timestamp }, - evm_block_header: - EVMBlockHeader { + chain_header: + ChainHeader::Eth(EVMBlockHeader { hash, receipts_root, transactions_root, - }, + }), }) => ( number + 1, hash, @@ -294,7 +296,7 @@ fn next_bip_from_blueprints( receipts_root, transactions_root, ), - Err(_) => ( + _ => ( U256::zero(), GENESIS_PARENT_HASH, Timestamp::from(0), @@ -477,11 +479,9 @@ fn promote_block( drop_blueprint(safe_host.host, block_header.blueprint_header.number)?; store_current_block_header(safe_host.host, &block_header)?; - Event::BlueprintApplied { - number: block_header.blueprint_header.number, - hash: block_header.evm_block_header.hash, - } - .store(safe_host.host)?; + let event = Event::blueprint_applied(block_header); + + event.store(safe_host.host)?; let written = outbox_queue.flush_queue(safe_host.host); // Log to Info only if we flushed messages. diff --git a/etherlink/kernel_latest/kernel/src/blueprint_storage.rs b/etherlink/kernel_latest/kernel/src/blueprint_storage.rs index d86e615dad2a..e1e808caea3c 100644 --- a/etherlink/kernel_latest/kernel/src/blueprint_storage.rs +++ b/etherlink/kernel_latest/kernel/src/blueprint_storage.rs @@ -4,6 +4,7 @@ // SPDX-License-Identifier: MIT use crate::blueprint::Blueprint; +use crate::chains::ChainFamily; use crate::configuration::{Configuration, ConfigurationMode}; use crate::error::{Error, StorageError}; use crate::sequencer_blueprint::{ @@ -19,7 +20,8 @@ use std::fmt::Debug; use tezos_ethereum::block::EthBlock; use tezos_ethereum::eth_gen::OwnedHash; use tezos_ethereum::rlp_helpers::{ - self, append_timestamp, append_u256_le, decode_field_u256_le, decode_timestamp, + self, append_timestamp, append_u256_le, decode_field, decode_field_u256_le, + decode_timestamp, }; use tezos_ethereum::tx_common::EthereumTransactionCommon; use tezos_evm_logging::{log, Level::*}; @@ -113,21 +115,33 @@ pub struct EVMBlockHeader { #[derive(PartialEq, Debug, Clone)] pub struct BlockHeader { pub blueprint_header: BlueprintHeader, - pub evm_block_header: H, + pub chain_header: H, +} +// Part of the block header which is specific of the Michelson chain. All +// fields are needed to build the next block +#[derive(PartialEq, Debug, Clone)] +pub struct TezBlockHeader { + pub hash: H256, } -impl From for BlockHeader { +#[derive(PartialEq, Debug, Clone)] +pub enum ChainHeader { + Tez(TezBlockHeader), + Eth(EVMBlockHeader), +} + +impl From for BlockHeader { fn from(block: EthBlock) -> Self { Self { blueprint_header: BlueprintHeader { number: block.number, timestamp: block.timestamp, }, - evm_block_header: EVMBlockHeader { + chain_header: ChainHeader::Eth(EVMBlockHeader { hash: block.hash, receipts_root: block.receipts_root, transactions_root: block.transactions_root, - }, + }), } } } @@ -262,42 +276,71 @@ impl Decodable for EVMBlockHeader { } } -impl Encodable for BlockHeader { +impl Encodable for TezBlockHeader { + fn rlp_append(&self, stream: &mut rlp::RlpStream) { + let Self { hash } = self; + stream.begin_list(1); + stream.append(hash); + } +} + +impl Decodable for TezBlockHeader { + fn decode(decoder: &rlp::Rlp) -> Result { + rlp_helpers::check_list(decoder, 1)?; + let mut it = decoder.iter(); + let hash = decode_field(&rlp_helpers::next(&mut it)?, "hash")?; + Ok(Self { hash }) + } +} + +impl Encodable for ChainHeader { + fn rlp_append(&self, stream: &mut rlp::RlpStream) { + match self { + Self::Eth(evm_block_header) => { + stream.append(evm_block_header); + } + Self::Tez(tez_block_header) => { + stream.append(tez_block_header); + } + } + } +} + +impl Encodable for BlockHeader { fn rlp_append(&self, stream: &mut rlp::RlpStream) { let Self { blueprint_header: BlueprintHeader { number, timestamp }, - evm_block_header, + chain_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); + stream.append(chain_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 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")?; + let chain_header = + rlp_helpers::decode_field(&rlp_helpers::next(&mut it)?, "block_header")?; Ok(Self { blueprint_header: BlueprintHeader { number, timestamp }, - evm_block_header, + chain_header, }) } } pub fn store_current_block_header( host: &mut Host, - current_block_header: &BlockHeader, + current_block_header: &BlockHeader, ) -> Result<(), Error> { store_rlp(current_block_header, host, &EVM_CURRENT_BLOCK_HEADER).map_err(Error::from) } @@ -315,6 +358,34 @@ pub fn read_current_blueprint_header( Ok(block_header.blueprint_header) } +pub fn read_current_block_header_for_family( + host: &Host, + chain_family: &ChainFamily, +) -> Result, Error> { + match chain_family { + ChainFamily::Evm => { + let BlockHeader { + blueprint_header, + chain_header, + } = read_current_block_header::(host)?; + Ok(BlockHeader { + blueprint_header, + chain_header: ChainHeader::Eth(chain_header), + }) + } + ChainFamily::Michelson => { + let BlockHeader { + blueprint_header, + chain_header, + } = read_current_block_header::(host)?; + Ok(BlockHeader { + blueprint_header, + chain_header: ChainHeader::Tez(chain_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. @@ -643,7 +714,7 @@ mod tests { use crate::chains::ChainConfig; use crate::configuration::{DalConfiguration, TezosContracts}; use crate::delayed_inbox::Hash; - use crate::sequencer_blueprint::rlp_roundtrip; + use crate::sequencer_blueprint::{rlp_roundtrip, rlp_roundtrip_f}; use crate::storage::store_last_info_per_level_timestamp; use crate::tick_model::constants::MAX_ALLOWED_TICKS; use primitive_types::H256; @@ -652,6 +723,7 @@ mod tests { use tezos_evm_runtime::runtime::MockKernelHost; use tezos_smart_rollup_encoding::public_key::PublicKey; use tezos_smart_rollup_host::runtime::Runtime as SdkRuntime; // Used to put traits interface in the scope + use tezos_tezlink::block::TezBlock; fn test_invalid_sequencer_blueprint_is_removed(enable_dal: bool) { let mut host = MockKernelHost::default(); @@ -822,7 +894,7 @@ mod tests { } #[test] - fn test_block_header_roundtrip() { + fn test_evm_block_header_roundtrip() { let blueprint_header = BlueprintHeader { number: 42.into(), timestamp: Timestamp::from(10), @@ -837,9 +909,44 @@ mod tests { let block_header = BlockHeader { blueprint_header, - evm_block_header, + chain_header: ChainHeader::Eth(evm_block_header), + }; + + rlp_roundtrip_f( + block_header, + |BlockHeader { + blueprint_header, + chain_header, + }| BlockHeader { + blueprint_header, + chain_header: ChainHeader::Eth(chain_header), + }, + ); + } + + #[test] + fn test_tez_block_header_roundtrip() { + let blueprint_header = BlueprintHeader { + number: 42.into(), + timestamp: Timestamp::from(10), + }; + let tez_block_header = TezBlockHeader { + hash: TezBlock::genesis_block_hash(), + }; + let block_header = BlockHeader { + blueprint_header, + chain_header: ChainHeader::Tez(tez_block_header), }; - rlp_roundtrip(block_header); + rlp_roundtrip_f( + block_header, + |BlockHeader { + blueprint_header, + chain_header, + }| BlockHeader { + blueprint_header, + chain_header: ChainHeader::Tez(chain_header), + }, + ); } } diff --git a/etherlink/kernel_latest/kernel/src/event.rs b/etherlink/kernel_latest/kernel/src/event.rs index 2d641c0c4a08..770ce5e4ef57 100644 --- a/etherlink/kernel_latest/kernel/src/event.rs +++ b/etherlink/kernel_latest/kernel/src/event.rs @@ -3,7 +3,12 @@ // // SPDX-License-Identifier: MIT -use crate::{storage, transaction::Transaction, upgrade}; +use crate::{ + blueprint_storage::{BlockHeader, BlueprintHeader, ChainHeader}, + storage, + transaction::Transaction, + upgrade, +}; use primitive_types::{H256, U256}; use rlp::{Encodable, RlpStream}; use tezos_ethereum::rlp_helpers::{append_timestamp, append_u256_le}; @@ -73,4 +78,25 @@ impl Event<'_> { pub fn store(&self, host: &mut Host) -> anyhow::Result<()> { storage::store_event(host, self) } + + pub fn blueprint_applied(block: BlockHeader) -> Self { + let BlockHeader { + blueprint_header: + BlueprintHeader { + number, + timestamp: _, + }, + chain_header, + } = block; + match chain_header { + ChainHeader::Eth(evm_block_header) => Self::BlueprintApplied { + number, + hash: evm_block_header.hash, + }, + ChainHeader::Tez(_tez_block_header) => Self::BlueprintApplied { + number, + hash: H256::zero(), + }, + } + } } diff --git a/etherlink/kernel_latest/kernel/src/inbox.rs b/etherlink/kernel_latest/kernel/src/inbox.rs index ce43e9f76532..71c6a3d049d2 100644 --- a/etherlink/kernel_latest/kernel/src/inbox.rs +++ b/etherlink/kernel_latest/kernel/src/inbox.rs @@ -624,7 +624,7 @@ mod tests { use super::*; use crate::blueprint_storage::{ blueprint_path, store_current_block_header, BlockHeader, BlueprintHeader, - EVMBlockHeader, + ChainHeader, EVMBlockHeader, }; use crate::chains::test_chain_config; use crate::configuration::TezosContracts; @@ -1252,6 +1252,11 @@ mod tests { // Prepare the host. let mut host = MockKernelHost::default(); let address = smart_rollup_address(); + let evm_block_header = EVMBlockHeader { + hash: crate::block::GENESIS_PARENT_HASH, + receipts_root: vec![], + transactions_root: vec![], + }; store_current_block_header( &mut host, &BlockHeader { @@ -1259,11 +1264,7 @@ mod tests { number: head_level, timestamp: 0.into(), }, - evm_block_header: EVMBlockHeader { - hash: crate::block::GENESIS_PARENT_HASH, - receipts_root: vec![], - transactions_root: vec![], - }, + chain_header: ChainHeader::Eth(evm_block_header), }, ) .unwrap(); diff --git a/etherlink/kernel_latest/kernel/src/sequencer_blueprint.rs b/etherlink/kernel_latest/kernel/src/sequencer_blueprint.rs index 0f1c2b3d7c4b..1cef967e1974 100644 --- a/etherlink/kernel_latest/kernel/src/sequencer_blueprint.rs +++ b/etherlink/kernel_latest/kernel/src/sequencer_blueprint.rs @@ -243,11 +243,19 @@ impl Decodable for SequencerBlueprint { } #[cfg(test)] -pub fn rlp_roundtrip(v: S) { +pub fn rlp_roundtrip_f( + v: S, + f: impl FnOnce(T) -> S, +) { let bytes = v.rlp_bytes(); - let v2: S = + let v2: T = rlp_helpers::FromRlpBytes::from_rlp_bytes(&bytes).expect("Should be decodable"); - assert_eq!(v, v2, "Roundtrip failed on {:?}", v) + assert_eq!(v, f(v2), "Roundtrip failed on {:?}", v) +} + +#[cfg(test)] +pub fn rlp_roundtrip(v: S) { + rlp_roundtrip_f::(v, |s| s) } #[cfg(test)] -- GitLab From bfe0edafaba265b7ed0054bffdd29f652ad61874 Mon Sep 17 00:00:00 2001 From: arnaud Date: Thu, 6 Mar 2025 13:38:12 +0100 Subject: [PATCH 5/8] Etherlink/Kernel/Tezlink: Pass BlockHeader instead of the whole block in promote --- etherlink/kernel_latest/kernel/src/block.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/etherlink/kernel_latest/kernel/src/block.rs b/etherlink/kernel_latest/kernel/src/block.rs index 8b55bdb53673..a3d9c3cbfc8e 100644 --- a/etherlink/kernel_latest/kernel/src/block.rs +++ b/etherlink/kernel_latest/kernel/src/block.rs @@ -466,14 +466,13 @@ fn promote_block( safe_host: &mut SafeStorage<&mut Host>, outbox_queue: &OutboxQueue<'_, impl Path>, block_in_progress_provenance: &BlockInProgressProvenance, - block: EthBlock, + block_header: BlockHeader, config: &mut Configuration, delayed_txs: Vec, ) -> anyhow::Result<()> { if let BlockInProgressProvenance::Storage = block_in_progress_provenance { storage::delete_block_in_progress(safe_host)?; } - let block_header = BlockHeader::from(block); safe_host.promote()?; safe_host.promote_trace()?; drop_blueprint(safe_host.host, block_header.blueprint_header.number)?; @@ -613,11 +612,12 @@ pub fn produce( included_delayed_transactions, block, }) => { + let block_header: BlockHeader = (*block).into(); promote_block( &mut safe_host, &outbox_queue, &block_in_progress_provenance, - *block, + block_header, config, included_delayed_transactions, )?; -- GitLab From 107da8911dd426570cc2c2ae2031d0866c1182de Mon Sep 17 00:00:00 2001 From: arnaud Date: Thu, 6 Mar 2025 14:06:27 +0100 Subject: [PATCH 6/8] Etherlink/Kernel/Tezlink: Introduce L2Block and produce a Tezlink block (dummy) --- etherlink/kernel_latest/kernel/src/block.rs | 28 ++++++++++++------- .../kernel/src/blueprint_storage.rs | 14 +++++++++- etherlink/kernel_latest/kernel/src/l2block.rs | 23 +++++++++++++++ etherlink/kernel_latest/kernel/src/lib.rs | 1 + 4 files changed, 55 insertions(+), 11 deletions(-) create mode 100644 etherlink/kernel_latest/kernel/src/l2block.rs diff --git a/etherlink/kernel_latest/kernel/src/block.rs b/etherlink/kernel_latest/kernel/src/block.rs index a3d9c3cbfc8e..3349c195e140 100644 --- a/etherlink/kernel_latest/kernel/src/block.rs +++ b/etherlink/kernel_latest/kernel/src/block.rs @@ -18,6 +18,7 @@ use crate::configuration::ConfigurationMode; use crate::delayed_inbox::DelayedInbox; use crate::error::Error; use crate::event::Event; +use crate::l2block::L2Block; use crate::storage; use crate::transaction::Transaction; use crate::upgrade; @@ -33,7 +34,6 @@ 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::EthBlock; use tezos_ethereum::transaction::TransactionHash; use tezos_evm_logging::{log, Level::*, Verbosity}; use tezos_evm_runtime::runtime::Runtime; @@ -41,6 +41,7 @@ use tezos_evm_runtime::safe_storage::SafeStorage; use tezos_smart_rollup::outbox::OutboxQueue; use tezos_smart_rollup::types::Timestamp; use tezos_smart_rollup_host::path::Path; +use tezos_tezlink::block::TezBlock; use tick_model::estimate_remaining_ticks_for_transaction_execution; pub const GENESIS_PARENT_HASH: H256 = H256([0xff; 32]); @@ -77,7 +78,7 @@ pub enum BlockComputationResult { RebootNeeded, Finished { included_delayed_transactions: Vec, - block: Box, + block: L2Block, }, } @@ -249,14 +250,14 @@ fn compute( #[allow(clippy::large_enum_variant)] pub enum BlockInProgress { Etherlink(EthBlockInProgress), - Tezlink(U256), + Tezlink(U256, Timestamp), } impl BlockInProgress { pub fn number(&self) -> U256 { match self { Self::Etherlink(bip) => bip.number, - Self::Tezlink(n) => *n, + Self::Tezlink(n, _) => *n, } } } @@ -350,7 +351,7 @@ fn next_bip_from_blueprints( ))) } ChainConfig::Michelson(_) => Ok(BlueprintParsing::Next(Box::new( - BlockInProgress::Tezlink(next_bip_number), + BlockInProgress::Tezlink(next_bip_number, blueprint.timestamp), ))), } } @@ -419,7 +420,7 @@ fn compute_bip( .context("Failed to finalize the block in progress")?; Ok(BlockComputationResult::Finished { included_delayed_transactions, - block: Box::new(new_block), + block: L2Block::Etherlink(Box::new(new_block)), }) } } @@ -588,14 +589,22 @@ pub fn produce( &chain_config.evm_config, ) } - (ChainConfig::Michelson(_), BlockInProgress::Tezlink(number)) => { + (ChainConfig::Michelson(_), BlockInProgress::Tezlink(number, timestamp)) => { log!( safe_host, Debug, "Computing the BlockInProgress for Tezlink at level {}", number ); - return Ok(ComputationResult::Finished); + Ok(BlockComputationResult::Finished { + included_delayed_transactions: vec![], + block: L2Block::Tezlink(TezBlock { + number, + hash: TezBlock::genesis_block_hash(), + timestamp, + previous_hash: TezBlock::genesis_block_hash(), + }), + }) } (_, _) => { // This case should be correctly handled by this MR https://gitlab.com/tezos/tezos/-/merge_requests/17259 @@ -612,12 +621,11 @@ pub fn produce( included_delayed_transactions, block, }) => { - let block_header: BlockHeader = (*block).into(); promote_block( &mut safe_host, &outbox_queue, &block_in_progress_provenance, - block_header, + block.header(), config, included_delayed_transactions, )?; diff --git a/etherlink/kernel_latest/kernel/src/blueprint_storage.rs b/etherlink/kernel_latest/kernel/src/blueprint_storage.rs index e1e808caea3c..cf7edcd02de7 100644 --- a/etherlink/kernel_latest/kernel/src/blueprint_storage.rs +++ b/etherlink/kernel_latest/kernel/src/blueprint_storage.rs @@ -31,6 +31,7 @@ use tezos_smart_rollup_core::MAX_INPUT_MESSAGE_SIZE; use tezos_smart_rollup_host::path::*; use tezos_smart_rollup_host::runtime::RuntimeError; use tezos_storage::{read_rlp, store_read_slice, store_rlp}; +use tezos_tezlink::block::TezBlock; pub const EVM_BLUEPRINTS: RefPath = RefPath::assert_from(b"/evm/blueprints"); @@ -146,6 +147,18 @@ impl From for BlockHeader { } } +impl From for BlockHeader { + fn from(block: TezBlock) -> Self { + Self { + blueprint_header: BlueprintHeader { + number: block.number, + timestamp: block.timestamp, + }, + chain_header: ChainHeader::Tez(TezBlockHeader { hash: block.hash }), + } + } +} + 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, @@ -723,7 +736,6 @@ mod tests { use tezos_evm_runtime::runtime::MockKernelHost; use tezos_smart_rollup_encoding::public_key::PublicKey; use tezos_smart_rollup_host::runtime::Runtime as SdkRuntime; // Used to put traits interface in the scope - use tezos_tezlink::block::TezBlock; fn test_invalid_sequencer_blueprint_is_removed(enable_dal: bool) { let mut host = MockKernelHost::default(); diff --git a/etherlink/kernel_latest/kernel/src/l2block.rs b/etherlink/kernel_latest/kernel/src/l2block.rs new file mode 100644 index 000000000000..c7c37e07fbef --- /dev/null +++ b/etherlink/kernel_latest/kernel/src/l2block.rs @@ -0,0 +1,23 @@ +use tezos_ethereum::block::EthBlock; +use tezos_tezlink::block::TezBlock; + +use crate::blueprint_storage::{BlockHeader, ChainHeader}; + +#[derive(PartialEq, Debug)] +pub enum L2Block { + // The difference in size between the two variant is too big + // So we need to Box the EthBlock struct to prevent a warning + // When TezBlock will be complete we could remove the Box or + // box the whole L2Block structure enum instead + Etherlink(Box), + Tezlink(TezBlock), +} + +impl L2Block { + pub fn header(self) -> BlockHeader { + match self { + Self::Etherlink(block) => (*block).into(), + Self::Tezlink(block) => block.into(), + } + } +} diff --git a/etherlink/kernel_latest/kernel/src/lib.rs b/etherlink/kernel_latest/kernel/src/lib.rs index cff1eab4fb74..13c702d711a2 100644 --- a/etherlink/kernel_latest/kernel/src/lib.rs +++ b/etherlink/kernel_latest/kernel/src/lib.rs @@ -55,6 +55,7 @@ mod fallback_upgrade; mod fees; mod gas_price; mod inbox; +mod l2block; mod linked_list; mod migration; mod parsing; -- GitLab From e1ae51957db4fed0ac1abf3cdb1b1423fd712570 Mon Sep 17 00:00:00 2001 From: arnaud Date: Thu, 6 Mar 2025 17:25:38 +0100 Subject: [PATCH 7/8] Etherlink/Kernel/Tezlink: Use Header in block production instead of the raw information --- etherlink/kernel_latest/kernel/src/block.rs | 85 +++++++++---------- .../kernel/src/blueprint_storage.rs | 74 +++++++++++++--- .../kernel_latest/kernel/src/stage_one.rs | 5 +- 3 files changed, 101 insertions(+), 63 deletions(-) diff --git a/etherlink/kernel_latest/kernel/src/block.rs b/etherlink/kernel_latest/kernel/src/block.rs index 3349c195e140..4300adc9fc4c 100644 --- a/etherlink/kernel_latest/kernel/src/block.rs +++ b/etherlink/kernel_latest/kernel/src/block.rs @@ -10,8 +10,7 @@ use crate::apply::{ }; use crate::blueprint_storage::{ drop_blueprint, read_blueprint, read_current_block_header_for_family, - store_current_block_header, BlockHeader, BlueprintHeader, ChainHeader, - EVMBlockHeader, + store_current_block_header, BlockHeader, ChainHeader, }; use crate::chains::{ChainConfig, EvmLimits}; use crate::configuration::ConfigurationMode; @@ -275,43 +274,24 @@ fn next_bip_from_blueprints( kernel_upgrade: &Option, ) -> Result { let chain_family = config.chain_config.get_chain_family(); - let ( - next_bip_number, - next_bip_parent_hash, - previous_timestamp, - receipts_root, - transactions_root, - ) = match read_current_block_header_for_family(host, &chain_family) { - Ok(BlockHeader { - blueprint_header: BlueprintHeader { number, timestamp }, - chain_header: - ChainHeader::Eth(EVMBlockHeader { - hash, - receipts_root, - transactions_root, - }), - }) => ( - number + 1, - hash, - timestamp, - receipts_root, - transactions_root, - ), - _ => ( - U256::zero(), - GENESIS_PARENT_HASH, - Timestamp::from(0), - vec![0; 32], - vec![0; 32], - ), - }; - let (blueprint, size) = read_blueprint( - host, - config, - next_bip_number, - next_bip_parent_hash, - previous_timestamp, - )?; + let (next_bip_number, timestamp, chain_header) = + match read_current_block_header_for_family(host, &chain_family) { + Err(_) => ( + U256::zero(), + Timestamp::from(0), + ChainHeader::genesis_header(chain_family), + ), + Ok(BlockHeader { + blueprint_header, + chain_header, + }) => ( + blueprint_header.number + 1, + blueprint_header.timestamp, + chain_header, + ), + }; + let (blueprint, size) = + read_blueprint(host, config, next_bip_number, timestamp, &chain_header)?; log!(host, Benchmarking, "Size of blueprint: {}", size); match blueprint { Some(blueprint) => { @@ -323,8 +303,8 @@ fn next_bip_from_blueprints( return Ok(BlueprintParsing::None); } } - match &config.chain_config { - ChainConfig::Evm(chain_config) => { + match (&config.chain_config, chain_header) { + (ChainConfig::Evm(chain_config), ChainHeader::Eth(header)) => { let gas_price = crate::gas_price::base_fee_per_gas( host, blueprint.timestamp, @@ -334,11 +314,11 @@ fn next_bip_from_blueprints( let bip = block_in_progress::EthBlockInProgress::from_blueprint( blueprint, next_bip_number, - next_bip_parent_hash, + header.hash, tick_counter.c, gas_price, - receipts_root, - transactions_root, + header.receipts_root, + header.transactions_root, ); tezos_evm_logging::log!( @@ -350,9 +330,20 @@ fn next_bip_from_blueprints( BlockInProgress::Etherlink(bip), ))) } - ChainConfig::Michelson(_) => Ok(BlueprintParsing::Next(Box::new( - BlockInProgress::Tezlink(next_bip_number, blueprint.timestamp), - ))), + (ChainConfig::Michelson(_), ChainHeader::Tez(_)) => { + Ok(BlueprintParsing::Next(Box::new(BlockInProgress::Tezlink( + next_bip_number, + blueprint.timestamp, + )))) + } + (_, _) => { + log!( + host, + Fatal, + "Incoherent state between the configuration and the header read in the durable storage" + ); + Ok(BlueprintParsing::None) + } } } None => Ok(BlueprintParsing::None), diff --git a/etherlink/kernel_latest/kernel/src/blueprint_storage.rs b/etherlink/kernel_latest/kernel/src/blueprint_storage.rs index cf7edcd02de7..2e325dde0812 100644 --- a/etherlink/kernel_latest/kernel/src/blueprint_storage.rs +++ b/etherlink/kernel_latest/kernel/src/blueprint_storage.rs @@ -3,6 +3,7 @@ // // SPDX-License-Identifier: MIT +use crate::block::GENESIS_PARENT_HASH; use crate::blueprint::Blueprint; use crate::chains::ChainFamily; use crate::configuration::{Configuration, ConfigurationMode}; @@ -131,6 +132,36 @@ pub enum ChainHeader { Eth(EVMBlockHeader), } +impl ChainHeader { + fn evm_genesis() -> Self { + Self::Eth(EVMBlockHeader { + hash: GENESIS_PARENT_HASH, + receipts_root: vec![0; 32], + transactions_root: vec![0; 32], + }) + } + + fn tez_genesis() -> Self { + Self::Tez(TezBlockHeader { + hash: TezBlock::genesis_block_hash(), + }) + } + + pub fn genesis_header(chain_family: ChainFamily) -> ChainHeader { + match chain_family { + ChainFamily::Evm => Self::evm_genesis(), + ChainFamily::Michelson => Self::tez_genesis(), + } + } + + pub fn hash(&self) -> H256 { + match self { + Self::Eth(header) => header.hash, + Self::Tez(header) => header.hash, + } + } +} + impl From for BlockHeader { fn from(block: EthBlock) -> Self { Self { @@ -487,7 +518,7 @@ fn parse_and_validate_blueprint( current_blueprint_size: usize, evm_node_flag: bool, max_blueprint_lookahead_in_seconds: i64, - parent_hash: H256, + parent_chain_header: &ChainHeader, head_timestamp: Timestamp, ) -> anyhow::Result<(BlueprintValidity, usize)> { // Decode @@ -496,7 +527,7 @@ fn parse_and_validate_blueprint( Ok(blueprint_with_hashes) => { // Validate parent hash #[cfg(not(feature = "benchmark"))] - if parent_hash != blueprint_with_hashes.parent_hash { + if parent_chain_header.hash() != blueprint_with_hashes.parent_hash { return Ok((BlueprintValidity::InvalidParentHash, bytes.len())); } @@ -572,7 +603,7 @@ fn read_all_chunks_and_validate( blueprint_path: &OwnedPath, nb_chunks: u16, config: &mut Configuration, - parent_hash: H256, + previous_chain_header: &ChainHeader, previous_timestamp: Timestamp, ) -> anyhow::Result<(Option, usize)> { let mut chunks = vec![]; @@ -614,7 +645,7 @@ fn read_all_chunks_and_validate( size, *evm_node_flag, *max_blueprint_lookahead_in_seconds, - parent_hash, + previous_chain_header, previous_timestamp, )?; if let (BlueprintValidity::Valid(blueprint), size_with_delayed_transactions) = @@ -639,8 +670,8 @@ pub fn read_blueprint( host: &mut Host, config: &mut Configuration, number: U256, - parent_hash: H256, previous_timestamp: Timestamp, + previous_chain_header: &ChainHeader, ) -> anyhow::Result<(Option, usize)> { let blueprint_path = blueprint_path(number)?; let exists = host.store_has(&blueprint_path)?.is_some(); @@ -661,7 +692,7 @@ pub fn read_blueprint( &blueprint_path, nb_chunks, config, - parent_hash, + previous_chain_header, previous_timestamp, )?; Ok((blueprint, size)) @@ -693,17 +724,24 @@ pub fn read_next_blueprint( host: &mut Host, config: &mut Configuration, ) -> anyhow::Result<(Option, usize)> { - use crate::block_storage; - let (number, parent_hash, previous_timestamp) = - match block_storage::read_current(host) { - Ok(block) => (block.number + 1, block.hash, block.timestamp), + let chain_family = config.chain_config.get_chain_family(); + let (number, previous_timestamp, block_header) = + match read_current_block_header_for_family(host, &chain_family) { + Ok(BlockHeader { + blueprint_header, + chain_header, + }) => ( + blueprint_header.number + 1, + blueprint_header.timestamp, + chain_header, + ), Err(_) => ( U256::zero(), - crate::block::GENESIS_PARENT_HASH, Timestamp::from(0), + ChainHeader::genesis_header(chain_family), ), }; - read_blueprint(host, config, number, parent_hash, previous_timestamp) + read_blueprint(host, config, number, previous_timestamp, &block_header) } pub fn drop_blueprint(host: &mut Host, number: U256) -> Result<(), Error> { @@ -809,7 +847,11 @@ mod tests { 0, false, 500, - GENESIS_PARENT_HASH, + &ChainHeader::Eth(EVMBlockHeader { + hash: GENESIS_PARENT_HASH, + receipts_root: vec![0; 32], + transactions_root: vec![0; 32], + }), Timestamp::from(0), ) .expect("Should be able to parse blueprint"); @@ -871,7 +913,11 @@ mod tests { 0, false, 500, - GENESIS_PARENT_HASH, + &ChainHeader::Eth(EVMBlockHeader { + hash: GENESIS_PARENT_HASH, + receipts_root: vec![0; 32], + transactions_root: vec![0; 32], + }), Timestamp::from(0), ) .expect("Should be able to parse blueprint"); diff --git a/etherlink/kernel_latest/kernel/src/stage_one.rs b/etherlink/kernel_latest/kernel/src/stage_one.rs index 77125efc4868..ff7bd59edc8b 100644 --- a/etherlink/kernel_latest/kernel/src/stage_one.rs +++ b/etherlink/kernel_latest/kernel/src/stage_one.rs @@ -201,7 +201,8 @@ pub fn fetch_blueprints( #[cfg(test)] mod tests { use crate::{ - chains::test_chain_config, + blueprint_storage::ChainHeader, + chains::{test_chain_config, ChainFamily}, dal_slot_import_signal::{ DalSlotImportSignals, DalSlotIndicesList, DalSlotIndicesOfLevel, UnsignedDalSlotSignals, @@ -511,8 +512,8 @@ mod tests { &mut host, &mut conf, U256::from(10), - crate::block::GENESIS_PARENT_HASH, Timestamp::from(0), + &ChainHeader::genesis_header(ChainFamily::Evm), ) .expect("Blueprint reading shouldn't fail") .0 -- GitLab From 0434e6f8cd68073c5dc06518de3900e33bdd8883 Mon Sep 17 00:00:00 2001 From: arnaud Date: Fri, 7 Mar 2025 10:26:00 +0100 Subject: [PATCH 8/8] Etherlink/Kernel/Tezlink/Test: Dummy test for tezlink block production --- etherlink/kernel_latest/kernel/src/block.rs | 36 ++++++++++++++++++-- etherlink/kernel_latest/kernel/src/chains.rs | 6 ++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/etherlink/kernel_latest/kernel/src/block.rs b/etherlink/kernel_latest/kernel/src/block.rs index 4300adc9fc4c..b1a3648e02bf 100644 --- a/etherlink/kernel_latest/kernel/src/block.rs +++ b/etherlink/kernel_latest/kernel/src/block.rs @@ -667,6 +667,7 @@ mod tests { use crate::blueprint_storage::read_next_blueprint; use crate::blueprint_storage::store_inbox_blueprint; use crate::blueprint_storage::store_inbox_blueprint_by_number; + use crate::chains::MichelsonChainConfig; use crate::fees::DA_FEE_PER_BYTE; use crate::fees::MINIMUM_BASE_FEE_PER_GAS; use crate::storage::read_block_in_progress; @@ -677,7 +678,7 @@ mod tests { use crate::transaction::TransactionContent; use crate::transaction::TransactionContent::Ethereum; use crate::transaction::TransactionContent::EthereumDelayed; - use crate::transaction::Transactions::EthTxs; + use crate::transaction::Transactions; use crate::{retrieve_block_fees, retrieve_chain_id}; use evm_execution::account_storage::{ account_path, init_account_storage, EthereumAccountStorage, @@ -697,7 +698,14 @@ mod tests { fn blueprint(transactions: Vec) -> Blueprint { Blueprint { - transactions: EthTxs(transactions), + transactions: Transactions::EthTxs(transactions), + timestamp: Timestamp::from(0i64), + } + } + + fn tezlink_blueprint() -> Blueprint { + Blueprint { + transactions: Transactions::TezTxs, timestamp: Timestamp::from(0i64), } } @@ -754,6 +762,15 @@ mod tests { } } + fn dummy_tez_configuration() -> Configuration { + Configuration { + chain_config: ChainConfig::Michelson(MichelsonChainConfig::create_config( + DUMMY_CHAIN_ID, + )), + ..Configuration::default() + } + } + fn dummy_block_fees() -> BlockFees { BlockFees::new( U256::from(DUMMY_BASE_FEE_PER_GAS), @@ -940,6 +957,21 @@ mod tests { } } + #[test] + // Test if tezlink block production doesn't panic + fn test_produce_tezlink_block() { + let mut host = MockKernelHost::default(); + + let mut config = dummy_tez_configuration(); + + store_blueprints(&mut host, vec![tezlink_blueprint()]); + + let computation = produce(&mut host, &mut config, None, None) + .expect("The block production failed."); + + assert_eq!(computation, ComputationResult::RebootNeeded) + } + #[test] // Test if the invalid transactions are producing receipts fn test_invalid_transactions_receipt_status() { diff --git a/etherlink/kernel_latest/kernel/src/chains.rs b/etherlink/kernel_latest/kernel/src/chains.rs index 7906077b5938..d3bccff5a72f 100644 --- a/etherlink/kernel_latest/kernel/src/chains.rs +++ b/etherlink/kernel_latest/kernel/src/chains.rs @@ -40,6 +40,12 @@ pub struct MichelsonChainConfig { pub chain_id: U256, } +impl MichelsonChainConfig { + pub fn create_config(chain_id: U256) -> Self { + Self { chain_id } + } +} + #[allow(clippy::large_enum_variant)] pub enum ChainConfig { Evm(EvmChainConfig), -- GitLab