diff --git a/Cargo.lock b/Cargo.lock index a7b9c6a0cb..0e9fda1981 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2972,8 +2972,6 @@ version = "10.0.1" dependencies = [ "bincode", "derive-where", - "paste", - "phf", "revm-bytecode", "revm-database-interface", "revm-primitives", diff --git a/bins/revme/src/cmd/bench/analysis.rs b/bins/revme/src/cmd/bench/analysis.rs index 359738567e..c0bd96dc87 100644 --- a/bins/revme/src/cmd/bench/analysis.rs +++ b/bins/revme/src/cmd/bench/analysis.rs @@ -1,7 +1,6 @@ use database::{BenchmarkDB, EthereumBenchmarkWiring}; use revm::{ bytecode::Bytecode, - interpreter::analysis::to_analysed, primitives::{address, bytes, Bytes, TxKind}, Evm, }; @@ -11,7 +10,7 @@ pub fn run() { let contract_data : Bytes = hex::decode( "6060604052341561000f57600080fd5b604051610dd1380380610dd18339810160405280805190602001909190805182019190602001805190602001909190805182019190505083600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508360008190555082600390805190602001906100a79291906100e3565b5081600460006101000a81548160ff021916908360ff16021790555080600590805190602001906100d99291906100e3565b5050505050610188565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061012457805160ff1916838001178555610152565b82800160010185558215610152579182015b82811115610151578251825591602001919060010190610136565b5b50905061015f9190610163565b5090565b61018591905b80821115610181576000816000905550600101610169565b5090565b90565b610c3a806101976000396000f3006060604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014257806318160ddd1461019c57806323b872dd146101c557806327e235e31461023e578063313ce5671461028b5780635c658165146102ba57806370a082311461032657806395d89b4114610373578063a9059cbb14610401578063dd62ed3e1461045b575b600080fd5b34156100bf57600080fd5b6100c76104c7565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101075780820151818401526020810190506100ec565b50505050905090810190601f1680156101345780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561014d57600080fd5b610182600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610565565b604051808215151515815260200191505060405180910390f35b34156101a757600080fd5b6101af610657565b6040518082815260200191505060405180910390f35b34156101d057600080fd5b610224600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061065d565b604051808215151515815260200191505060405180910390f35b341561024957600080fd5b610275600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506108f7565b6040518082815260200191505060405180910390f35b341561029657600080fd5b61029e61090f565b604051808260ff1660ff16815260200191505060405180910390f35b34156102c557600080fd5b610310600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610922565b6040518082815260200191505060405180910390f35b341561033157600080fd5b61035d600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610947565b6040518082815260200191505060405180910390f35b341561037e57600080fd5b610386610990565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103c65780820151818401526020810190506103ab565b50505050905090810190601f1680156103f35780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561040c57600080fd5b610441600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610a2e565b604051808215151515815260200191505060405180910390f35b341561046657600080fd5b6104b1600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610b87565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561055d5780601f106105325761010080835404028352916020019161055d565b820191906000526020600020905b81548152906001019060200180831161054057829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015801561072e5750828110155b151561073957600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108865782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a265780601f106109fb57610100808354040283529160200191610a26565b820191906000526020600020905b815481529060010190602001808311610a0957829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610a7e57600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a72305820df254047bc8f2904ad3e966b6db116d703bebd40efadadb5e738c836ffc8f58a0029").unwrap().into(); let bytecode_raw = Bytecode::new_raw(contract_data.clone()); - let bytecode_analysed = to_analysed(Bytecode::new_raw(contract_data)); + let bytecode_analysed = Bytecode::new_raw(contract_data).into_analyzed(); // BenchmarkDB is dummy state that implements Database trait. let mut evm = Evm::::builder() diff --git a/bins/revme/src/cmd/bench/snailtracer.rs b/bins/revme/src/cmd/bench/snailtracer.rs index fe729f12b5..72aa904a33 100644 --- a/bins/revme/src/cmd/bench/snailtracer.rs +++ b/bins/revme/src/cmd/bench/snailtracer.rs @@ -1,13 +1,12 @@ use database::{BenchmarkDB, EthereumBenchmarkWiring}; use revm::{ bytecode::Bytecode, - interpreter::analysis::to_analysed, primitives::{address, bytes, Bytes, TxKind}, Evm, }; pub fn simple_example() { - let bytecode = to_analysed(Bytecode::new_raw(CONTRACT_DATA.clone())); + let bytecode = Bytecode::new_raw(CONTRACT_DATA.clone()).into_analyzed(); // BenchmarkDB is dummy state that implements Database trait. let mut evm = Evm::::builder() diff --git a/bins/revme/src/cmd/bytecode.rs b/bins/revme/src/cmd/bytecode.rs index 26f39141df..ada0fead98 100644 --- a/bins/revme/src/cmd/bytecode.rs +++ b/bins/revme/src/cmd/bytecode.rs @@ -1,10 +1,6 @@ use clap::Parser; use revm::{ - bytecode::Eof, - interpreter::{ - analysis::{validate_eof_inner, CodeType, EofError}, - opcode::eof_printer::print_eof_code, - }, + bytecode::eof::{self, validate_eof_inner, CodeType, Eof, EofError}, primitives::{Bytes, MAX_INITCODE_SIZE}, }; use std::io; @@ -62,7 +58,7 @@ impl Cmd { Err(e) => eprintln!("Decoding Error: {:#?}", e), } } else { - print_eof_code(&bytes) + eof::printer::print(&bytes) } return; } diff --git a/bins/revme/src/cmd/eofvalidation.rs b/bins/revme/src/cmd/eofvalidation.rs index 1badad7e77..40d0696225 100644 --- a/bins/revme/src/cmd/eofvalidation.rs +++ b/bins/revme/src/cmd/eofvalidation.rs @@ -4,7 +4,7 @@ pub use test_suite::{PragueTestResult, TestResult, TestSuite, TestUnit, TestVect use crate::{cmd::Error, dir_utils::find_all_json_tests}; use clap::Parser; -use revm::interpreter::analysis::{validate_raw_eof_inner, CodeType, EofError}; +use revm::bytecode::eof::{validate_raw_eof_inner, CodeType, EofError}; use std::collections::BTreeMap; use std::path::{Path, PathBuf}; diff --git a/bins/revme/src/cmd/statetest/runner.rs b/bins/revme/src/cmd/statetest/runner.rs index 1ca40dbeb6..1496e57ac9 100644 --- a/bins/revme/src/cmd/statetest/runner.rs +++ b/bins/revme/src/cmd/statetest/runner.rs @@ -9,7 +9,6 @@ use inspector::{inspector_handle_register, inspectors::TracerEip3155}; use revm::{ bytecode::Bytecode, database_interface::EmptyDB, - interpreter::analysis::to_analysed, primitives::{keccak256, Bytes, TxKind, B256}, specification::{eip7702::AuthorizationList, hardfork::SpecId}, wiring::{ @@ -278,7 +277,7 @@ pub fn execute_test_suite( let mut cache_state = database::CacheState::new(false); for (address, info) in unit.pre { let code_hash = keccak256(&info.code); - let bytecode = to_analysed(Bytecode::new_raw(info.code)); + let bytecode = Bytecode::new_raw(info.code).into_analyzed(); let acc_info = revm::state::AccountInfo { balance: info.balance, code_hash, diff --git a/crates/bytecode/Cargo.toml b/crates/bytecode/Cargo.toml index 8bb2a0a03b..b4a1040494 100644 --- a/crates/bytecode/Cargo.toml +++ b/crates/bytecode/Cargo.toml @@ -42,9 +42,9 @@ phf = { version = "0.11", default-features = false, optional = true, features = ] } [features] -default = ["std"] +default = ["std", "parse"] std = ["serde?/std", "primitives/std"] hashbrown = ["primitives/hashbrown"] serde = ["dep:serde", "primitives/serde", "bitvec/serde"] serde-json = ["serde"] -parse = [] +parse = ["phf", "paste"] diff --git a/crates/bytecode/src/bytecode.rs b/crates/bytecode/src/bytecode.rs new file mode 100644 index 0000000000..f1a4a58ae2 --- /dev/null +++ b/crates/bytecode/src/bytecode.rs @@ -0,0 +1,246 @@ +use crate::{ + eip7702::{Eip7702Bytecode, EIP7702_MAGIC_BYTES}, + BytecodeDecodeError, Eof, JumpTable, LegacyAnalyzedBytecode, LegacyRawBytecode, + EOF_MAGIC_BYTES, +}; +use core::fmt::Debug; +use primitives::{keccak256, Address, Bytes, B256, KECCAK_EMPTY}; +use std::sync::Arc; + +/// State of the [`Bytecode`] analysis. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum Bytecode { + /// No analysis has been performed. + LegacyRaw(LegacyRawBytecode), + /// The bytecode has been analyzed for valid jump destinations. + LegacyAnalyzed(LegacyAnalyzedBytecode), + /// Ethereum Object Format + Eof(Arc), + /// EIP-7702 delegated bytecode + Eip7702(Eip7702Bytecode), +} + +impl Default for Bytecode { + #[inline] + fn default() -> Self { + // Creates a new legacy analyzed [`Bytecode`] with exactly one STOP opcode. + Self::new() + } +} + +impl Bytecode { + // Creates a new legacy analyzed [`Bytecode`] with exactly one STOP opcode. + #[inline] + pub fn new() -> Self { + Self::LegacyAnalyzed(LegacyAnalyzedBytecode::default()) + } + + /// Return jump table if bytecode is analyzed + #[inline] + pub fn legacy_jump_table(&self) -> Option<&JumpTable> { + match &self { + Self::LegacyAnalyzed(analyzed) => Some(analyzed.jump_table()), + _ => None, + } + } + + /// Calculate hash of the bytecode. + pub fn hash_slow(&self) -> B256 { + if self.is_empty() { + KECCAK_EMPTY + } else { + keccak256(self.original_byte_slice()) + } + } + + /// Return reference to the EOF if bytecode is EOF. + #[inline] + pub const fn eof(&self) -> Option<&Arc> { + match self { + Self::Eof(eof) => Some(eof), + _ => None, + } + } + + /// Returns true if bytecode is EOF. + #[inline] + pub const fn is_eof(&self) -> bool { + matches!(self, Self::Eof(_)) + } + + /// Returns true if bytecode is EIP-7702. + pub const fn is_eip7702(&self) -> bool { + matches!(self, Self::Eip7702(_)) + } + + /// Creates a new legacy [`Bytecode`]. + #[inline] + pub fn new_legacy(raw: Bytes) -> Self { + Self::LegacyRaw(raw.into()) + } + + /// Creates a new raw [`Bytecode`]. + /// + /// # Panics + /// + /// Panics if bytecode is in incorrect format. + #[inline] + pub fn new_raw(bytecode: Bytes) -> Self { + Self::new_raw_checked(bytecode).expect("Expect correct EOF bytecode") + } + + /// Creates a new EIP-7702 [`Bytecode`] from [`Address`]. + #[inline] + pub fn new_eip7702(address: Address) -> Self { + Self::Eip7702(Eip7702Bytecode::new(address)) + } + + /// Creates a new raw [`Bytecode`]. + /// + /// Returns an error on incorrect Bytecode format. + #[inline] + pub fn new_raw_checked(bytecode: Bytes) -> Result { + let prefix = bytecode.get(..2); + match prefix { + Some(prefix) if prefix == &EOF_MAGIC_BYTES => { + let eof = Eof::decode(bytecode)?; + Ok(Self::Eof(Arc::new(eof))) + } + Some(prefix) if prefix == &EIP7702_MAGIC_BYTES => { + let eip7702 = Eip7702Bytecode::new_raw(bytecode)?; + Ok(Self::Eip7702(eip7702)) + } + _ => Ok(Self::LegacyRaw(bytecode.into())), + } + } + + /// Perform bytecode analysis. + /// + /// The analysis finds and caches valid jump destinations for later execution as an optimization step. + /// + /// If the bytecode is already analyzed, it is returned as-is. + #[inline] + pub fn into_analyzed(self) -> Bytecode { + let Bytecode::LegacyRaw(bytecode) = self else { + return self; + }; + + Bytecode::LegacyAnalyzed(bytecode.into_analyzed()) + } + + /// Create new checked bytecode. + /// + /// # Safety + /// + /// Bytecode needs to end with STOP (0x00) opcode as checked bytecode assumes + /// that it is safe to iterate over bytecode without checking lengths. + pub unsafe fn new_analyzed( + bytecode: Bytes, + original_len: usize, + jump_table: JumpTable, + ) -> Self { + Self::LegacyAnalyzed(LegacyAnalyzedBytecode::new( + bytecode, + original_len, + jump_table, + )) + } + + /// Returns a reference to the bytecode. + /// + /// In case of EOF this will be the first code section. + #[inline] + pub fn bytecode(&self) -> &Bytes { + match self { + Self::LegacyRaw(bytes) => bytes, + Self::LegacyAnalyzed(analyzed) => analyzed.bytecode(), + Self::Eof(eof) => eof + .body + .code(0) + .expect("Valid EOF has at least one code section"), + Self::Eip7702(code) => code.raw(), + } + } + + /// Returns false if bytecode can't be executed in Interpreter. + pub fn is_execution_ready(&self) -> bool { + !matches!(self, Self::LegacyRaw(_)) + } + + /// Returns bytes + #[inline] + pub fn bytes(&self) -> Bytes { + match self { + Self::LegacyAnalyzed(analyzed) => analyzed.bytecode().clone(), + _ => self.original_bytes(), + } + } + + /// Returns bytes slice + #[inline] + pub fn bytes_slice(&self) -> &[u8] { + match self { + Self::LegacyAnalyzed(analyzed) => analyzed.bytecode(), + _ => self.original_byte_slice(), + } + } + + /// Returns a reference to the original bytecode. + #[inline] + pub fn original_bytes(&self) -> Bytes { + match self { + Self::LegacyRaw(bytes) => bytes.0.clone(), + Self::LegacyAnalyzed(analyzed) => analyzed.original_bytes(), + Self::Eof(eof) => eof.raw().clone(), + Self::Eip7702(eip7702) => eip7702.raw().clone(), + } + } + + /// Returns the original bytecode as a byte slice. + #[inline] + pub fn original_byte_slice(&self) -> &[u8] { + match self { + Self::LegacyRaw(bytes) => bytes, + Self::LegacyAnalyzed(analyzed) => analyzed.original_byte_slice(), + Self::Eof(eof) => eof.raw(), + Self::Eip7702(eip7702) => eip7702.raw(), + } + } + + /// Returns the length of the original bytes. + #[inline] + pub fn len(&self) -> usize { + self.original_byte_slice().len() + } + + /// Returns whether the bytecode is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +#[cfg(test)] +mod tests { + use super::{Bytecode, Eof}; + use std::sync::Arc; + + #[test] + fn eof_arc_clone() { + let eof = Arc::new(Eof::default()); + let bytecode = Bytecode::Eof(Arc::clone(&eof)); + + // Cloning the Bytecode should not clone the underlying Eof + let cloned_bytecode = bytecode.clone(); + if let Bytecode::Eof(original_arc) = bytecode { + if let Bytecode::Eof(cloned_arc) = cloned_bytecode { + assert!(Arc::ptr_eq(&original_arc, &cloned_arc)); + } else { + panic!("Cloned bytecode is not Eof"); + } + } else { + panic!("Original bytecode is not Eof"); + } + } +} diff --git a/crates/bytecode/src/errors.rs b/crates/bytecode/src/decode_errors.rs similarity index 100% rename from crates/bytecode/src/errors.rs rename to crates/bytecode/src/decode_errors.rs diff --git a/crates/bytecode/src/legacy.rs b/crates/bytecode/src/legacy.rs index a9751afd0a..e9afe03790 100644 --- a/crates/bytecode/src/legacy.rs +++ b/crates/bytecode/src/legacy.rs @@ -4,4 +4,4 @@ mod raw; pub use analyzed::LegacyAnalyzedBytecode; pub use jump_map::JumpTable; -pub use raw::LegacyRawBytecode; +pub use raw::{analyze_legacy, LegacyRawBytecode}; diff --git a/crates/bytecode/src/legacy/raw.rs b/crates/bytecode/src/legacy/raw.rs index 983584cd70..c12252cb2c 100644 --- a/crates/bytecode/src/legacy/raw.rs +++ b/crates/bytecode/src/legacy/raw.rs @@ -11,31 +11,7 @@ pub struct LegacyRawBytecode(pub Bytes); impl LegacyRawBytecode { pub fn analysis(&self) -> JumpTable { - let mut jumps: BitVec = bitvec![u8, Lsb0; 0; self.0.len()]; - - let range = self.0.as_ptr_range(); - let start = range.start; - let mut iterator = start; - let end = range.end; - while iterator < end { - let opcode = unsafe { *iterator }; - if opcode::JUMPDEST == opcode { - // SAFETY: jumps are max length of the code - unsafe { jumps.set_unchecked(iterator.offset_from(start) as usize, true) } - iterator = unsafe { iterator.offset(1) }; - } else { - let push_offset = opcode.wrapping_sub(opcode::PUSH1); - if push_offset < 32 { - // SAFETY: iterator access range is checked in the while loop - iterator = unsafe { iterator.offset((push_offset + 2) as isize) }; - } else { - // SAFETY: iterator access range is checked in the while loop - iterator = unsafe { iterator.offset(1) }; - } - } - } - - JumpTable(Arc::new(jumps)) + analyze_legacy(&self.0) } pub fn into_analyzed(self) -> LegacyAnalyzedBytecode { @@ -67,3 +43,32 @@ impl Deref for LegacyRawBytecode { &self.0 } } + +/// Analyze the bytecode to find the jumpdests +pub fn analyze_legacy(bytetecode: &[u8]) -> JumpTable { + let mut jumps: BitVec = bitvec![u8, Lsb0; 0; bytetecode.len()]; + + let range = bytetecode.as_ptr_range(); + let start = range.start; + let mut iterator = start; + let end = range.end; + while iterator < end { + let opcode = unsafe { *iterator }; + if opcode::JUMPDEST == opcode { + // SAFETY: jumps are max length of the code + unsafe { jumps.set_unchecked(iterator.offset_from(start) as usize, true) } + iterator = unsafe { iterator.offset(1) }; + } else { + let push_offset = opcode.wrapping_sub(opcode::PUSH1); + if push_offset < 32 { + // SAFETY: iterator access range is checked in the while loop + iterator = unsafe { iterator.offset((push_offset + 2) as isize) }; + } else { + // SAFETY: iterator access range is checked in the while loop + iterator = unsafe { iterator.offset(1) }; + } + } + } + + JumpTable(Arc::new(jumps)) +} diff --git a/crates/bytecode/src/lib.rs b/crates/bytecode/src/lib.rs index d67291bf0a..ac667c956e 100644 --- a/crates/bytecode/src/lib.rs +++ b/crates/bytecode/src/lib.rs @@ -5,14 +5,17 @@ #[cfg(not(feature = "std"))] extern crate alloc as std; +pub mod bytecode; +pub mod decode_errors; pub mod eip7702; pub mod eof; -pub mod errors; pub mod legacy; pub mod opcode; pub mod utils; pub use bitvec; +pub use bytecode::Bytecode; +pub use decode_errors::BytecodeDecodeError; pub use eof::{ verification::{ validate_eof, validate_eof_code, validate_eof_codes, validate_eof_inner, validate_raw_eof, @@ -20,248 +23,4 @@ pub use eof::{ }, Eof, EOF_MAGIC, EOF_MAGIC_BYTES, EOF_MAGIC_HASH, }; -pub use errors::BytecodeDecodeError; pub use legacy::{JumpTable, LegacyAnalyzedBytecode, LegacyRawBytecode}; - -use core::fmt::Debug; -use eip7702::{Eip7702Bytecode, EIP7702_MAGIC_BYTES}; -use primitives::{keccak256, Address, Bytes, B256, KECCAK_EMPTY}; -use std::sync::Arc; - -/// State of the [`Bytecode`] analysis. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum Bytecode { - /// No analysis has been performed. - LegacyRaw(LegacyRawBytecode), - /// The bytecode has been analyzed for valid jump destinations. - LegacyAnalyzed(LegacyAnalyzedBytecode), - /// Ethereum Object Format - Eof(Arc), - /// EIP-7702 delegated bytecode - Eip7702(Eip7702Bytecode), -} - -impl Default for Bytecode { - #[inline] - fn default() -> Self { - // Creates a new legacy analyzed [`Bytecode`] with exactly one STOP opcode. - Self::new() - } -} - -impl Bytecode { - // Creates a new legacy analyzed [`Bytecode`] with exactly one STOP opcode. - #[inline] - pub fn new() -> Self { - Self::LegacyAnalyzed(LegacyAnalyzedBytecode::default()) - } - - /// Return jump table if bytecode is analyzed - #[inline] - pub fn legacy_jump_table(&self) -> Option<&JumpTable> { - match &self { - Self::LegacyAnalyzed(analyzed) => Some(analyzed.jump_table()), - _ => None, - } - } - - /// Calculate hash of the bytecode. - pub fn hash_slow(&self) -> B256 { - if self.is_empty() { - KECCAK_EMPTY - } else { - keccak256(self.original_byte_slice()) - } - } - - /// Return reference to the EOF if bytecode is EOF. - #[inline] - pub const fn eof(&self) -> Option<&Arc> { - match self { - Self::Eof(eof) => Some(eof), - _ => None, - } - } - - /// Returns true if bytecode is EOF. - #[inline] - pub const fn is_eof(&self) -> bool { - matches!(self, Self::Eof(_)) - } - - /// Returns true if bytecode is EIP-7702. - pub const fn is_eip7702(&self) -> bool { - matches!(self, Self::Eip7702(_)) - } - - /// Creates a new legacy [`Bytecode`]. - #[inline] - pub fn new_legacy(raw: Bytes) -> Self { - Self::LegacyRaw(raw.into()) - } - - /// Creates a new raw [`Bytecode`]. - /// - /// # Panics - /// - /// Panics if bytecode is in incorrect format. - #[inline] - pub fn new_raw(bytecode: Bytes) -> Self { - Self::new_raw_checked(bytecode).expect("Expect correct EOF bytecode") - } - - /// Creates a new EIP-7702 [`Bytecode`] from [`Address`]. - #[inline] - pub fn new_eip7702(address: Address) -> Self { - Self::Eip7702(Eip7702Bytecode::new(address)) - } - - /// Creates a new raw [`Bytecode`]. - /// - /// Returns an error on incorrect Bytecode format. - #[inline] - pub fn new_raw_checked(bytecode: Bytes) -> Result { - let prefix = bytecode.get(..2); - match prefix { - Some(prefix) if prefix == &EOF_MAGIC_BYTES => { - let eof = Eof::decode(bytecode)?; - Ok(Self::Eof(Arc::new(eof))) - } - Some(prefix) if prefix == &EIP7702_MAGIC_BYTES => { - let eip7702 = Eip7702Bytecode::new_raw(bytecode)?; - Ok(Self::Eip7702(eip7702)) - } - _ => Ok(Self::LegacyRaw(bytecode.into())), - } - } - - /// Perform bytecode analysis. - /// - /// The analysis finds and caches valid jump destinations for later execution as an optimization step. - /// - /// If the bytecode is already analyzed, it is returned as-is. - #[inline] - pub fn into_analyzed(self) -> Bytecode { - let Bytecode::LegacyRaw(bytecode) = self else { - return self; - }; - - Bytecode::LegacyAnalyzed(bytecode.into_analyzed()) - } - - /// Create new checked bytecode. - /// - /// # Safety - /// - /// Bytecode needs to end with STOP (0x00) opcode as checked bytecode assumes - /// that it is safe to iterate over bytecode without checking lengths. - pub unsafe fn new_analyzed( - bytecode: Bytes, - original_len: usize, - jump_table: JumpTable, - ) -> Self { - Self::LegacyAnalyzed(LegacyAnalyzedBytecode::new( - bytecode, - original_len, - jump_table, - )) - } - - /// Returns a reference to the bytecode. - /// - /// In case of EOF this will be the first code section. - #[inline] - pub fn bytecode(&self) -> &Bytes { - match self { - Self::LegacyRaw(bytes) => bytes, - Self::LegacyAnalyzed(analyzed) => analyzed.bytecode(), - Self::Eof(eof) => eof - .body - .code(0) - .expect("Valid EOF has at least one code section"), - Self::Eip7702(code) => code.raw(), - } - } - - /// Returns false if bytecode can't be executed in Interpreter. - pub fn is_execution_ready(&self) -> bool { - !matches!(self, Self::LegacyRaw(_)) - } - - /// Returns bytes - #[inline] - pub fn bytes(&self) -> Bytes { - match self { - Self::LegacyAnalyzed(analyzed) => analyzed.bytecode().clone(), - _ => self.original_bytes(), - } - } - - /// Returns bytes slice - #[inline] - pub fn bytes_slice(&self) -> &[u8] { - match self { - Self::LegacyAnalyzed(analyzed) => analyzed.bytecode(), - _ => self.original_byte_slice(), - } - } - - /// Returns a reference to the original bytecode. - #[inline] - pub fn original_bytes(&self) -> Bytes { - match self { - Self::LegacyRaw(bytes) => bytes.0.clone(), - Self::LegacyAnalyzed(analyzed) => analyzed.original_bytes(), - Self::Eof(eof) => eof.raw().clone(), - Self::Eip7702(eip7702) => eip7702.raw().clone(), - } - } - - /// Returns the original bytecode as a byte slice. - #[inline] - pub fn original_byte_slice(&self) -> &[u8] { - match self { - Self::LegacyRaw(bytes) => bytes, - Self::LegacyAnalyzed(analyzed) => analyzed.original_byte_slice(), - Self::Eof(eof) => eof.raw(), - Self::Eip7702(eip7702) => eip7702.raw(), - } - } - - /// Returns the length of the original bytes. - #[inline] - pub fn len(&self) -> usize { - self.original_byte_slice().len() - } - - /// Returns whether the bytecode is empty. - #[inline] - pub fn is_empty(&self) -> bool { - self.len() == 0 - } -} - -#[cfg(test)] -mod tests { - use super::{Bytecode, Eof}; - use std::sync::Arc; - - #[test] - fn eof_arc_clone() { - let eof = Arc::new(Eof::default()); - let bytecode = Bytecode::Eof(Arc::clone(&eof)); - - // Cloning the Bytecode should not clone the underlying Eof - let cloned_bytecode = bytecode.clone(); - if let Bytecode::Eof(original_arc) = bytecode { - if let Bytecode::Eof(cloned_arc) = cloned_bytecode { - assert!(Arc::ptr_eq(&original_arc, &cloned_arc)); - } else { - panic!("Cloned bytecode is not Eof"); - } - } else { - panic!("Original bytecode is not Eof"); - } - } -} diff --git a/crates/bytecode/src/opcode.rs b/crates/bytecode/src/opcode.rs index a5d57c15e6..1e42207f43 100644 --- a/crates/bytecode/src/opcode.rs +++ b/crates/bytecode/src/opcode.rs @@ -1,21 +1,9 @@ //! EVM opcode definitions and utilities. -use core::{fmt, ptr::NonNull}; - -/// An error indicating that an opcode is invalid. -#[derive(Debug, PartialEq, Eq)] -#[cfg(feature = "parse")] -pub struct OpCodeError(()); - #[cfg(feature = "parse")] -impl fmt::Display for OpCodeError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("invalid opcode") - } -} +pub mod parse; -#[cfg(feature = "parse")] -impl core::error::Error for OpCodeError {} +use core::{fmt, ptr::NonNull}; /// An EVM opcode. /// @@ -36,16 +24,6 @@ impl fmt::Display for OpCode { } } -#[cfg(feature = "parse")] -impl core::str::FromStr for OpCode { - type Err = OpCodeError; - - #[inline] - fn from_str(s: &str) -> Result { - Self::parse(s).ok_or(OpCodeError(())) - } -} - impl OpCode { /// Instantiate a new opcode from a u8. #[inline] @@ -56,13 +34,6 @@ impl OpCode { } } - /// Parses an opcode from a string. This is the inverse of [`as_str`](Self::as_str). - #[inline] - #[cfg(feature = "parse")] - pub fn parse(s: &str) -> Option { - NAME_TO_OPCODE.get(s).copied() - } - /// Returns true if the opcode is a jump destination. #[inline] pub const fn is_jumpdest(&self) -> bool { @@ -353,25 +324,6 @@ pub const fn stack_io(mut op: OpCodeInfo, inputs: u8, outputs: u8) -> OpCodeInfo /// Alias for the [`JUMPDEST`] opcode. pub const NOP: u8 = JUMPDEST; -/// Callback for creating a [`phf`] map with `stringify_with_cb`. -#[cfg(feature = "parse")] -macro_rules! phf_map_cb { - ($(#[doc = $s:literal] $id:ident)*) => { - phf::phf_map! { - $($s => OpCode::$id),* - } - }; -} - -/// Stringifies identifiers with `paste` so that they are available as literals. -/// This doesn't work with `stringify!` because it cannot be expanded inside of another macro. -#[cfg(feature = "parse")] -macro_rules! stringify_with_cb { - ($callback:ident; $($id:ident)*) => { paste::paste! { - $callback! { $(#[doc = "" $id ""] $id)* } - }}; -} - macro_rules! opcodes { ($($val:literal => $name:ident => $($modifier:ident $(( $($modifier_arg:expr),* ))?),*);* $(;)?) => { // Constants for each opcode. This also takes care of duplicate names. @@ -402,20 +354,32 @@ macro_rules! opcodes { map }; + /// Maps each name to its opcode. #[cfg(feature = "parse")] - static NAME_TO_OPCODE: phf::Map<&'static str, OpCode> = stringify_with_cb! { phf_map_cb; $($name)* }; - - // /// Returns the instruction function for the given opcode and spec. - // pub const fn instruction(opcode: u8) -> Instruction { - // match opcode { - // $($name => $f,)* - // _ => control::unknown, - // } - // } + pub(crate) static NAME_TO_OPCODE: phf::Map<&'static str, OpCode> = stringify_with_cb! { phf_map_cb; $($name)* }; }; } +/// Callback for creating a [`phf`] map with `stringify_with_cb`. +#[cfg(feature = "parse")] +macro_rules! phf_map_cb { + ($(#[doc = $s:literal] $id:ident)*) => { + phf::phf_map! { + $($s => OpCode::$id),* + } + }; +} + +/// Stringifies identifiers with `paste` so that they are available as literals. +/// This doesn't work with `stringify!` because it cannot be expanded inside of another macro. +#[cfg(feature = "parse")] +macro_rules! stringify_with_cb { + ($callback:ident; $($id:ident)*) => { paste::paste! { + $callback! { $(#[doc = "" $id ""] $id)* } + }}; +} + // When adding new opcodes: // 1. add the opcode to the list below; make sure it's sorted by opcode value // 2. implement the opcode in the corresponding module; diff --git a/crates/bytecode/src/opcode/parse.rs b/crates/bytecode/src/opcode/parse.rs new file mode 100644 index 0000000000..d172ae4a3a --- /dev/null +++ b/crates/bytecode/src/opcode/parse.rs @@ -0,0 +1,32 @@ +use super::OpCode; +use crate::opcode::NAME_TO_OPCODE; +use core::fmt; + +/// An error indicating that an opcode is invalid. +#[derive(Debug, PartialEq, Eq)] +pub struct OpCodeError(()); + +impl fmt::Display for OpCodeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("invalid opcode") + } +} + +impl core::error::Error for OpCodeError {} + +impl core::str::FromStr for OpCode { + type Err = OpCodeError; + + #[inline] + fn from_str(s: &str) -> Result { + Self::parse(s).ok_or(OpCodeError(())) + } +} + +impl OpCode { + /// Parses an opcode from a string. This is the inverse of [`as_str`](Self::as_str). + #[inline] + pub fn parse(s: &str) -> Option { + NAME_TO_OPCODE.get(s).copied() + } +} diff --git a/crates/inspector/src/customprinter.rs b/crates/inspector/src/customprinter.rs index 8167b156b7..b4b0700dc8 100644 --- a/crates/inspector/src/customprinter.rs +++ b/crates/inspector/src/customprinter.rs @@ -3,8 +3,8 @@ use crate::{inspectors::GasInspector, Inspector}; use revm::{ - interpreter::{CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter}, bytecode::opcode::OpCode, + interpreter::{CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter}, primitives::{Address, U256}, EvmContext, EvmWiring, }; diff --git a/crates/inspector/src/handler_register.rs b/crates/inspector/src/handler_register.rs index c04e1c842d..88e13f0e79 100644 --- a/crates/inspector/src/handler_register.rs +++ b/crates/inspector/src/handler_register.rs @@ -3,7 +3,7 @@ use core::cell::RefCell; use revm::{ bytecode::opcode, handler::register::EvmHandler, - interpreter::{tables::DynInstruction, InstructionResult, Interpreter}, + interpreter::{table::DynInstruction, InstructionResult, Interpreter}, wiring::result::EVMResultGeneric, Context, EvmWiring, FrameOrResult, FrameResult, JournalEntry, }; diff --git a/crates/interpreter/Cargo.toml b/crates/interpreter/Cargo.toml index 313e210833..fe3dd7cfd7 100644 --- a/crates/interpreter/Cargo.toml +++ b/crates/interpreter/Cargo.toml @@ -28,11 +28,8 @@ primitives.workspace = true specification.workspace = true wiring.workspace = true +# mics derive-where = { version = "1.2.7", default-features = false } -paste = { version = "1.0", optional = true } -phf = { version = "0.11", default-features = false, optional = true, features = [ - "macros", -] } # optional serde = { version = "1.0", default-features = false, features = [ @@ -47,10 +44,9 @@ serde_json = "1.0" bincode = "1.3" [features] -default = ["std", "parse"] +default = ["std"] std = ["serde?/std", "primitives/std", "wiring/std"] hashbrown = ["primitives/hashbrown"] serde = ["dep:serde", "primitives/serde", "bytecode/serde", "wiring/serde"] arbitrary = ["std", "primitives/arbitrary"] -parse = ["dep:paste", "dep:phf"] memory_limit = ["wiring/memory_limit"] diff --git a/crates/interpreter/src/host/results.rs b/crates/interpreter/src/host/results.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/crates/interpreter/src/host/trait.rs b/crates/interpreter/src/host/trait.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/crates/interpreter/src/instructions.rs b/crates/interpreter/src/instructions.rs index 7f11f93f4d..89fe5132ed 100644 --- a/crates/interpreter/src/instructions.rs +++ b/crates/interpreter/src/instructions.rs @@ -14,3 +14,233 @@ pub mod memory; pub mod stack; pub mod system; pub mod utility; + +use crate::Host; +use specification::hardfork::Spec; + +/// Returns the instruction function for the given opcode and spec. +pub const fn instruction(opcode: u8) -> crate::table::Instruction { + let table = instruction_table::(); + table[opcode as usize] +} + +pub const fn instruction_table() -> [crate::table::Instruction; 256] +{ + use bytecode::opcode::*; + let mut table = [control::unknown as crate::table::Instruction; 256]; + + table[STOP as usize] = control::stop; + table[ADD as usize] = arithmetic::add; + table[STOP as usize] = control::stop; + + table[ADD as usize] = arithmetic::add; + table[MUL as usize] = arithmetic::mul; + table[SUB as usize] = arithmetic::sub; + table[DIV as usize] = arithmetic::div; + table[SDIV as usize] = arithmetic::sdiv; + table[MOD as usize] = arithmetic::rem; + table[SMOD as usize] = arithmetic::smod; + table[ADDMOD as usize] = arithmetic::addmod; + table[MULMOD as usize] = arithmetic::mulmod; + table[EXP as usize] = arithmetic::exp::; + table[SIGNEXTEND as usize] = arithmetic::signextend; + + table[LT as usize] = bitwise::lt; + table[GT as usize] = bitwise::gt; + table[SLT as usize] = bitwise::slt; + table[SGT as usize] = bitwise::sgt; + table[EQ as usize] = bitwise::eq; + table[ISZERO as usize] = bitwise::iszero; + table[AND as usize] = bitwise::bitand; + table[OR as usize] = bitwise::bitor; + table[XOR as usize] = bitwise::bitxor; + table[NOT as usize] = bitwise::not; + table[BYTE as usize] = bitwise::byte; + table[SHL as usize] = bitwise::shl::; + table[SHR as usize] = bitwise::shr::; + table[SAR as usize] = bitwise::sar::; + + table[KECCAK256 as usize] = system::keccak256; + + table[ADDRESS as usize] = system::address; + table[BALANCE as usize] = host::balance::; + table[ORIGIN as usize] = host_env::origin; + table[CALLER as usize] = system::caller; + table[CALLVALUE as usize] = system::callvalue; + table[CALLDATALOAD as usize] = system::calldataload; + table[CALLDATASIZE as usize] = system::calldatasize; + table[CALLDATACOPY as usize] = system::calldatacopy; + table[CODESIZE as usize] = system::codesize; + table[CODECOPY as usize] = system::codecopy; + + table[GASPRICE as usize] = host_env::gasprice; + table[EXTCODESIZE as usize] = host::extcodesize::; + table[EXTCODECOPY as usize] = host::extcodecopy::; + table[RETURNDATASIZE as usize] = system::returndatasize::; + table[RETURNDATACOPY as usize] = system::returndatacopy::; + table[EXTCODEHASH as usize] = host::extcodehash::; + table[BLOCKHASH as usize] = host::blockhash::; + table[COINBASE as usize] = host_env::coinbase; + table[TIMESTAMP as usize] = host_env::timestamp; + table[NUMBER as usize] = host_env::block_number; + table[DIFFICULTY as usize] = host_env::difficulty::; + table[GASLIMIT as usize] = host_env::gaslimit; + table[CHAINID as usize] = host_env::chainid::; + table[SELFBALANCE as usize] = host::selfbalance::; + table[BASEFEE as usize] = host_env::basefee::; + table[BLOBHASH as usize] = host_env::blob_hash::; + table[BLOBBASEFEE as usize] = host_env::blob_basefee::; + + table[POP as usize] = stack::pop; + table[MLOAD as usize] = memory::mload; + table[MSTORE as usize] = memory::mstore; + table[MSTORE8 as usize] = memory::mstore8; + table[SLOAD as usize] = host::sload::; + table[SSTORE as usize] = host::sstore::; + table[JUMP as usize] = control::jump; + table[JUMPI as usize] = control::jumpi; + table[PC as usize] = control::pc; + table[MSIZE as usize] = memory::msize; + table[GAS as usize] = system::gas; + table[JUMPDEST as usize] = control::jumpdest_or_nop; + table[TLOAD as usize] = host::tload::; + table[TSTORE as usize] = host::tstore::; + table[MCOPY as usize] = memory::mcopy::; + + table[PUSH0 as usize] = stack::push0::; + table[PUSH1 as usize] = stack::push::<1, H>; + table[PUSH2 as usize] = stack::push::<2, H>; + table[PUSH3 as usize] = stack::push::<3, H>; + table[PUSH4 as usize] = stack::push::<4, H>; + table[PUSH5 as usize] = stack::push::<5, H>; + table[PUSH6 as usize] = stack::push::<6, H>; + table[PUSH7 as usize] = stack::push::<7, H>; + table[PUSH8 as usize] = stack::push::<8, H>; + table[PUSH9 as usize] = stack::push::<9, H>; + table[PUSH10 as usize] = stack::push::<10, H>; + table[PUSH11 as usize] = stack::push::<11, H>; + table[PUSH12 as usize] = stack::push::<12, H>; + table[PUSH13 as usize] = stack::push::<13, H>; + table[PUSH14 as usize] = stack::push::<14, H>; + table[PUSH15 as usize] = stack::push::<15, H>; + table[PUSH16 as usize] = stack::push::<16, H>; + table[PUSH17 as usize] = stack::push::<17, H>; + table[PUSH18 as usize] = stack::push::<18, H>; + table[PUSH19 as usize] = stack::push::<19, H>; + table[PUSH20 as usize] = stack::push::<20, H>; + table[PUSH21 as usize] = stack::push::<21, H>; + table[PUSH22 as usize] = stack::push::<22, H>; + table[PUSH23 as usize] = stack::push::<23, H>; + table[PUSH24 as usize] = stack::push::<24, H>; + table[PUSH25 as usize] = stack::push::<25, H>; + table[PUSH26 as usize] = stack::push::<26, H>; + table[PUSH27 as usize] = stack::push::<27, H>; + table[PUSH28 as usize] = stack::push::<28, H>; + table[PUSH29 as usize] = stack::push::<29, H>; + table[PUSH30 as usize] = stack::push::<30, H>; + table[PUSH31 as usize] = stack::push::<31, H>; + table[PUSH32 as usize] = stack::push::<32, H>; + + table[DUP1 as usize] = stack::dup::<1, H>; + table[DUP2 as usize] = stack::dup::<2, H>; + table[DUP3 as usize] = stack::dup::<3, H>; + table[DUP4 as usize] = stack::dup::<4, H>; + table[DUP5 as usize] = stack::dup::<5, H>; + table[DUP6 as usize] = stack::dup::<6, H>; + table[DUP7 as usize] = stack::dup::<7, H>; + table[DUP8 as usize] = stack::dup::<8, H>; + table[DUP9 as usize] = stack::dup::<9, H>; + table[DUP10 as usize] = stack::dup::<10, H>; + table[DUP11 as usize] = stack::dup::<11, H>; + table[DUP12 as usize] = stack::dup::<12, H>; + table[DUP13 as usize] = stack::dup::<13, H>; + table[DUP14 as usize] = stack::dup::<14, H>; + table[DUP15 as usize] = stack::dup::<15, H>; + table[DUP16 as usize] = stack::dup::<16, H>; + + table[SWAP1 as usize] = stack::swap::<1, H>; + table[SWAP2 as usize] = stack::swap::<2, H>; + table[SWAP3 as usize] = stack::swap::<3, H>; + table[SWAP4 as usize] = stack::swap::<4, H>; + table[SWAP5 as usize] = stack::swap::<5, H>; + table[SWAP6 as usize] = stack::swap::<6, H>; + table[SWAP7 as usize] = stack::swap::<7, H>; + table[SWAP8 as usize] = stack::swap::<8, H>; + table[SWAP9 as usize] = stack::swap::<9, H>; + table[SWAP10 as usize] = stack::swap::<10, H>; + table[SWAP11 as usize] = stack::swap::<11, H>; + table[SWAP12 as usize] = stack::swap::<12, H>; + table[SWAP13 as usize] = stack::swap::<13, H>; + table[SWAP14 as usize] = stack::swap::<14, H>; + table[SWAP15 as usize] = stack::swap::<15, H>; + table[SWAP16 as usize] = stack::swap::<16, H>; + + table[LOG0 as usize] = host::log::<0, H>; + table[LOG1 as usize] = host::log::<1, H>; + table[LOG2 as usize] = host::log::<2, H>; + table[LOG3 as usize] = host::log::<3, H>; + table[LOG4 as usize] = host::log::<4, H>; + + table[DATALOAD as usize] = data::data_load; + table[DATALOADN as usize] = data::data_loadn; + table[DATASIZE as usize] = data::data_size; + table[DATACOPY as usize] = data::data_copy; + + table[RJUMP as usize] = control::rjump; + table[RJUMPI as usize] = control::rjumpi; + table[RJUMPV as usize] = control::rjumpv; + table[CALLF as usize] = control::callf; + table[RETF as usize] = control::retf; + table[JUMPF as usize] = control::jumpf; + table[DUPN as usize] = stack::dupn; + table[SWAPN as usize] = stack::swapn; + table[EXCHANGE as usize] = stack::exchange; + + table[EOFCREATE as usize] = contract::eofcreate; + + table[RETURNCONTRACT as usize] = contract::return_contract; + + table[CREATE as usize] = contract::create::; + table[CALL as usize] = contract::call::; + table[CALLCODE as usize] = contract::call_code::; + table[RETURN as usize] = control::ret; + table[DELEGATECALL as usize] = contract::delegate_call::; + table[CREATE2 as usize] = contract::create::; + + table[RETURNDATALOAD as usize] = system::returndataload; + table[EXTCALL as usize] = contract::extcall::; + table[EXTDELEGATECALL as usize] = contract::extdelegatecall::; + table[STATICCALL as usize] = contract::static_call::; + table[EXTSTATICCALL as usize] = contract::extstaticcall; + table[REVERT as usize] = control::revert::; + table[INVALID as usize] = control::invalid; + table[SELFDESTRUCT as usize] = host::selfdestruct::; + table +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::DummyHost; + use bytecode::opcode::*; + use specification::hardfork::LatestSpec; + use wiring::DefaultEthereumWiring; + + #[test] + fn all_instructions_and_opcodes_used() { + // known unknown instruction we compare it with other instructions from table. + let unknown_instruction = 0x0C_usize; + let instr_table = instruction_table::, LatestSpec>(); + + let unknown_istr = instr_table[unknown_instruction]; + for (i, instr) in instr_table.iter().enumerate() { + let is_opcode_unknown = OpCode::new(i as u8).is_none(); + let is_instr_unknown = *instr == unknown_istr; + assert_eq!( + is_instr_unknown, is_opcode_unknown, + "Opcode 0x{:X?} is not handled", + i + ); + } + } +} diff --git a/crates/interpreter/src/instructions/control.rs b/crates/interpreter/src/instructions/control.rs index ff1aed5b76..cf4582912c 100644 --- a/crates/interpreter/src/instructions/control.rs +++ b/crates/interpreter/src/instructions/control.rs @@ -203,7 +203,7 @@ pub fn unknown(interpreter: &mut Interpreter, _host: &mut H) { #[cfg(test)] mod test { use super::*; - use crate::{tables::make_instruction_table, DummyHost, FunctionReturnFrame, Gas, Interpreter}; + use crate::{table::make_instruction_table, DummyHost, FunctionReturnFrame, Gas, Interpreter}; use bytecode::opcode::{CALLF, JUMPF, NOP, RETF, RJUMP, RJUMPI, RJUMPV, STOP}; use bytecode::{ eof::{Eof, TypesSection}, diff --git a/crates/interpreter/src/instructions/data.rs b/crates/interpreter/src/instructions/data.rs index 0dcc3425fa..a9257205b5 100644 --- a/crates/interpreter/src/instructions/data.rs +++ b/crates/interpreter/src/instructions/data.rs @@ -90,7 +90,7 @@ mod test { use wiring::DefaultEthereumWiring; use super::*; - use crate::{tables::make_instruction_table, DummyHost, Gas, Interpreter}; + use crate::{table::make_instruction_table, DummyHost, Gas, Interpreter}; use bytecode::opcode::{DATACOPY, DATALOAD, DATALOADN, DATASIZE}; fn dummy_eof(code_bytes: Bytes) -> Bytecode { diff --git a/crates/interpreter/src/instructions/stack.rs b/crates/interpreter/src/instructions/stack.rs index af8fe60aff..4d2bf5881a 100644 --- a/crates/interpreter/src/instructions/stack.rs +++ b/crates/interpreter/src/instructions/stack.rs @@ -86,7 +86,7 @@ pub fn exchange(interpreter: &mut Interpreter, _host: &mut H) mod test { use super::*; - use crate::{tables::make_instruction_table, DummyHost, Gas, InstructionResult}; + use crate::{table::make_instruction_table, DummyHost, Gas, InstructionResult}; use bytecode::opcode::{DUPN, EXCHANGE, SWAPN}; use bytecode::Bytecode; use specification::hardfork::PragueSpec; diff --git a/crates/interpreter/src/instructions/system.rs b/crates/interpreter/src/instructions/system.rs index 7cf2619ae7..a4729af04c 100644 --- a/crates/interpreter/src/instructions/system.rs +++ b/crates/interpreter/src/instructions/system.rs @@ -185,7 +185,7 @@ pub fn gas(interpreter: &mut Interpreter, _host: &mut H) { #[cfg(test)] mod test { use super::*; - use crate::{tables::make_instruction_table, DummyHost, Gas, InstructionResult}; + use crate::{table::make_instruction_table, DummyHost, Gas, InstructionResult}; use bytecode::opcode::{RETURNDATACOPY, RETURNDATALOAD}; use bytecode::Bytecode; use primitives::bytes; diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index 371a1e6eed..8198eec0f2 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -468,7 +468,7 @@ pub fn resize_memory(memory: &mut SharedMemory, gas: &mut Gas, new_size: usize) #[cfg(test)] mod tests { use super::*; - use crate::{tables::InstructionTable, DummyHost}; + use crate::{table::InstructionTable, DummyHost}; use specification::hardfork::CancunSpec; use wiring::DefaultEthereumWiring; @@ -478,14 +478,13 @@ mod tests { let mut host = crate::DummyHost::::default(); let table: &InstructionTable> = - &crate::tables::make_instruction_table::, CancunSpec>( - ); + &crate::table::make_instruction_table::, CancunSpec>(); let _ = interp.run(EMPTY_SHARED_MEMORY, table, &mut host); let host: &mut dyn Host = &mut host as &mut dyn Host; let table: &InstructionTable> = - &crate::tables::make_instruction_table::< + &crate::table::make_instruction_table::< dyn Host, CancunSpec, >(); diff --git a/crates/interpreter/src/lib.rs b/crates/interpreter/src/lib.rs index 32261091e7..e2821e06f5 100644 --- a/crates/interpreter/src/lib.rs +++ b/crates/interpreter/src/lib.rs @@ -24,8 +24,7 @@ mod instruction_result; pub mod instructions; pub mod interpreter; pub mod interpreter_action; -pub mod opcode; -pub mod tables; +pub mod table; // Reexport primary types. pub use function_stack::{FunctionReturnFrame, FunctionStack}; @@ -43,5 +42,5 @@ pub use interpreter_action::{ EOFCreateKind, InterpreterAction, }; pub use primitives::{MAX_CODE_SIZE, MAX_INITCODE_SIZE}; -pub use tables::Instruction; +pub use table::Instruction; pub use wiring::default::CreateScheme; diff --git a/crates/interpreter/src/opcode.rs b/crates/interpreter/src/opcode.rs deleted file mode 100644 index 4fc6cd03e6..0000000000 --- a/crates/interpreter/src/opcode.rs +++ /dev/null @@ -1,213 +0,0 @@ -//! EVM opcode definitions and utilities. - -use crate::{instructions::*, Host}; -use specification::hardfork::Spec; - -/// Returns the instruction function for the given opcode and spec. -pub const fn instruction( - opcode: u8, -) -> crate::tables::Instruction { - let table = instruction_table::(); - table[opcode as usize] -} - -pub const fn instruction_table( -) -> [crate::tables::Instruction; 256] { - use bytecode::opcode::*; - let mut table = [control::unknown as crate::tables::Instruction; 256]; - - table[STOP as usize] = control::stop; - table[ADD as usize] = arithmetic::add; - table[STOP as usize] = control::stop; - - table[ADD as usize] = arithmetic::add; - table[MUL as usize] = arithmetic::mul; - table[SUB as usize] = arithmetic::sub; - table[DIV as usize] = arithmetic::div; - table[SDIV as usize] = arithmetic::sdiv; - table[MOD as usize] = arithmetic::rem; - table[SMOD as usize] = arithmetic::smod; - table[ADDMOD as usize] = arithmetic::addmod; - table[MULMOD as usize] = arithmetic::mulmod; - table[EXP as usize] = arithmetic::exp::; - table[SIGNEXTEND as usize] = arithmetic::signextend; - - table[LT as usize] = bitwise::lt; - table[GT as usize] = bitwise::gt; - table[SLT as usize] = bitwise::slt; - table[SGT as usize] = bitwise::sgt; - table[EQ as usize] = bitwise::eq; - table[ISZERO as usize] = bitwise::iszero; - table[AND as usize] = bitwise::bitand; - table[OR as usize] = bitwise::bitor; - table[XOR as usize] = bitwise::bitxor; - table[NOT as usize] = bitwise::not; - table[BYTE as usize] = bitwise::byte; - table[SHL as usize] = bitwise::shl::; - table[SHR as usize] = bitwise::shr::; - table[SAR as usize] = bitwise::sar::; - - table[KECCAK256 as usize] = system::keccak256; - - table[ADDRESS as usize] = system::address; - table[BALANCE as usize] = host::balance::; - table[ORIGIN as usize] = host_env::origin; - table[CALLER as usize] = system::caller; - table[CALLVALUE as usize] = system::callvalue; - table[CALLDATALOAD as usize] = system::calldataload; - table[CALLDATASIZE as usize] = system::calldatasize; - table[CALLDATACOPY as usize] = system::calldatacopy; - table[CODESIZE as usize] = system::codesize; - table[CODECOPY as usize] = system::codecopy; - - table[GASPRICE as usize] = host_env::gasprice; - table[EXTCODESIZE as usize] = host::extcodesize::; - table[EXTCODECOPY as usize] = host::extcodecopy::; - table[RETURNDATASIZE as usize] = system::returndatasize::; - table[RETURNDATACOPY as usize] = system::returndatacopy::; - table[EXTCODEHASH as usize] = host::extcodehash::; - table[BLOCKHASH as usize] = host::blockhash::; - table[COINBASE as usize] = host_env::coinbase; - table[TIMESTAMP as usize] = host_env::timestamp; - table[NUMBER as usize] = host_env::block_number; - table[DIFFICULTY as usize] = host_env::difficulty::; - table[GASLIMIT as usize] = host_env::gaslimit; - table[CHAINID as usize] = host_env::chainid::; - table[SELFBALANCE as usize] = host::selfbalance::; - table[BASEFEE as usize] = host_env::basefee::; - table[BLOBHASH as usize] = host_env::blob_hash::; - table[BLOBBASEFEE as usize] = host_env::blob_basefee::; - - table[POP as usize] = stack::pop; - table[MLOAD as usize] = memory::mload; - table[MSTORE as usize] = memory::mstore; - table[MSTORE8 as usize] = memory::mstore8; - table[SLOAD as usize] = host::sload::; - table[SSTORE as usize] = host::sstore::; - table[JUMP as usize] = control::jump; - table[JUMPI as usize] = control::jumpi; - table[PC as usize] = control::pc; - table[MSIZE as usize] = memory::msize; - table[GAS as usize] = system::gas; - table[JUMPDEST as usize] = control::jumpdest_or_nop; - table[TLOAD as usize] = host::tload::; - table[TSTORE as usize] = host::tstore::; - table[MCOPY as usize] = memory::mcopy::; - - table[PUSH0 as usize] = stack::push0::; - table[PUSH1 as usize] = stack::push::<1, H>; - table[PUSH2 as usize] = stack::push::<2, H>; - table[PUSH3 as usize] = stack::push::<3, H>; - table[PUSH4 as usize] = stack::push::<4, H>; - table[PUSH5 as usize] = stack::push::<5, H>; - table[PUSH6 as usize] = stack::push::<6, H>; - table[PUSH7 as usize] = stack::push::<7, H>; - table[PUSH8 as usize] = stack::push::<8, H>; - table[PUSH9 as usize] = stack::push::<9, H>; - table[PUSH10 as usize] = stack::push::<10, H>; - table[PUSH11 as usize] = stack::push::<11, H>; - table[PUSH12 as usize] = stack::push::<12, H>; - table[PUSH13 as usize] = stack::push::<13, H>; - table[PUSH14 as usize] = stack::push::<14, H>; - table[PUSH15 as usize] = stack::push::<15, H>; - table[PUSH16 as usize] = stack::push::<16, H>; - table[PUSH17 as usize] = stack::push::<17, H>; - table[PUSH18 as usize] = stack::push::<18, H>; - table[PUSH19 as usize] = stack::push::<19, H>; - table[PUSH20 as usize] = stack::push::<20, H>; - table[PUSH21 as usize] = stack::push::<21, H>; - table[PUSH22 as usize] = stack::push::<22, H>; - table[PUSH23 as usize] = stack::push::<23, H>; - table[PUSH24 as usize] = stack::push::<24, H>; - table[PUSH25 as usize] = stack::push::<25, H>; - table[PUSH26 as usize] = stack::push::<26, H>; - table[PUSH27 as usize] = stack::push::<27, H>; - table[PUSH28 as usize] = stack::push::<28, H>; - table[PUSH29 as usize] = stack::push::<29, H>; - table[PUSH30 as usize] = stack::push::<30, H>; - table[PUSH31 as usize] = stack::push::<31, H>; - table[PUSH32 as usize] = stack::push::<32, H>; - - table[DUP1 as usize] = stack::dup::<1, H>; - table[DUP2 as usize] = stack::dup::<2, H>; - table[DUP3 as usize] = stack::dup::<3, H>; - table[DUP4 as usize] = stack::dup::<4, H>; - table[DUP5 as usize] = stack::dup::<5, H>; - table[DUP6 as usize] = stack::dup::<6, H>; - table[DUP7 as usize] = stack::dup::<7, H>; - table[DUP8 as usize] = stack::dup::<8, H>; - table[DUP9 as usize] = stack::dup::<9, H>; - table[DUP10 as usize] = stack::dup::<10, H>; - table[DUP11 as usize] = stack::dup::<11, H>; - table[DUP12 as usize] = stack::dup::<12, H>; - table[DUP13 as usize] = stack::dup::<13, H>; - table[DUP14 as usize] = stack::dup::<14, H>; - table[DUP15 as usize] = stack::dup::<15, H>; - table[DUP16 as usize] = stack::dup::<16, H>; - - table[SWAP1 as usize] = stack::swap::<1, H>; - table[SWAP2 as usize] = stack::swap::<2, H>; - table[SWAP3 as usize] = stack::swap::<3, H>; - table[SWAP4 as usize] = stack::swap::<4, H>; - table[SWAP5 as usize] = stack::swap::<5, H>; - table[SWAP6 as usize] = stack::swap::<6, H>; - table[SWAP7 as usize] = stack::swap::<7, H>; - table[SWAP8 as usize] = stack::swap::<8, H>; - table[SWAP9 as usize] = stack::swap::<9, H>; - table[SWAP10 as usize] = stack::swap::<10, H>; - table[SWAP11 as usize] = stack::swap::<11, H>; - table[SWAP12 as usize] = stack::swap::<12, H>; - table[SWAP13 as usize] = stack::swap::<13, H>; - table[SWAP14 as usize] = stack::swap::<14, H>; - table[SWAP15 as usize] = stack::swap::<15, H>; - table[SWAP16 as usize] = stack::swap::<16, H>; - - table[LOG0 as usize] = host::log::<0, H>; - table[LOG1 as usize] = host::log::<1, H>; - table[LOG2 as usize] = host::log::<2, H>; - table[LOG3 as usize] = host::log::<3, H>; - table[LOG4 as usize] = host::log::<4, H>; - - table[DATALOAD as usize] = data::data_load; - table[DATALOADN as usize] = data::data_loadn; - table[DATASIZE as usize] = data::data_size; - table[DATACOPY as usize] = data::data_copy; - - table[RJUMP as usize] = control::rjump; - table[RJUMPI as usize] = control::rjumpi; - table[RJUMPV as usize] = control::rjumpv; - table[CALLF as usize] = control::callf; - table[RETF as usize] = control::retf; - table[JUMPF as usize] = control::jumpf; - table[DUPN as usize] = stack::dupn; - table[SWAPN as usize] = stack::swapn; - table[EXCHANGE as usize] = stack::exchange; - - table[EOFCREATE as usize] = contract::eofcreate; - - table[RETURNCONTRACT as usize] = contract::return_contract; - - table[CREATE as usize] = contract::create::; - table[CALL as usize] = contract::call::; - table[CALLCODE as usize] = contract::call_code::; - table[RETURN as usize] = control::ret; - table[DELEGATECALL as usize] = contract::delegate_call::; - table[CREATE2 as usize] = contract::create::; - - table[RETURNDATALOAD as usize] = system::returndataload; - table[EXTCALL as usize] = contract::extcall::; - table[EXTDELEGATECALL as usize] = contract::extdelegatecall::; - table[STATICCALL as usize] = contract::static_call::; - table[EXTSTATICCALL as usize] = contract::extstaticcall; - - table[REVERT as usize] = control::revert::; - table[INVALID as usize] = control::invalid; - table[SELFDESTRUCT as usize] = host::selfdestruct::; - table -} - -#[cfg(test)] -mod tests { - use super::*; - use bytecode::opcode::*; -} diff --git a/crates/interpreter/src/tables.rs b/crates/interpreter/src/table.rs similarity index 98% rename from crates/interpreter/src/tables.rs rename to crates/interpreter/src/table.rs index 0aa8a4238d..1dc4afaac7 100644 --- a/crates/interpreter/src/tables.rs +++ b/crates/interpreter/src/table.rs @@ -1,6 +1,6 @@ #![allow(clippy::wrong_self_convention)] -use crate::{instructions::control, opcode::instruction, Host, Interpreter}; +use crate::{instructions::control, instructions::instruction, Host, Interpreter}; use specification::hardfork::Spec; use std::boxed::Box; diff --git a/crates/revm/benches/bench.rs b/crates/revm/benches/bench.rs index baf0f2dc16..411e7df610 100644 --- a/crates/revm/benches/bench.rs +++ b/crates/revm/benches/bench.rs @@ -2,7 +2,7 @@ use criterion::{ criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, }; use database::BenchmarkDB; -use interpreter::{tables::make_instruction_table, SharedMemory, EMPTY_SHARED_MEMORY}; +use interpreter::{table::make_instruction_table, SharedMemory, EMPTY_SHARED_MEMORY}; use revm::{ bytecode::Bytecode, interpreter::{Contract, DummyHost, Interpreter}, diff --git a/crates/revm/src/evm_wiring.rs b/crates/revm/src/evm_wiring.rs index 5ef17bd589..280fbbfee7 100644 --- a/crates/revm/src/evm_wiring.rs +++ b/crates/revm/src/evm_wiring.rs @@ -3,7 +3,7 @@ use crate::{ EvmHandler, }; use database_interface::Database; -use interpreter::tables::InstructionTables; +use interpreter::table::InstructionTables; use specification::spec_to_generic; use std::fmt::Debug; use std::vec::Vec; diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index 55611cfa03..ed810c0ff7 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -10,7 +10,7 @@ pub use handle_types::*; use crate::{Context, EvmWiring, Frame}; use core::mem; -use interpreter::{tables::InstructionTables, Host, InterpreterAction, SharedMemory}; +use interpreter::{table::InstructionTables, Host, InterpreterAction, SharedMemory}; use register::{EvmHandler, HandleRegisters}; use specification::spec_to_generic; use std::vec::Vec; diff --git a/crates/revm/src/handler/handle_types/execution.rs b/crates/revm/src/handler/handle_types/execution.rs index ddfa43080b..c6c512462c 100644 --- a/crates/revm/src/handler/handle_types/execution.rs +++ b/crates/revm/src/handler/handle_types/execution.rs @@ -3,7 +3,7 @@ use crate::{ FrameOrResult, FrameResult, }; use interpreter::{ - tables::InstructionTables, CallInputs, CallOutcome, CreateInputs, CreateOutcome, + table::InstructionTables, CallInputs, CallOutcome, CreateInputs, CreateOutcome, EOFCreateInputs, InterpreterAction, InterpreterResult, SharedMemory, }; use specification::hardfork::Spec; diff --git a/crates/revm/src/handler/mainnet/execution.rs b/crates/revm/src/handler/mainnet/execution.rs index effc59b237..c35a93f57e 100644 --- a/crates/revm/src/handler/mainnet/execution.rs +++ b/crates/revm/src/handler/mainnet/execution.rs @@ -4,7 +4,7 @@ use crate::{ }; use core::mem; use interpreter::{ - return_ok, return_revert, tables::InstructionTables, CallInputs, CallOutcome, CreateInputs, + return_ok, return_revert, table::InstructionTables, CallInputs, CallOutcome, CreateInputs, CreateOutcome, EOFCreateInputs, Gas, InstructionResult, InterpreterAction, InterpreterResult, SharedMemory, EMPTY_SHARED_MEMORY, }; diff --git a/examples/contract_deployment/src/main.rs b/examples/contract_deployment/src/main.rs index 7dba23307f..5d46984da1 100644 --- a/examples/contract_deployment/src/main.rs +++ b/examples/contract_deployment/src/main.rs @@ -4,7 +4,7 @@ use anyhow::{anyhow, bail}; use database::InMemoryDB; use revm::{ - interpreter::opcode, + bytecode::opcode, primitives::{hex, Bytes, TxKind, U256}, wiring::{ result::{ExecutionResult, Output},