diff --git a/Cargo.lock b/Cargo.lock index 0f15190..e747958 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4588,7 +4588,6 @@ dependencies = [ "alloy-consensus", "alloy-primitives", "alloy-rpc-types", - "anvil-core", "color-eyre", "hashbrown", "kona-primitives", @@ -4670,6 +4669,7 @@ name = "opt8n" version = "0.2.0" dependencies = [ "alloy-eips", + "alloy-primitives", "alloy-rpc-types", "anvil", "anvil-core", @@ -4682,6 +4682,7 @@ dependencies = [ "futures", "http-body-util", "hyper 1.4.1", + "op-alloy-consensus", "op-alloy-rpc-types", "op-test-vectors", "reqwest", diff --git a/bin/opt8n/Cargo.toml b/bin/opt8n/Cargo.toml index 2cbc4cd..b297c89 100644 --- a/bin/opt8n/Cargo.toml +++ b/bin/opt8n/Cargo.toml @@ -26,6 +26,7 @@ shellwords.workspace = true # Alloy alloy-eips.workspace = true alloy-rpc-types.workspace = true +alloy-primitives.workspace = true # Foundry foundry-common.workspace = true @@ -38,6 +39,7 @@ revm.workspace = true # OP Types op-test-vectors.workspace = true op-alloy-rpc-types.workspace = true +op-alloy-consensus.workspace = true thiserror.workspace = true reqwest.workspace = true -hyper = "1.4.1" \ No newline at end of file +hyper = "1.4.1" diff --git a/bin/opt8n/src/opt8n.rs b/bin/opt8n/src/opt8n.rs index b41d159..0c203b9 100644 --- a/bin/opt8n/src/opt8n.rs +++ b/bin/opt8n/src/opt8n.rs @@ -2,12 +2,19 @@ use alloy_eips::eip2718::Encodable2718; use alloy_eips::BlockId; -use alloy_rpc_types::trace::geth::{PreStateConfig, PreStateFrame}; +use alloy_rpc_types::{ + trace::geth::{PreStateConfig, PreStateFrame}, + TransactionReceipt, +}; use anvil::{cmd::NodeArgs, eth::EthApi, NodeConfig, NodeHandle}; -use anvil_core::eth::block::Block; -use anvil_core::eth::transaction::PendingTransaction; +use anvil_core::eth::transaction::{PendingTransaction, TypedTransaction}; +use anvil_core::eth::{block::Block, transaction::TypedReceipt}; use cast::traces::{GethTraceBuilder, TracingInspectorConfig}; use clap::Parser; +use op_alloy_consensus::{ + OpDepositReceipt, OpDepositReceiptWithBloom, OpReceiptEnvelope, OpTypedTransaction, TxDeposit, +}; +use op_alloy_rpc_types::OpTransactionReceipt; use std::{ error::Error, fs::{self, File}, @@ -15,7 +22,7 @@ use std::{ }; use color_eyre::eyre::{ensure, eyre, Result}; -use op_test_vectors::execution::{ExecutionFixture, ExecutionReceipt, ExecutionResult}; +use op_test_vectors::execution::{ExecutionEnvironment, ExecutionFixture, ExecutionResult}; use revm::{ db::{AlloyDB, CacheDB}, primitives::{BlobExcessGasAndPrice, BlockEnv, CfgEnv, Env, SpecId, U256}, @@ -142,7 +149,7 @@ impl Opt8n { self.capture_pre_post_alloc(&block)?; // Append block transactions and receipts to the execution fixture - let mut receipts: Vec = Vec::with_capacity(block.transactions.len()); + let mut receipts: Vec = Vec::with_capacity(block.transactions.len()); for tx in block.transactions.iter() { if let Some(receipt) = self .eth_api @@ -150,11 +157,12 @@ impl Opt8n { .transaction_receipt(tx.transaction.hash()) .await? { - receipts.push(receipt.try_into()?); + let op_receipt = tx_receipt_to_op_tx_receipt(receipt); + receipts.push(op_receipt); } - self.execution_fixture - .transactions - .push(tx.transaction.clone()); + + let op_tx = typed_tx_to_op_typed_tx(&tx.transaction); + self.execution_fixture.transactions.push(op_tx); } let block_header = &block.header; @@ -166,7 +174,17 @@ impl Opt8n { receipts, }; - self.execution_fixture.env = block.into(); + let execution_environment = ExecutionEnvironment { + current_coinbase: block_header.beneficiary, + current_difficulty: block_header.difficulty, + current_gas_limit: U256::from(block.header.gas_limit), + previous_hash: block_header.parent_hash, + current_number: U256::from(block.header.number), + current_timestamp: U256::from(block_header.timestamp), + block_hashes: None, + }; + + self.execution_fixture.env = execution_environment; self.execution_fixture.result = execution_result; // Ensure pre and post states are different @@ -183,6 +201,94 @@ impl Opt8n { } } +// TODO: Consider adding `From` implementation for +// `TypedTransaction` -> `OpTypedTransaction` in `op-alloy-consensus` +fn typed_tx_to_op_typed_tx(tx: &TypedTransaction) -> OpTypedTransaction { + let op_tx = match tx { + TypedTransaction::Legacy(signed_tx) => OpTypedTransaction::Legacy(signed_tx.tx().clone()), + TypedTransaction::EIP2930(signed_tx) => OpTypedTransaction::Eip2930(signed_tx.tx().clone()), + + TypedTransaction::EIP1559(signed_tx) => OpTypedTransaction::Eip1559(signed_tx.tx().clone()), + TypedTransaction::EIP4844(signed_tx) => OpTypedTransaction::Eip4844(signed_tx.tx().clone()), + TypedTransaction::Deposit(deposit_tx) => { + let op_deposit_tx = TxDeposit { + source_hash: deposit_tx.source_hash, + from: deposit_tx.from, + to: deposit_tx.kind, + mint: Some( + deposit_tx + .mint + .try_into() + .expect("Mint is greater than u128"), + ), + value: deposit_tx.value, + gas_limit: deposit_tx.gas_limit, + is_system_transaction: deposit_tx.is_system_tx, + input: deposit_tx.input.clone(), + }; + + OpTypedTransaction::Deposit(op_deposit_tx) + } + TypedTransaction::EIP7702(_) => { + unimplemented!("EIP7702 not implemented") + } + }; + + op_tx +} + +// TODO: Consider adding `From` implementation for +// `TransactionReceipt` -> `OpTransactionReceipt` in `op-alloy-consensus` +fn tx_receipt_to_op_tx_receipt( + receipt: TransactionReceipt>, +) -> OpTransactionReceipt { + let receipt_envelope = receipt.inner; + let op_receipt_envelope = match receipt_envelope { + TypedReceipt::Legacy(receipt_with_bloom) => OpReceiptEnvelope::Legacy(receipt_with_bloom), + TypedReceipt::EIP2930(receipt_with_bloom) => OpReceiptEnvelope::Eip2930(receipt_with_bloom), + TypedReceipt::EIP1559(receipt_with_bloom) => OpReceiptEnvelope::Eip1559(receipt_with_bloom), + TypedReceipt::EIP4844(receipt_with_bloom) => OpReceiptEnvelope::Eip4844(receipt_with_bloom), + TypedReceipt::EIP7702(_) => { + unimplemented!("EIP7702 not implemented") + } + TypedReceipt::Deposit(deposit_receipt) => { + let op_deposit_receipt = OpDepositReceipt { + inner: deposit_receipt.inner.receipt, + deposit_nonce: deposit_receipt.deposit_nonce, + deposit_receipt_version: deposit_receipt.deposit_receipt_version, + }; + + let op_deposit_receipt_with_bloom = OpDepositReceiptWithBloom { + receipt: op_deposit_receipt, + logs_bloom: deposit_receipt.inner.logs_bloom, + }; + + OpReceiptEnvelope::Deposit(op_deposit_receipt_with_bloom) + } + }; + + + + OpTransactionReceipt { + inner: TransactionReceipt { + inner: op_receipt_envelope, + transaction_hash: receipt.transaction_hash, + transaction_index: receipt.transaction_index, + block_hash: receipt.block_hash, + block_number: receipt.block_number, + gas_used: receipt.gas_used, + effective_gas_price: receipt.effective_gas_price, + blob_gas_used: receipt.blob_gas_used, + blob_gas_price: receipt.blob_gas_price, + from: receipt.from, + to: receipt.to, + contract_address: receipt.contract_address, + state_root: receipt.state_root, + authorization_list: receipt.authorization_list, + }, + } +} + /// Creates a new EVM instance from a given block, chain, database, and spec id. pub fn evm<'a, DB>(block: &Block, chain_id: u64, db: DB, spec_id: SpecId) -> Evm<'a, (), Box> where diff --git a/crates/op-test-vectors/Cargo.toml b/crates/op-test-vectors/Cargo.toml index 5f2850c..95eff10 100644 --- a/crates/op-test-vectors/Cargo.toml +++ b/crates/op-test-vectors/Cargo.toml @@ -15,9 +15,6 @@ serde.workspace = true color-eyre.workspace = true hashbrown.workspace = true -# Foundry -anvil-core.workspace = true - # Alloy alloy-rpc-types.workspace = true alloy-primitives.workspace = true diff --git a/crates/op-test-vectors/src/execution.rs b/crates/op-test-vectors/src/execution.rs index a9c8a7f..5e889fb 100644 --- a/crates/op-test-vectors/src/execution.rs +++ b/crates/op-test-vectors/src/execution.rs @@ -2,10 +2,9 @@ use alloy_primitives::{Address, Bloom, B256, U256}; use alloy_rpc_types::trace::geth::AccountState; -use alloy_rpc_types::{Log, TransactionReceipt}; -use anvil_core::eth::block::Block; -use anvil_core::eth::transaction::{TypedReceipt, TypedTransaction}; -use color_eyre::eyre; + +use op_alloy_consensus::OpTypedTransaction; +use op_alloy_rpc_types::OpTransactionReceipt; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -24,7 +23,7 @@ pub struct ExecutionFixture { pub out_alloc: HashMap, /// Transactions to execute. #[serde(rename = "txs")] - pub transactions: Vec, + pub transactions: Vec, /// The expected result after executing transactions. pub result: ExecutionResult, } @@ -51,20 +50,6 @@ pub struct ExecutionEnvironment { pub block_hashes: Option>, } -impl From for ExecutionEnvironment { - fn from(block: Block) -> Self { - Self { - current_coinbase: block.header.beneficiary, - current_difficulty: block.header.difficulty, - current_gas_limit: U256::from(block.header.gas_limit), - previous_hash: block.header.parent_hash, - current_number: U256::from(block.header.number), - current_timestamp: U256::from(block.header.timestamp), - block_hashes: None, - } - } -} - /// The execution result is the expected result after running the transactions /// in the execution environment over the pre-state. #[derive(Serialize, Deserialize, Debug, Default)] @@ -79,53 +64,7 @@ pub struct ExecutionResult { /// The logs bloom. pub logs_bloom: Bloom, /// A list of execution receipts for each executed transaction. - pub receipts: Vec, -} - -/// An execution receipt is the result of running a transaction in the execution environment. -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct ExecutionReceipt { - /// The state root. - pub root: B256, - /// The hash of the transaction. - pub transaction_hash: B256, - /// The contract address that the transaction created. - #[serde(skip_serializing_if = "Option::is_none")] - pub contract_address: Option
, - /// The gas used by the transaction. - pub gas_used: U256, - /// The block hash. - pub block_hash: B256, - /// The transaction index. - pub transaction_index: U256, - /// The inner log receipt. - #[serde(flatten)] - pub inner: TypedReceipt, -} - -impl TryFrom>> for ExecutionReceipt { - type Error = eyre::Error; - - fn try_from(receipt: TransactionReceipt>) -> eyre::Result { - Ok(Self { - transaction_hash: receipt.transaction_hash, - root: receipt - .state_root - .ok_or_else(|| eyre::eyre!("missing state root"))?, - contract_address: receipt.contract_address, - gas_used: U256::from(receipt.gas_used), - block_hash: receipt - .block_hash - .ok_or_else(|| eyre::eyre!("missing block hash"))?, - transaction_index: U256::from( - receipt - .transaction_index - .ok_or_else(|| eyre::eyre!("missing transaction index"))?, - ), - inner: receipt.inner, - }) - } + pub receipts: Vec, } #[cfg(test)] @@ -159,53 +98,4 @@ mod tests { .expect("failed to parse expected result"); assert_eq!(serialized_value, expected_value); } - - #[test] - fn test_exec_receipt_try_from_tx_receipt() { - let tx_receipt_str = include_str!("./testdata/tx_receipt.json"); - let tx_receipt: TransactionReceipt> = - serde_json::from_str(tx_receipt_str).expect("failed to parse tx receipt"); - let exec_receipt = ExecutionReceipt::try_from(tx_receipt.clone()) - .expect("failed to convert tx receipt to exec receipt"); - assert_eq!(exec_receipt.transaction_hash, tx_receipt.transaction_hash); - assert_eq!(exec_receipt.root, tx_receipt.state_root.unwrap()); - assert_eq!(exec_receipt.contract_address, tx_receipt.contract_address); - assert_eq!(exec_receipt.gas_used, U256::from(tx_receipt.gas_used)); - assert_eq!(exec_receipt.block_hash, tx_receipt.block_hash.unwrap()); - assert_eq!( - exec_receipt.transaction_index, - U256::from(tx_receipt.transaction_index.unwrap()) - ); - assert_eq!(exec_receipt.inner, tx_receipt.inner); - } - - #[test] - fn test_exec_receipt_try_from_missing_root() { - let tx_receipt_str = include_str!("./testdata/tx_receipt.json"); - let mut tx_receipt: TransactionReceipt> = - serde_json::from_str(tx_receipt_str).expect("failed to parse tx receipt"); - tx_receipt.state_root = None; - let exec_receipt = ExecutionReceipt::try_from(tx_receipt); - assert!(exec_receipt.is_err()); - } - - #[test] - fn test_exec_receipt_try_from_missing_block_hash() { - let tx_receipt_str = include_str!("./testdata/tx_receipt.json"); - let mut tx_receipt: TransactionReceipt> = - serde_json::from_str(tx_receipt_str).expect("failed to parse tx receipt"); - tx_receipt.block_hash = None; - let exec_receipt = ExecutionReceipt::try_from(tx_receipt); - assert!(exec_receipt.is_err()); - } - - #[test] - fn test_exec_receipt_try_from_missing_tx_index() { - let tx_receipt_str = include_str!("./testdata/tx_receipt.json"); - let mut tx_receipt: TransactionReceipt> = - serde_json::from_str(tx_receipt_str).expect("failed to parse tx receipt"); - tx_receipt.transaction_index = None; - let exec_receipt = ExecutionReceipt::try_from(tx_receipt); - assert!(exec_receipt.is_err()); - } } diff --git a/crates/op-test-vectors/src/testdata/result.json b/crates/op-test-vectors/src/testdata/result.json index 686635f..b0a0e1f 100644 --- a/crates/op-test-vectors/src/testdata/result.json +++ b/crates/op-test-vectors/src/testdata/result.json @@ -4,18 +4,22 @@ "receiptRoot": "0x86ceb80cb6bef8fe4ac0f1c99409f67cb2554c4432f374e399b94884eb3e6562", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "receipts": [ - { - "root": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "status": "0x1", - "type": "0x0", - "cumulativeGasUsed": "0xa878", - "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "logs": [], - "transactionHash": "0x4e6549e2276d1bc256b2a56ead2d9705a51a8bf54e3775fbd2e98c91fb0e4494", - "contractAddress": "0x0000000000000000000000000000000000000000", - "gasUsed": "0xa878", - "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "transactionIndex": "0x0" - } + { + "root": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "status": "0x1", + "type": "0x0", + "from": "0x25ace71c97b33cc4729cf772ae268934f7ab5fa1", + "to": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "cumulativeGasUsed": "0xa878", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "logs": [], + "transactionHash": "0x4e6549e2276d1bc256b2a56ead2d9705a51a8bf54e3775fbd2e98c91fb0e4494", + "contractAddress": "0x0000000000000000000000000000000000000000", + "blockNumber": "0x76976ad", + "gasUsed": "0xa878", + "effectiveGasPrice": "0x1", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "transactionIndex": "0x0" + } ] -} +} \ No newline at end of file