diff --git a/crates/evm/Cargo.toml b/crates/evm/Cargo.toml index 063c19cb..58aa610c 100644 --- a/crates/evm/Cargo.toml +++ b/crates/evm/Cargo.toml @@ -11,26 +11,17 @@ repository.workspace = true [dependencies] signet-extract.workspace = true signet-types.workspace = true +signet-zenith.workspace = true trevm = { workspace = true, features = ["secp256r1"] } -signet-zenith.workspace = true alloy.workspace = true -reth.workspace = true thiserror.workspace = true tracing.workspace = true [dev-dependencies] -reth-chainspec.workspace = true -reth-db.workspace = true -reth-db-common.workspace = true - trevm = { workspace = true, features = ["test-utils"] } -signet-types = { workspace = true, features = ["test-utils"] } -signet-extract = { workspace = true, features = ["test-utils"] } - [features] default = [] -test-utils = ["signet-types/test-utils"] diff --git a/crates/evm/src/convert.rs b/crates/evm/src/convert.rs deleted file mode 100644 index 678fd0e8..00000000 --- a/crates/evm/src/convert.rs +++ /dev/null @@ -1,218 +0,0 @@ -//! Bespoke conversion utilities for converting between alloy and reth types. - -use alloy::{ - consensus::{ReceiptEnvelope, TxEip1559}, - primitives::{Address, Signature, U256}, - sol_types::SolCall, -}; -use reth::{ - primitives::{Transaction, TransactionSigned}, - revm::context::TransactTo, -}; -use signet_extract::ExtractedEvent; -use signet_types::{MagicSig, MagicSigInfo}; -use signet_zenith::{Passage, Transactor}; -use trevm::{revm::context::TxEnv, Tx}; - -/// This is the default minimum gas cost for a transaction, used by Ethereum -/// for simple sends to accounts without code. -pub(crate) const BASE_TX_GAS_COST: u64 = 21_000; - -/// Utility trait to convert a type to a Reth primitive type. -/// This is used mainly where we need to convert to a reth primitive type -/// because reth does not support the alloy equivalents. -pub trait ToRethPrimitive { - /// The Reth primitive type that the type can be converted to. - type RethPrimitive; - - /// Convert the type to a Reth primitive type. - fn to_reth(self) -> Self::RethPrimitive; -} - -/// Contains information necessary to produce a [`TransactionSigned`] for the -/// extracted [`Transactor::Transact`] event. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) struct Transact<'a, 'b> { - /// The extracted event for the transact event. - pub transact: &'a ExtractedEvent<'b, Transactor::Transact>, - /// The nonce of the transaction. - pub nonce: u64, -} - -impl Transact<'_, '_> { - /// Get the magic signature for the transact event, containing sender - /// information. - pub(crate) fn magic_sig(&self) -> MagicSig { - MagicSig { - ty: MagicSigInfo::Transact { sender: self.transact.sender() }, - txid: self.transact.tx_hash(), - event_idx: self.transact.log_index, - } - } - - /// Get the reth transaction signature for the transact event. - pub(crate) fn signature(&self) -> Signature { - self.magic_sig().into() - } -} - -impl ToRethPrimitive for Transact<'_, '_> { - type RethPrimitive = TransactionSigned; - - fn to_reth(self) -> Self::RethPrimitive { - TransactionSigned::new_unhashed( - Transaction::Eip1559(TxEip1559 { - chain_id: self.transact.rollup_chain_id(), - nonce: self.nonce, - gas_limit: self.transact.gas.to::(), - max_fee_per_gas: self.transact.maxFeePerGas.to::(), - max_priority_fee_per_gas: 0, - to: self.transact.to.into(), - value: self.transact.value, - access_list: Default::default(), - input: self.transact.data.clone(), - }), - self.signature(), - ) - } -} - -/// Contains information necessary to produce a [`TransactionSigned`] for the -/// extracted [`Passage::Enter`] event. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) struct Enter<'a, 'b> { - /// The extracted event for the enter event. - pub enter: &'a ExtractedEvent<'b, Passage::Enter>, - /// The nonce of the transaction. - pub nonce: u64, -} - -impl Enter<'_, '_> { - /// Get the magic signature for the enter event. - pub(crate) fn magic_sig(&self) -> MagicSig { - MagicSig { - ty: MagicSigInfo::Enter, - txid: self.enter.tx_hash(), - event_idx: self.enter.log_index, - } - } - - /// Get the reth transaction signature for the enter event. - pub(crate) fn signature(&self) -> Signature { - self.magic_sig().into() - } -} - -impl ToRethPrimitive for Enter<'_, '_> { - type RethPrimitive = TransactionSigned; - - fn to_reth(self) -> Self::RethPrimitive { - TransactionSigned::new_unhashed( - Transaction::Eip1559(TxEip1559 { - chain_id: self.enter.rollup_chain_id(), - nonce: self.nonce, - gas_limit: BASE_TX_GAS_COST, - max_fee_per_gas: 0, - max_priority_fee_per_gas: 0, - to: self.enter.rollupRecipient.into(), - value: self.enter.amount, - access_list: Default::default(), - input: Default::default(), - }), - self.signature(), - ) - } -} - -/// Contains information necessary to produce a [`TransactionSigned`] for the -/// extracted [`Passage::EnterToken`] event. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) struct EnterToken<'a, 'b> { - /// The extracted event for the enter token event. - pub enter_token: &'a ExtractedEvent<'b, Passage::EnterToken>, - /// The nonce of the transaction. - pub nonce: u64, - /// The address of the token being minted. - pub token: Address, -} - -impl EnterToken<'_, '_> { - /// Get the magic signature for the enter token event. - pub(crate) fn magic_sig(&self) -> MagicSig { - MagicSig { - ty: MagicSigInfo::EnterToken, - txid: self.enter_token.tx_hash(), - event_idx: self.enter_token.log_index, - } - } - - /// Get the reth transaction signature for the enter token event. - pub(crate) fn signature(&self) -> Signature { - self.magic_sig().into() - } -} - -impl Tx for EnterToken<'_, '_> { - fn fill_tx_env(&self, tx_env: &mut TxEnv) { - self.enter_token.fill_tx_env(tx_env); - tx_env.kind = TransactTo::Call(self.token); - tx_env.nonce = self.nonce; - } -} - -impl ToRethPrimitive for EnterToken<'_, '_> { - type RethPrimitive = TransactionSigned; - - fn to_reth(self) -> Self::RethPrimitive { - let input = signet_zenith::mintCall { - amount: self.enter_token.amount(), - to: self.enter_token.rollupRecipient, - } - .abi_encode() - .into(); - - TransactionSigned::new_unhashed( - Transaction::Eip1559(TxEip1559 { - chain_id: self.enter_token.rollup_chain_id(), - nonce: self.nonce, - gas_limit: BASE_TX_GAS_COST, - max_fee_per_gas: 0, - max_priority_fee_per_gas: 0, - // NB: set to the address of the token contract. - to: self.token.into(), - value: U256::ZERO, - access_list: Default::default(), - input, // NB: set to the ABI-encoded input for the `mint` function, which dictates the amount and recipient. - }), - self.signature(), - ) - } -} - -// Reth does not preserve envelope status for receipts, so -// the DB model will not support envelopes. -impl ToRethPrimitive for ReceiptEnvelope { - type RethPrimitive = reth::primitives::Receipt; - - fn to_reth(self) -> Self::RethPrimitive { - let success = self.is_success(); - let cumulative_gas_used = self.cumulative_gas_used(); - let tx_type = match self.tx_type() { - alloy::consensus::TxType::Legacy => reth::primitives::TxType::Legacy, - alloy::consensus::TxType::Eip2930 => reth::primitives::TxType::Eip2930, - alloy::consensus::TxType::Eip1559 => reth::primitives::TxType::Eip1559, - alloy::consensus::TxType::Eip4844 => reth::primitives::TxType::Eip4844, - alloy::consensus::TxType::Eip7702 => reth::primitives::TxType::Eip7702, - }; - - let r = match self { - ReceiptEnvelope::Legacy(r) - | ReceiptEnvelope::Eip2930(r) - | ReceiptEnvelope::Eip1559(r) - | ReceiptEnvelope::Eip4844(r) => r, - _ => panic!("unsupported receipt type"), - }; - - reth::primitives::Receipt { tx_type, success, cumulative_gas_used, logs: r.receipt.logs } - } -} diff --git a/crates/evm/src/driver.rs b/crates/evm/src/driver.rs index 3a0bdfb9..82e26903 100644 --- a/crates/evm/src/driver.rs +++ b/crates/evm/src/driver.rs @@ -1,20 +1,21 @@ use crate::{ - convert::{Enter, EnterToken, Transact}, - orders::SignetInspector, - BlockResult, EvmNeedsTx, EvmTransacted, RunTxResult, SignetLayered, ToRethPrimitive, BASE_GAS, + orders::SignetInspector, BlockResult, EvmNeedsTx, EvmTransacted, ExecutionOutcome, RunTxResult, + SignetLayered, BASE_GAS, }; use alloy::{ - consensus::{Header, ReceiptEnvelope, Transaction as _, TxType}, + consensus::{ + transaction::SignerRecoverable, BlockHeader, Header, ReceiptEnvelope, Transaction as _, + TxType, + }, eips::eip1559::{BaseFeeParams, INITIAL_BASE_FEE as EIP1559_INITIAL_BASE_FEE}, primitives::{Address, Bloom, U256}, }; -use reth::{ - core::primitives::SignerRecoverable, - primitives::{Block, BlockBody, Receipt, RecoveredBlock, SealedHeader, TransactionSigned}, - providers::ExecutionOutcome, +use signet_extract::{Extractable, ExtractedEvent, Extracts}; +use signet_types::{ + constants::SignetSystemConstants, + primitives::{BlockBody, RecoveredBlock, SealedBlock, SealedHeader, TransactionSigned}, + AggregateFills, MarketError, }; -use signet_extract::{ExtractedEvent, Extracts}; -use signet_types::{constants::SignetSystemConstants, AggregateFills, MarketError}; use signet_zenith::{Passage, Transactor, MINTER_ADDRESS}; use std::collections::{HashSet, VecDeque}; use tracing::{debug, debug_span, warn}; @@ -68,6 +69,41 @@ macro_rules! run_tx_early_return { }; } +/// Shim to impl [`Tx`] for [`Passage::EnterToken`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct EnterTokenFiller<'a, 'b, R> { + /// The extracted event for the enter token event. + pub enter_token: &'a ExtractedEvent<'b, R, Passage::EnterToken>, + /// The nonce of the transaction. + pub nonce: u64, + /// The address of the token being minted. + pub token: Address, +} + +impl Tx for EnterTokenFiller<'_, '_, R> { + fn fill_tx_env(&self, tx_env: &mut TxEnv) { + self.enter_token.event.fill_tx_env(tx_env); + tx_env.kind = TransactTo::Call(self.token); + tx_env.nonce = self.nonce; + } +} + +/// Shim to impl [`Tx`] for [`Transactor::Transact`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct TransactFiller<'a, 'b, R> { + /// The extracted event for the transact event. + pub transact: &'a ExtractedEvent<'b, R, Transactor::Transact>, + /// The nonce of the transaction. + pub nonce: u64, +} + +impl Tx for TransactFiller<'_, '_, R> { + fn fill_tx_env(&self, tx_env: &mut TxEnv) { + self.transact.event.fill_tx_env(tx_env); + tx_env.nonce = self.nonce; + } +} + /// Used internally to signal that the transaction should be discarded. enum ControlFlow where @@ -227,9 +263,9 @@ impl Tx for FillShim<'_> { /// A driver for the Signet EVM #[derive(Debug)] -pub struct SignetDriver<'a, 'b> { +pub struct SignetDriver<'a, 'b, C: Extractable> { /// The block extracts. - extracts: &'a Extracts<'b>, + extracts: &'a Extracts<'b, C>, /// Parent rollup block. parent: SealedHeader, @@ -254,10 +290,10 @@ pub struct SignetDriver<'a, 'b> { payable_gas_used: u64, } -impl<'a, 'b> SignetDriver<'a, 'b> { +impl<'a, 'b, C: Extractable> SignetDriver<'a, 'b, C> { /// Create a new driver. pub fn new( - extracts: &'a Extracts<'b>, + extracts: &'a Extracts<'b, C>, to_process: VecDeque, parent: SealedHeader, constants: SignetSystemConstants, @@ -279,7 +315,7 @@ impl<'a, 'b> SignetDriver<'a, 'b> { } /// Get the extracts being executed by the driver. - pub const fn extracts(&self) -> &Extracts<'b> { + pub const fn extracts(&self) -> &Extracts<'b, C> { self.extracts } @@ -300,7 +336,7 @@ impl<'a, 'b> SignetDriver<'a, 'b> { /// beneficiary of the current block. pub fn beneficiary(&self) -> Address { - self.extracts.ru_header().map(|h| h.rewardAddress).unwrap_or(self.parent.beneficiary) + self.extracts.ru_header().map(|h| h.rewardAddress).unwrap_or(self.parent.beneficiary()) } /// Base fee beneficiary of the current block. @@ -310,7 +346,7 @@ impl<'a, 'b> SignetDriver<'a, 'b> { /// Gas limit of the current block. pub fn gas_limit(&self) -> u64 { - self.extracts.ru_header().map(|h| h.gas_limit()).unwrap_or(self.parent.gas_limit) + self.extracts.ru_header().map(|h| h.gas_limit()).unwrap_or(self.parent.gas_limit()) } /// Base fee of the current block. @@ -338,17 +374,13 @@ impl<'a, 'b> SignetDriver<'a, 'b> { } /// Consume the driver, producing the sealed block and receipts. - pub fn finish(self) -> (RecoveredBlock, Vec) { - let (header, hash) = self.construct_sealed_header().split(); + pub fn finish(self) -> (RecoveredBlock, Vec) { + let header = self.construct_sealed_header(); let (receipts, senders, _) = self.output.into_parts(); - let block = RecoveredBlock::new( - Block::new(header, BlockBody { transactions: self.processed, ..Default::default() }), - senders, - hash, - ); - - let receipts = receipts.into_iter().map(|re| re.to_reth()).collect(); + let body = BlockBody { transactions: self.processed, ommers: vec![], withdrawals: None }; + let block = SealedBlock { header, body }; + let block = RecoveredBlock::new(block, senders); (block, receipts) } @@ -363,12 +395,7 @@ impl<'a, 'b> SignetDriver<'a, 'b> { let (sealed_block, receipts) = self.finish(); BlockResult { sealed_block, - execution_outcome: ExecutionOutcome::new( - trevm.finish(), - vec![receipts], - ru_height, - vec![], - ), + execution_outcome: ExecutionOutcome::new(trevm.finish(), vec![receipts], ru_height), } } @@ -390,7 +417,7 @@ impl<'a, 'b> SignetDriver<'a, 'b> { /// Make a receipt for an enter. fn make_enter_receipt( &self, - enter: &ExtractedEvent<'_, Passage::Enter>, + enter: &ExtractedEvent<'_, C::Receipt, Passage::Enter>, ) -> alloy::consensus::Receipt { let cumulative_gas_used = self.cumulative_gas_used().saturating_add(BASE_GAS as u64); @@ -405,18 +432,18 @@ impl<'a, 'b> SignetDriver<'a, 'b> { parent_hash: self.parent.hash(), number: self.ru_height(), gas_limit: self.gas_limit(), - timestamp: self.extracts.host_block.timestamp, + timestamp: self.extracts.host_block.timestamp(), base_fee_per_gas: Some(self.base_fee()), beneficiary: self.beneficiary(), logs_bloom: self.logs_bloom(), gas_used: self.cumulative_gas_used(), - difficulty: self.extracts.host_block.difficulty, + difficulty: self.extracts.host_block.difficulty(), - mix_hash: self.extracts.host_block.mix_hash, - nonce: self.extracts.host_block.nonce, - parent_beacon_block_root: self.extracts.host_block.parent_beacon_block_root, + mix_hash: self.extracts.host_block.mix_hash().unwrap_or_default(), + nonce: self.extracts.host_block.nonce().unwrap_or_default(), + parent_beacon_block_root: self.extracts.host_block.parent_beacon_block_root(), ..Default::default() } @@ -425,8 +452,7 @@ impl<'a, 'b> SignetDriver<'a, 'b> { /// Construct a sealed header for DB and evm execution. fn construct_sealed_header(&self) -> SealedHeader { let header = self.construct_header(); - let hash = header.hash_slow(); - SealedHeader::new(header, hash) + SealedHeader::new(header) } /// Check the [`AggregateFills`], discard if invalid, otherwise accumulate @@ -439,7 +465,7 @@ impl<'a, 'b> SignetDriver<'a, 'b> { &mut self, mut trevm: EvmTransacted, tx: TransactionSigned, - extract: Option<&ExtractedEvent<'_, Transactor::Transact>>, + extract: Option<&ExtractedEvent<'_, C::Receipt, Transactor::Transact>>, ) -> RunTxResult where Db: Database + DatabaseCommit, @@ -597,7 +623,7 @@ impl<'a, 'b> SignetDriver<'a, 'b> { ); // push receipt and transaction to the block - self.processed.push(Enter { enter, nonce }.to_reth()); + self.processed.push(enter.make_transaction(nonce)); self.output.push_result( ReceiptEnvelope::Eip1559(self.make_enter_receipt(enter).into()), MINTER_ADDRESS, @@ -645,22 +671,20 @@ impl<'a, 'b> SignetDriver<'a, 'b> { let nonce = trevm_try!(trevm.try_read_nonce(MINTER_ADDRESS).map_err(EVMError::Database), trevm); - let to_execute = EnterToken { - enter_token: &self.extracts.enter_tokens[idx], - nonce, - token: ru_token_addr, - }; + let extract = &self.extracts.enter_tokens[idx]; - let mut t = run_tx_early_return!(self, trevm, &to_execute, MINTER_ADDRESS); + let filler = EnterTokenFiller { enter_token: extract, nonce, token: ru_token_addr }; + let mut t = run_tx_early_return!(self, trevm, &filler, MINTER_ADDRESS); // push a sys_log to the outcome if let ExecutionResult::Success { logs, .. } = t.result_mut_unchecked() { - let sys_log = crate::sys_log::EnterToken::from(&self.extracts.enter_tokens[idx]).into(); + let sys_log = crate::sys_log::EnterToken::from(extract).into(); logs.push(sys_log) } + let tx = extract.make_transaction(nonce, ru_token_addr); // No need to check AggregateFills. This call cannot result in orders. - Ok(self.accept_tx(t, to_execute.to_reth())) + Ok(self.accept_tx(t, tx)) } /// Execute all [`EnterToken`] events. @@ -716,9 +740,10 @@ impl<'a, 'b> SignetDriver<'a, 'b> { let sender = self.extracts.transacts[idx].event.sender; let nonce = trevm_try!(trevm.try_read_nonce(sender).map_err(EVMError::Database), trevm); - let to_execute = Transact { transact: &self.extracts.transacts[idx], nonce }; + let transact = &self.extracts.transacts[idx]; + let to_execute = TransactFiller { transact, nonce }; - let mut t = run_tx_early_return!(self, trevm, &self.extracts.transacts[idx].event, sender); + let mut t = run_tx_early_return!(self, trevm, &to_execute, sender); { // NB: This is a little sensitive. @@ -726,7 +751,6 @@ impl<'a, 'b> SignetDriver<'a, 'b> { // to ensure they can pay the full price, that check may be // invalidated by transaction execution. As a result, we have to // perform the same check here, again. - let transact = &self.extracts.transacts[idx].event; let gas_used = t.result().gas_used(); // Set gas used to the transact gas limit @@ -765,7 +789,10 @@ impl<'a, 'b> SignetDriver<'a, 'b> { } } - self.check_fills_and_accept(t, to_execute.to_reth(), Some(&self.extracts.transacts[idx])) + // Convert the transact event into a transaction, and then check the + // aggregate fills + let tx = transact.make_transaction(nonce); + self.check_fills_and_accept(t, tx, Some(transact)) } /// Execute all transact events. @@ -840,13 +867,13 @@ impl<'a, 'b> SignetDriver<'a, 'b> { } } -impl trevm::Cfg for SignetDriver<'_, '_> { +impl trevm::Cfg for SignetDriver<'_, '_, C> { fn fill_cfg_env(&self, cfg_env: &mut CfgEnv) { cfg_env.chain_id = self.extracts.chain_id; } } -impl BlockDriver> for SignetDriver<'_, '_> +impl BlockDriver> for SignetDriver<'_, '_, C> where Db: Database + DatabaseCommit, Insp: Inspector>, @@ -911,7 +938,7 @@ where } } -impl trevm::Block for SignetDriver<'_, '_> { +impl trevm::Block for SignetDriver<'_, '_, C> { fn fill_block_env(&self, block_env: &mut BlockEnv) { let BlockEnv { number, @@ -925,345 +952,12 @@ impl trevm::Block for SignetDriver<'_, '_> { } = block_env; *number = self.ru_height(); *beneficiary = self.beneficiary(); - *timestamp = self.extracts.host_block.timestamp; + *timestamp = self.extracts.host_block.timestamp(); *gas_limit = self.gas_limit(); *basefee = self.base_fee(); - *difficulty = self.extracts.host_block.difficulty; - *prevrandao = Some(self.extracts.host_block.mix_hash); + *difficulty = self.extracts.host_block.difficulty(); + *prevrandao = self.extracts.host_block.mix_hash(); *blob_excess_gas_and_price = Some(BlobExcessGasAndPrice { excess_blob_gas: 0, blob_gasprice: 0 }); } } - -#[cfg(test)] -mod test { - use super::*; - use crate::test_utils::*; - use alloy::{ - consensus::{ - constants::{ETH_TO_WEI, GWEI_TO_WEI}, - SignableTransaction, TxEip1559, - }, - primitives::{Sealable, B256, B64}, - signers::{local::PrivateKeySigner, Signature, SignerSync}, - }; - use reth::primitives::{Block, RecoveredBlock, Transaction}; - use signet_extract::ExtractedEvent; - use signet_types::test_utils::*; - use trevm::revm::database::in_memory_db::InMemoryDB; - - /// Make a fake block with a specific number. - pub(super) fn fake_block(number: u64) -> RecoveredBlock { - let header = Header { - difficulty: U256::from(0x4000_0000), - number, - mix_hash: B256::repeat_byte(0xed), - nonce: B64::repeat_byte(0xbe), - timestamp: 1716555586, // the time when i wrote this function lol - excess_blob_gas: Some(0), - ..Default::default() - }; - let (header, hash) = header.seal_slow().into_parts(); - RecoveredBlock::new( - Block::new( - header, - BlockBody { transactions: vec![], ommers: vec![], withdrawals: None }, - ), - vec![], - hash, - ) - } - - /// Make a simple send transaction. - pub(super) fn simple_send( - to: Address, - amount: U256, - nonce: u64, - ) -> reth::primitives::Transaction { - TxEip1559 { - nonce, - gas_limit: 21_000, - to: alloy::primitives::TxKind::Call(to), - value: amount, - chain_id: RU_CHAIN_ID, - max_fee_per_gas: GWEI_TO_WEI as u128 * 100, - max_priority_fee_per_gas: GWEI_TO_WEI as u128, - ..Default::default() - } - .into() - } - - /// Sign a transaction with a wallet. - pub(super) fn sign_tx_with_key_pair( - wallet: &PrivateKeySigner, - tx: Transaction, - ) -> TransactionSigned { - let signature = wallet.sign_hash_sync(&tx.signature_hash()).unwrap(); - TransactionSigned::new_unhashed(tx, signature) - } - - /// Make a wallet with a deterministic keypair. - pub(super) fn make_wallet(i: u8) -> PrivateKeySigner { - PrivateKeySigner::from_bytes(&B256::repeat_byte(i)).unwrap() - } - - struct TestEnv { - pub wallets: Vec, - pub nonces: [u64; 10], - pub sequence: u64, - } - - impl TestEnv { - fn new() -> Self { - let wallets = (1..=10).map(make_wallet).collect::>(); - - Self { wallets, nonces: [0; 10], sequence: 1 } - } - - fn driver<'a, 'b>( - &self, - extracts: &'a mut Extracts<'b>, - txns: Vec, - ) -> SignetDriver<'a, 'b> { - let (header, hash) = - Header { gas_limit: 30_000_000, ..Default::default() }.seal_slow().into_parts(); - SignetDriver::new( - extracts, - txns.into(), - SealedHeader::new(header, hash), - SignetSystemConstants::test(), - ) - } - - fn trevm(&self) -> crate::EvmNeedsBlock { - let mut trevm = test_signet_evm(); - for wallet in &self.wallets { - let address = wallet.address(); - trevm.test_set_balance(address, U256::from(ETH_TO_WEI * 100)); - } - trevm - } - - /// Get the next zenith header in the sequence - fn next_block(&mut self) -> RecoveredBlock { - let block = fake_block(self.sequence); - self.sequence += 1; - block - } - - fn signed_simple_send( - &mut self, - from: usize, - to: Address, - amount: U256, - ) -> TransactionSigned { - let wallet = &self.wallets[from]; - let tx = simple_send(to, amount, self.nonces[from]); - let tx = sign_tx_with_key_pair(wallet, tx); - self.nonces[from] += 1; - tx - } - } - - #[test] - fn test_simple_send() { - let mut context = TestEnv::new(); - - // Set up a simple transfer - let to = Address::repeat_byte(2); - let tx = context.signed_simple_send(0, to, U256::from(100)); - - // Setup the driver - let block = context.next_block(); - let mut extracts = Extracts::empty(&block); - let mut driver = context.driver(&mut extracts, vec![tx.clone()]); - - // Run the EVM - let mut trevm = context.trevm().drive_block(&mut driver).unwrap(); - let (sealed_block, receipts) = driver.finish(); - - // Assert that the EVM balance increased - assert_eq!(sealed_block.senders().len(), 1); - assert_eq!(sealed_block.body().transactions().next(), Some(&tx)); - assert_eq!(receipts.len(), 1); - - assert_eq!(trevm.read_balance(to), U256::from(100)); - } - - #[test] - fn test_two_sends() { - let mut context = TestEnv::new(); - - // Set up a simple transfer - let to = Address::repeat_byte(2); - let tx1 = context.signed_simple_send(0, to, U256::from(100)); - - let to2 = Address::repeat_byte(3); - let tx2 = context.signed_simple_send(0, to2, U256::from(100)); - - // Setup the driver - let block = context.next_block(); - let mut extracts = Extracts::empty(&block); - let mut driver = context.driver(&mut extracts, vec![tx1.clone(), tx2.clone()]); - - // Run the EVM - let mut trevm = context.trevm().drive_block(&mut driver).unwrap(); - let (sealed_block, receipts) = driver.finish(); - - // Assert that the EVM balance increased - assert_eq!(sealed_block.senders().len(), 2); - assert_eq!(sealed_block.body().transactions().collect::>(), vec![&tx1, &tx2]); - assert_eq!(receipts.len(), 2); - - assert_eq!(trevm.read_balance(to), U256::from(100)); - assert_eq!(trevm.read_balance(to2), U256::from(100)); - } - - #[test] - fn test_execute_two_blocks() { - let mut context = TestEnv::new(); - let sender = context.wallets[0].address(); - - let to = Address::repeat_byte(2); - let tx = context.signed_simple_send(0, to, U256::from(100)); - - // Setup the driver - let block = context.next_block(); - let mut extracts = Extracts::empty(&block); - let mut driver = context.driver(&mut extracts, vec![tx.clone()]); - - // Run the EVM - let mut trevm = context.trevm().drive_block(&mut driver).unwrap(); - let (sealed_block, receipts) = driver.finish(); - - assert_eq!(sealed_block.senders().len(), 1); - assert_eq!(sealed_block.body().transactions().collect::>(), vec![&tx]); - assert_eq!(receipts.len(), 1); - assert_eq!(trevm.read_balance(to), U256::from(100)); - assert_eq!(trevm.read_nonce(sender), 1); - - // Repeat the above for the next block - // same recipient - let tx = context.signed_simple_send(0, to, U256::from(100)); - - // Setup the driver - let block = context.next_block(); - let mut extracts = Extracts::empty(&block); - let mut driver = context.driver(&mut extracts, vec![tx.clone()]); - - // Run the EVM - let mut trevm = trevm.drive_block(&mut driver).unwrap(); - let (sealed_block, receipts) = driver.finish(); - - assert_eq!(sealed_block.senders().len(), 1); - assert_eq!(sealed_block.body().transactions().collect::>(), vec![&tx]); - assert_eq!(receipts.len(), 1); - assert_eq!(trevm.read_balance(to), U256::from(200)); - } - - #[test] - fn test_an_enter() { - let mut context = TestEnv::new(); - let user = Address::repeat_byte(2); - - // Set up a fake event - let fake_tx = fake_tx(); - let fake_receipt: reth::primitives::Receipt = Default::default(); - - let enter = signet_zenith::Passage::Enter { - rollupChainId: U256::from(RU_CHAIN_ID), - rollupRecipient: user, - amount: U256::from(100), - }; - - // Setup the driver - let block = context.next_block(); - let mut extracts = Extracts::empty(&block); - extracts.enters.push(ExtractedEvent { - tx: &fake_tx, - receipt: &fake_receipt, - log_index: 0, - event: enter, - }); - let mut driver = context.driver(&mut extracts, vec![]); - - // Run the EVM - let mut trevm = context.trevm().drive_block(&mut driver).unwrap(); - let (sealed_block, receipts) = driver.finish(); - - assert_eq!(sealed_block.senders().len(), 1); - assert_eq!( - sealed_block.body().transactions().collect::>(), - vec![&Enter { enter: &extracts.enters[0], nonce: 0 }.to_reth()] - ); - assert_eq!(receipts.len(), 1); - assert_eq!(trevm.read_balance(user), U256::from(100)); - assert_eq!(trevm.read_nonce(user), 0); - } - - #[test] - fn test_a_transact() { - let mut context = TestEnv::new(); - let sender = Address::repeat_byte(1); - let recipient = Address::repeat_byte(2); - - // Set up a couple fake events - let fake_tx = fake_tx(); - let fake_receipt: reth::primitives::Receipt = Default::default(); - - let enter = signet_zenith::Passage::Enter { - rollupChainId: U256::from(RU_CHAIN_ID), - rollupRecipient: sender, - amount: U256::from(ETH_TO_WEI), - }; - - let transact = signet_zenith::Transactor::Transact { - rollupChainId: U256::from(RU_CHAIN_ID), - sender, - to: recipient, - data: Default::default(), - value: U256::from(100), - gas: U256::from(21_000), - maxFeePerGas: U256::from(GWEI_TO_WEI), - }; - - // Setup the driver - let block = context.next_block(); - let mut extracts = Extracts::empty(&block); - extracts.enters.push(ExtractedEvent { - tx: &fake_tx, - receipt: &fake_receipt, - log_index: 0, - event: enter, - }); - extracts.transacts.push(ExtractedEvent { - tx: &fake_tx, - receipt: &fake_receipt, - log_index: 0, - event: transact, - }); - - let mut driver = context.driver(&mut extracts, vec![]); - - // Run the EVM - let mut trevm = context.trevm().drive_block(&mut driver).unwrap(); - let (sealed_block, receipts) = driver.finish(); - - assert_eq!(sealed_block.senders(), vec![MINTER_ADDRESS, sender]); - assert_eq!( - sealed_block.body().transactions().collect::>(), - vec![ - &Enter { enter: &extracts.enters[0], nonce: 0 }.to_reth(), - &Transact { transact: &extracts.transacts[0], nonce: 0 }.to_reth() - ] - ); - assert_eq!(receipts.len(), 2); - assert_eq!(trevm.read_balance(recipient), U256::from(100)); - } - - fn fake_tx() -> TransactionSigned { - let tx = TxEip1559::default(); - let signature = Signature::test_signature(); - TransactionSigned::new_unhashed(tx.into(), signature) - } -} diff --git a/crates/evm/src/journal/index.rs b/crates/evm/src/journal/index.rs index a880d2a4..16f21de0 100644 --- a/crates/evm/src/journal/index.rs +++ b/crates/evm/src/journal/index.rs @@ -1,15 +1,13 @@ use alloy::primitives::{Address, B256, U256}; -use reth::{ - providers::ExecutionOutcome, - revm::{ - db::{states::StorageSlot, AccountStatus, BundleAccount, BundleState}, - primitives::{AccountInfo, Bytecode}, - }, -}; +use reth::providers::ExecutionOutcome; use std::{ borrow::Cow, collections::{BTreeMap, HashMap}, }; +use trevm::revm::{ + db::{states::StorageSlot, AccountStatus, BundleAccount, BundleState}, + primitives::{AccountInfo, Bytecode}, +}; /// Outcome of an account info after block execution. Post-6780, accounts /// cannot be destroyed, only created or modified. In either case, the new and diff --git a/crates/evm/src/lib.rs b/crates/evm/src/lib.rs index b89a2881..9be4eb94 100644 --- a/crates/evm/src/lib.rs +++ b/crates/evm/src/lib.rs @@ -15,10 +15,6 @@ mod aliases; pub use aliases::*; -/// Utilities for converting types to Reth primitives. -pub mod convert; -pub use convert::ToRethPrimitive; - mod driver; pub use driver::SignetDriver; @@ -28,6 +24,9 @@ pub use journal::HostJournal; mod orders; pub use orders::{Framed, FramedFilleds, FramedOrders, OrderDetector, SignetInspector}; +mod outcome; +pub use outcome::ExecutionOutcome; + mod precompiles; pub use precompiles::signet_precompiles; @@ -78,31 +77,3 @@ where .build_trevm() .expect("db set") } - -/// Test utilities for the Signet EVM impl. -#[cfg(any(test, feature = "test-utils"))] -pub mod test_utils { - use crate::signet_evm; - use reth::revm::{context::CfgEnv, primitives::hardfork::SpecId}; - use signet_types::test_utils::*; - use trevm::revm::database::in_memory_db::InMemoryDB; - - /// Create a new Signet EVM with an in-memory database for testing. - pub fn test_signet_evm() -> super::EvmNeedsBlock - { - signet_evm(InMemoryDB::default(), TEST_SYS).fill_cfg(&TestCfg) - } - - /// Test configuration for the Signet EVM. - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - pub struct TestCfg; - - impl trevm::Cfg for TestCfg { - fn fill_cfg_env(&self, cfg_env: &mut reth::revm::context::CfgEnv) { - let CfgEnv { chain_id, spec, .. } = cfg_env; - - *chain_id = RU_CHAIN_ID; - *spec = SpecId::default(); - } - } -} diff --git a/crates/evm/src/outcome.rs b/crates/evm/src/outcome.rs new file mode 100644 index 00000000..d350e531 --- /dev/null +++ b/crates/evm/src/outcome.rs @@ -0,0 +1,33 @@ +use alloy::consensus::ReceiptEnvelope; +use trevm::revm::database::BundleState; + +/// The outcome of a block execution, containing the bundle state, +/// receipts, and the first block number in the execution. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ExecutionOutcome { + /// The bundle state after execution. + pub bundle: BundleState, + /// The receipts generated during execution, grouped by block. + pub receipts: Vec>, + /// The first block number in the execution. + pub first_block: u64, +} + +impl Default for ExecutionOutcome { + fn default() -> Self { + Self { bundle: BundleState::default(), receipts: vec![], first_block: 0 } + } +} + +impl ExecutionOutcome { + /// Create a new execution outcome. + pub const fn new(bundle: BundleState, receipts: Vec>, first_block: u64) -> Self { + Self { bundle, receipts, first_block } + } + + /// Append another execution outcome to this one. + pub fn append(&mut self, other: Self) { + self.bundle.extend(other.bundle); + self.receipts.extend(other.receipts); + } +} diff --git a/crates/evm/src/precompiles.rs b/crates/evm/src/precompiles.rs index be9c427b..d5f81658 100644 --- a/crates/evm/src/precompiles.rs +++ b/crates/evm/src/precompiles.rs @@ -1,5 +1,5 @@ -use reth::revm::precompile::{secp256r1, Precompiles}; use std::sync::OnceLock; +use trevm::revm::precompile::{secp256r1, Precompiles}; static PRECOMPILES: OnceLock = OnceLock::new(); diff --git a/crates/evm/src/result.rs b/crates/evm/src/result.rs index 7f114221..4ebdd2bd 100644 --- a/crates/evm/src/result.rs +++ b/crates/evm/src/result.rs @@ -1,9 +1,6 @@ -use crate::journal::HostJournal; -use alloy::primitives::B256; -use reth::{ - primitives::{Block, RecoveredBlock}, - providers::ExecutionOutcome, -}; +use crate::{journal::HostJournal, ExecutionOutcome}; +use alloy::{consensus::Header, primitives::B256}; +use signet_types::primitives::{RecoveredBlock, TransactionSigned}; use trevm::journal::BundleStateIndex; /// Output of a block execution. @@ -11,26 +8,26 @@ use trevm::journal::BundleStateIndex; /// This is a convenience struct that combines the consensus block object with /// the result of its execution. #[derive(Debug, Default)] -pub struct BlockResult { +pub struct BlockResult { /// A reth [`RecoveredBlock`], containing the sealed block and a vec of /// transaction sender. - pub sealed_block: RecoveredBlock, + pub sealed_block: RecoveredBlock, /// The reth [`ExecutionOutcome`] containing the net state changes and /// receipts. pub execution_outcome: ExecutionOutcome, } -impl BlockResult { +impl BlockResult { /// Create a new block result. pub const fn new( - sealed_block: RecoveredBlock, + sealed_block: RecoveredBlock, execution_outcome: ExecutionOutcome, ) -> Self { Self { sealed_block, execution_outcome } } /// Get the sealed block. - pub const fn sealed_block(&self) -> &RecoveredBlock { + pub const fn sealed_block(&self) -> &RecoveredBlock { &self.sealed_block } diff --git a/crates/evm/src/sys_log.rs b/crates/evm/src/sys_log.rs index 97b96b34..e6cdffb3 100644 --- a/crates/evm/src/sys_log.rs +++ b/crates/evm/src/sys_log.rs @@ -1,4 +1,4 @@ -use alloy::{primitives::Log, sol_types::SolEvent}; +use alloy::{consensus::TxReceipt, primitives::Log, sol_types::SolEvent}; use signet_extract::ExtractedEvent; use signet_zenith::{Passage, Transactor, MINTER_ADDRESS}; @@ -28,8 +28,8 @@ alloy::sol! { ); } -impl From<&ExtractedEvent<'_, Passage::Enter>> for Enter { - fn from(event: &ExtractedEvent<'_, Passage::Enter>) -> Self { +impl> From<&ExtractedEvent<'_, R, Passage::Enter>> for Enter { + fn from(event: &ExtractedEvent<'_, R, Passage::Enter>) -> Self { Enter { recipient: event.event.rollupRecipient, txHash: event.tx_hash(), @@ -45,8 +45,8 @@ impl From for Log { } } -impl From<&ExtractedEvent<'_, Passage::EnterToken>> for EnterToken { - fn from(event: &ExtractedEvent<'_, Passage::EnterToken>) -> Self { +impl> From<&ExtractedEvent<'_, R, Passage::EnterToken>> for EnterToken { + fn from(event: &ExtractedEvent<'_, R, Passage::EnterToken>) -> Self { EnterToken { recipient: event.event.rollupRecipient, txHash: event.tx_hash(), @@ -63,8 +63,8 @@ impl From for Log { } } -impl From<&ExtractedEvent<'_, Transactor::Transact>> for Transact { - fn from(event: &ExtractedEvent<'_, Transactor::Transact>) -> Self { +impl> From<&ExtractedEvent<'_, R, Transactor::Transact>> for Transact { + fn from(event: &ExtractedEvent<'_, R, Transactor::Transact>) -> Self { Transact { sender: event.event.sender, txHash: event.tx_hash(), diff --git a/crates/extract/Cargo.toml b/crates/extract/Cargo.toml index 1de06698..4390f906 100644 --- a/crates/extract/Cargo.toml +++ b/crates/extract/Cargo.toml @@ -14,17 +14,6 @@ signet-types.workspace = true signet-zenith.workspace = true alloy.workspace = true -reth.workspace = true tracing.workspace = true -# test utils -reth-exex = { workspace = true, optional = true } - -[dev-dependencies] -signet-types = { workspace = true, features = ["test-utils"] } -reth-exex = { workspace = true } - -[features] -default = [] -test-utils = ["dep:reth-exex", "signet-types/test-utils"] diff --git a/crates/extract/src/block.rs b/crates/extract/src/block.rs index 7be3bc45..477f5417 100644 --- a/crates/extract/src/block.rs +++ b/crates/extract/src/block.rs @@ -1,5 +1,5 @@ -use crate::ExtractedEvent; -use reth::primitives::{Block, RecoveredBlock}; +use crate::{Extractable, ExtractedEvent}; +use alloy::consensus::BlockHeader; use signet_types::AggregateFills; use signet_zenith::{Passage, Transactor, Zenith}; @@ -7,41 +7,26 @@ use signet_zenith::{Passage, Transactor, Zenith}; /// from a block object, the extracted events, and a [`AggregateFills`] /// populated with the fills present in the host block. #[derive(Debug, Clone)] -pub struct Extracts<'a> { +pub struct Extracts<'a, C: Extractable> { /// The host block. - pub host_block: &'a RecoveredBlock, + pub host_block: &'a C::Block, /// The rollup chain ID. pub chain_id: u64, /// The rollup block number. pub ru_height: u64, /// The submitted event. - pub submitted: Option>, + pub submitted: Option>, /// The enters. - pub enters: Vec>, + pub enters: Vec>, /// The transacts. - pub transacts: Vec>, + pub transacts: Vec>, /// The enter tokens. - pub enter_tokens: Vec>, + pub enter_tokens: Vec>, /// The net fills extracted from the host block. pub(crate) context: AggregateFills, } -impl Extracts<'_> { - /// Get the header of the block that was submitted (if any). - pub fn ru_header(&self) -> Option { - self.submitted.as_ref().map(|s| s.ru_header(self.host_block_number())) - } - - /// Get the host block number. - pub const fn host_block_number(&self) -> u64 { - self.host_block.sealed_block().header().number - } - - /// Get the host block timestamp. - pub const fn host_block_timestamp(&self) -> u64 { - self.host_block.sealed_block().header().timestamp - } - +impl Extracts<'_, C> { /// True if the host block contains a [`BlockSubmitted`] event. /// /// [`BlockSubmitted`]: Zenith::BlockSubmitted @@ -70,10 +55,27 @@ impl Extracts<'_> { } } -#[cfg(any(test, feature = "test-utils"))] -impl<'a> Extracts<'a> { +impl Extracts<'_, C> { + /// Get the host block number. + pub fn host_block_number(&self) -> u64 { + self.host_block.number() + } + + /// Get the host block timestamp. + pub fn host_block_timestamp(&self) -> u64 { + self.host_block.timestamp() + } + + /// Get the header of the block that was submitted (if any). + pub fn ru_header(&self) -> Option { + self.submitted.as_ref().map(|s| s.ru_header(self.host_block_number())) + } +} + +impl<'a, C: Extractable> Extracts<'a, C> { /// Used for testing. - pub fn empty(host_block: &'a RecoveredBlock) -> Self { + #[doc(hidden)] + pub fn empty(host_block: &'a C::Block) -> Self { Self { host_block, chain_id: 0, diff --git a/crates/extract/src/extracted.rs b/crates/extract/src/extracted.rs index 0729ec98..5fe9f6ea 100644 --- a/crates/extract/src/extracted.rs +++ b/crates/extract/src/extracted.rs @@ -1,8 +1,17 @@ -use alloy::primitives::{Log, TxHash, U256}; -use reth::primitives::{Receipt, TransactionSigned}; +use crate::Events; +use alloy::{ + consensus::{TxEip1559, TxReceipt}, + primitives::{Address, Log, TxHash, U256}, + sol_types::SolCall, +}; +use signet_types::{ + primitives::{Transaction, TransactionSigned}, + MagicSig, MagicSigInfo, +}; use signet_zenith::{Passage, RollupOrders, Transactor, Zenith}; -use crate::Events; +/// Basic gas cost for a transaction. +const BASE_TX_GAS_COST: u64 = 21_000; /// A single event extracted from the host chain. /// @@ -12,26 +21,34 @@ use crate::Events; /// /// Events may be either the enum type [`Events`], or a specific event type. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct ExtractedEvent<'a, T = Events> { +pub struct ExtractedEvent<'a, R, E = Events> { /// The transaction that caused the event pub tx: &'a TransactionSigned, /// The receipt that the event was extracted from. - pub receipt: &'a Receipt, + pub receipt: &'a R, /// The index of the log in the receipt's logs. pub log_index: usize, /// The extracted event. - pub event: T, + pub event: E, } -impl std::ops::Deref for ExtractedEvent<'_, T> { - type Target = T; +impl std::ops::Deref for ExtractedEvent<'_, R, E> +where + R: TxReceipt, + E: Into, +{ + type Target = E; fn deref(&self) -> &Self::Target { &self.event } } -impl ExtractedEvent<'_, T> { +impl ExtractedEvent<'_, R, E> +where + R: TxReceipt, + E: Into, +{ /// Get the transaction hash of the extracted event. pub fn tx_hash(&self) -> TxHash { *self.tx.hash() @@ -39,11 +56,14 @@ impl ExtractedEvent<'_, T> { /// Borrow the raw log from the receipt. pub fn raw_log(&self) -> &Log { - &self.receipt.logs[self.log_index] + &self.receipt.logs()[self.log_index] } } -impl<'a> ExtractedEvent<'a, Events> { +impl<'a, R> ExtractedEvent<'a, R, Events> +where + R: TxReceipt, +{ /// True if the event is an [`Passage::EnterToken`]. pub const fn is_enter_token(&self) -> bool { self.event.is_enter_token() @@ -59,7 +79,7 @@ impl<'a> ExtractedEvent<'a, Events> { /// not an [`EnterToken`], it returns an error. /// /// [`EnterToken`]: Passage::EnterToken - pub fn try_into_enter_token(self) -> Result, Self> { + pub fn try_into_enter_token(self) -> Result, Self> { match self.event { Events::EnterToken(event) => Ok(ExtractedEvent { tx: self.tx, @@ -87,7 +107,7 @@ impl<'a> ExtractedEvent<'a, Events> { /// [`Enter`], it will be returned as an error. /// /// [`Enter`]: Passage::Enter - pub fn try_into_enter(self) -> Result, Self> { + pub fn try_into_enter(self) -> Result, Self> { match self.event { Events::Enter(event) => Ok(ExtractedEvent { tx: self.tx, @@ -117,7 +137,7 @@ impl<'a> ExtractedEvent<'a, Events> { /// [`BlockSubmitted`]: Zenith::BlockSubmitted pub fn try_into_block_submitted( self, - ) -> Result, Self> { + ) -> Result, Self> { match self.event { Events::BlockSubmitted(event) => Ok(ExtractedEvent { tx: self.tx, @@ -143,7 +163,7 @@ impl<'a> ExtractedEvent<'a, Events> { /// an [`Transact`], it will be returned as an error. /// /// [`Transact`]: Transactor::Transact - pub fn try_into_transact(self) -> Result, Self> { + pub fn try_into_transact(self) -> Result, Self> { match self.event { Events::Transact(event) => Ok(ExtractedEvent { tx: self.tx, @@ -168,7 +188,7 @@ impl<'a> ExtractedEvent<'a, Events> { /// Attempt to convert this event into an [`RollupOrders::Filled`]. If the /// event is not an [`RollupOrders::Filled`], it will be returned as an /// error. - pub fn try_into_filled(self) -> Result, Self> { + pub fn try_into_filled(self) -> Result, Self> { match self.event { Events::Filled(event) => Ok(ExtractedEvent { tx: self.tx, @@ -181,7 +201,110 @@ impl<'a> ExtractedEvent<'a, Events> { } } -impl ExtractedEvent<'_, Zenith::BlockSubmitted> { +impl> ExtractedEvent<'_, R, Transactor::Transact> { + /// Create a magic signature for the transact event, containing sender + /// information. + pub fn magic_sig(&self) -> MagicSig { + MagicSig { + ty: MagicSigInfo::Transact { sender: self.sender() }, + txid: self.tx_hash(), + event_idx: self.log_index, + } + } + + /// Create the signature for the transact event. + fn signature(&self) -> alloy::primitives::Signature { + self.magic_sig().into() + } + + /// Make the transaction that corresponds to this transact event, + /// using the provided nonce. + pub fn make_transaction(&self, nonce: u64) -> TransactionSigned { + TransactionSigned::new_unhashed( + Transaction::Eip1559(TxEip1559 { + chain_id: self.rollup_chain_id(), + nonce, + gas_limit: self.gas.to::(), + max_fee_per_gas: self.maxFeePerGas.to::(), + max_priority_fee_per_gas: 0, + to: self.to.into(), + value: self.value, + access_list: Default::default(), + input: self.data.clone(), + }), + self.signature(), + ) + } +} + +impl> ExtractedEvent<'_, R, Passage::Enter> { + /// Get the magic signature for the enter event. + pub fn magic_sig(&self) -> MagicSig { + MagicSig { ty: MagicSigInfo::Enter, txid: self.tx_hash(), event_idx: self.log_index } + } + + /// Get the reth transaction signature for the enter event. + fn signature(&self) -> alloy::primitives::Signature { + self.magic_sig().into() + } + + /// Make the transaction that corresponds to this enter event, using the + /// provided nonce. + pub fn make_transaction(&self, nonce: u64) -> TransactionSigned { + TransactionSigned::new_unhashed( + Transaction::Eip1559(TxEip1559 { + chain_id: self.rollup_chain_id(), + nonce, + gas_limit: BASE_TX_GAS_COST, + max_fee_per_gas: 0, + max_priority_fee_per_gas: 0, + to: self.rollupRecipient.into(), + value: self.amount, + access_list: Default::default(), + input: Default::default(), + }), + self.signature(), + ) + } +} + +impl> ExtractedEvent<'_, R, Passage::EnterToken> { + /// Get the magic signature for the enter token event. + pub fn magic_sig(&self) -> MagicSig { + MagicSig { ty: MagicSigInfo::EnterToken, txid: self.tx_hash(), event_idx: self.log_index } + } + + /// Get the reth transaction signature for the enter token event. + fn signature(&self) -> alloy::primitives::Signature { + self.magic_sig().into() + } + + /// Make the transaction that corresponds to this enter token event, + /// using the provided nonce. + pub fn make_transaction(&self, nonce: u64, token: Address) -> TransactionSigned { + let input = signet_zenith::mintCall { amount: self.amount(), to: self.rollupRecipient } + .abi_encode() + .into(); + + TransactionSigned::new_unhashed( + Transaction::Eip1559(TxEip1559 { + chain_id: self.rollup_chain_id(), + nonce, + gas_limit: BASE_TX_GAS_COST, + max_fee_per_gas: 0, + max_priority_fee_per_gas: 0, + // NB: set to the address of the token contract. + to: token.into(), + value: U256::ZERO, + access_list: Default::default(), + input, // NB: set to the ABI-encoded input for the `mint` function, which dictates the amount and recipient. + }), + self.signature(), + ) + } +} + +impl ExtractedEvent<'_, R, Zenith::BlockSubmitted> { /// Get the header of the block that was submitted. pub fn ru_header(&self, host_block_number: u64) -> Zenith::BlockHeader { Zenith::BlockHeader::from_block_submitted(self.event, U256::from(host_block_number)) diff --git a/crates/extract/src/extractor.rs b/crates/extract/src/extractor.rs index 07692e1e..c4495e66 100644 --- a/crates/extract/src/extractor.rs +++ b/crates/extract/src/extractor.rs @@ -1,11 +1,12 @@ -use crate::{Events, ExtractedEvent, Extracts}; -use alloy::primitives::{Log, LogData}; -use reth::{ - primitives::{Block, Receipt, RecoveredBlock}, - providers::Chain, +use crate::{ + r#trait::{Extractable, HasTxns}, + Events, ExtractedEvent, Extracts, +}; +use alloy::{ + consensus::{BlockHeader, TxReceipt}, + primitives::Log, }; use signet_types::{constants::SignetSystemConstants, AggregateFills}; -use signet_zenith::Passage; use tracing::debug_span; /// Extracts Zenith events from a chain. @@ -35,7 +36,7 @@ impl Extractor { } /// Extract a [`Events`] from a log, checking the chain ID. - fn extract_log(&self, log: &Log) -> Option { + fn extract_log(&self, log: &Log) -> Option { if log.address == self.constants.host_zenith() { return Events::decode_zenith(log, self.constants.ru_chain_id()); } @@ -52,23 +53,23 @@ impl Extractor { } /// Extract Zenith events from a receipt. - fn extract_receipt<'a: 'c, 'b: 'c, 'c>( + fn extract_receipt<'a: 'c, 'b: 'c, 'c, R: TxReceipt>( &'a self, - receipt: &'b Receipt, + receipt: &'b R, ) -> impl Iterator + 'c { - receipt.logs.iter().enumerate().filter_map(|(i, log)| { + receipt.logs().iter().enumerate().filter_map(|(i, log)| { let log = self.extract_log(log)?; Some((i, log)) }) } /// Extract blocks from a chain. - fn produce_event_extracts<'a: 'c, 'b: 'c, 'c>( + fn produce_event_extracts<'a: 'c, 'b: 'c, 'c, C: Extractable>( &'a self, - block: &'b RecoveredBlock, - receipts: &'b [Receipt], - ) -> impl Iterator> { - block.body().transactions.iter().zip(receipts.iter()).flat_map(|(tx, receipt)| { + block: &'b C::Block, + receipts: &'b [C::Receipt], + ) -> impl Iterator> { + block.transactions().iter().zip(receipts.iter()).flat_map(|(tx, receipt)| { self.extract_receipt(receipt).map(move |(log_index, event)| ExtractedEvent { tx, receipt, @@ -85,24 +86,24 @@ impl Extractor { /// - Accumulate the fills. /// - Associate each event with block, tx and receipt references. /// - Yield the extracted block info. - pub fn extract_signet<'a: 'c, 'b: 'c, 'c>( + pub fn extract_signet<'a: 'c, 'b: 'c, 'c, C: Extractable>( &'a self, - chain: &'b Chain, - ) -> impl Iterator> { + chain: &'b C, + ) -> impl Iterator> { chain .blocks_and_receipts() - .filter(|(block, _)| block.number > self.constants.host_deploy_height()) + .filter(|(block, _)| block.number() > self.constants.host_deploy_height()) .map(move |(block, receipts)| { - let height = block.number; + let height = block.number(); let ru_height = self.constants.host_block_to_rollup_block_num(height).unwrap(); let host_block = block; let mut context = AggregateFills::new(); let mut enters = vec![]; let mut transacts = vec![]; - let mut enter_tokens: Vec> = vec![]; + let mut enter_tokens = vec![]; let mut submitted = None; - for event in self.produce_event_extracts(block, receipts) { + for event in self.produce_event_extracts::(block, receipts) { let _span = debug_span!( "tx_loop", height, @@ -149,37 +150,3 @@ impl Extractor { }) } } - -#[cfg(test)] -mod test { - use super::*; - use crate::test_utils::*; - use alloy::{ - consensus::constants::GWEI_TO_WEI, - primitives::{Address, U256}, - }; - use signet_types::test_utils::*; - - #[test] - fn extraction() { - let mut ru_block = RuBlockSpec::test() - .with_gas_limit(12345) - .with_reward_address(Address::repeat_byte(0x99)); - ru_block.add_simple_send(&TEST_SIGNERS[0], TEST_USERS[1], U256::from(GWEI_TO_WEI), 0); - - let hbs = HostBlockSpec::test() - .with_block_number(1) - .enter(TEST_USERS[0], (GWEI_TO_WEI * 4) as usize) - .enter(TEST_USERS[1], (GWEI_TO_WEI * 2) as usize) - .enter_token(TEST_USERS[2], 10_000_000, HOST_USDC) - .simple_transact(TEST_USERS[0], TEST_USERS[4], [1, 2, 3, 4], GWEI_TO_WEI as usize) - .fill(HOST_USDT, TEST_USERS[4], 10_000) - .submit_block(ru_block); - let (chain, _) = hbs.to_chain(); - - let extractor = Extractor::new(TEST_SYS); - let extracts = extractor.extract_signet(&chain).next().unwrap(); - - hbs.assert_conforms(&extracts); - } -} diff --git a/crates/extract/src/lib.rs b/crates/extract/src/lib.rs index 750fdc0e..0e4c5b80 100644 --- a/crates/extract/src/lib.rs +++ b/crates/extract/src/lib.rs @@ -25,6 +25,9 @@ #![deny(unused_must_use, rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +mod block; +pub use block::Extracts; + mod events; pub use events::Events; @@ -34,9 +37,5 @@ pub use extracted::ExtractedEvent; mod extractor; pub use extractor::Extractor; -mod block; -pub use block::Extracts; - -#[cfg(any(test, feature = "test-utils"))] -/// Utils for unit and integration tests. -pub mod test_utils; +mod r#trait; +pub use r#trait::{Extractable, HasTxns}; diff --git a/crates/extract/src/trait.rs b/crates/extract/src/trait.rs new file mode 100644 index 00000000..c866a343 --- /dev/null +++ b/crates/extract/src/trait.rs @@ -0,0 +1,37 @@ +use alloy::{consensus::TxReceipt, primitives::Log}; +use signet_types::primitives::{RecoveredBlock, TransactionSigned}; + +/// A trait for types that can be extracted. +pub trait Extractable: core::fmt::Debug + Sync { + /// The block type that this extractor works with. + type Block: alloy::consensus::BlockHeader + HasTxns + core::fmt::Debug + Sync; + /// The receipt type that this extractor works with. + type Receipt: TxReceipt + core::fmt::Debug + Sync; + + /// An iterator over the blocks and their receipts. + fn blocks_and_receipts(&self) -> impl Iterator)>; +} + +/// A trait for types that contain transactions. +pub trait HasTxns { + /// Get the transactions in the block. + fn transactions(&self) -> &[TransactionSigned]; +} + +impl HasTxns for signet_types::primitives::BlockBody { + fn transactions(&self) -> &[TransactionSigned] { + &self.transactions + } +} + +impl HasTxns for signet_types::primitives::SealedBlock { + fn transactions(&self) -> &[TransactionSigned] { + self.body.transactions.as_slice() + } +} + +impl HasTxns for RecoveredBlock { + fn transactions(&self) -> &[TransactionSigned] { + self.block.body.transactions.as_slice() + } +} diff --git a/crates/sim/Cargo.toml b/crates/sim/Cargo.toml index fcd2af70..40edd199 100644 --- a/crates/sim/Cargo.toml +++ b/crates/sim/Cargo.toml @@ -9,20 +9,16 @@ homepage.workspace = true repository.workspace = true [dependencies] -alloy.workspace = true signet-bundle.workspace = true signet-evm.workspace = true signet-types.workspace = true signet-zenith.workspace = true + +alloy.workspace = true + tokio.workspace = true tracing.workspace = true trevm.workspace = true [dev-dependencies] -signet-constants = { workspace = true, features = ["test-utils"] } -signet-types = { workspace = true, features = ["test-utils"] } -signet-evm = { workspace = true, features = ["test-utils"] } tracing-subscriber.workspace = true - -[features] -test-utils = ["signet-types/test-utils", "signet-evm/test-utils"] \ No newline at end of file diff --git a/crates/sim/src/item.rs b/crates/sim/src/item.rs index 578ac131..d0868229 100644 --- a/crates/sim/src/item.rs +++ b/crates/sim/src/item.rs @@ -62,10 +62,11 @@ impl SimItem { } } -#[cfg(any(test, feature = "test-utils"))] +// Testing functions impl SimItem { /// Create an invalid test item. This will be a [`TxEnvelope`] containing /// an EIP-1559 transaction with an invalid signature and hash. + #[doc(hidden)] pub fn invalid_item() -> Self { TxEnvelope::Eip1559(alloy::consensus::Signed::new_unchecked( alloy::consensus::TxEip1559::default(), @@ -78,6 +79,7 @@ impl SimItem { /// Create an invalid test item with a given gas limit and max priority fee /// per gas. As [`Self::invalid_test_item`] but with a custom gas limit and /// `max_priority_fee_per_gas`. + #[doc(hidden)] pub fn invalid_item_with_score(gas_limit: u64, mpfpg: u128) -> Self { let tx = alloy::consensus::TxEip1559 { gas_limit, diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml new file mode 100644 index 00000000..91908e3c --- /dev/null +++ b/crates/test-utils/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "signet-test-utils" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +signet-constants = { workspace = true, features = ["test-utils"] } +signet-types.workspace = true +signet-evm.workspace = true +signet-zenith.workspace = true +signet-extract.workspace = true +signet-bundle.workspace = true +signet-sim.workspace = true + +trevm = { workspace = true, features = ["test-utils"] } + +alloy.workspace = true + +reth.workspace = true +reth-exex.workspace = true + +tracing-subscriber.workspace = true +tokio.workspace = true \ No newline at end of file diff --git a/crates/test-utils/src/chain.rs b/crates/test-utils/src/chain.rs new file mode 100644 index 00000000..25faf305 --- /dev/null +++ b/crates/test-utils/src/chain.rs @@ -0,0 +1,68 @@ +use alloy::{ + consensus::{Header, ReceiptEnvelope}, + primitives::{B256, B64, U256}, +}; +pub use signet_constants::test_utils::*; +use signet_evm::ExecutionOutcome; +use signet_extract::Extractable; +use signet_types::primitives::{ + BlockBody, RecoveredBlock, SealedBlock, SealedHeader, TransactionSigned, +}; + +/// A simple chain of blocks with receipts. +#[derive(Clone, PartialEq, Eq)] +pub struct Chain { + /// The blocks + pub blocks: Vec>, + + pub execution_outcome: ExecutionOutcome, +} + +impl Default for Chain { + fn default() -> Self { + Self { blocks: vec![], execution_outcome: Default::default() } + } +} + +impl core::fmt::Debug for Chain { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Chain").field("blocks", &self.blocks.len()).finish_non_exhaustive() + } +} + +impl Chain { + /// Create a new chain from a block + pub fn from_block(block: RecoveredBlock, execution_outcome: ExecutionOutcome) -> Self { + Self { blocks: vec![block], execution_outcome } + } + + pub fn append_block(&mut self, block: RecoveredBlock, outcome: ExecutionOutcome) { + self.blocks.push(block); + self.execution_outcome.append(outcome); + } +} + +impl Extractable for Chain { + type Block = RecoveredBlock; + type Receipt = ReceiptEnvelope; + + fn blocks_and_receipts(&self) -> impl Iterator)> { + self.blocks.iter().zip(self.execution_outcome.receipts.iter()) + } +} + +/// Make a fake block with a specific number. +pub fn fake_block(number: u64) -> RecoveredBlock { + let header = Header { + difficulty: U256::from(0x4000_0000), + number, + mix_hash: B256::repeat_byte(0xed), + nonce: B64::repeat_byte(0xbe), + timestamp: 1716555586, // the time when i wrote this function lol + excess_blob_gas: Some(0), + ..Default::default() + }; + let sealed = SealedHeader::new(header); + let block = SealedBlock::new_unchecked(sealed, BlockBody::default()); + RecoveredBlock::new(block, vec![]) +} diff --git a/crates/test-utils/src/convert.rs b/crates/test-utils/src/convert.rs new file mode 100644 index 00000000..c59f6b45 --- /dev/null +++ b/crates/test-utils/src/convert.rs @@ -0,0 +1,97 @@ +//! Bespoke conversion utilities for converting between alloy and reth types. + +use alloy::consensus::ReceiptEnvelope; +use signet_evm::ExecutionOutcome; +use signet_types::primitives::{RecoveredBlock, SealedBlock}; + +/// Utility trait to convert a type to a Reth primitive type. +/// This is used mainly where we need to convert to a reth primitive type +/// because reth does not support the alloy equivalents. +pub trait ToRethPrimitive { + /// The Reth primitive type that the type can be converted to. + type RethPrimitive; + + /// Convert the type to a Reth primitive type. + fn to_reth(self) -> Self::RethPrimitive; +} + +// Reth does not preserve envelope status for receipts, so +// the DB model will not support envelopes. +impl ToRethPrimitive for ReceiptEnvelope { + type RethPrimitive = reth::primitives::Receipt; + + fn to_reth(self) -> Self::RethPrimitive { + let success = self.is_success(); + let cumulative_gas_used = self.cumulative_gas_used(); + let tx_type = match self.tx_type() { + alloy::consensus::TxType::Legacy => reth::primitives::TxType::Legacy, + alloy::consensus::TxType::Eip2930 => reth::primitives::TxType::Eip2930, + alloy::consensus::TxType::Eip1559 => reth::primitives::TxType::Eip1559, + alloy::consensus::TxType::Eip4844 => reth::primitives::TxType::Eip4844, + alloy::consensus::TxType::Eip7702 => reth::primitives::TxType::Eip7702, + }; + + let r = match self { + ReceiptEnvelope::Legacy(r) + | ReceiptEnvelope::Eip2930(r) + | ReceiptEnvelope::Eip1559(r) + | ReceiptEnvelope::Eip4844(r) => r, + _ => panic!("unsupported receipt type"), + }; + + reth::primitives::Receipt { tx_type, success, cumulative_gas_used, logs: r.receipt.logs } + } +} + +impl ToRethPrimitive for SealedBlock { + type RethPrimitive = reth::primitives::SealedBlock; + + fn to_reth(self) -> Self::RethPrimitive { + let (hash, header) = self.header.split(); + reth::primitives::SealedBlock::new_unchecked( + reth::primitives::Block::new(header, self.body), + hash, + ) + } +} + +impl ToRethPrimitive for RecoveredBlock { + type RethPrimitive = reth::primitives::RecoveredBlock; + + fn to_reth(self) -> Self::RethPrimitive { + let hash = self.block.header.hash(); + reth::primitives::RecoveredBlock::new(self.block.to_reth().into_block(), self.senders, hash) + } +} + +impl ToRethPrimitive for crate::chain::Chain { + type RethPrimitive = reth::providers::Chain; + + fn to_reth(self) -> Self::RethPrimitive { + reth::providers::Chain::new(self.blocks.to_reth(), self.execution_outcome.to_reth(), None) + } +} + +impl ToRethPrimitive for Vec +where + T: ToRethPrimitive, +{ + type RethPrimitive = Vec; + + fn to_reth(self) -> Self::RethPrimitive { + self.into_iter().map(ToRethPrimitive::to_reth).collect() + } +} + +impl ToRethPrimitive for ExecutionOutcome { + type RethPrimitive = reth::providers::ExecutionOutcome; + + fn to_reth(self) -> Self::RethPrimitive { + reth::providers::ExecutionOutcome { + bundle: self.bundle, + receipts: self.receipts.into_iter().map(ToRethPrimitive::to_reth).collect(), + first_block: self.first_block, + requests: vec![], + } + } +} diff --git a/crates/test-utils/src/evm.rs b/crates/test-utils/src/evm.rs new file mode 100644 index 00000000..cf209e2c --- /dev/null +++ b/crates/test-utils/src/evm.rs @@ -0,0 +1,21 @@ +use reth::revm::context::CfgEnv; +use signet_constants::test_utils::*; +use trevm::revm::{database::in_memory_db::InMemoryDB, primitives::hardfork::SpecId}; + +/// Create a new Signet EVM with an in-memory database for testing. +pub fn test_signet_evm() -> signet_evm::EvmNeedsBlock { + signet_evm::signet_evm(InMemoryDB::default(), TEST_SYS).fill_cfg(&TestCfg) +} + +/// Test configuration for the Signet EVM. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct TestCfg; + +impl trevm::Cfg for TestCfg { + fn fill_cfg_env(&self, cfg_env: &mut CfgEnv) { + let CfgEnv { chain_id, spec, .. } = cfg_env; + + *chain_id = RU_CHAIN_ID; + *spec = SpecId::default(); + } +} diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs new file mode 100644 index 00000000..905af884 --- /dev/null +++ b/crates/test-utils/src/lib.rs @@ -0,0 +1,7 @@ +pub mod chain; +pub mod convert; +pub mod evm; +pub mod specs; +pub mod users; + +pub use signet_constants::test_utils as test_constants; diff --git a/crates/extract/src/test_utils/host_spec.rs b/crates/test-utils/src/specs/host_spec.rs similarity index 87% rename from crates/extract/src/test_utils/host_spec.rs rename to crates/test-utils/src/specs/host_spec.rs index a9b46ff6..ebfc14aa 100644 --- a/crates/extract/src/test_utils/host_spec.rs +++ b/crates/test-utils/src/specs/host_spec.rs @@ -1,18 +1,19 @@ use crate::{ - test_utils::{NotificationSpec, NotificationWithSidecars, RuBlockSpec}, - Events, Extracts, + chain::Chain, + specs::{NotificationSpec, NotificationWithSidecars, RuBlockSpec}, }; use alloy::{ - consensus::{constants::GWEI_TO_WEI, BlobTransactionSidecar, TxEip1559, TxEip4844}, - primitives::{Address, Bytes, FixedBytes, Log, LogData, Sealable, B256, U256}, + consensus::{ + constants::GWEI_TO_WEI, BlobTransactionSidecar, Header, Receipt, ReceiptEnvelope, + TxEip1559, TxEip4844, + }, + primitives::{Address, Bytes, FixedBytes, Log, LogData, B256, U256}, signers::Signature, }; -use reth::{ - primitives::{ - Block, BlockBody, Header, Receipt, RecoveredBlock, SealedBlock, SealedHeader, Transaction, - TransactionSigned, TxType, - }, - providers::{Chain, ExecutionOutcome}, +use signet_evm::ExecutionOutcome; +use signet_extract::{Events, Extractable, Extracts}; +use signet_types::primitives::{ + BlockBody, RecoveredBlock, SealedBlock, SealedHeader, Transaction, TransactionSigned, }; use signet_types::{ constants::{KnownChains, ParseChainError, SignetSystemConstants}, @@ -41,13 +42,13 @@ pub struct HostBlockSpec { pub constants: SignetSystemConstants, /// The Zenith-event receipts in the block. - pub receipts: Vec, + pub receipts: Vec, /// The Ru block associated with this host block (if any). pub ru_block: Option, /// The sidecar associated with the Ru block (if any). pub sidecar: Option, /// The receipt for the Ru block (if any). - pub ru_block_receipt: Option, + pub ru_block_receipt: Option, /// The block number. This will be overridden when making chains of blocks. pub block_number: AtomicU64, @@ -308,7 +309,7 @@ impl HostBlockSpec { /// This function is a little weird because reth @ 1.2.0 rejiggered the /// block structs in odd ways. pub fn header(&self) -> SealedHeader { - let (header, hash) = Header { + let header = Header { difficulty: U256::from(0x4000_0000), number: self.block_number(), mix_hash: B256::repeat_byte(0xed), @@ -316,36 +317,28 @@ impl HostBlockSpec { timestamp: 1716555586, // the time when i wrote this function lol excess_blob_gas: Some(0), ..Default::default() - } - .seal_slow() - .into_parts(); - SealedHeader::new(header, hash) + }; + SealedHeader::new(header) } /// Make a block - /// - /// This function is a little weird because reth @ 1.2.0 rejiggered the - /// block structs in odd ways. - pub fn block(&self) -> SealedBlock { - let (header, hash) = self.header().split(); - SealedBlock::new_unchecked( - Block::new( - header, - BlockBody { transactions: self.make_txns(), ommers: vec![], withdrawals: None }, - ), - hash, - ) + pub fn sealed_block(&self) -> SealedBlock { + let header = self.header(); + + let body = BlockBody { transactions: self.make_txns(), ommers: vec![], withdrawals: None }; + + SealedBlock::new_unchecked(header, body) } /// Make a block with senders /// /// This function is a little weird because reth @ 1.2.0 rejiggered the /// block structs in odd ways. - pub fn recovered_block(&self) -> RecoveredBlock { - let (block, hash) = self.block().split(); + pub fn recovered_block(&self) -> RecoveredBlock { + let block = self.sealed_block(); let senders = block.body.transactions().map(|_| Address::ZERO).collect::>(); - RecoveredBlock::new(block, senders, hash) + RecoveredBlock::new(block, senders) } /// Make an execution outcome @@ -355,19 +348,14 @@ impl HostBlockSpec { receipts.first_mut().unwrap().push(receipt); } - ExecutionOutcome { - bundle: Default::default(), - receipts, - first_block: self.block_number(), - requests: vec![], - } + ExecutionOutcome { bundle: Default::default(), receipts, first_block: self.block_number() } } /// Make a chain pub fn to_chain(&self) -> (Chain, Option) { let execution_outcome = self.execution_outcome(); - let chain = Chain::from_block(self.recovered_block(), execution_outcome, None); + let chain = Chain::from_block(self.recovered_block(), execution_outcome); (chain, self.sidecar.clone()) } @@ -388,7 +376,7 @@ impl HostBlockSpec { } /// Assert that the block conforms to the spec - pub fn assert_conforms(&self, extracts: &Extracts<'_>) { + pub fn assert_conforms(&self, extracts: &Extracts<'_, C>) { if let Some(ru_block) = &self.ru_block { ru_block.assert_conforms(extracts); } @@ -430,21 +418,17 @@ impl FromStr for HostBlockSpec { let chain: KnownChains = s.parse()?; match chain { KnownChains::Pecorino => Ok(Self::pecorino()), - #[cfg(any(test, feature = "test-utils"))] KnownChains::Test => Ok(Self::test()), } } } -fn to_receipt(address: Address, t: &T) -> Receipt +fn to_receipt(address: Address, t: &T) -> ReceiptEnvelope where for<'a> &'a T: Into, { let log = Log { address, data: t.into() }; - Receipt { - tx_type: TxType::Eip1559, - success: true, - cumulative_gas_used: 30_000, - logs: vec![log], - } + ReceiptEnvelope::Eip1559( + Receipt { status: true.into(), cumulative_gas_used: 30_000, logs: vec![log] }.into(), + ) } diff --git a/crates/extract/src/test_utils/mod.rs b/crates/test-utils/src/specs/mod.rs similarity index 95% rename from crates/extract/src/test_utils/mod.rs rename to crates/test-utils/src/specs/mod.rs index d771a424..3cd1e744 100644 --- a/crates/extract/src/test_utils/mod.rs +++ b/crates/test-utils/src/specs/mod.rs @@ -12,7 +12,7 @@ use alloy::{ primitives::{Address, TxKind, B256, U256}, signers::{local::PrivateKeySigner, SignerSync}, }; -use reth::primitives::{Transaction, TransactionSigned}; +use signet_types::primitives::{Transaction, TransactionSigned}; /// Sign a transaction with a wallet. pub fn sign_tx_with_key_pair(wallet: &PrivateKeySigner, tx: Transaction) -> TransactionSigned { diff --git a/crates/extract/src/test_utils/notif_spec.rs b/crates/test-utils/src/specs/notif_spec.rs similarity index 86% rename from crates/extract/src/test_utils/notif_spec.rs rename to crates/test-utils/src/specs/notif_spec.rs index 277633fa..69a3855e 100644 --- a/crates/extract/src/test_utils/notif_spec.rs +++ b/crates/test-utils/src/specs/notif_spec.rs @@ -1,7 +1,7 @@ -use crate::test_utils::HostBlockSpec; +use crate::{convert::ToRethPrimitive, specs::HostBlockSpec}; use alloy::consensus::BlobTransactionSidecar; -use reth::primitives::TransactionSigned; use reth_exex::ExExNotification; +use signet_types::primitives::TransactionSigned; use std::{collections::BTreeMap, sync::Arc}; /// A notification spec. @@ -60,7 +60,7 @@ impl NotificationSpec { let (mut chain, sidecar) = self.new[0].to_chain(); // accumulate sidecar if necessary if let Some(sidecar) = sidecar { - let tx = self.new[0].block().body().transactions().last().unwrap().clone(); + let tx = self.new[0].sealed_block().body.transactions().last().unwrap().clone(); sidecars.insert(num, (sidecar, tx)); } @@ -72,7 +72,7 @@ impl NotificationSpec { // accumualate the sidecar here if necessary if let Some(sidecar) = block.sidecar.clone() { - let tx = block.block().body().transactions().last().unwrap().clone(); + let tx = block.sealed_block().body.transactions().last().unwrap().clone(); sidecars.insert(block.block_number(), (sidecar, tx)); } @@ -87,17 +87,21 @@ impl NotificationSpec { match (old_chain, new_chain) { (Some(old_chain), Some(new_chain)) => NotificationWithSidecars { notification: ExExNotification::ChainReorged { - old: Arc::new(old_chain), - new: Arc::new(new_chain), + old: Arc::new(old_chain.to_reth()), + new: Arc::new(new_chain.to_reth()), }, sidecars, }, (Some(old_chain), None) => NotificationWithSidecars { - notification: ExExNotification::ChainReverted { old: Arc::new(old_chain) }, + notification: ExExNotification::ChainReverted { + old: Arc::new(old_chain.to_reth()), + }, sidecars, }, (None, Some(new_chain)) => NotificationWithSidecars { - notification: ExExNotification::ChainCommitted { new: Arc::new(new_chain) }, + notification: ExExNotification::ChainCommitted { + new: Arc::new(new_chain.to_reth()), + }, sidecars, }, (None, None) => panic!("missing old and new chains"), diff --git a/crates/extract/src/test_utils/ru_spec.rs b/crates/test-utils/src/specs/ru_spec.rs similarity index 92% rename from crates/extract/src/test_utils/ru_spec.rs rename to crates/test-utils/src/specs/ru_spec.rs index b13b5117..f95e464d 100644 --- a/crates/extract/src/test_utils/ru_spec.rs +++ b/crates/test-utils/src/specs/ru_spec.rs @@ -1,5 +1,3 @@ -use crate::Extracts; - use super::{sign_tx_with_key_pair, simple_send}; use alloy::{ consensus::{BlobTransactionSidecar, SidecarBuilder, SimpleCoder, TxEnvelope}, @@ -8,8 +6,12 @@ use alloy::{ rlp::Encodable, signers::local::PrivateKeySigner, }; -use reth::primitives::TransactionSigned; -use signet_types::constants::{KnownChains, ParseChainError, SignetSystemConstants}; +use signet_constants::test_utils::*; +use signet_extract::{Extractable, Extracts}; +use signet_types::{ + constants::{KnownChains, ParseChainError, SignetSystemConstants}, + primitives::TransactionSigned, +}; use signet_zenith::Zenith::{self}; use std::str::FromStr; @@ -123,9 +125,7 @@ impl RuBlockSpec { sequencer: Address::repeat_byte(3), rollupChainId: U256::from(self.constants.ru_chain_id()), gasLimit: U256::from(self.gas_limit.unwrap_or(100_000_000)), - rewardAddress: self - .reward_address - .unwrap_or(signet_types::test_utils::DEFAULT_REWARD_ADDRESS), + rewardAddress: self.reward_address.unwrap_or(DEFAULT_REWARD_ADDRESS), blockDataHash: bdh, }; @@ -133,7 +133,7 @@ impl RuBlockSpec { } /// Assert that extracted data conforms to the block spec. - pub fn assert_conforms(&self, extracts: &Extracts<'_>) { + pub fn assert_conforms(&self, extracts: &Extracts<'_, C>) { let submitted = extracts.submitted.as_ref().unwrap(); if let Some(gas_limit) = self.gas_limit { @@ -153,7 +153,6 @@ impl FromStr for RuBlockSpec { let chain: KnownChains = s.parse()?; match chain { KnownChains::Pecorino => Ok(Self::pecorino()), - #[cfg(any(test, feature = "test-utils"))] KnownChains::Test => Ok(Self::test()), } } diff --git a/crates/types/src/test_utils.rs b/crates/test-utils/src/users.rs similarity index 96% rename from crates/types/src/test_utils.rs rename to crates/test-utils/src/users.rs index cf1162bb..8acae893 100644 --- a/crates/types/src/test_utils.rs +++ b/crates/test-utils/src/users.rs @@ -4,8 +4,6 @@ use alloy::{ }; use std::sync::LazyLock; -pub use signet_constants::test_utils::*; - /// Test signers used in tests. pub static TEST_SIGNERS: LazyLock<[PrivateKeySigner; 10]> = LazyLock::new(|| { [ diff --git a/crates/sim/tests/basic_sim.rs b/crates/test-utils/tests/basic_sim.rs similarity index 96% rename from crates/sim/tests/basic_sim.rs rename to crates/test-utils/tests/basic_sim.rs index d70e3e1a..95fb9b14 100644 --- a/crates/sim/tests/basic_sim.rs +++ b/crates/test-utils/tests/basic_sim.rs @@ -7,9 +7,12 @@ use alloy::{ primitives::{Address, TxKind, U256}, signers::Signature, }; -use signet_evm::test_utils::TestCfg; use signet_sim::{BlockBuild, SimCache}; -use signet_types::test_utils::*; +use signet_test_utils::{ + evm::TestCfg, + test_constants::*, + users::{TEST_SIGNERS, TEST_USERS}, +}; use std::sync::Arc; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer}; use trevm::{ diff --git a/crates/test-utils/tests/evm.rs b/crates/test-utils/tests/evm.rs new file mode 100644 index 00000000..599fcd28 --- /dev/null +++ b/crates/test-utils/tests/evm.rs @@ -0,0 +1,270 @@ +use alloy::{ + consensus::{ + constants::{ETH_TO_WEI, GWEI_TO_WEI}, + Header, ReceiptEnvelope, TxEip1559, + }, + primitives::{Address, U256}, + signers::{local::PrivateKeySigner, Signature}, +}; +use signet_constants::SignetSystemConstants; +use signet_evm::SignetDriver; +use signet_extract::{Extractable, ExtractedEvent, Extracts}; +use signet_test_utils::{ + chain::{fake_block, Chain, RU_CHAIN_ID}, + evm::test_signet_evm, + specs::{make_wallet, sign_tx_with_key_pair, simple_send}, +}; +use signet_types::primitives::{RecoveredBlock, SealedHeader, TransactionSigned}; +use signet_zenith::MINTER_ADDRESS; +use trevm::revm::database::in_memory_db::InMemoryDB; + +struct TestEnv { + pub wallets: Vec, + pub nonces: [u64; 10], + pub sequence: u64, +} + +impl TestEnv { + fn new() -> Self { + let wallets = (1..=10).map(make_wallet).collect::>(); + + Self { wallets, nonces: [0; 10], sequence: 1 } + } + + fn driver<'a, 'b, C: Extractable>( + &self, + extracts: &'a mut Extracts<'b, C>, + txns: Vec, + ) -> SignetDriver<'a, 'b, C> { + let header = Header { gas_limit: 30_000_000, ..Default::default() }; + SignetDriver::new( + extracts, + txns.into(), + SealedHeader::new(header), + SignetSystemConstants::test(), + ) + } + + fn trevm(&self) -> signet_evm::EvmNeedsBlock { + let mut trevm = test_signet_evm(); + for wallet in &self.wallets { + let address = wallet.address(); + trevm.test_set_balance(address, U256::from(ETH_TO_WEI * 100)); + } + trevm + } + + /// Get the next zenith header in the sequence + fn next_block(&mut self) -> RecoveredBlock { + let block = fake_block(self.sequence); + self.sequence += 1; + block + } + + fn signed_simple_send(&mut self, from: usize, to: Address, amount: U256) -> TransactionSigned { + let wallet = &self.wallets[from]; + let tx = simple_send(to, amount, self.nonces[from], RU_CHAIN_ID); + let tx = sign_tx_with_key_pair(wallet, tx); + self.nonces[from] += 1; + tx + } +} + +#[test] +fn test_simple_send() { + let mut context = TestEnv::new(); + + // Set up a simple transfer + let to = Address::repeat_byte(2); + let tx = context.signed_simple_send(0, to, U256::from(100)); + + // Setup the driver + let block = context.next_block(); + let mut extracts = Extracts::::empty(&block); + let mut driver = context.driver(&mut extracts, vec![tx.clone()]); + + // Run the EVM + let mut trevm = context.trevm().drive_block(&mut driver).unwrap(); + let (sealed_block, receipts) = driver.finish(); + + // Assert that the EVM balance increased + assert_eq!(sealed_block.senders.len(), 1); + assert_eq!(sealed_block.block.body.transactions().next(), Some(&tx)); + assert_eq!(receipts.len(), 1); + + assert_eq!(trevm.read_balance(to), U256::from(100)); +} + +#[test] +fn test_two_sends() { + let mut context = TestEnv::new(); + + // Set up a simple transfer + let to = Address::repeat_byte(2); + let tx1 = context.signed_simple_send(0, to, U256::from(100)); + + let to2 = Address::repeat_byte(3); + let tx2 = context.signed_simple_send(0, to2, U256::from(100)); + + // Setup the driver + let block = context.next_block(); + let mut extracts = Extracts::::empty(&block); + let mut driver = context.driver(&mut extracts, vec![tx1.clone(), tx2.clone()]); + + // Run the EVM + let mut trevm = context.trevm().drive_block(&mut driver).unwrap(); + let (sealed_block, receipts) = driver.finish(); + + // Assert that the EVM balance increased + assert_eq!(sealed_block.senders.len(), 2); + assert_eq!(sealed_block.block.body.transactions().collect::>(), vec![&tx1, &tx2]); + assert_eq!(receipts.len(), 2); + + assert_eq!(trevm.read_balance(to), U256::from(100)); + assert_eq!(trevm.read_balance(to2), U256::from(100)); +} + +#[test] +fn test_execute_two_blocks() { + let mut context = TestEnv::new(); + let sender = context.wallets[0].address(); + + let to = Address::repeat_byte(2); + let tx = context.signed_simple_send(0, to, U256::from(100)); + + // Setup the driver + let block = context.next_block(); + let mut extracts = Extracts::::empty(&block); + let mut driver = context.driver(&mut extracts, vec![tx.clone()]); + + // Run the EVM + let mut trevm = context.trevm().drive_block(&mut driver).unwrap(); + let (sealed_block, receipts) = driver.finish(); + + assert_eq!(sealed_block.senders.len(), 1); + assert_eq!(sealed_block.block.body.transactions().collect::>(), vec![&tx]); + assert_eq!(receipts.len(), 1); + assert_eq!(trevm.read_balance(to), U256::from(100)); + assert_eq!(trevm.read_nonce(sender), 1); + + // Repeat the above for the next block + // same recipient + let tx = context.signed_simple_send(0, to, U256::from(100)); + + // Setup the driver + let block = context.next_block(); + let mut extracts = Extracts::::empty(&block); + let mut driver = context.driver(&mut extracts, vec![tx.clone()]); + + // Run the EVM + let mut trevm = trevm.drive_block(&mut driver).unwrap(); + let (sealed_block, receipts) = driver.finish(); + + assert_eq!(sealed_block.senders.len(), 1); + assert_eq!(sealed_block.block.body.transactions().collect::>(), vec![&tx]); + assert_eq!(receipts.len(), 1); + assert_eq!(trevm.read_balance(to), U256::from(200)); +} + +#[test] +fn test_an_enter() { + let mut context = TestEnv::new(); + let user = Address::repeat_byte(2); + + // Set up a fake event + let fake_tx = fake_tx(); + let fake_receipt = ReceiptEnvelope::Eip1559(Default::default()); + + let enter = signet_zenith::Passage::Enter { + rollupChainId: U256::from(RU_CHAIN_ID), + rollupRecipient: user, + amount: U256::from(100), + }; + + // Setup the driver + let block = context.next_block(); + let mut extracts = Extracts::::empty(&block); + extracts.enters.push(ExtractedEvent { + tx: &fake_tx, + receipt: &fake_receipt, + log_index: 0, + event: enter, + }); + let mut driver = context.driver(&mut extracts, vec![]); + + // Run the EVM + let mut trevm = context.trevm().drive_block(&mut driver).unwrap(); + let (sealed_block, receipts) = driver.finish(); + + assert_eq!(sealed_block.senders.len(), 1); + assert_eq!( + sealed_block.block.body.transactions().collect::>(), + vec![&extracts.enters[0].make_transaction(0)] + ); + assert_eq!(receipts.len(), 1); + assert_eq!(trevm.read_balance(user), U256::from(100)); + assert_eq!(trevm.read_nonce(user), 0); +} + +#[test] +fn test_a_transact() { + let mut context = TestEnv::new(); + let sender = Address::repeat_byte(1); + let recipient = Address::repeat_byte(2); + + // Set up a couple fake events + let fake_tx = fake_tx(); + let fake_receipt = ReceiptEnvelope::Eip1559(Default::default()); + + let enter = signet_zenith::Passage::Enter { + rollupChainId: U256::from(RU_CHAIN_ID), + rollupRecipient: sender, + amount: U256::from(ETH_TO_WEI), + }; + + let transact = signet_zenith::Transactor::Transact { + rollupChainId: U256::from(RU_CHAIN_ID), + sender, + to: recipient, + data: Default::default(), + value: U256::from(100), + gas: U256::from(21_000), + maxFeePerGas: U256::from(GWEI_TO_WEI), + }; + + // Setup the driver + let block = context.next_block(); + let mut extracts = Extracts::::empty(&block); + extracts.enters.push(ExtractedEvent { + tx: &fake_tx, + receipt: &fake_receipt, + log_index: 0, + event: enter, + }); + extracts.transacts.push(ExtractedEvent { + tx: &fake_tx, + receipt: &fake_receipt, + log_index: 0, + event: transact, + }); + + let mut driver = context.driver(&mut extracts, vec![]); + + // Run the EVM + let mut trevm = context.trevm().drive_block(&mut driver).unwrap(); + let (sealed_block, receipts) = driver.finish(); + + assert_eq!(sealed_block.senders, vec![MINTER_ADDRESS, sender]); + assert_eq!( + sealed_block.block.body.transactions().collect::>(), + vec![&extracts.enters[0].make_transaction(0), &extracts.transacts[0].make_transaction(0)] + ); + assert_eq!(receipts.len(), 2); + assert_eq!(trevm.read_balance(recipient), U256::from(100)); +} + +fn fake_tx() -> TransactionSigned { + let tx = TxEip1559::default(); + let signature = Signature::test_signature(); + TransactionSigned::new_unhashed(tx.into(), signature) +} diff --git a/crates/test-utils/tests/extract.rs b/crates/test-utils/tests/extract.rs new file mode 100644 index 00000000..6f6f9dd8 --- /dev/null +++ b/crates/test-utils/tests/extract.rs @@ -0,0 +1,32 @@ +use alloy::{ + consensus::constants::GWEI_TO_WEI, + primitives::{Address, U256}, +}; +use signet_extract::Extractor; +use signet_test_utils::{ + specs::{HostBlockSpec, RuBlockSpec}, + test_constants::*, + users::*, +}; + +#[test] +fn extraction() { + let mut ru_block = + RuBlockSpec::test().with_gas_limit(12345).with_reward_address(Address::repeat_byte(0x99)); + ru_block.add_simple_send(&TEST_SIGNERS[0], TEST_USERS[1], U256::from(GWEI_TO_WEI), 0); + + let hbs = HostBlockSpec::test() + .with_block_number(1) + .enter(TEST_USERS[0], (GWEI_TO_WEI * 4) as usize) + .enter(TEST_USERS[1], (GWEI_TO_WEI * 2) as usize) + .enter_token(TEST_USERS[2], 10_000_000, HOST_USDC) + .simple_transact(TEST_USERS[0], TEST_USERS[4], [1, 2, 3, 4], GWEI_TO_WEI as usize) + .fill(HOST_USDT, TEST_USERS[4], 10_000) + .submit_block(ru_block); + let (chain, _) = hbs.to_chain(); + + let extractor = Extractor::new(TEST_SYS); + let extracts = extractor.extract_signet(&chain).next().unwrap(); + + hbs.assert_conforms(&extracts); +} diff --git a/crates/types/Cargo.toml b/crates/types/Cargo.toml index 8202be8f..7a7bbeab 100644 --- a/crates/types/Cargo.toml +++ b/crates/types/Cargo.toml @@ -22,6 +22,3 @@ chrono.workspace = true serde_json.workspace = true tokio = { version = "1.37.0", features = ["macros"] } -[features] -default = [] -test-utils = ["signet-constants/test-utils"] diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index 41c1e2ff..19a9df26 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -22,17 +22,16 @@ pub use signet_constants::PairedHeights; mod agg; pub use agg::{AggregateFills, AggregateOrders, MarketError}; -mod signing; -pub use signing::{ - SignedFill, SignedOrder, SignedPermitError, SigningError, UnsignedFill, UnsignedOrder, -}; - mod magic_sig; pub use magic_sig::{MagicSig, MagicSigInfo}; +/// Primitive block types used in Signet. +pub mod primitives; + mod seq; pub use seq::{RequestSigner, SignRequest, SignResponse}; -#[cfg(any(test, feature = "test-utils"))] -/// Utils for unit and integration tests. -pub mod test_utils; +mod signing; +pub use signing::{ + SignedFill, SignedOrder, SignedPermitError, SigningError, UnsignedFill, UnsignedOrder, +}; diff --git a/crates/types/src/primitives/block.rs b/crates/types/src/primitives/block.rs new file mode 100644 index 00000000..cf9bc1a6 --- /dev/null +++ b/crates/types/src/primitives/block.rs @@ -0,0 +1,348 @@ +//! Many of these types are re-produced from the `reth-primitives` crate family. + +use alloy::{ + consensus::{ + Block as AlloyBlock, BlockBody as AlloyBlockBody, BlockHeader, EthereumTxEnvelope, + EthereumTypedTransaction, Header, TxEip4844, + }, + primitives::{Address, BlockHash, BlockNumber, Bloom, Bytes, B256, B64, U256}, +}; +use std::sync::OnceLock; + +/// A type alias for the block body used in Ethereum blocks. +pub type BlockBody = AlloyBlockBody; + +/// A Sealed header type +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct SealedHeader { + /// Block hash + hash: OnceLock, + /// Locked Header fields. + header: H, +} + +impl SealedHeader { + /// Create a new sealed header. + pub const fn new(header: H) -> Self { + Self { hash: OnceLock::new(), header } + } + + /// Get the header + pub const fn header(&self) -> &H { + &self.header + } +} + +impl SealedHeader { + /// Get the block hash of the sealed header. + pub fn hash(&self) -> BlockHash { + *self.hash.get_or_init(|| BlockHash::from(self.header.hash_slow())) + } + + /// Split the sealed header into its components. + pub fn split(self) -> (BlockHash, Header) { + let hash = self.hash(); + (hash, self.header) + } +} + +impl BlockHeader for SealedHeader { + fn parent_hash(&self) -> B256 { + self.header.parent_hash() + } + + fn ommers_hash(&self) -> B256 { + self.header.ommers_hash() + } + + fn beneficiary(&self) -> Address { + self.header.beneficiary() + } + + fn state_root(&self) -> B256 { + self.header.state_root() + } + + fn transactions_root(&self) -> B256 { + self.header.transactions_root() + } + + fn receipts_root(&self) -> B256 { + self.header.receipts_root() + } + + fn withdrawals_root(&self) -> Option { + self.header.withdrawals_root() + } + + fn logs_bloom(&self) -> Bloom { + self.header.logs_bloom() + } + + fn difficulty(&self) -> U256 { + self.header.difficulty() + } + + fn number(&self) -> BlockNumber { + self.header.number() + } + + fn gas_limit(&self) -> u64 { + self.header.gas_limit() + } + + fn gas_used(&self) -> u64 { + self.header.gas_used() + } + + fn timestamp(&self) -> u64 { + self.header.timestamp() + } + + fn mix_hash(&self) -> Option { + self.header.mix_hash() + } + + fn nonce(&self) -> Option { + self.header.nonce() + } + + fn base_fee_per_gas(&self) -> Option { + self.header.base_fee_per_gas() + } + + fn blob_gas_used(&self) -> Option { + self.header.blob_gas_used() + } + + fn excess_blob_gas(&self) -> Option { + self.header.excess_blob_gas() + } + + fn parent_beacon_block_root(&self) -> Option { + self.header.parent_beacon_block_root() + } + + fn requests_hash(&self) -> Option { + self.header.requests_hash() + } + + fn extra_data(&self) -> &Bytes { + self.header.extra_data() + } +} + +/// Ethereum sealed block type. +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct SealedBlock { + /// The sealed header of the block. + pub header: SealedHeader, + /// The transactions in the block. + pub body: AlloyBlockBody, +} + +impl SealedBlock { + /// Create a new sealed block without checking the header hash. + pub const fn new_unchecked(header: SealedHeader, body: AlloyBlockBody) -> Self { + Self { header, body } + } +} + +impl BlockHeader for SealedBlock { + fn parent_hash(&self) -> B256 { + self.header.parent_hash() + } + + fn ommers_hash(&self) -> B256 { + self.header.ommers_hash() + } + + fn beneficiary(&self) -> Address { + self.header.beneficiary() + } + + fn state_root(&self) -> B256 { + self.header.state_root() + } + + fn transactions_root(&self) -> B256 { + self.header.transactions_root() + } + + fn receipts_root(&self) -> B256 { + self.header.receipts_root() + } + + fn withdrawals_root(&self) -> Option { + self.header.withdrawals_root() + } + + fn logs_bloom(&self) -> Bloom { + self.header.logs_bloom() + } + + fn difficulty(&self) -> U256 { + self.header.difficulty() + } + + fn number(&self) -> BlockNumber { + self.header.number() + } + + fn gas_limit(&self) -> u64 { + self.header.gas_limit() + } + + fn gas_used(&self) -> u64 { + self.header.gas_used() + } + + fn timestamp(&self) -> u64 { + self.header.timestamp() + } + + fn mix_hash(&self) -> Option { + self.header.mix_hash() + } + + fn nonce(&self) -> Option { + self.header.nonce() + } + + fn base_fee_per_gas(&self) -> Option { + self.header.base_fee_per_gas() + } + + fn blob_gas_used(&self) -> Option { + self.header.blob_gas_used() + } + + fn excess_blob_gas(&self) -> Option { + self.header.excess_blob_gas() + } + + fn parent_beacon_block_root(&self) -> Option { + self.header.parent_beacon_block_root() + } + + fn requests_hash(&self) -> Option { + self.header.requests_hash() + } + + fn extra_data(&self) -> &Bytes { + self.header.extra_data() + } +} + +/// A [`SealedBlock`] with the senders of the transactions. +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct RecoveredBlock { + /// The block. + pub block: SealedBlock, + /// The senders + pub senders: Vec
, +} + +impl RecoveredBlock { + /// Create a new recovered block. + pub const fn new(block: SealedBlock, senders: Vec
) -> Self { + Self { block, senders } + } +} + +impl BlockHeader for RecoveredBlock { + fn parent_hash(&self) -> B256 { + self.block.parent_hash() + } + + fn ommers_hash(&self) -> B256 { + self.block.ommers_hash() + } + + fn beneficiary(&self) -> Address { + self.block.beneficiary() + } + + fn state_root(&self) -> B256 { + self.block.state_root() + } + + fn transactions_root(&self) -> B256 { + self.block.transactions_root() + } + + fn receipts_root(&self) -> B256 { + self.block.receipts_root() + } + + fn withdrawals_root(&self) -> Option { + self.block.withdrawals_root() + } + + fn logs_bloom(&self) -> Bloom { + self.block.logs_bloom() + } + + fn difficulty(&self) -> U256 { + self.block.difficulty() + } + + fn number(&self) -> BlockNumber { + self.block.number() + } + + fn gas_limit(&self) -> u64 { + self.block.gas_limit() + } + + fn gas_used(&self) -> u64 { + self.block.gas_used() + } + + fn timestamp(&self) -> u64 { + self.block.timestamp() + } + + fn mix_hash(&self) -> Option { + self.block.mix_hash() + } + + fn nonce(&self) -> Option { + self.block.nonce() + } + + fn base_fee_per_gas(&self) -> Option { + self.block.base_fee_per_gas() + } + + fn blob_gas_used(&self) -> Option { + self.block.blob_gas_used() + } + + fn excess_blob_gas(&self) -> Option { + self.block.excess_blob_gas() + } + + fn parent_beacon_block_root(&self) -> Option { + self.block.parent_beacon_block_root() + } + + fn requests_hash(&self) -> Option { + self.block.requests_hash() + } + + fn extra_data(&self) -> &Bytes { + self.block.extra_data() + } +} + +/// Typed Transaction type without a signature +pub type Transaction = EthereumTypedTransaction; + +/// Signed transaction. +pub type TransactionSigned = EthereumTxEnvelope; + +/// Ethereum full block. +/// +/// Withdrawals can be optionally included at the end of the RLP encoded message. +pub type Block = AlloyBlock; diff --git a/crates/types/src/primitives/mod.rs b/crates/types/src/primitives/mod.rs new file mode 100644 index 00000000..88df8ef6 --- /dev/null +++ b/crates/types/src/primitives/mod.rs @@ -0,0 +1,4 @@ +mod block; +pub use block::{ + Block, BlockBody, RecoveredBlock, SealedBlock, SealedHeader, Transaction, TransactionSigned, +};