diff --git a/crates/bundle/src/lib.rs b/crates/bundle/src/lib.rs index 86b1a092..76ffcae6 100644 --- a/crates/bundle/src/lib.rs +++ b/crates/bundle/src/lib.rs @@ -51,6 +51,6 @@ pub use call::{SignetBundleDriver, SignetCallBundle, SignetCallBundleResponse}; mod send; pub use send::{ - SignetEthBundle, SignetEthBundleDriver, SignetEthBundleError, SignetEthBundleInsp, - SignetEthBundleResponse, + BundleInspector, SignetEthBundle, SignetEthBundleDriver, SignetEthBundleError, + SignetEthBundleInsp, SignetEthBundleResponse, }; diff --git a/crates/bundle/src/send/bundle.rs b/crates/bundle/src/send/bundle.rs index 5582382a..ee5baf08 100644 --- a/crates/bundle/src/send/bundle.rs +++ b/crates/bundle/src/send/bundle.rs @@ -12,7 +12,14 @@ use alloy::{ use serde::{Deserialize, Serialize}; use signet_types::{SignedFill, SignedPermitError}; use signet_zenith::HostOrders::HostOrdersInstance; -use trevm::{revm::Database, BundleError}; +use trevm::{ + inspectors::{Layered, TimeLimit}, + revm::{inspector::NoOpInspector, Database}, + BundleError, +}; + +/// The inspector type required by the Signet bundle driver. +pub type BundleInspector = Layered; /// Bundle of transactions for `signet_sendBundle`. /// diff --git a/crates/bundle/src/send/driver.rs b/crates/bundle/src/send/driver.rs index 7911f09c..45803591 100644 --- a/crates/bundle/src/send/driver.rs +++ b/crates/bundle/src/send/driver.rs @@ -1,8 +1,10 @@ use crate::send::SignetEthBundle; use alloy::primitives::U256; -use signet_evm::{DriveBundleResult, EvmNeedsTx, EvmTransacted, SignetInspector, SignetLayered}; +use signet_evm::{ + DriveBundleResult, EvmErrored, EvmNeedsTx, EvmTransacted, SignetInspector, SignetLayered, +}; use signet_types::{AggregateFills, MarketError, SignedPermitError}; -use tracing::debug; +use tracing::{debug, error}; use trevm::{ helpers::Ctx, inspectors::{Layered, TimeLimit}, @@ -143,9 +145,7 @@ impl<'a> SignetEthBundleDriver<'a> { // We check the AggregateFills here, and if it fails, we discard the // transaction outcome and push a failure receipt. - self.agg_fills.checked_remove_ru_tx_events(&agg_orders, &agg_fills).inspect_err(|err| { - debug!(%err, "Discarding transaction outcome due to market error"); - }) + self.agg_fills.checked_remove_ru_tx_events(&agg_orders, &agg_fills) } } @@ -200,6 +200,8 @@ where let txs = trevm_try!(self.bundle.decode_and_validate_txs(), trevm); for tx in txs.into_iter() { + let _span = tracing::debug_span!("bundle_tx_loop", tx_hash = %tx.hash()).entered(); + // Update the inner deadline. let limit = trevm.inner_mut_unchecked().ctx_inspector().1.outer_mut().outer_mut(); *limit = TimeLimit::new(self.deadline - std::time::Instant::now()); @@ -209,7 +211,10 @@ where // Temporary rebinding of trevm within each loop iteration. // The type of t is `EvmTransacted`, while the type of trevm is // `EvmNeedsTx`. - let mut t = trevm.run_tx(&tx).map_err(|err| err.err_into())?; + let mut t = trevm + .run_tx(&tx) + .map_err(EvmErrored::err_into) + .inspect_err(|err| error!(err = %err.error(), "error while running transaction"))?; // Check the result of the transaction. let result = t.result(); @@ -221,10 +226,14 @@ where // not, and the tx is not marked as revertible by the bundle, we // error our simulation. if result.is_success() { - if self.check_fills(&mut t).is_err() - && !self.bundle.reverting_tx_hashes().contains(tx_hash) - { - return Err(t.errored(BundleError::BundleReverted.into())); + if self.check_fills(&mut t).is_err() { + debug!("transaction dropped due to insufficient fills"); + if self.bundle.reverting_tx_hashes().contains(tx_hash) { + trevm = t.reject(); + continue; + } else { + return Err(t.errored(BundleError::BundleReverted.into())); + } } self.total_gas_used = self.total_gas_used.saturating_add(gas_used); @@ -233,13 +242,14 @@ where // not marked as revertible by the bundle, we error our // simulation. if !self.bundle.reverting_tx_hashes().contains(tx_hash) { + debug!("transaction reverted, not marked as revertible"); return Err(t.errored(BundleError::BundleReverted.into())); } self.total_gas_used = self.total_gas_used.saturating_add(gas_used); } - // If we did not shortcut return, we accept the state changes - // from this transaction. + // If we did not shortcut return/continue, we accept the state + // changes from this transaction. trevm = t.accept_state() } diff --git a/crates/bundle/src/send/mod.rs b/crates/bundle/src/send/mod.rs index 92f23d62..4e3abbc2 100644 --- a/crates/bundle/src/send/mod.rs +++ b/crates/bundle/src/send/mod.rs @@ -1,5 +1,5 @@ mod bundle; -pub use bundle::{SignetEthBundle, SignetEthBundleResponse}; +pub use bundle::{BundleInspector, SignetEthBundle, SignetEthBundleResponse}; mod driver; pub use driver::{SignetEthBundleDriver, SignetEthBundleError, SignetEthBundleInsp}; diff --git a/crates/test-utils/src/contracts/counter.rs b/crates/test-utils/src/contracts/counter.rs index 0a3a93a5..7bc136ca 100644 --- a/crates/test-utils/src/contracts/counter.rs +++ b/crates/test-utils/src/contracts/counter.rs @@ -1,5 +1,5 @@ use alloy::{ - primitives::{b256, bytes, Address, Bytes, B256}, + primitives::{b256, bytes, Address, Bytes, B256, U256}, providers::Provider, }; @@ -14,6 +14,9 @@ alloy::sol! { } } +/// The storage slot where the counter value is stored in the Counter contract. +pub const COUNTER_SLOT: U256 = U256::ZERO; + /// A test address for the Counter.sol contract, which will be pre-deployed in /// test EVMs. pub const COUNTER_TEST_ADDRESS: Address = Address::repeat_byte(0x49); diff --git a/crates/test-utils/src/contracts/mod.rs b/crates/test-utils/src/contracts/mod.rs index 3ce43243..0441a8c7 100644 --- a/crates/test-utils/src/contracts/mod.rs +++ b/crates/test-utils/src/contracts/mod.rs @@ -1,5 +1,11 @@ +/// A Counter contract that can be incremented. pub mod counter; +/// Orders, Passage, and Token system contracts. pub mod system; +/// A token contract, deployed for Wbtc and Weth. pub mod token; + +/// A contract that always reverts. +pub mod reverts; diff --git a/crates/test-utils/src/contracts/reverts.rs b/crates/test-utils/src/contracts/reverts.rs new file mode 100644 index 00000000..7a6872f6 --- /dev/null +++ b/crates/test-utils/src/contracts/reverts.rs @@ -0,0 +1,21 @@ +use crate::contracts::counter::Counter; +use alloy::{ + primitives::{bytes, Address, Bytes}, + providers::Provider, +}; + +/// A test address for the Revert contract, which will be pre-deployed in +/// test EVMs. +pub const REVERT_TEST_ADDRESS: Address = Address::repeat_byte(0x50); + +/// Revert deploycode for testing purposes. +pub const REVERT_DEPLOY_CODE: Bytes = bytes!("0x608060405234601957600e601d565b60c76028823960c790f35b6023565b60405190565b5f80fdfe608060405234156073576013565b60405190565b5f80fd5b60209181520190565b5f7f5265766572742063616c6c656400000000000000000000000000000000000000910152565b6052600d6020926017565b6059816020565b0190565b60709060208101905f8183039101526047565b90565b6079600d565b62461bcd60e51b815280608d60048201605d565b0390fdfea2646970667358221220ca26ecb1412cc6e87fe99712d1085a242349863e03e3e43ef6ee7dde7a3478cc64736f6c634300081a0033"); + +/// Revert bytecode for testing purposes. +pub const REVERT_BYTECODE: Bytes = bytes!( + "0x608060405234156073576013565b60405190565b5f80fd5b60209181520190565b5f7f5265766572742063616c6c656400000000000000000000000000000000000000910152565b6052600d6020926017565b6059816020565b0190565b60709060208101905f8183039101526047565b90565b6079600d565b62461bcd60e51b815280608d60048201605d565b0390fdfea2646970667358221220ca26ecb1412cc6e87fe99712d1085a242349863e03e3e43ef6ee7dde7a3478cc64736f6c634300081a0033"); + +/// Get an instance of the pre-deployed Counter contract. +pub fn revert(p: P) -> Counter::CounterInstance

{ + Counter::CounterInstance::new(REVERT_TEST_ADDRESS, p) +} diff --git a/crates/test-utils/src/evm.rs b/crates/test-utils/src/evm.rs index 1d3a6c44..adc7eab9 100644 --- a/crates/test-utils/src/evm.rs +++ b/crates/test-utils/src/evm.rs @@ -1,22 +1,57 @@ -use signet_constants::test_utils::*; -use trevm::revm::{ - context::CfgEnv, database::in_memory_db::InMemoryDB, primitives::hardfork::SpecId, - state::Bytecode, +use crate::{ + contracts::{ + counter::{COUNTER_BYTECODE, COUNTER_TEST_ADDRESS}, + reverts::{REVERT_BYTECODE, REVERT_TEST_ADDRESS}, + system::{RU_ORDERS_BYTECODE, RU_PASSAGE_BYTECODE}, + token::{ + MINTER, MINTER_SLOT, NAME_SLOT, SYMBOL_SLOT, TOKEN_BYTECODE, WBTC_NAME, WBTC_SYMBOL, + WETH_NAME, WETH_SYMBOL, + }, + }, + users::TEST_USERS, }; - -use crate::contracts::{ - counter::{COUNTER_BYTECODE, COUNTER_TEST_ADDRESS}, - system::{RU_ORDERS_BYTECODE, RU_PASSAGE_BYTECODE}, - token::{ - MINTER, MINTER_SLOT, NAME_SLOT, SYMBOL_SLOT, TOKEN_BYTECODE, WBTC_NAME, WBTC_SYMBOL, - WETH_NAME, WETH_SYMBOL, +use alloy::{consensus::constants::ETH_TO_WEI, primitives::U256}; +use signet_constants::test_utils::*; +use trevm::{ + helpers::Ctx, + revm::{ + context::CfgEnv, database::in_memory_db::InMemoryDB, inspector::NoOpInspector, + primitives::hardfork::SpecId, state::Bytecode, Inspector, }, }; -/// Create a new Signet EVM with an in-memory database for testing. Deploy -/// system contracts and pre-deployed tokens. +/// Create a new Signet EVM with an in-memory database for testing. +/// +/// Performs initial setup to +/// - Deploy [`RU_ORDERS`] and and [`RU_PASSAGE`] system contracts +/// - Deploy a [`COUNTER`] contract for testing at [`COUNTER_TEST_ADDRESS`]. +/// - Deploy Token contracts for WBTC and WETH with their respective bytecodes +/// and storage. +/// - Deploy a `Revert` contract for testing at [`REVERT_TEST_ADDRESS`]. +/// - Fund the [`TEST_USERS`] with 1000 ETH each. +/// +/// [`COUNTER`]: crate::contracts::counter::Counter pub fn test_signet_evm() -> signet_evm::EvmNeedsBlock { - let mut evm = signet_evm::signet_evm(InMemoryDB::default(), TEST_SYS).fill_cfg(&TestCfg); + test_signet_evm_with_inspector(NoOpInspector) +} + +/// Create a new Signet EVM with an in-memory database for testing. +/// +/// Performs initial setup to +/// - Deploy [`RU_ORDERS`] and and [`RU_PASSAGE`] system contracts +/// - Deploy a [`COUNTER`] contract for testing at [`COUNTER_TEST_ADDRESS`]. +/// - Deploy Token contracts for WBTC and WETH with their respective bytecodes +/// and storage. +/// - Deploy a `Revert` contract for testing at [`REVERT_TEST_ADDRESS`]. +/// - Fund the [`TEST_USERS`] with 1000 ETH each. +/// +/// [`COUNTER`]: crate::contracts::counter::Counter +pub fn test_signet_evm_with_inspector(inspector: I) -> signet_evm::EvmNeedsBlock +where + I: Inspector>, +{ + let mut evm = signet_evm::signet_evm_with_inspector(InMemoryDB::default(), inspector, TEST_SYS) + .fill_cfg(&TestCfg); // Set the bytecode for system contracts evm.set_bytecode_unchecked(TEST_SYS.ru_orders(), Bytecode::new_legacy(RU_ORDERS_BYTECODE)); @@ -37,6 +72,14 @@ pub fn test_signet_evm() -> signet_evm::EvmNeedsBlock { // Set the bytecode for the Counter contract evm.set_bytecode_unchecked(COUNTER_TEST_ADDRESS, Bytecode::new_legacy(COUNTER_BYTECODE)); + // Set the bytecode for the Revert contract + evm.set_bytecode_unchecked(REVERT_TEST_ADDRESS, Bytecode::new_legacy(REVERT_BYTECODE)); + + // increment the balance for each test signer + TEST_USERS.iter().copied().for_each(|user| { + evm.set_balance_unchecked(user, U256::from(1000 * ETH_TO_WEI)); + }); + evm } diff --git a/crates/test-utils/src/specs/mod.rs b/crates/test-utils/src/specs/mod.rs index 7db757b0..918243aa 100644 --- a/crates/test-utils/src/specs/mod.rs +++ b/crates/test-utils/src/specs/mod.rs @@ -8,16 +8,22 @@ mod ru_spec; pub use ru_spec::RuBlockSpec; use alloy::{ - consensus::{constants::GWEI_TO_WEI, SignableTransaction, TxEip1559}, + consensus::{ + constants::GWEI_TO_WEI, SignableTransaction, TxEip1559, TxEnvelope, TypedTransaction, + }, + eips::Encodable2718, primitives::{Address, TxKind, B256, U256}, + rpc::types::mev::EthSendBundle, signers::{local::PrivateKeySigner, SignerSync}, + sol_types::SolCall, }; -use signet_types::primitives::{Transaction, TransactionSigned}; +use signet_bundle::SignetEthBundle; +use signet_types::SignedFill; /// Sign a transaction with a wallet. -pub fn sign_tx_with_key_pair(wallet: &PrivateKeySigner, tx: Transaction) -> TransactionSigned { +pub fn sign_tx_with_key_pair(wallet: &PrivateKeySigner, tx: TypedTransaction) -> TxEnvelope { let signature = wallet.sign_hash_sync(&tx.signature_hash()).unwrap(); - TransactionSigned::new_unhashed(tx, signature) + TxEnvelope::new_unhashed(tx, signature) } /// Make a wallet with a deterministic keypair. @@ -26,7 +32,7 @@ pub fn make_wallet(i: u8) -> PrivateKeySigner { } /// Make a simple send transaction. -pub fn simple_send(to: Address, amount: U256, nonce: u64, ru_chain_id: u64) -> Transaction { +pub fn simple_send(to: Address, amount: U256, nonce: u64, ru_chain_id: u64) -> TypedTransaction { TxEip1559 { nonce, gas_limit: 21_000, @@ -39,3 +45,54 @@ pub fn simple_send(to: Address, amount: U256, nonce: u64, ru_chain_id: u64) -> T } .into() } + +/// Make a simple contract call transaction. +pub fn simple_call( + to: Address, + input: &T, + value: U256, + nonce: u64, + ru_chain_id: u64, +) -> TypedTransaction +where + T: SolCall, +{ + TxEip1559 { + nonce, + gas_limit: 2_100_000, + to: TxKind::Call(to), + value, + 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, + input: input.abi_encode().into(), + ..Default::default() + } + .into() +} + +/// Create a simple bundle from a list of transactions. +pub fn simple_bundle<'a>( + txs: impl IntoIterator, + host_fills: Option, + block_number: u64, +) -> SignetEthBundle { + let txs = txs.into_iter().map(|tx| tx.encoded_2718().into()).collect(); + + SignetEthBundle { + bundle: EthSendBundle { + txs, + block_number, + min_timestamp: None, + max_timestamp: None, + reverting_tx_hashes: vec![], + replacement_uuid: None, + dropping_tx_hashes: vec![], + refund_percent: None, + refund_recipient: None, + refund_tx_hashes: vec![], + extra_fields: Default::default(), + }, + host_fills, + } +} diff --git a/crates/test-utils/src/specs/ru_spec.rs b/crates/test-utils/src/specs/ru_spec.rs index 77ebca61..5531acf9 100644 --- a/crates/test-utils/src/specs/ru_spec.rs +++ b/crates/test-utils/src/specs/ru_spec.rs @@ -8,10 +8,7 @@ use alloy::{ }; use signet_constants::test_utils::*; use signet_extract::{Extractable, Extracts}; -use signet_types::{ - constants::{KnownChains, ParseChainError, SignetSystemConstants}, - primitives::TransactionSigned, -}; +use signet_types::constants::{KnownChains, ParseChainError, SignetSystemConstants}; use signet_zenith::Zenith::{self}; use std::str::FromStr; @@ -66,7 +63,7 @@ impl RuBlockSpec { } /// Add a transaction to the block. - pub fn add_tx(&mut self, tx: &TransactionSigned) { + pub fn add_tx(&mut self, tx: &TxEnvelope) { self.tx.push(tx.encoded_2718()); } @@ -81,7 +78,7 @@ impl RuBlockSpec { } /// Add a transaction to the block, returning self. - pub fn tx(mut self, tx: &TransactionSigned) -> Self { + pub fn tx(mut self, tx: &TxEnvelope) -> Self { self.add_tx(tx); self } @@ -99,7 +96,7 @@ impl RuBlockSpec { to: Address, amount: U256, nonce: u64, - ) -> TransactionSigned { + ) -> TxEnvelope { let tx = sign_tx_with_key_pair( wallet, simple_send(to, amount, nonce, self.constants.ru_chain_id()), diff --git a/crates/test-utils/tests/basic_sim.rs b/crates/test-utils/tests/basic_sim.rs index 50aab433..8653972e 100644 --- a/crates/test-utils/tests/basic_sim.rs +++ b/crates/test-utils/tests/basic_sim.rs @@ -85,6 +85,10 @@ pub async fn test_simulator() { dbg!(time.elapsed()); } +// utilities below this point are reproduced from other places, however, +// because this test modifies the _db_ rather than the _evm_, +// we need to handle them slightly differently here. + /// Modify an account with a closure and commit the modified account. /// /// This code is reproduced and modified from trevm diff --git a/crates/test-utils/tests/bundle.rs b/crates/test-utils/tests/bundle.rs index 7c507ea7..64706f55 100644 --- a/crates/test-utils/tests/bundle.rs +++ b/crates/test-utils/tests/bundle.rs @@ -3,5 +3,301 @@ //! - Txns must not revert, unless marked as revertible. //! - Txns must not be dropped by market rules, unless marked as droppable. -#[tokio::test] -async fn test_bundle_guarantees() {} +use alloy::{ + consensus::TypedTransaction, + primitives::{keccak256, Address, U256}, + signers::local::PrivateKeySigner, + uint, +}; +use signet_bundle::{ + BundleInspector, SignetEthBundle, SignetEthBundleDriver, SignetEthBundleError, +}; +use signet_constants::pecorino::{HOST_WBTC, HOST_WETH}; +use signet_evm::EvmNeedsTx; +use signet_test_utils::{ + chain::{HOST_CHAIN_ID, RU_CHAIN_ID, RU_ORDERS, TEST_SYS}, + contracts::{ + counter::{Counter, COUNTER_SLOT, COUNTER_TEST_ADDRESS}, + reverts::REVERT_TEST_ADDRESS, + }, + evm::test_signet_evm_with_inspector, + specs::{sign_tx_with_key_pair, simple_bundle, simple_call, simple_send}, + users::{TEST_SIGNERS, TEST_USERS}, +}; +use signet_types::SignedFill; +use signet_zenith::HostOrders::{ + initiateCall, Input, Output, Permit2Batch, PermitBatchTransferFrom, TokenPermissions, +}; +use std::{ + sync::LazyLock, + time::{Duration, Instant}, +}; +use trevm::{ + inspectors::{Layered, TimeLimit}, + revm::{database::InMemoryDB, inspector::NoOpInspector}, + BundleDriver, BundleError, NoopBlock, +}; + +static SENDER_WALLET: LazyLock<&PrivateKeySigner> = LazyLock::new(|| &TEST_SIGNERS[0]); + +static ORDERER: LazyLock

= LazyLock::new(|| TEST_USERS[1]); +static ORDERER_WALLET: LazyLock<&PrivateKeySigner> = LazyLock::new(|| &TEST_SIGNERS[1]); + +const FILLER: Address = Address::repeat_byte(0x30); +const TX_0_RECIPIENT: Address = Address::repeat_byte(0x31); +const TX_2_RECIPIENT: Address = Address::repeat_byte(0x32); + +const INPUT_AMOUNT: U256 = uint!(100_000_000_000_000_000_000_U256); +const OUTPUT_WBTC: U256 = uint!(100_U256); +const OUTPUT_WETH: U256 = uint!(200_U256); + +fn bundle_evm() -> EvmNeedsTx { + let inspector: BundleInspector<_> = + Layered::new(TimeLimit::new(Duration::from_secs(5)), NoOpInspector); + test_signet_evm_with_inspector(inspector).fill_block(&NoopBlock) +} + +fn permit_2_batch(owner: Address, nonce: U256) -> Permit2Batch { + Permit2Batch { + permit: PermitBatchTransferFrom { + permitted: vec![ + TokenPermissions { token: HOST_WBTC, amount: OUTPUT_WBTC }, + TokenPermissions { token: HOST_WETH, amount: OUTPUT_WETH }, + ], + nonce, + deadline: U256::MAX, + }, + owner, + signature: Default::default(), + } +} + +fn host_fills(owner: Address, nonce: U256) -> SignedFill { + let permit = permit_2_batch(owner, nonce); + let outputs = vec![ + Output { + token: HOST_WBTC, + amount: OUTPUT_WBTC, + recipient: TX_0_RECIPIENT, + chainId: RU_CHAIN_ID as u32, + }, + Output { + token: HOST_WETH, + amount: OUTPUT_WETH, + recipient: TX_2_RECIPIENT, + chainId: RU_CHAIN_ID as u32, + }, + ]; + SignedFill { permit, outputs } +} + +fn simple_order(nonce: u64) -> TypedTransaction { + simple_call( + RU_ORDERS, + &initiateCall { + deadline: U256::MAX, + inputs: vec![Input { token: Address::ZERO, amount: INPUT_AMOUNT }], + outputs: vec![ + Output { + token: HOST_WBTC, + amount: OUTPUT_WBTC, + recipient: TX_0_RECIPIENT, + chainId: HOST_CHAIN_ID as u32, + }, + Output { + token: HOST_WETH, + amount: OUTPUT_WETH, + recipient: TX_2_RECIPIENT, + chainId: HOST_CHAIN_ID as u32, + }, + ], + }, + INPUT_AMOUNT, + nonce, + RU_CHAIN_ID, + ) +} + +// This bundle contains two transactions: +// 1. A simple send from user 0 to 0x3131.... +// 2. A call to the Counter contract to increment the counter. +// 3. A simple send from user 0 to 0x3232.... +fn test_bundle( + counter_reverts: bool, + order: bool, + host_fills: Option, +) -> SignetEthBundle { + let call_addr = if counter_reverts { REVERT_TEST_ADDRESS } else { COUNTER_TEST_ADDRESS }; + + let tx_1 = simple_send(TX_0_RECIPIENT, U256::ONE, 0, RU_CHAIN_ID); + let tx_2 = if order { + simple_order(0) + } else { + simple_call(call_addr, &Counter::incrementCall, U256::ZERO, 0, RU_CHAIN_ID) + }; + let tx_3 = simple_send(TX_2_RECIPIENT, U256::ONE, 1, RU_CHAIN_ID); + + let tx_1 = sign_tx_with_key_pair(&SENDER_WALLET, tx_1); + let tx_2 = sign_tx_with_key_pair(&ORDERER_WALLET, tx_2); + let tx_3 = sign_tx_with_key_pair(&SENDER_WALLET, tx_3); + + simple_bundle(&[tx_1, tx_2, tx_3], host_fills, 0) +} + +fn order_bundle(host_fills: Option) -> SignetEthBundle { + test_bundle(false, true, host_fills) +} + +fn counter_bundle(should_revert: bool) -> SignetEthBundle { + test_bundle(should_revert, false, None) +} + +#[test] +fn test_bundle_ok() { + let trevm = bundle_evm(); + + let bundle = counter_bundle(false); + + let mut driver = SignetEthBundleDriver::new( + &bundle, + TEST_SYS.host_chain_id(), + Instant::now() + Duration::from_secs(5), + ); + + // We expect this to work. + let trevm = driver.run_bundle(trevm).unwrap(); + + // Assert that the bundle was executed successfully. + + // Check the balance of the recipient increased. + assert_eq!(trevm.read_balance_ref(TX_0_RECIPIENT), U256::ONE); + assert_eq!(trevm.read_storage_ref(COUNTER_TEST_ADDRESS, COUNTER_SLOT), U256::from(1)); + assert_eq!(trevm.read_balance_ref(TX_2_RECIPIENT), U256::ONE); +} + +#[test] +fn test_bundle_revert() { + let trevm = bundle_evm(); + + let bundle = counter_bundle(true); + + let mut driver = SignetEthBundleDriver::new( + &bundle, + TEST_SYS.host_chain_id(), + Instant::now() + Duration::from_secs(5), + ); + + let (err, trevm) = driver.run_bundle(trevm).unwrap_err().take_err(); + assert!(matches!(err, SignetEthBundleError::BundleError(BundleError::BundleReverted))); + + // Erroring leaves the evm in a dirty state. The first txn was executed, + // the second reverted, and the third was not executed. + assert_eq!(trevm.read_balance_ref(TX_0_RECIPIENT), U256::ONE); + assert_eq!(trevm.read_storage_ref(COUNTER_TEST_ADDRESS, COUNTER_SLOT), U256::ZERO); + assert_eq!(trevm.read_balance_ref(TX_2_RECIPIENT), U256::ZERO); +} + +#[test] +fn test_bundle_droppable() { + let trevm = bundle_evm(); + + let mut bundle = counter_bundle(true); + + // Mark the second transaction as droppable. + let hash = keccak256(&bundle.txs()[1]); + bundle.bundle.reverting_tx_hashes.push(hash); + + let mut driver = SignetEthBundleDriver::new( + &bundle, + TEST_SYS.host_chain_id(), + Instant::now() + Duration::from_secs(5), + ); + + // We expect this to work and drop the second transaction. + let trevm = driver.run_bundle(trevm).unwrap(); + + // Check the balance of the recipients increased, and the counter was not incremented. + assert_eq!(trevm.read_balance_ref(TX_0_RECIPIENT), U256::ONE); + assert_eq!(trevm.read_storage_ref(COUNTER_TEST_ADDRESS, COUNTER_SLOT), U256::ZERO); + assert_eq!(trevm.read_balance_ref(TX_2_RECIPIENT), U256::ONE); +} + +#[test] +fn test_order_bundle() { + let trevm = bundle_evm(); + + let inital_balance = trevm.read_balance_ref(*ORDERER); + + let host_fills = host_fills(FILLER, U256::from(0)); + + let bundle = order_bundle(Some(host_fills)); + + let mut driver = SignetEthBundleDriver::new( + &bundle, + TEST_SYS.host_chain_id(), + Instant::now() + Duration::from_secs(5), + ); + + // We expect this to work and drop the second transaction. + let trevm = driver.run_bundle(trevm).unwrap(); + + // Check the balance of the recipients increased, and the balance of the + // sender decreased by at least the input amount. + let post_balance = trevm.read_balance_ref(*ORDERER); + + assert_eq!(trevm.read_balance_ref(TX_0_RECIPIENT), U256::ONE); + assert!(post_balance < inital_balance - INPUT_AMOUNT); + assert_eq!(trevm.read_balance_ref(TX_2_RECIPIENT), U256::ONE); +} + +#[test] +fn test_order_bundle_revert() { + let trevm = bundle_evm(); + + let inital_balance = trevm.read_balance_ref(*ORDERER); + + // This should cause the order to be invalid, as no fill is provided. + let bundle = order_bundle(None); + + let mut driver = SignetEthBundleDriver::new( + &bundle, + TEST_SYS.host_chain_id(), + Instant::now() + Duration::from_secs(5), + ); + + let (err, trevm) = driver.run_bundle(trevm).unwrap_err().take_err(); + assert!(matches!(err, SignetEthBundleError::BundleError(BundleError::BundleReverted))); + + // Erroring leaves the evm in a dirty state. The first txn was executed, + // the second was dropped, and the third was not executed. + assert_eq!(trevm.read_balance_ref(TX_0_RECIPIENT), U256::ONE); + assert_eq!(trevm.read_balance_ref(*ORDERER), inital_balance); + assert_eq!(trevm.read_balance_ref(TX_2_RECIPIENT), U256::ZERO); +} + +#[test] +fn test_order_bundle_droppable() { + let trevm = bundle_evm(); + + let inital_balance = trevm.read_balance_ref(*ORDERER); + + let mut bundle = order_bundle(None); + + // Mark the second transaction as droppable. + let hash = keccak256(&bundle.txs()[1]); + bundle.bundle.reverting_tx_hashes.push(hash); + + let mut driver = SignetEthBundleDriver::new( + &bundle, + TEST_SYS.host_chain_id(), + Instant::now() + Duration::from_secs(5), + ); + + // We expect this to work and drop the second transaction. + let trevm = driver.run_bundle(trevm).unwrap(); + + // The order tx was dropped, but both sends were executed. + assert_eq!(trevm.read_balance_ref(TX_0_RECIPIENT), U256::ONE); + assert_eq!(trevm.read_balance_ref(*ORDERER), inital_balance); + assert_eq!(trevm.read_balance_ref(TX_2_RECIPIENT), U256::ONE); +} diff --git a/crates/test-utils/tests/evm.rs b/crates/test-utils/tests/evm.rs index 2d1bf97c..1d8fe9f2 100644 --- a/crates/test-utils/tests/evm.rs +++ b/crates/test-utils/tests/evm.rs @@ -1,7 +1,7 @@ use alloy::{ consensus::{ constants::{ETH_TO_WEI, GWEI_TO_WEI}, - Header, ReceiptEnvelope, TxEip1559, + Header, ReceiptEnvelope, TxEip1559, TxEnvelope, }, primitives::{Address, U256}, signers::{local::PrivateKeySigner, Signature}, @@ -68,7 +68,7 @@ impl TestEnv { block } - fn signed_simple_send(&mut self, from: usize, to: Address, amount: U256) -> TransactionSigned { + fn signed_simple_send(&mut self, from: usize, to: Address, amount: U256) -> TxEnvelope { 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); @@ -88,7 +88,7 @@ fn test_simple_send() { // Setup the driver let block = context.next_block(); let mut extracts = Extracts::::empty(&block); - let mut driver = context.driver(&mut extracts, vec![tx.clone()]); + let mut driver = context.driver(&mut extracts, vec![tx.clone().into()]); // Run the EVM let mut trevm = context.trevm().drive_block(&mut driver).unwrap(); @@ -96,7 +96,7 @@ fn test_simple_send() { // 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!(sealed_block.block.body.transactions().next(), Some(&tx.clone().into())); assert_eq!(receipts.len(), 1); assert_eq!(trevm.read_balance(to), U256::from(100)); @@ -116,7 +116,7 @@ fn test_two_sends() { // 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()]); + let mut driver = context.driver(&mut extracts, vec![tx1.clone().into(), tx2.clone().into()]); // Run the EVM let mut trevm = context.trevm().drive_block(&mut driver).unwrap(); @@ -124,7 +124,10 @@ fn test_two_sends() { // 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!( + sealed_block.block.body.transactions().collect::>(), + vec![&tx1.clone().into(), &tx2.clone().into()] + ); assert_eq!(receipts.len(), 2); assert_eq!(trevm.read_balance(to), U256::from(100)); @@ -142,14 +145,17 @@ fn test_execute_two_blocks() { // Setup the driver let block = context.next_block(); let mut extracts = Extracts::::empty(&block); - let mut driver = context.driver(&mut extracts, vec![tx.clone()]); + let mut driver = context.driver(&mut extracts, vec![tx.clone().into()]); // 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!( + sealed_block.block.body.transactions().collect::>(), + vec![&tx.clone().into()] + ); assert_eq!(receipts.len(), 1); assert_eq!(trevm.read_balance(to), U256::from(100)); assert_eq!(trevm.read_nonce(sender), 1); @@ -161,14 +167,17 @@ fn test_execute_two_blocks() { // Setup the driver let block = context.next_block(); let mut extracts = Extracts::::empty(&block); - let mut driver = context.driver(&mut extracts, vec![tx.clone()]); + let mut driver = context.driver(&mut extracts, vec![tx.clone().into()]); // 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!( + sealed_block.block.body.transactions().collect::>(), + vec![&tx.clone().into()] + ); assert_eq!(receipts.len(), 1); assert_eq!(trevm.read_balance(to), U256::from(200)); }