diff --git a/etherlink/kernel_evm/kernel/src/apply.rs b/etherlink/kernel_evm/kernel/src/apply.rs index be5de92826336d5476d5b1b4cea9d61065382e93..a1b18a6f24acccf3a093b7546e1d5bedfa241abf 100644 --- a/etherlink/kernel_evm/kernel/src/apply.rs +++ b/etherlink/kernel_evm/kernel/src/apply.rs @@ -35,7 +35,7 @@ use tezos_smart_rollup_host::path::{Path, RefPath}; use crate::bridge::{execute_deposit, Deposit}; use crate::error::Error; use crate::fees::{tx_execution_gas_limit, FeeUpdates}; -use crate::inbox::{Transaction, TransactionContent}; +use crate::transaction::{Transaction, TransactionContent}; // This implementation of `Transaction` is used to share the logic of // transaction receipt and transaction object making. The functions diff --git a/etherlink/kernel_evm/kernel/src/block.rs b/etherlink/kernel_evm/kernel/src/block.rs index 1555f0bad9792dd578ca6a2e7b046547d4531eae..acee6f1899b9aa94e8c5914905250cd1e636ea06 100644 --- a/etherlink/kernel_evm/kernel/src/block.rs +++ b/etherlink/kernel_evm/kernel/src/block.rs @@ -17,8 +17,8 @@ use crate::configuration::ConfigurationMode; use crate::delayed_inbox::DelayedInbox; use crate::error::Error; use crate::event::Event; -use crate::inbox::Transaction; use crate::storage; +use crate::transaction::Transaction; use crate::upgrade; use crate::upgrade::KernelUpgrade; use crate::Configuration; @@ -620,14 +620,15 @@ mod tests { use crate::blueprint_storage::store_inbox_blueprint_by_number; use crate::fees::DA_FEE_PER_BYTE; use crate::fees::MINIMUM_BASE_FEE_PER_GAS; - use crate::inbox::Transaction; - use crate::inbox::TransactionContent; - use crate::inbox::TransactionContent::Ethereum; - use crate::inbox::TransactionContent::EthereumDelayed; use crate::storage::read_block_in_progress; use crate::storage::read_last_info_per_level_timestamp; use crate::storage::{read_transaction_receipt, read_transaction_receipt_status}; use crate::tick_model::constants::MAX_ALLOWED_TICKS; + use crate::transaction::Transaction; + use crate::transaction::TransactionContent; + use crate::transaction::TransactionContent::Ethereum; + use crate::transaction::TransactionContent::EthereumDelayed; + use crate::transaction::Transactions::EthTxs; use crate::{retrieve_block_fees, retrieve_chain_id}; use evm_execution::account_storage::{ account_path, init_account_storage, EthereumAccountStorage, @@ -647,7 +648,7 @@ mod tests { fn blueprint(transactions: Vec) -> Blueprint { Blueprint { - transactions, + transactions: EthTxs(transactions), timestamp: Timestamp::from(0i64), } } diff --git a/etherlink/kernel_evm/kernel/src/block_in_progress.rs b/etherlink/kernel_evm/kernel/src/block_in_progress.rs index 12a0e3ff898c3d87e71d2c112ca3cfe99db4be1b..126410a7c2707e39dfaa056ea4822769cddeb91a 100644 --- a/etherlink/kernel_evm/kernel/src/block_in_progress.rs +++ b/etherlink/kernel_evm/kernel/src/block_in_progress.rs @@ -10,9 +10,9 @@ use crate::block_storage; use crate::error::Error; use crate::error::TransferError::CumulativeGasUsedOverflow; use crate::gas_price::base_fee_per_gas; -use crate::inbox::Transaction; use crate::storage::{self, object_path, receipt_path}; use crate::tick_model; +use crate::transaction::{Transaction, Transactions::EthTxs, Transactions::TezTxs}; use anyhow::Context; use evm_execution::account_storage::EVM_ACCOUNTS_PATH; use primitive_types::{H160, H256, U256}; @@ -285,7 +285,14 @@ impl BlockInProgress { transactions_root: OwnedHash, ) -> BlockInProgress { // blueprint is turn into a ring to allow popping from the front - let ring = blueprint.transactions.into(); + let ring = match blueprint.transactions { + EthTxs(transactions) => transactions.into(), + TezTxs => + // This should never happen as we are in an Ethereum block in progress + { + VecDeque::new() + } + }; BlockInProgress::new_with_ticks( current_block_number, parent_hash, @@ -577,7 +584,7 @@ mod tests { use super::BlockInProgress; use crate::bridge::Deposit; - use crate::inbox::{Transaction, TransactionContent}; + use crate::transaction::{Transaction, TransactionContent}; use primitive_types::{H160, H256, U256}; use rlp::{Decodable, Encodable, Rlp}; use tezos_ethereum::{ diff --git a/etherlink/kernel_evm/kernel/src/blueprint.rs b/etherlink/kernel_evm/kernel/src/blueprint.rs index 4b5fd2a928e4074a7a54a10bce18d124ef8d1584..9f17143847a09ecda1ff713a12cfcd63387248ff 100644 --- a/etherlink/kernel_evm/kernel/src/blueprint.rs +++ b/etherlink/kernel_evm/kernel/src/blueprint.rs @@ -5,23 +5,25 @@ // // SPDX-License-Identifier: MIT -use crate::inbox::Transaction; +use crate::transaction::Transactions; use rlp::{Decodable, DecoderError, Encodable}; -use tezos_ethereum::rlp_helpers::{self, append_timestamp, decode_timestamp}; +use tezos_ethereum::rlp_helpers::{ + self, append_timestamp, decode_field, decode_timestamp, +}; use tezos_smart_rollup_encoding::timestamp::Timestamp; /// The blueprint of a block is a list of transactions. #[derive(PartialEq, Debug, Clone)] pub struct Blueprint { - pub transactions: Vec, + pub transactions: Transactions, pub timestamp: Timestamp, } impl Encodable for Blueprint { fn rlp_append(&self, stream: &mut rlp::RlpStream) { stream.begin_list(2); - stream.append_list(&self.transactions); + stream.append(&self.transactions); append_timestamp(stream, self.timestamp); } } @@ -36,8 +38,7 @@ impl Decodable for Blueprint { } let mut it = decoder.iter(); - let transactions = - rlp_helpers::decode_list(&rlp_helpers::next(&mut it)?, "transactions")?; + let transactions = decode_field(&rlp_helpers::next(&mut it)?, "transactions")?; let timestamp = decode_timestamp(&rlp_helpers::next(&mut it)?)?; Ok(Blueprint { @@ -51,7 +52,9 @@ impl Decodable for Blueprint { mod tests { use super::*; - use crate::inbox::TransactionContent::Ethereum; + use crate::transaction::{ + Transaction, TransactionContent::Ethereum, Transactions::EthTxs, + }; use primitive_types::{H160, U256}; use rlp::Rlp; use tezos_ethereum::{ @@ -88,7 +91,7 @@ mod tests { #[test] fn test_encode_blueprint() { let proposal = Blueprint { - transactions: vec![dummy_transaction(0), dummy_transaction(1)], + transactions: EthTxs(vec![dummy_transaction(0), dummy_transaction(1)]), timestamp: Timestamp::from(0i64), }; let encoded = proposal.rlp_bytes(); diff --git a/etherlink/kernel_evm/kernel/src/blueprint_storage.rs b/etherlink/kernel_evm/kernel/src/blueprint_storage.rs index 1df6f8bef9c025eca6bc787da29ce80f9d447293..d86e615dad2ac20cd789c7e4f5eb0dffe4efb5ca 100644 --- a/etherlink/kernel_evm/kernel/src/blueprint_storage.rs +++ b/etherlink/kernel_evm/kernel/src/blueprint_storage.rs @@ -6,11 +6,11 @@ use crate::blueprint::Blueprint; use crate::configuration::{Configuration, ConfigurationMode}; use crate::error::{Error, StorageError}; -use crate::inbox::{Transaction, TransactionContent}; use crate::sequencer_blueprint::{ BlueprintWithDelayedHashes, UnsignedSequencerBlueprint, }; use crate::storage::read_last_info_per_level_timestamp; +use crate::transaction::{Transaction, TransactionContent, Transactions::EthTxs}; use crate::{delayed_inbox, DelayedInbox}; use primitive_types::{H256, U256}; use rlp::{Decodable, DecoderError, Encodable}; @@ -381,7 +381,7 @@ fn fetch_delayed_txs( delayed_txs.extend(transactions_with_hashes); Ok(( BlueprintValidity::Valid(Blueprint { - transactions: delayed_txs, + transactions: EthTxs(delayed_txs), timestamp: blueprint_with_hashes.timestamp, }), total_size, @@ -540,7 +540,7 @@ fn read_all_chunks_and_validate( host, Benchmarking, "Number of transactions in blueprint: {}", - blueprint.transactions.len() + blueprint.transactions.number_of_txs() ); Ok((Some(blueprint), size_with_delayed_transactions)) } else { diff --git a/etherlink/kernel_evm/kernel/src/delayed_inbox.rs b/etherlink/kernel_evm/kernel/src/delayed_inbox.rs index 840236908060d47f567ec00402d408afe4c0c71e..1394ed34b9c2663a8b71cae500ce5fbaa047b671 100644 --- a/etherlink/kernel_evm/kernel/src/delayed_inbox.rs +++ b/etherlink/kernel_evm/kernel/src/delayed_inbox.rs @@ -4,9 +4,9 @@ use crate::{ bridge::Deposit, event::Event, - inbox::{Transaction, TransactionContent}, linked_list::LinkedList, storage::{self, read_last_info_per_level_timestamp}, + transaction::{Transaction, TransactionContent}, }; use anyhow::Result; use evm_execution::fa_bridge::deposit::FaDeposit; @@ -387,13 +387,13 @@ impl DelayedInbox { mod tests { use super::DelayedInbox; use super::Hash; - use crate::inbox::Transaction; use crate::storage::read_last_info_per_level_timestamp; + use crate::transaction::Transaction; use primitive_types::{H160, U256}; use tezos_evm_runtime::runtime::MockKernelHost; use tezos_smart_rollup_encoding::timestamp::Timestamp; - use crate::inbox::TransactionContent::{Ethereum, EthereumDelayed}; + use crate::transaction::TransactionContent::{Ethereum, EthereumDelayed}; use tezos_ethereum::{ transaction::TRANSACTION_HASH_SIZE, tx_common::EthereumTransactionCommon, }; diff --git a/etherlink/kernel_evm/kernel/src/event.rs b/etherlink/kernel_evm/kernel/src/event.rs index 4a4266307c3e34d0a012e76334fb592f3fc4c826..2d641c0c4a0863a7db785078fe4c359f9a6b81b1 100644 --- a/etherlink/kernel_evm/kernel/src/event.rs +++ b/etherlink/kernel_evm/kernel/src/event.rs @@ -3,7 +3,7 @@ // // SPDX-License-Identifier: MIT -use crate::{inbox::Transaction, storage, upgrade}; +use crate::{storage, transaction::Transaction, upgrade}; use primitive_types::{H256, U256}; use rlp::{Encodable, RlpStream}; use tezos_ethereum::rlp_helpers::{append_timestamp, append_u256_le}; diff --git a/etherlink/kernel_evm/kernel/src/evm_node_entrypoint.rs b/etherlink/kernel_evm/kernel/src/evm_node_entrypoint.rs index c2791fae5d405cf86ad7a2b336e555eec8de85a1..8488474170a4daf26cdb0fc4d71168b782a7274d 100644 --- a/etherlink/kernel_evm/kernel/src/evm_node_entrypoint.rs +++ b/etherlink/kernel_evm/kernel/src/evm_node_entrypoint.rs @@ -8,7 +8,7 @@ //! only. It allows to call specific functions of the kernel without //! using the inbox and a specific message. -use crate::{delayed_inbox::DelayedInbox, inbox::Transaction}; +use crate::{delayed_inbox::DelayedInbox, transaction::Transaction}; use tezos_ethereum::rlp_helpers::FromRlpBytes; use tezos_evm_runtime::runtime::KernelHost; use tezos_smart_rollup_core::rollup_host::RollupHost; diff --git a/etherlink/kernel_evm/kernel/src/fees.rs b/etherlink/kernel_evm/kernel/src/fees.rs index 7d4353a438f23a58cc98db6494e8b4afac33cc3e..f9e2574044187a01a2eb0927fc4b2d1bc30f09de 100644 --- a/etherlink/kernel_evm/kernel/src/fees.rs +++ b/etherlink/kernel_evm/kernel/src/fees.rs @@ -15,7 +15,7 @@ //! //! Additionally, we charge a _data-availability_ fee, for each tx posted through L1. -use crate::inbox::TransactionContent; +use crate::transaction::TransactionContent; use evm_execution::account_storage::{account_path, EthereumAccountStorage}; use evm_execution::handler::ExecutionOutcome; diff --git a/etherlink/kernel_evm/kernel/src/inbox.rs b/etherlink/kernel_evm/kernel/src/inbox.rs index 608add59a3d480ea8ababeb93d07f80e8d12fc62..ce43e9f76532aee4bd71755dfb757fa7e4598299 100644 --- a/etherlink/kernel_evm/kernel/src/inbox.rs +++ b/etherlink/kernel_evm/kernel/src/inbox.rs @@ -7,7 +7,7 @@ use crate::blueprint_storage::store_sequencer_blueprint; use crate::bridge::Deposit; -use crate::chains::ChainConfig; +use crate::chains::{ChainConfig, ChainFamily}; use crate::configuration::{DalConfiguration, TezosContracts}; use crate::dal::fetch_and_parse_sequencer_blueprint_from_dal; use crate::dal_slot_import_signal::DalSlotImportSignals; @@ -17,7 +17,6 @@ use crate::parsing::{ SequencerParsingContext, MAX_SIZE_PER_CHUNK, }; -use crate::fees::tx_execution_gas_limit; use crate::sequencer_blueprint::UnsignedSequencerBlueprint; use crate::storage::{ chunked_hash_transaction_path, chunked_transaction_num_chunks, @@ -25,176 +24,28 @@ use crate::storage::{ read_last_info_per_level_timestamp, remove_chunked_transaction, remove_sequencer, store_l1_level, store_last_info_per_level_timestamp, store_transaction_chunk, }; -use crate::tick_model::constants::{BASE_GAS, TICKS_FOR_BLUEPRINT_INTERCEPT}; +use crate::tick_model::constants::TICKS_FOR_BLUEPRINT_INTERCEPT; use crate::tick_model::maximum_ticks_for_sequencer_chunk; +use crate::transaction::{ + Transaction, TransactionContent, Transactions, + Transactions::{EthTxs, TezTxs}, +}; use crate::upgrade::*; use crate::Error; use crate::{simulation, upgrade}; -use evm_execution::fa_bridge::{deposit::FaDeposit, FA_DEPOSIT_PROXY_GAS_LIMIT}; -use evm_execution::EthereumError; +use evm_execution::fa_bridge::deposit::FaDeposit; use primitive_types::U256; -use rlp::{Decodable, DecoderError, Encodable}; use sha3::{Digest, Keccak256}; use tezos_crypto_rs::hash::ContractKt1Hash; -use tezos_ethereum::block::BlockFees; -use tezos_ethereum::rlp_helpers::{decode_field, decode_tx_hash, next}; -use tezos_ethereum::transaction::{ - TransactionHash, TransactionType, TRANSACTION_HASH_SIZE, -}; +use tezos_ethereum::transaction::{TransactionHash, TRANSACTION_HASH_SIZE}; use tezos_ethereum::tx_common::EthereumTransactionCommon; use tezos_evm_logging::{log, Level::*}; use tezos_evm_runtime::runtime::Runtime; use tezos_smart_rollup_encoding::public_key::PublicKey; -#[allow(clippy::large_enum_variant)] -#[derive(Debug, PartialEq, Clone)] -pub enum TransactionContent { - Ethereum(EthereumTransactionCommon), - Deposit(Deposit), - EthereumDelayed(EthereumTransactionCommon), - FaDeposit(FaDeposit), -} - -const ETHEREUM_TX_TAG: u8 = 1; -const DEPOSIT_TX_TAG: u8 = 2; -const ETHEREUM_DELAYED_TX_TAG: u8 = 3; -const FA_DEPOSIT_TX_TAG: u8 = 4; - -impl Encodable for TransactionContent { - fn rlp_append(&self, stream: &mut rlp::RlpStream) { - stream.begin_list(2); - match &self { - TransactionContent::Ethereum(eth) => { - stream.append(ÐEREUM_TX_TAG); - eth.rlp_append(stream) - } - TransactionContent::Deposit(dep) => { - stream.append(&DEPOSIT_TX_TAG); - dep.rlp_append(stream) - } - TransactionContent::EthereumDelayed(eth) => { - stream.append(ÐEREUM_DELAYED_TX_TAG); - eth.rlp_append(stream) - } - TransactionContent::FaDeposit(fa_dep) => { - stream.append(&FA_DEPOSIT_TX_TAG); - fa_dep.rlp_append(stream) - } - } - } -} - -impl Decodable for TransactionContent { - fn decode(decoder: &rlp::Rlp) -> Result { - if !decoder.is_list() { - return Err(DecoderError::RlpExpectedToBeList); - } - if decoder.item_count()? != 2 { - return Err(DecoderError::RlpIncorrectListLen); - } - let tag: u8 = decoder.at(0)?.as_val()?; - let tx = decoder.at(1)?; - match tag { - DEPOSIT_TX_TAG => { - let deposit = Deposit::decode(&tx)?; - Ok(Self::Deposit(deposit)) - } - ETHEREUM_TX_TAG => { - let eth = EthereumTransactionCommon::decode(&tx)?; - Ok(Self::Ethereum(eth)) - } - ETHEREUM_DELAYED_TX_TAG => { - let eth = EthereumTransactionCommon::decode(&tx)?; - Ok(Self::EthereumDelayed(eth)) - } - FA_DEPOSIT_TX_TAG => { - let fa_deposit = FaDeposit::decode(&tx)?; - Ok(Self::FaDeposit(fa_deposit)) - } - _ => Err(DecoderError::Custom("Unknown transaction tag.")), - } - } -} - -#[derive(Debug, PartialEq, Clone)] -pub struct Transaction { - pub tx_hash: TransactionHash, - pub content: TransactionContent, -} - -impl Transaction { - pub fn data_size(&self) -> u64 { - match &self.content { - TransactionContent::Deposit(_) => 0, - TransactionContent::Ethereum(e) | TransactionContent::EthereumDelayed(e) => { - // FIXME: probably need to take into account the access list - e.data.len() as u64 - } - TransactionContent::FaDeposit(_) => 0, - } - } - - pub fn is_delayed(&self) -> bool { - match &self.content { - TransactionContent::Deposit(_) - | TransactionContent::EthereumDelayed(_) - | TransactionContent::FaDeposit(_) => true, - TransactionContent::Ethereum(_) => false, - } - } - - pub fn execution_gas_limit(&self, fees: &BlockFees) -> Result { - match &self.content { - TransactionContent::Deposit(_) => Ok(BASE_GAS), - TransactionContent::Ethereum(e) => tx_execution_gas_limit(e, fees, false), - TransactionContent::EthereumDelayed(e) => { - tx_execution_gas_limit(e, fees, true) - } - TransactionContent::FaDeposit(_) => Ok(FA_DEPOSIT_PROXY_GAS_LIMIT), - } - } -} - -impl Encodable for Transaction { - fn rlp_append(&self, stream: &mut rlp::RlpStream) { - stream.begin_list(2); - stream.append_iter(self.tx_hash); - stream.append(&self.content); - } -} - -impl Decodable for Transaction { - fn decode(decoder: &rlp::Rlp) -> Result { - if !decoder.is_list() { - return Err(DecoderError::RlpExpectedToBeList); - } - if decoder.item_count()? != 2 { - return Err(DecoderError::RlpIncorrectListLen); - } - let mut it = decoder.iter(); - let tx_hash: TransactionHash = decode_tx_hash(next(&mut it)?)?; - let content: TransactionContent = - decode_field(&next(&mut it)?, "Transaction content")?; - Ok(Transaction { tx_hash, content }) - } -} - -impl Transaction { - pub fn type_(&self) -> TransactionType { - match &self.content { - // The deposit is considered arbitrarily as a legacy transaction - TransactionContent::Deposit(_) | TransactionContent::FaDeposit(_) => { - TransactionType::Legacy - } - TransactionContent::Ethereum(tx) - | TransactionContent::EthereumDelayed(tx) => tx.type_, - } - } -} - #[derive(Debug, PartialEq)] pub struct ProxyInboxContent { - pub transactions: Vec, + pub transactions: Transactions, } pub fn read_input( @@ -626,9 +477,11 @@ pub fn read_proxy_inbox( garbage_collect_blocks: bool, chain_configuration: &ChainConfig, ) -> Result, anyhow::Error> { - let mut res = ProxyInboxContent { - transactions: vec![], + let transactions = match ChainConfig::get_chain_family(chain_configuration) { + ChainFamily::Evm => EthTxs(vec![]), + ChainFamily::Michelson => TezTxs, }; + let mut res = ProxyInboxContent { transactions }; // The mutable variable is used to retrieve the information of whether the // inbox was empty or not. As we consume all the inbox in one go, if the // variable remains true, that means that the inbox was already consumed @@ -778,11 +631,12 @@ mod tests { use crate::dal_slot_import_signal::{ DalSlotIndicesList, DalSlotIndicesOfLevel, UnsignedDalSlotSignals, }; - use crate::inbox::TransactionContent::Ethereum; use crate::parsing::RollupType; use crate::storage::*; use crate::tick_model::constants::MAX_ALLOWED_TICKS; + use crate::transaction::TransactionContent::Ethereum; use primitive_types::U256; + use rlp::Encodable; use std::fmt::Write; use tezos_crypto_rs::hash::SmartRollupHash; use tezos_crypto_rs::hash::UnknownSignature; @@ -936,7 +790,7 @@ mod tests { tx_hash, content: Ethereum(tx), }]; - assert_eq!(inbox_content.transactions, expected_transactions); + assert_eq!(inbox_content.transactions, EthTxs(expected_transactions)); } #[test] @@ -968,7 +822,7 @@ mod tests { tx_hash, content: Ethereum(tx), }]; - assert_eq!(inbox_content.transactions, expected_transactions); + assert_eq!(inbox_content.transactions, EthTxs(expected_transactions)); } #[test] @@ -1220,7 +1074,7 @@ mod tests { assert_eq!( inbox_content, ProxyInboxContent { - transactions: vec![], + transactions: EthTxs(vec![]), } ); @@ -1244,7 +1098,7 @@ mod tests { tx_hash, content: Ethereum(tx), }]; - assert_eq!(inbox_content.transactions, expected_transactions); + assert_eq!(inbox_content.transactions, EthTxs(expected_transactions)); } #[test] @@ -1306,7 +1160,7 @@ mod tests { tx_hash, content: Ethereum(tx), }]; - assert_eq!(inbox_content.transactions, expected_transactions); + assert_eq!(inbox_content.transactions, EthTxs(expected_transactions)); } #[test] diff --git a/etherlink/kernel_evm/kernel/src/lib.rs b/etherlink/kernel_evm/kernel/src/lib.rs index 6eefbba8977a335b48bc71194956dfb8524d1681..cff1eab4fb745221d5bc73bd00adfe5756ca1059 100644 --- a/etherlink/kernel_evm/kernel/src/lib.rs +++ b/etherlink/kernel_evm/kernel/src/lib.rs @@ -64,6 +64,7 @@ mod simulation; mod stage_one; mod storage; mod tick_model; +mod transaction; mod upgrade; extern crate alloc; @@ -379,7 +380,7 @@ mod tests { }; use crate::{ blueprint::Blueprint, - inbox::{Transaction, TransactionContent}, + transaction::{Transaction, TransactionContent}, upgrade::KernelUpgrade, }; use evm::Config; @@ -406,6 +407,7 @@ mod tests { }; use tezos_evm_runtime::runtime::MockKernelHost; + use crate::transaction::Transactions::EthTxs; use tezos_evm_runtime::runtime::Runtime; use tezos_smart_rollup::michelson::ticket::FA2_1Ticket; use tezos_smart_rollup::michelson::{ @@ -481,7 +483,7 @@ mod tests { fn blueprint(transactions: Vec) -> Blueprint { Blueprint { - transactions, + transactions: EthTxs(transactions), timestamp: Timestamp::from(0i64), } } diff --git a/etherlink/kernel_evm/kernel/src/parsing.rs b/etherlink/kernel_evm/kernel/src/parsing.rs index da53f5c4da469e809730a75dccedbccc40c81af9..9d9d0bf09325f9a379426e053ab4a76f7d8e920d 100644 --- a/etherlink/kernel_evm/kernel/src/parsing.rs +++ b/etherlink/kernel_evm/kernel/src/parsing.rs @@ -14,8 +14,8 @@ use crate::tick_model::constants::{ use crate::{ bridge::Deposit, dal_slot_import_signal::DalSlotImportSignals, - inbox::{Transaction, TransactionContent}, sequencer_blueprint::{SequencerBlueprint, UnsignedSequencerBlueprint}, + transaction::{Transaction, TransactionContent}, upgrade::KernelUpgrade, upgrade::SequencerUpgrade, }; diff --git a/etherlink/kernel_evm/kernel/src/sequencer_blueprint.rs b/etherlink/kernel_evm/kernel/src/sequencer_blueprint.rs index bba541f59d9a6ea56483f67b58b9e8d25fc25a87..0f1c2b3d7c4ba7b0fc1c4e7712290edcbd13889a 100644 --- a/etherlink/kernel_evm/kernel/src/sequencer_blueprint.rs +++ b/etherlink/kernel_evm/kernel/src/sequencer_blueprint.rs @@ -254,8 +254,9 @@ pub fn rlp_roundtrip(v: mod tests { use super::{rlp_roundtrip, SequencerBlueprint, UnsignedSequencerBlueprint}; use crate::blueprint::Blueprint; - use crate::inbox::Transaction; - use crate::inbox::TransactionContent::Ethereum; + use crate::transaction::Transaction; + use crate::transaction::TransactionContent::Ethereum; + use crate::transaction::Transactions::EthTxs; use primitive_types::{H160, U256}; use tezos_crypto_rs::hash::UnknownSignature; use tezos_ethereum::{ @@ -292,7 +293,7 @@ mod tests { } fn dummy_blueprint_unsigned(chain_id: Option) -> UnsignedSequencerBlueprint { - let transactions = vec![dummy_transaction(0), dummy_transaction(1)]; + let transactions = EthTxs(vec![dummy_transaction(0), dummy_transaction(1)]); let timestamp = Timestamp::from(42); let blueprint = Blueprint { timestamp, diff --git a/etherlink/kernel_evm/kernel/src/stage_one.rs b/etherlink/kernel_evm/kernel/src/stage_one.rs index 86f89e6a803321d9a55174dc2aea362cff8b81e3..77125efc48689ef85a1cc654517a43645a00949a 100644 --- a/etherlink/kernel_evm/kernel/src/stage_one.rs +++ b/etherlink/kernel_evm/kernel/src/stage_one.rs @@ -17,6 +17,7 @@ use crate::event::Event; use crate::inbox::{read_proxy_inbox, read_sequencer_inbox}; use crate::inbox::{ProxyInboxContent, StageOneStatus}; use crate::storage::read_last_info_per_level_timestamp; +use crate::transaction::Transactions::EthTxs; use anyhow::Ok; use std::ops::Add; use tezos_crypto_rs::hash::ContractKt1Hash; @@ -104,7 +105,7 @@ fn fetch_delayed_transactions( // Create a new blueprint with the timed out transactions let blueprint = Blueprint { - transactions: timed_out, + transactions: EthTxs(timed_out), timestamp, }; // Store the blueprint. @@ -411,7 +412,9 @@ mod tests { .expect("Blueprint reading shouldn't fail") .0 { - Some(Blueprint { transactions, .. }) => assert!(transactions.len() == 1), + Some(Blueprint { transactions, .. }) => { + assert!(transactions.number_of_txs() == 1) + } _ => panic!("There should be a blueprint"), } } @@ -432,7 +435,9 @@ mod tests { .expect("Blueprint reading shouldn't fail") .0 { - Some(Blueprint { transactions, .. }) => assert!(transactions.len() == 1), + Some(Blueprint { transactions, .. }) => { + assert!(transactions.number_of_txs() == 1) + } _ => panic!("There should be a blueprint"), } } @@ -574,7 +579,7 @@ mod tests { None => panic!("There should be an InboxContent"), Some(ProxyInboxContent { transactions, .. }) => assert_eq!( transactions, - vec![], + EthTxs(vec![]), "The proxy shouldn't have read any transaction" ), }; @@ -688,7 +693,7 @@ mod tests { { None => panic!("There should be a blueprint"), Some(Blueprint { transactions, .. }) => - assert_eq!(transactions.len(), 0, + assert_eq!(transactions.number_of_txs(), 0, "The transaction from the delayed bridge entrypoint should have been rejected in proxy mode"), } } @@ -713,7 +718,7 @@ mod tests { { None => panic!("There should be a blueprint"), Some(Blueprint { transactions, .. }) => assert_eq!( - transactions.len(), + transactions.number_of_txs(), 1, "The deposit should have been picked in the blueprint" ), @@ -742,7 +747,7 @@ mod tests { { None => panic!("There should be a blueprint"), Some(Blueprint { transactions, .. }) => assert_eq!( - transactions.len(), + transactions.number_of_txs(), 0, "The deposit shouldn't have been picked in the blueprint as it is invalid" ), diff --git a/etherlink/kernel_evm/kernel/src/tick_model.rs b/etherlink/kernel_evm/kernel/src/tick_model.rs index 098fee6b3158cb86e299464285fb93cb07e104c8..3f523d46e2b699e1e4530731189ce1454eb908a2 100644 --- a/etherlink/kernel_evm/kernel/src/tick_model.rs +++ b/etherlink/kernel_evm/kernel/src/tick_model.rs @@ -4,7 +4,7 @@ use tezos_ethereum::transaction::IndexedLog; -use crate::inbox::Transaction; +use crate::transaction::Transaction; use self::constants::TICKS_FOR_CRYPTO; @@ -131,7 +131,7 @@ pub fn ticks_of_valid_transaction( transaction: &Transaction, resulting_ticks: u64, ) -> u64 { - use crate::inbox::TransactionContent::*; + use crate::transaction::TransactionContent::*; match &transaction.content { Ethereum(_) | EthereumDelayed(_) => { diff --git a/etherlink/kernel_evm/kernel/src/transaction.rs b/etherlink/kernel_evm/kernel/src/transaction.rs new file mode 100644 index 0000000000000000000000000000000000000000..de228d038fd127192027f0d8b4b49f8017d67c03 --- /dev/null +++ b/etherlink/kernel_evm/kernel/src/transaction.rs @@ -0,0 +1,206 @@ +// SPDX-FileCopyrightText: 2022-2024 TriliTech +// SPDX-FileCopyrightText: 2023 Nomadic Labs +// SPDX-FileCopyrightText: 2023-2025 Functori +// SPDX-FileCopyrightText: 2023 Marigold +// +// SPDX-License-Identifier: MIT + +use crate::bridge::Deposit; +use crate::fees::tx_execution_gas_limit; + +use crate::tick_model::constants::BASE_GAS; +use evm_execution::fa_bridge::{deposit::FaDeposit, FA_DEPOSIT_PROXY_GAS_LIMIT}; +use evm_execution::EthereumError; +use rlp::{Decodable, DecoderError, Encodable}; +use tezos_ethereum::block::BlockFees; +use tezos_ethereum::rlp_helpers::{self, decode_field, decode_tx_hash, next}; +use tezos_ethereum::transaction::{TransactionHash, TransactionType}; +use tezos_ethereum::tx_common::EthereumTransactionCommon; + +#[allow(clippy::large_enum_variant)] +#[derive(Debug, PartialEq, Clone)] +pub enum TransactionContent { + Ethereum(EthereumTransactionCommon), + Deposit(Deposit), + EthereumDelayed(EthereumTransactionCommon), + FaDeposit(FaDeposit), +} + +const ETHEREUM_TX_TAG: u8 = 1; +const DEPOSIT_TX_TAG: u8 = 2; +const ETHEREUM_DELAYED_TX_TAG: u8 = 3; +const FA_DEPOSIT_TX_TAG: u8 = 4; + +impl Encodable for TransactionContent { + fn rlp_append(&self, stream: &mut rlp::RlpStream) { + stream.begin_list(2); + match &self { + TransactionContent::Ethereum(eth) => { + stream.append(ÐEREUM_TX_TAG); + eth.rlp_append(stream) + } + TransactionContent::Deposit(dep) => { + stream.append(&DEPOSIT_TX_TAG); + dep.rlp_append(stream) + } + TransactionContent::EthereumDelayed(eth) => { + stream.append(ÐEREUM_DELAYED_TX_TAG); + eth.rlp_append(stream) + } + TransactionContent::FaDeposit(fa_dep) => { + stream.append(&FA_DEPOSIT_TX_TAG); + fa_dep.rlp_append(stream) + } + } + } +} + +impl Decodable for TransactionContent { + fn decode(decoder: &rlp::Rlp) -> Result { + if !decoder.is_list() { + return Err(DecoderError::RlpExpectedToBeList); + } + if decoder.item_count()? != 2 { + return Err(DecoderError::RlpIncorrectListLen); + } + let tag: u8 = decoder.at(0)?.as_val()?; + let tx = decoder.at(1)?; + match tag { + DEPOSIT_TX_TAG => { + let deposit = Deposit::decode(&tx)?; + Ok(Self::Deposit(deposit)) + } + ETHEREUM_TX_TAG => { + let eth = EthereumTransactionCommon::decode(&tx)?; + Ok(Self::Ethereum(eth)) + } + ETHEREUM_DELAYED_TX_TAG => { + let eth = EthereumTransactionCommon::decode(&tx)?; + Ok(Self::EthereumDelayed(eth)) + } + FA_DEPOSIT_TX_TAG => { + let fa_deposit = FaDeposit::decode(&tx)?; + Ok(Self::FaDeposit(fa_deposit)) + } + _ => Err(DecoderError::Custom("Unknown transaction tag.")), + } + } +} + +#[derive(PartialEq, Debug, Clone)] +pub enum Transactions { + EthTxs(Vec), + TezTxs, +} + +impl Transactions { + pub fn number_of_txs(&self) -> usize { + match self { + Self::EthTxs(transactions) => transactions.len(), + Self::TezTxs => 0, + } + } + + pub fn push(&mut self, tx: Transaction) { + match self { + Self::EthTxs(transactions) => transactions.push(tx), + Self::TezTxs => (), + } + } +} + +impl Encodable for Transactions { + fn rlp_append(&self, s: &mut rlp::RlpStream) { + match self { + Self::EthTxs(transactions) => { + s.append_list(transactions); + } + Self::TezTxs => { + s.append_list::(&[]); + } + } + } +} + +impl Decodable for Transactions { + fn decode(rlp: &rlp::Rlp) -> Result { + let transactions = rlp_helpers::decode_list(rlp, "transactions")?; + Ok(Self::EthTxs(transactions)) + } +} + +#[derive(Debug, PartialEq, Clone)] +pub struct Transaction { + pub tx_hash: TransactionHash, + pub content: TransactionContent, +} + +impl Transaction { + pub fn data_size(&self) -> u64 { + match &self.content { + TransactionContent::Deposit(_) => 0, + TransactionContent::Ethereum(e) | TransactionContent::EthereumDelayed(e) => { + // FIXME: probably need to take into account the access list + e.data.len() as u64 + } + TransactionContent::FaDeposit(_) => 0, + } + } + + pub fn is_delayed(&self) -> bool { + match &self.content { + TransactionContent::Deposit(_) + | TransactionContent::EthereumDelayed(_) + | TransactionContent::FaDeposit(_) => true, + TransactionContent::Ethereum(_) => false, + } + } + + pub fn execution_gas_limit(&self, fees: &BlockFees) -> Result { + match &self.content { + TransactionContent::Deposit(_) => Ok(BASE_GAS), + TransactionContent::Ethereum(e) => tx_execution_gas_limit(e, fees, false), + TransactionContent::EthereumDelayed(e) => { + tx_execution_gas_limit(e, fees, true) + } + TransactionContent::FaDeposit(_) => Ok(FA_DEPOSIT_PROXY_GAS_LIMIT), + } + } +} + +impl Encodable for Transaction { + fn rlp_append(&self, stream: &mut rlp::RlpStream) { + stream.begin_list(2); + stream.append_iter(self.tx_hash); + stream.append(&self.content); + } +} + +impl Decodable for Transaction { + fn decode(decoder: &rlp::Rlp) -> Result { + if !decoder.is_list() { + return Err(DecoderError::RlpExpectedToBeList); + } + if decoder.item_count()? != 2 { + return Err(DecoderError::RlpIncorrectListLen); + } + let mut it = decoder.iter(); + let tx_hash: TransactionHash = decode_tx_hash(next(&mut it)?)?; + let content: TransactionContent = + decode_field(&next(&mut it)?, "Transaction content")?; + Ok(Transaction { tx_hash, content }) + } +} + +impl Transaction { + pub fn type_(&self) -> TransactionType { + match &self.content { + // The deposit is considered arbitrarily as a legacy transaction + TransactionContent::Deposit(_) | TransactionContent::FaDeposit(_) => { + TransactionType::Legacy + } + TransactionContent::Ethereum(tx) + | TransactionContent::EthereumDelayed(tx) => tx.type_, + } + } +}