Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(op-test-vectors): Update ExecutionFixture to use op-alloy-consensus types #73

Merged
merged 10 commits into from
Aug 24, 2024
Merged
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ revm = { version = "12.1", features = ["alloydb", "optimism"] }

# Kona + OP Types
superchain-registry = "0.2.6"
kona-derive = { git = "https://github.com/ethereum-optimism/kona", rev = "4e57dd35ea08b31d0baa293c7a12165f28e6cd92", features = ["online"] }
kona-derive = { git = "https://github.com/ethereum-optimism/kona", rev = "4e57dd35ea08b31d0baa293c7a12165f28e6cd92", features = [
"online",
] }

# Internal
op-test-vectors = { path = "crates/op-test-vectors" }
4 changes: 3 additions & 1 deletion bin/opt8n/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"
hyper = "1.4.1"
126 changes: 116 additions & 10 deletions bin/opt8n/src/opt8n.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,27 @@

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},
path::PathBuf,
};

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},
Expand Down Expand Up @@ -142,19 +149,20 @@ impl Opt8n {
self.capture_pre_post_alloc(&block)?;

// Append block transactions and receipts to the execution fixture
let mut receipts: Vec<ExecutionReceipt> = Vec::with_capacity(block.transactions.len());
let mut receipts: Vec<OpTransactionReceipt> = Vec::with_capacity(block.transactions.len());
for tx in block.transactions.iter() {
if let Some(receipt) = self
.eth_api
.backend
.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;
Expand All @@ -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
Expand All @@ -183,6 +201,94 @@ impl Opt8n {
}
}

// TODO: Consider adding `From` implementation for
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree - I will add issues in op-alloy for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, I can open a PR for these issues on op-alloy later today/tomorrow.

// `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<TypedReceipt<alloy_rpc_types::Log>>,
) -> 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<DB>>
where
Expand Down
3 changes: 0 additions & 3 deletions crates/op-test-vectors/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
120 changes: 5 additions & 115 deletions crates/op-test-vectors/src/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -24,7 +23,7 @@ pub struct ExecutionFixture {
pub out_alloc: HashMap<Address, AccountState>,
/// Transactions to execute.
#[serde(rename = "txs")]
pub transactions: Vec<TypedTransaction>,
pub transactions: Vec<OpTypedTransaction>,
/// The expected result after executing transactions.
pub result: ExecutionResult,
}
Expand All @@ -51,20 +50,6 @@ pub struct ExecutionEnvironment {
pub block_hashes: Option<HashMap<U256, B256>>,
}

impl From<Block> 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)]
Expand All @@ -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<ExecutionReceipt>,
}

/// 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<Address>,
/// 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<Log>,
}

impl TryFrom<TransactionReceipt<TypedReceipt<Log>>> for ExecutionReceipt {
type Error = eyre::Error;

fn try_from(receipt: TransactionReceipt<TypedReceipt<Log>>) -> eyre::Result<Self> {
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<OpTransactionReceipt>,
}

#[cfg(test)]
Expand Down Expand Up @@ -159,53 +98,4 @@ mod tests {
.expect("failed to parse expected result");
assert_eq!(serialized_value, expected_value);
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a test/tests here for deserializing the op types?

#[test]
fn test_exec_receipt_try_from_tx_receipt() {
let tx_receipt_str = include_str!("./testdata/tx_receipt.json");
let tx_receipt: TransactionReceipt<TypedReceipt<Log>> =
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<TypedReceipt<Log>> =
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<TypedReceipt<Log>> =
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<TypedReceipt<Log>> =
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());
}
}
Loading