From 1237dbc6503c6a4716ea039949a5861b3629b319 Mon Sep 17 00:00:00 2001 From: rakita Date: Mon, 23 Sep 2024 13:56:51 +0200 Subject: [PATCH 1/3] feat: Merge validation/analyzis with Bytecode --- Cargo.lock | 5 +- bins/revme/src/cmd/bench/analysis.rs | 3 +- bins/revme/src/cmd/bench/snailtracer.rs | 3 +- bins/revme/src/cmd/bytecode.rs | 8 +- bins/revme/src/cmd/eofvalidation.rs | 2 +- bins/revme/src/cmd/statetest/runner.rs | 3 +- crates/bytecode/Cargo.toml | 10 +- crates/bytecode/src/bytecode.rs | 246 ++++++++++ .../src/{errors.rs => decode_errors.rs} | 0 crates/bytecode/src/eof.rs | 15 + .../src/eof/printer.rs} | 9 +- .../src/eof/verification.rs} | 65 +-- crates/bytecode/src/legacy.rs | 69 +-- crates/bytecode/src/legacy/analyzed.rs | 65 +++ crates/bytecode/src/legacy/raw.rs | 74 +++ crates/bytecode/src/lib.rs | 247 +--------- .../{interpreter => bytecode}/src/opcode.rs | 452 ++++++++---------- crates/bytecode/src/opcode/parse.rs | 32 ++ crates/bytecode/src/utils.rs | 17 + crates/inspector/src/customprinter.rs | 3 +- crates/inspector/src/eip3155.rs | 2 +- crates/inspector/src/gas.rs | 4 +- crates/inspector/src/handler_register.rs | 7 +- crates/interpreter/Cargo.toml | 8 +- crates/interpreter/src/host/results.rs | 0 crates/interpreter/src/host/trait.rs | 0 crates/interpreter/src/instructions.rs | 230 +++++++++ .../interpreter/src/instructions/control.rs | 50 +- crates/interpreter/src/instructions/data.rs | 6 +- crates/interpreter/src/instructions/stack.rs | 20 +- crates/interpreter/src/instructions/system.rs | 6 +- crates/interpreter/src/interpreter.rs | 8 +- .../interpreter/src/interpreter/contract.rs | 3 +- crates/interpreter/src/lib.rs | 8 +- .../src/{opcode/tables.rs => table.rs} | 3 +- crates/revm/benches/bench.rs | 10 +- crates/revm/src/context/evm_context.rs | 6 +- crates/revm/src/context/inner_evm_context.rs | 6 +- crates/revm/src/evm.rs | 6 +- crates/revm/src/evm_wiring.rs | 2 +- crates/revm/src/handler.rs | 2 +- .../src/handler/handle_types/execution.rs | 2 +- crates/revm/src/handler/mainnet/execution.rs | 2 +- crates/specification/src/constantans.rs | 2 + crates/specification/src/lib.rs | 1 + examples/contract_deployment/src/main.rs | 2 +- 46 files changed, 1006 insertions(+), 718 deletions(-) create mode 100644 crates/bytecode/src/bytecode.rs rename crates/bytecode/src/{errors.rs => decode_errors.rs} (100%) rename crates/{interpreter/src/opcode/eof_printer.rs => bytecode/src/eof/printer.rs} (90%) rename crates/{interpreter/src/interpreter/analysis.rs => bytecode/src/eof/verification.rs} (93%) create mode 100644 crates/bytecode/src/legacy/analyzed.rs create mode 100644 crates/bytecode/src/legacy/raw.rs rename crates/{interpreter => bytecode}/src/opcode.rs (51%) create mode 100644 crates/bytecode/src/opcode/parse.rs create mode 100644 crates/bytecode/src/utils.rs create mode 100644 crates/interpreter/src/host/results.rs create mode 100644 crates/interpreter/src/host/trait.rs rename crates/interpreter/src/{opcode/tables.rs => table.rs} (98%) create mode 100644 crates/specification/src/constantans.rs diff --git a/Cargo.lock b/Cargo.lock index 806c3dff72..8338fc0391 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2909,7 +2909,10 @@ name = "revm-bytecode" version = "1.0.0" dependencies = [ "bitvec", + "paste", + "phf", "revm-primitives", + "revm-specification", "serde", ] @@ -2969,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 f1418d4e5b..b4a1040494 100644 --- a/crates/bytecode/Cargo.toml +++ b/crates/bytecode/Cargo.toml @@ -24,6 +24,7 @@ all = "warn" [dependencies] # revm primitives.workspace = true +specification.workspace = true # Jumpmap bitvec = { version = "1", default-features = false, features = ["alloc"] } @@ -34,9 +35,16 @@ serde = { version = "1.0", default-features = false, features = [ "rc", ], optional = true } +# parse opcode feature +paste = { version = "1.0", optional = true } +phf = { version = "0.11", default-features = false, optional = true, features = [ + "macros", +] } + [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 = ["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/eof.rs b/crates/bytecode/src/eof.rs index 5dcab9e83f..6aa7a00597 100644 --- a/crates/bytecode/src/eof.rs +++ b/crates/bytecode/src/eof.rs @@ -1,11 +1,14 @@ mod body; mod decode_helpers; mod header; +pub mod printer; mod types_section; +pub mod verification; pub use body::EofBody; pub use header::EofHeader; pub use types_section::TypesSection; +pub use verification::*; use core::cmp::min; use primitives::{b256, bytes, Bytes, B256}; @@ -48,6 +51,18 @@ impl Default for Eof { } impl Eof { + pub fn validate(&self) -> Result<(), EofError> { + validate_eof(self) + } + + pub fn valitate_raw(bytes: Bytes) -> Result { + validate_raw_eof(bytes) + } + + pub fn validate_mode(&self, mode: CodeType) -> Result<(), EofError> { + validate_eof_inner(self, Some(mode)) + } + /// Creates a new EOF container from the given body. pub fn new(body: EofBody) -> Self { body.into_eof() diff --git a/crates/interpreter/src/opcode/eof_printer.rs b/crates/bytecode/src/eof/printer.rs similarity index 90% rename from crates/interpreter/src/opcode/eof_printer.rs rename to crates/bytecode/src/eof/printer.rs index 995da4be7a..36ead724f6 100644 --- a/crates/interpreter/src/opcode/eof_printer.rs +++ b/crates/bytecode/src/eof/printer.rs @@ -1,14 +1,13 @@ #[cfg(feature = "std")] -pub fn print_eof_code(code: &[u8]) { - use super::*; - use crate::instructions::utility::read_i16; +pub fn print(code: &[u8]) { + use crate::{opcode::*, utils::read_i16}; use primitives::hex; // We can check validity and jump destinations in one pass. let mut i = 0; while i < code.len() { let op = code[i]; - let opcode = &OPCODE_INFO_JUMPTABLE[op as usize]; + let opcode = &OPCODE_INFO[op as usize]; let Some(opcode) = opcode else { println!("Unknown opcode: 0x{:02X}", op); @@ -64,6 +63,6 @@ mod test { #[test] fn sanity_test() { - print_eof_code(&hex!("6001e200ffff00")); + print(&hex!("6001e200ffff00")); } } diff --git a/crates/interpreter/src/interpreter/analysis.rs b/crates/bytecode/src/eof/verification.rs similarity index 93% rename from crates/interpreter/src/interpreter/analysis.rs rename to crates/bytecode/src/eof/verification.rs index 6935a6dad8..5371aa55c2 100644 --- a/crates/interpreter/src/interpreter/analysis.rs +++ b/crates/bytecode/src/eof/verification.rs @@ -1,68 +1,13 @@ use crate::{ - instructions::utility::{read_i16, read_u16}, - opcode, OPCODE_INFO_JUMPTABLE, STACK_LIMIT, -}; -use bytecode::{ - bitvec::prelude::{bitvec, BitVec, Lsb0}, eof::{Eof, EofDecodeError, TypesSection}, - legacy::{JumpTable, LegacyAnalyzedBytecode}, - Bytecode, + opcode::{self, OPCODE_INFO}, + utils::{read_i16, read_u16}, }; use primitives::{Bytes, MAX_INITCODE_SIZE}; +use specification::constantans::STACK_LIMIT; use core::{convert::identity, mem}; -use std::{borrow::Cow, fmt, sync::Arc, vec, vec::Vec}; - -/// 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 to_analysed(bytecode: Bytecode) -> Bytecode { - let (bytes, len) = match bytecode { - Bytecode::LegacyRaw(bytecode) => { - let len = bytecode.len(); - let mut padded_bytecode = Vec::with_capacity(len + 33); - padded_bytecode.extend_from_slice(&bytecode); - padded_bytecode.resize(len + 33, 0); - (Bytes::from(padded_bytecode), len) - } - n => return n, - }; - let jump_table = analyze(bytes.as_ref()); - - Bytecode::LegacyAnalyzed(LegacyAnalyzedBytecode::new(bytes, len, jump_table)) -} - -/// Analyze bytecode to build a jump map. -fn analyze(code: &[u8]) -> JumpTable { - let mut jumps: BitVec = bitvec![u8, Lsb0; 0; code.len()]; - - let range = code.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)) -} +use std::{borrow::Cow, fmt, vec, vec::Vec}; /// Decodes `raw` into an [`Eof`] container and validates it. pub fn validate_raw_eof(raw: Bytes) -> Result { @@ -507,7 +452,7 @@ pub fn validate_eof_code( // We can check validity and jump destinations in one pass. while i < code.len() { let op = code[i]; - let opcode = &OPCODE_INFO_JUMPTABLE[op as usize]; + let opcode = &OPCODE_INFO[op as usize]; let Some(opcode) = opcode else { // err unknown opcode. diff --git a/crates/bytecode/src/legacy.rs b/crates/bytecode/src/legacy.rs index 2fe1cdd5c7..e9afe03790 100644 --- a/crates/bytecode/src/legacy.rs +++ b/crates/bytecode/src/legacy.rs @@ -1,68 +1,7 @@ +mod analyzed; mod jump_map; +mod raw; +pub use analyzed::LegacyAnalyzedBytecode; pub use jump_map::JumpTable; - -use bitvec::{bitvec, order::Lsb0}; -use primitives::Bytes; -use std::sync::Arc; - -/// Legacy analyzed -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct LegacyAnalyzedBytecode { - /// Bytecode with 32 zero bytes padding. - bytecode: Bytes, - /// Original bytes length. - original_len: usize, - /// Jump table. - jump_table: JumpTable, -} - -impl Default for LegacyAnalyzedBytecode { - #[inline] - fn default() -> Self { - Self { - bytecode: Bytes::from_static(&[0]), - original_len: 0, - jump_table: JumpTable(Arc::new(bitvec![u8, Lsb0; 0])), - } - } -} - -impl LegacyAnalyzedBytecode { - /// Create new analyzed bytecode. - pub fn new(bytecode: Bytes, original_len: usize, jump_table: JumpTable) -> Self { - Self { - bytecode, - original_len, - jump_table, - } - } - - /// Returns a reference to the bytecode. - /// - /// The bytecode is padded with 32 zero bytes. - pub fn bytecode(&self) -> &Bytes { - &self.bytecode - } - - /// Original bytes length. - pub fn original_len(&self) -> usize { - self.original_len - } - - /// Original bytes without padding. - pub fn original_bytes(&self) -> Bytes { - self.bytecode.slice(..self.original_len) - } - - /// Original bytes without padding. - pub fn original_byte_slice(&self) -> &[u8] { - &self.bytecode[..self.original_len] - } - - /// Jumptable of analyzed bytes. - pub fn jump_table(&self) -> &JumpTable { - &self.jump_table - } -} +pub use raw::{analyze_legacy, LegacyRawBytecode}; diff --git a/crates/bytecode/src/legacy/analyzed.rs b/crates/bytecode/src/legacy/analyzed.rs new file mode 100644 index 0000000000..1a8f08511c --- /dev/null +++ b/crates/bytecode/src/legacy/analyzed.rs @@ -0,0 +1,65 @@ +use super::JumpTable; +use bitvec::{bitvec, order::Lsb0}; +use primitives::Bytes; +use std::sync::Arc; + +// Legacy analyzed +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct LegacyAnalyzedBytecode { + /// Bytecode with 32 zero bytes padding. + bytecode: Bytes, + /// Original bytes length. + original_len: usize, + /// Jump table. + jump_table: JumpTable, +} + +impl Default for LegacyAnalyzedBytecode { + #[inline] + fn default() -> Self { + Self { + bytecode: Bytes::from_static(&[0]), + original_len: 0, + jump_table: JumpTable(Arc::new(bitvec![u8, Lsb0; 0])), + } + } +} + +impl LegacyAnalyzedBytecode { + /// Create new analyzed bytecode. + pub fn new(bytecode: Bytes, original_len: usize, jump_table: JumpTable) -> Self { + Self { + bytecode, + original_len, + jump_table, + } + } + + /// Returns a reference to the bytecode. + /// + /// The bytecode is padded with 32 zero bytes. + pub fn bytecode(&self) -> &Bytes { + &self.bytecode + } + + /// Original bytes length. + pub fn original_len(&self) -> usize { + self.original_len + } + + /// Original bytes without padding. + pub fn original_bytes(&self) -> Bytes { + self.bytecode.slice(..self.original_len) + } + + /// Original bytes without padding. + pub fn original_byte_slice(&self) -> &[u8] { + &self.bytecode[..self.original_len] + } + + /// Jumptable of analyzed bytes. + pub fn jump_table(&self) -> &JumpTable { + &self.jump_table + } +} diff --git a/crates/bytecode/src/legacy/raw.rs b/crates/bytecode/src/legacy/raw.rs new file mode 100644 index 0000000000..c12252cb2c --- /dev/null +++ b/crates/bytecode/src/legacy/raw.rs @@ -0,0 +1,74 @@ +use super::{JumpTable, LegacyAnalyzedBytecode}; +use crate::opcode; +use bitvec::{bitvec, order::Lsb0, vec::BitVec}; +use core::ops::Deref; +use primitives::Bytes; +use std::{sync::Arc, vec::Vec}; + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct LegacyRawBytecode(pub Bytes); + +impl LegacyRawBytecode { + pub fn analysis(&self) -> JumpTable { + analyze_legacy(&self.0) + } + + pub fn into_analyzed(self) -> LegacyAnalyzedBytecode { + let jump_table = self.analysis(); + let len = self.0.len(); + let mut padded_bytecode = Vec::with_capacity(len + 33); + padded_bytecode.extend_from_slice(&self.0); + padded_bytecode.resize(len + 33, 0); + LegacyAnalyzedBytecode::new(padded_bytecode.into(), len, jump_table) + } +} + +impl From for LegacyRawBytecode { + fn from(bytes: Bytes) -> Self { + Self(bytes) + } +} + +impl From<[u8; N]> for LegacyRawBytecode { + fn from(bytes: [u8; N]) -> Self { + Self(bytes.into()) + } +} + +impl Deref for LegacyRawBytecode { + type Target = Bytes; + + fn deref(&self) -> &Self::Target { + &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 0d57202637..ac667c956e 100644 --- a/crates/bytecode/src/lib.rs +++ b/crates/bytecode/src/lib.rs @@ -5,241 +5,22 @@ #[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 eof::{Eof, EOF_MAGIC, EOF_MAGIC_BYTES, EOF_MAGIC_HASH}; -pub use errors::BytecodeDecodeError; -pub use legacy::{JumpTable, LegacyAnalyzedBytecode}; - -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(Bytes), - /// 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) - } - - /// 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)), - } - } - - /// 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.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"); - } - } -} +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, + validate_raw_eof_inner, CodeType, EofValidationError, + }, + Eof, EOF_MAGIC, EOF_MAGIC_BYTES, EOF_MAGIC_HASH, +}; +pub use legacy::{JumpTable, LegacyAnalyzedBytecode, LegacyRawBytecode}; diff --git a/crates/interpreter/src/opcode.rs b/crates/bytecode/src/opcode.rs similarity index 51% rename from crates/interpreter/src/opcode.rs rename to crates/bytecode/src/opcode.rs index 97b98b8c17..1e42207f43 100644 --- a/crates/interpreter/src/opcode.rs +++ b/crates/bytecode/src/opcode.rs @@ -1,32 +1,9 @@ //! EVM opcode definitions and utilities. -pub mod eof_printer; - -mod tables; -pub use tables::{ - make_boxed_instruction_table, make_instruction_table, update_boxed_instruction, - BoxedInstruction, BoxedInstructionTable, DynInstruction, Instruction, InstructionTable, - InstructionTables, -}; - -use crate::{instructions::*, Host}; -use core::{fmt, ptr::NonNull}; -use specification::hardfork::Spec; - -/// An error indicating that an opcode is invalid. -#[derive(Debug, PartialEq, Eq)] #[cfg(feature = "parse")] -pub struct OpCodeError(()); +pub mod parse; -#[cfg(feature = "parse")] -impl fmt::Display for OpCodeError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("invalid opcode") - } -} - -#[cfg(feature = "parse")] -impl core::error::Error for OpCodeError {} +use core::{fmt, ptr::NonNull}; /// An EVM opcode. /// @@ -39,7 +16,7 @@ pub struct OpCode(u8); impl fmt::Display for OpCode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let n = self.get(); - if let Some(val) = OPCODE_INFO_JUMPTABLE[n as usize] { + if let Some(val) = OPCODE_INFO[n as usize] { f.write_str(val.name()) } else { write!(f, "UNKNOWN(0x{n:02X})") @@ -47,33 +24,16 @@ 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] pub const fn new(opcode: u8) -> Option { - match OPCODE_INFO_JUMPTABLE[opcode as usize] { + match OPCODE_INFO[opcode as usize] { Some(_) => Some(Self(opcode)), None => None, } } - /// 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 { @@ -181,7 +141,7 @@ impl OpCode { /// Returns the opcode information. #[inline] pub const fn info(&self) -> OpCodeInfo { - if let Some(t) = OPCODE_INFO_JUMPTABLE[self.0 as usize] { + if let Some(t) = OPCODE_INFO[self.0 as usize] { t } else { panic!("opcode not found") @@ -364,27 +324,8 @@ 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 => $f:expr => $($modifier:ident $(( $($modifier_arg:expr),* ))?),*);* $(;)?) => { + ($($val:literal => $name:ident => $($modifier:ident $(( $($modifier_arg:expr),* ))?),*);* $(;)?) => { // Constants for each opcode. This also takes care of duplicate names. $( #[doc = concat!("The `", stringify!($val), "` (\"", stringify!($name),"\") opcode.")] @@ -396,7 +337,7 @@ macro_rules! opcodes { )*} /// Maps each opcode to its info. - pub const OPCODE_INFO_JUMPTABLE: [Option; 256] = { + pub const OPCODE_INFO: [Option; 256] = { let mut map = [None; 256]; let mut prev: u8 = 0; $( @@ -413,59 +354,70 @@ 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)* }; + pub(crate) 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, - } +/// 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; // the function signature must be the exact same as the others opcodes! { - 0x00 => STOP => control::stop => stack_io(0, 0), terminating; - - 0x01 => ADD => arithmetic::add => stack_io(2, 1); - 0x02 => MUL => arithmetic::mul => stack_io(2, 1); - 0x03 => SUB => arithmetic::sub => stack_io(2, 1); - 0x04 => DIV => arithmetic::div => stack_io(2, 1); - 0x05 => SDIV => arithmetic::sdiv => stack_io(2, 1); - 0x06 => MOD => arithmetic::rem => stack_io(2, 1); - 0x07 => SMOD => arithmetic::smod => stack_io(2, 1); - 0x08 => ADDMOD => arithmetic::addmod => stack_io(3, 1); - 0x09 => MULMOD => arithmetic::mulmod => stack_io(3, 1); - 0x0A => EXP => arithmetic::exp:: => stack_io(2, 1); - 0x0B => SIGNEXTEND => arithmetic::signextend => stack_io(2, 1); + 0x00 => STOP => stack_io(0, 0), terminating; + 0x01 => ADD => stack_io(2, 1); + 0x02 => MUL => stack_io(2, 1); + 0x03 => SUB => stack_io(2, 1); + 0x04 => DIV => stack_io(2, 1); + 0x05 => SDIV => stack_io(2, 1); + 0x06 => MOD => stack_io(2, 1); + 0x07 => SMOD => stack_io(2, 1); + 0x08 => ADDMOD => stack_io(3, 1); + 0x09 => MULMOD => stack_io(3, 1); + 0x0A => EXP => stack_io(2, 1); + 0x0B => SIGNEXTEND => stack_io(2, 1); // 0x0C // 0x0D // 0x0E // 0x0F - 0x10 => LT => bitwise::lt => stack_io(2, 1); - 0x11 => GT => bitwise::gt => stack_io(2, 1); - 0x12 => SLT => bitwise::slt => stack_io(2, 1); - 0x13 => SGT => bitwise::sgt => stack_io(2, 1); - 0x14 => EQ => bitwise::eq => stack_io(2, 1); - 0x15 => ISZERO => bitwise::iszero => stack_io(1, 1); - 0x16 => AND => bitwise::bitand => stack_io(2, 1); - 0x17 => OR => bitwise::bitor => stack_io(2, 1); - 0x18 => XOR => bitwise::bitxor => stack_io(2, 1); - 0x19 => NOT => bitwise::not => stack_io(1, 1); - 0x1A => BYTE => bitwise::byte => stack_io(2, 1); - 0x1B => SHL => bitwise::shl:: => stack_io(2, 1); - 0x1C => SHR => bitwise::shr:: => stack_io(2, 1); - 0x1D => SAR => bitwise::sar:: => stack_io(2, 1); + 0x10 => LT => stack_io(2, 1); + 0x11 => GT => stack_io(2, 1); + 0x12 => SLT => stack_io(2, 1); + 0x13 => SGT => stack_io(2, 1); + 0x14 => EQ => stack_io(2, 1); + 0x15 => ISZERO => stack_io(1, 1); + 0x16 => AND => stack_io(2, 1); + 0x17 => OR => stack_io(2, 1); + 0x18 => XOR => stack_io(2, 1); + 0x19 => NOT => stack_io(1, 1); + 0x1A => BYTE => stack_io(2, 1); + 0x1B => SHL => stack_io(2, 1); + 0x1C => SHR => stack_io(2, 1); + 0x1D => SAR => stack_io(2, 1); // 0x1E // 0x1F - 0x20 => KECCAK256 => system::keccak256 => stack_io(2, 1); + 0x20 => KECCAK256 => stack_io(2, 1); // 0x21 // 0x22 // 0x23 @@ -481,128 +433,128 @@ opcodes! { // 0x2D // 0x2E // 0x2F - 0x30 => ADDRESS => system::address => stack_io(0, 1); - 0x31 => BALANCE => host::balance:: => stack_io(1, 1); - 0x32 => ORIGIN => host_env::origin => stack_io(0, 1); - 0x33 => CALLER => system::caller => stack_io(0, 1); - 0x34 => CALLVALUE => system::callvalue => stack_io(0, 1); - 0x35 => CALLDATALOAD => system::calldataload => stack_io(1, 1); - 0x36 => CALLDATASIZE => system::calldatasize => stack_io(0, 1); - 0x37 => CALLDATACOPY => system::calldatacopy => stack_io(3, 0); - 0x38 => CODESIZE => system::codesize => stack_io(0, 1), not_eof; - 0x39 => CODECOPY => system::codecopy => stack_io(3, 0), not_eof; - - 0x3A => GASPRICE => host_env::gasprice => stack_io(0, 1); - 0x3B => EXTCODESIZE => host::extcodesize:: => stack_io(1, 1), not_eof; - 0x3C => EXTCODECOPY => host::extcodecopy:: => stack_io(4, 0), not_eof; - 0x3D => RETURNDATASIZE => system::returndatasize:: => stack_io(0, 1); - 0x3E => RETURNDATACOPY => system::returndatacopy:: => stack_io(3, 0); - 0x3F => EXTCODEHASH => host::extcodehash:: => stack_io(1, 1), not_eof; - 0x40 => BLOCKHASH => host::blockhash:: => stack_io(1, 1); - 0x41 => COINBASE => host_env::coinbase => stack_io(0, 1); - 0x42 => TIMESTAMP => host_env::timestamp => stack_io(0, 1); - 0x43 => NUMBER => host_env::block_number => stack_io(0, 1); - 0x44 => DIFFICULTY => host_env::difficulty:: => stack_io(0, 1); - 0x45 => GASLIMIT => host_env::gaslimit => stack_io(0, 1); - 0x46 => CHAINID => host_env::chainid:: => stack_io(0, 1); - 0x47 => SELFBALANCE => host::selfbalance:: => stack_io(0, 1); - 0x48 => BASEFEE => host_env::basefee:: => stack_io(0, 1); - 0x49 => BLOBHASH => host_env::blob_hash:: => stack_io(1, 1); - 0x4A => BLOBBASEFEE => host_env::blob_basefee:: => stack_io(0, 1); + 0x30 => ADDRESS => stack_io(0, 1); + 0x31 => BALANCE => stack_io(1, 1); + 0x32 => ORIGIN => stack_io(0, 1); + 0x33 => CALLER => stack_io(0, 1); + 0x34 => CALLVALUE => stack_io(0, 1); + 0x35 => CALLDATALOAD => stack_io(1, 1); + 0x36 => CALLDATASIZE => stack_io(0, 1); + 0x37 => CALLDATACOPY => stack_io(3, 0); + 0x38 => CODESIZE => stack_io(0, 1), not_eof; + 0x39 => CODECOPY => stack_io(3, 0), not_eof; + + 0x3A => GASPRICE => stack_io(0, 1); + 0x3B => EXTCODESIZE => stack_io(1, 1), not_eof; + 0x3C => EXTCODECOPY => stack_io(4, 0), not_eof; + 0x3D => RETURNDATASIZE => stack_io(0, 1); + 0x3E => RETURNDATACOPY => stack_io(3, 0); + 0x3F => EXTCODEHASH => stack_io(1, 1), not_eof; + 0x40 => BLOCKHASH => stack_io(1, 1); + 0x41 => COINBASE => stack_io(0, 1); + 0x42 => TIMESTAMP => stack_io(0, 1); + 0x43 => NUMBER => stack_io(0, 1); + 0x44 => DIFFICULTY => stack_io(0, 1); + 0x45 => GASLIMIT => stack_io(0, 1); + 0x46 => CHAINID => stack_io(0, 1); + 0x47 => SELFBALANCE => stack_io(0, 1); + 0x48 => BASEFEE => stack_io(0, 1); + 0x49 => BLOBHASH => stack_io(1, 1); + 0x4A => BLOBBASEFEE => stack_io(0, 1); // 0x4B // 0x4C // 0x4D // 0x4E // 0x4F - 0x50 => POP => stack::pop => stack_io(1, 0); - 0x51 => MLOAD => memory::mload => stack_io(1, 1); - 0x52 => MSTORE => memory::mstore => stack_io(2, 0); - 0x53 => MSTORE8 => memory::mstore8 => stack_io(2, 0); - 0x54 => SLOAD => host::sload:: => stack_io(1, 1); - 0x55 => SSTORE => host::sstore:: => stack_io(2, 0); - 0x56 => JUMP => control::jump => stack_io(1, 0), not_eof; - 0x57 => JUMPI => control::jumpi => stack_io(2, 0), not_eof; - 0x58 => PC => control::pc => stack_io(0, 1), not_eof; - 0x59 => MSIZE => memory::msize => stack_io(0, 1); - 0x5A => GAS => system::gas => stack_io(0, 1), not_eof; - 0x5B => JUMPDEST => control::jumpdest_or_nop => stack_io(0, 0); - 0x5C => TLOAD => host::tload:: => stack_io(1, 1); - 0x5D => TSTORE => host::tstore:: => stack_io(2, 0); - 0x5E => MCOPY => memory::mcopy:: => stack_io(3, 0); - - 0x5F => PUSH0 => stack::push0:: => stack_io(0, 1); - 0x60 => PUSH1 => stack::push::<1, H> => stack_io(0, 1), immediate_size(1); - 0x61 => PUSH2 => stack::push::<2, H> => stack_io(0, 1), immediate_size(2); - 0x62 => PUSH3 => stack::push::<3, H> => stack_io(0, 1), immediate_size(3); - 0x63 => PUSH4 => stack::push::<4, H> => stack_io(0, 1), immediate_size(4); - 0x64 => PUSH5 => stack::push::<5, H> => stack_io(0, 1), immediate_size(5); - 0x65 => PUSH6 => stack::push::<6, H> => stack_io(0, 1), immediate_size(6); - 0x66 => PUSH7 => stack::push::<7, H> => stack_io(0, 1), immediate_size(7); - 0x67 => PUSH8 => stack::push::<8, H> => stack_io(0, 1), immediate_size(8); - 0x68 => PUSH9 => stack::push::<9, H> => stack_io(0, 1), immediate_size(9); - 0x69 => PUSH10 => stack::push::<10, H> => stack_io(0, 1), immediate_size(10); - 0x6A => PUSH11 => stack::push::<11, H> => stack_io(0, 1), immediate_size(11); - 0x6B => PUSH12 => stack::push::<12, H> => stack_io(0, 1), immediate_size(12); - 0x6C => PUSH13 => stack::push::<13, H> => stack_io(0, 1), immediate_size(13); - 0x6D => PUSH14 => stack::push::<14, H> => stack_io(0, 1), immediate_size(14); - 0x6E => PUSH15 => stack::push::<15, H> => stack_io(0, 1), immediate_size(15); - 0x6F => PUSH16 => stack::push::<16, H> => stack_io(0, 1), immediate_size(16); - 0x70 => PUSH17 => stack::push::<17, H> => stack_io(0, 1), immediate_size(17); - 0x71 => PUSH18 => stack::push::<18, H> => stack_io(0, 1), immediate_size(18); - 0x72 => PUSH19 => stack::push::<19, H> => stack_io(0, 1), immediate_size(19); - 0x73 => PUSH20 => stack::push::<20, H> => stack_io(0, 1), immediate_size(20); - 0x74 => PUSH21 => stack::push::<21, H> => stack_io(0, 1), immediate_size(21); - 0x75 => PUSH22 => stack::push::<22, H> => stack_io(0, 1), immediate_size(22); - 0x76 => PUSH23 => stack::push::<23, H> => stack_io(0, 1), immediate_size(23); - 0x77 => PUSH24 => stack::push::<24, H> => stack_io(0, 1), immediate_size(24); - 0x78 => PUSH25 => stack::push::<25, H> => stack_io(0, 1), immediate_size(25); - 0x79 => PUSH26 => stack::push::<26, H> => stack_io(0, 1), immediate_size(26); - 0x7A => PUSH27 => stack::push::<27, H> => stack_io(0, 1), immediate_size(27); - 0x7B => PUSH28 => stack::push::<28, H> => stack_io(0, 1), immediate_size(28); - 0x7C => PUSH29 => stack::push::<29, H> => stack_io(0, 1), immediate_size(29); - 0x7D => PUSH30 => stack::push::<30, H> => stack_io(0, 1), immediate_size(30); - 0x7E => PUSH31 => stack::push::<31, H> => stack_io(0, 1), immediate_size(31); - 0x7F => PUSH32 => stack::push::<32, H> => stack_io(0, 1), immediate_size(32); - - 0x80 => DUP1 => stack::dup::<1, H> => stack_io(1, 2); - 0x81 => DUP2 => stack::dup::<2, H> => stack_io(2, 3); - 0x82 => DUP3 => stack::dup::<3, H> => stack_io(3, 4); - 0x83 => DUP4 => stack::dup::<4, H> => stack_io(4, 5); - 0x84 => DUP5 => stack::dup::<5, H> => stack_io(5, 6); - 0x85 => DUP6 => stack::dup::<6, H> => stack_io(6, 7); - 0x86 => DUP7 => stack::dup::<7, H> => stack_io(7, 8); - 0x87 => DUP8 => stack::dup::<8, H> => stack_io(8, 9); - 0x88 => DUP9 => stack::dup::<9, H> => stack_io(9, 10); - 0x89 => DUP10 => stack::dup::<10, H> => stack_io(10, 11); - 0x8A => DUP11 => stack::dup::<11, H> => stack_io(11, 12); - 0x8B => DUP12 => stack::dup::<12, H> => stack_io(12, 13); - 0x8C => DUP13 => stack::dup::<13, H> => stack_io(13, 14); - 0x8D => DUP14 => stack::dup::<14, H> => stack_io(14, 15); - 0x8E => DUP15 => stack::dup::<15, H> => stack_io(15, 16); - 0x8F => DUP16 => stack::dup::<16, H> => stack_io(16, 17); - - 0x90 => SWAP1 => stack::swap::<1, H> => stack_io(2, 2); - 0x91 => SWAP2 => stack::swap::<2, H> => stack_io(3, 3); - 0x92 => SWAP3 => stack::swap::<3, H> => stack_io(4, 4); - 0x93 => SWAP4 => stack::swap::<4, H> => stack_io(5, 5); - 0x94 => SWAP5 => stack::swap::<5, H> => stack_io(6, 6); - 0x95 => SWAP6 => stack::swap::<6, H> => stack_io(7, 7); - 0x96 => SWAP7 => stack::swap::<7, H> => stack_io(8, 8); - 0x97 => SWAP8 => stack::swap::<8, H> => stack_io(9, 9); - 0x98 => SWAP9 => stack::swap::<9, H> => stack_io(10, 10); - 0x99 => SWAP10 => stack::swap::<10, H> => stack_io(11, 11); - 0x9A => SWAP11 => stack::swap::<11, H> => stack_io(12, 12); - 0x9B => SWAP12 => stack::swap::<12, H> => stack_io(13, 13); - 0x9C => SWAP13 => stack::swap::<13, H> => stack_io(14, 14); - 0x9D => SWAP14 => stack::swap::<14, H> => stack_io(15, 15); - 0x9E => SWAP15 => stack::swap::<15, H> => stack_io(16, 16); - 0x9F => SWAP16 => stack::swap::<16, H> => stack_io(17, 17); - - 0xA0 => LOG0 => host::log::<0, H> => stack_io(2, 0); - 0xA1 => LOG1 => host::log::<1, H> => stack_io(3, 0); - 0xA2 => LOG2 => host::log::<2, H> => stack_io(4, 0); - 0xA3 => LOG3 => host::log::<3, H> => stack_io(5, 0); - 0xA4 => LOG4 => host::log::<4, H> => stack_io(6, 0); + 0x50 => POP => stack_io(1, 0); + 0x51 => MLOAD => stack_io(1, 1); + 0x52 => MSTORE => stack_io(2, 0); + 0x53 => MSTORE8 => stack_io(2, 0); + 0x54 => SLOAD => stack_io(1, 1); + 0x55 => SSTORE => stack_io(2, 0); + 0x56 => JUMP => stack_io(1, 0), not_eof; + 0x57 => JUMPI => stack_io(2, 0), not_eof; + 0x58 => PC => stack_io(0, 1), not_eof; + 0x59 => MSIZE => stack_io(0, 1); + 0x5A => GAS => stack_io(0, 1), not_eof; + 0x5B => JUMPDEST => stack_io(0, 0); + 0x5C => TLOAD => stack_io(1, 1); + 0x5D => TSTORE => stack_io(2, 0); + 0x5E => MCOPY => stack_io(3, 0); + + 0x5F => PUSH0 => stack_io(0, 1); + 0x60 => PUSH1 => stack_io(0, 1), immediate_size(1); + 0x61 => PUSH2 => stack_io(0, 1), immediate_size(2); + 0x62 => PUSH3 => stack_io(0, 1), immediate_size(3); + 0x63 => PUSH4 => stack_io(0, 1), immediate_size(4); + 0x64 => PUSH5 => stack_io(0, 1), immediate_size(5); + 0x65 => PUSH6 => stack_io(0, 1), immediate_size(6); + 0x66 => PUSH7 => stack_io(0, 1), immediate_size(7); + 0x67 => PUSH8 => stack_io(0, 1), immediate_size(8); + 0x68 => PUSH9 => stack_io(0, 1), immediate_size(9); + 0x69 => PUSH10 => stack_io(0, 1), immediate_size(10); + 0x6A => PUSH11 => stack_io(0, 1), immediate_size(11); + 0x6B => PUSH12 => stack_io(0, 1), immediate_size(12); + 0x6C => PUSH13 => stack_io(0, 1), immediate_size(13); + 0x6D => PUSH14 => stack_io(0, 1), immediate_size(14); + 0x6E => PUSH15 => stack_io(0, 1), immediate_size(15); + 0x6F => PUSH16 => stack_io(0, 1), immediate_size(16); + 0x70 => PUSH17 => stack_io(0, 1), immediate_size(17); + 0x71 => PUSH18 => stack_io(0, 1), immediate_size(18); + 0x72 => PUSH19 => stack_io(0, 1), immediate_size(19); + 0x73 => PUSH20 => stack_io(0, 1), immediate_size(20); + 0x74 => PUSH21 => stack_io(0, 1), immediate_size(21); + 0x75 => PUSH22 => stack_io(0, 1), immediate_size(22); + 0x76 => PUSH23 => stack_io(0, 1), immediate_size(23); + 0x77 => PUSH24 => stack_io(0, 1), immediate_size(24); + 0x78 => PUSH25 => stack_io(0, 1), immediate_size(25); + 0x79 => PUSH26 => stack_io(0, 1), immediate_size(26); + 0x7A => PUSH27 => stack_io(0, 1), immediate_size(27); + 0x7B => PUSH28 => stack_io(0, 1), immediate_size(28); + 0x7C => PUSH29 => stack_io(0, 1), immediate_size(29); + 0x7D => PUSH30 => stack_io(0, 1), immediate_size(30); + 0x7E => PUSH31 => stack_io(0, 1), immediate_size(31); + 0x7F => PUSH32 => stack_io(0, 1), immediate_size(32); + + 0x80 => DUP1 => stack_io(1, 2); + 0x81 => DUP2 => stack_io(2, 3); + 0x82 => DUP3 => stack_io(3, 4); + 0x83 => DUP4 => stack_io(4, 5); + 0x84 => DUP5 => stack_io(5, 6); + 0x85 => DUP6 => stack_io(6, 7); + 0x86 => DUP7 => stack_io(7, 8); + 0x87 => DUP8 => stack_io(8, 9); + 0x88 => DUP9 => stack_io(9, 10); + 0x89 => DUP10 => stack_io(10, 11); + 0x8A => DUP11 => stack_io(11, 12); + 0x8B => DUP12 => stack_io(12, 13); + 0x8C => DUP13 => stack_io(13, 14); + 0x8D => DUP14 => stack_io(14, 15); + 0x8E => DUP15 => stack_io(15, 16); + 0x8F => DUP16 => stack_io(16, 17); + + 0x90 => SWAP1 => stack_io(2, 2); + 0x91 => SWAP2 => stack_io(3, 3); + 0x92 => SWAP3 => stack_io(4, 4); + 0x93 => SWAP4 => stack_io(5, 5); + 0x94 => SWAP5 => stack_io(6, 6); + 0x95 => SWAP6 => stack_io(7, 7); + 0x96 => SWAP7 => stack_io(8, 8); + 0x97 => SWAP8 => stack_io(9, 9); + 0x98 => SWAP9 => stack_io(10, 10); + 0x99 => SWAP10 => stack_io(11, 11); + 0x9A => SWAP11 => stack_io(12, 12); + 0x9B => SWAP12 => stack_io(13, 13); + 0x9C => SWAP13 => stack_io(14, 14); + 0x9D => SWAP14 => stack_io(15, 15); + 0x9E => SWAP15 => stack_io(16, 16); + 0x9F => SWAP16 => stack_io(17, 17); + + 0xA0 => LOG0 => stack_io(2, 0); + 0xA1 => LOG1 => stack_io(3, 0); + 0xA2 => LOG2 => stack_io(4, 0); + 0xA3 => LOG3 => stack_io(5, 0); + 0xA4 => LOG4 => stack_io(6, 0); // 0xA5 // 0xA6 // 0xA7 @@ -646,10 +598,10 @@ opcodes! { // 0xCD // 0xCE // 0xCF - 0xD0 => DATALOAD => data::data_load => stack_io(1, 1); - 0xD1 => DATALOADN => data::data_loadn => stack_io(0, 1), immediate_size(2); - 0xD2 => DATASIZE => data::data_size => stack_io(0, 1); - 0xD3 => DATACOPY => data::data_copy => stack_io(3, 0); + 0xD0 => DATALOAD=> stack_io(1, 1); + 0xD1 => DATALOADN => stack_io(0, 1), immediate_size(2); + 0xD2 => DATASIZE=> stack_io(0, 1); + 0xD3 => DATACOPY=> stack_io(3, 0); // 0xD4 // 0xD5 // 0xD6 @@ -662,38 +614,38 @@ opcodes! { // 0xDD // 0xDE // 0xDF - 0xE0 => RJUMP => control::rjump => stack_io(0, 0), immediate_size(2), terminating; - 0xE1 => RJUMPI => control::rjumpi => stack_io(1, 0), immediate_size(2); - 0xE2 => RJUMPV => control::rjumpv => stack_io(1, 0), immediate_size(1); - 0xE3 => CALLF => control::callf => stack_io(0, 0), immediate_size(2); - 0xE4 => RETF => control::retf => stack_io(0, 0), terminating; - 0xE5 => JUMPF => control::jumpf => stack_io(0, 0), immediate_size(2), terminating; - 0xE6 => DUPN => stack::dupn => stack_io(0, 1), immediate_size(1); - 0xE7 => SWAPN => stack::swapn => stack_io(0, 0), immediate_size(1); - 0xE8 => EXCHANGE => stack::exchange => stack_io(0, 0), immediate_size(1); + 0xE0 => RJUMP => stack_io(0, 0), immediate_size(2), terminating; + 0xE1 => RJUMPI => stack_io(1, 0), immediate_size(2); + 0xE2 => RJUMPV => stack_io(1, 0), immediate_size(1); + 0xE3 => CALLF => stack_io(0, 0), immediate_size(2); + 0xE4 => RETF => stack_io(0, 0), terminating; + 0xE5 => JUMPF => stack_io(0, 0), immediate_size(2), terminating; + 0xE6 => DUPN => stack_io(0, 1), immediate_size(1); + 0xE7 => SWAPN => stack_io(0, 0), immediate_size(1); + 0xE8 => EXCHANGE => stack_io(0, 0), immediate_size(1); // 0xE9 // 0xEA // 0xEB - 0xEC => EOFCREATE => contract::eofcreate => stack_io(4, 1), immediate_size(1); + 0xEC => EOFCREATE => stack_io(4, 1), immediate_size(1); // 0xED - 0xEE => RETURNCONTRACT => contract::return_contract => stack_io(2, 0), immediate_size(1), terminating; + 0xEE => RETURNCONTRACT => stack_io(2, 0), immediate_size(1), terminating; // 0xEF - 0xF0 => CREATE => contract::create:: => stack_io(3, 1), not_eof; - 0xF1 => CALL => contract::call:: => stack_io(7, 1), not_eof; - 0xF2 => CALLCODE => contract::call_code:: => stack_io(7, 1), not_eof; - 0xF3 => RETURN => control::ret => stack_io(2, 0), terminating; - 0xF4 => DELEGATECALL => contract::delegate_call:: => stack_io(6, 1), not_eof; - 0xF5 => CREATE2 => contract::create:: => stack_io(4, 1), not_eof; + 0xF0 => CREATE => stack_io(3, 1), not_eof; + 0xF1 => CALL => stack_io(7, 1), not_eof; + 0xF2 => CALLCODE => stack_io(7, 1), not_eof; + 0xF3 => RETURN => stack_io(2, 0), terminating; + 0xF4 => DELEGATECALL => stack_io(6, 1), not_eof; + 0xF5 => CREATE2 => stack_io(4, 1), not_eof; // 0xF6 - 0xF7 => RETURNDATALOAD => system::returndataload => stack_io(1, 1); - 0xF8 => EXTCALL => contract::extcall:: => stack_io(4, 1); - 0xF9 => EXTDELEGATECALL => contract::extdelegatecall:: => stack_io(3, 1); - 0xFA => STATICCALL => contract::static_call:: => stack_io(6, 1), not_eof; - 0xFB => EXTSTATICCALL => contract::extstaticcall => stack_io(3, 1); + 0xF7 => RETURNDATALOAD => stack_io(1, 1); + 0xF8 => EXTCALL => stack_io(4, 1); + 0xF9 => EXTDELEGATECALL => stack_io(3, 1); + 0xFA => STATICCALL => stack_io(6, 1), not_eof; + 0xFB => EXTSTATICCALL => stack_io(3, 1); // 0xFC - 0xFD => REVERT => control::revert:: => stack_io(2, 0), terminating; - 0xFE => INVALID => control::invalid => stack_io(0, 0), terminating; - 0xFF => SELFDESTRUCT => host::selfdestruct:: => stack_io(1, 0), not_eof, terminating; + 0xFD => REVERT => stack_io(2, 0), terminating; + 0xFE => INVALID => stack_io(0, 0), terminating; + 0xFF => SELFDESTRUCT => stack_io(1, 0), not_eof, terminating; } #[cfg(test)] @@ -744,7 +696,7 @@ mod tests { expected[EOFCREATE as usize] = 1; expected[RETURNCONTRACT as usize] = 1; - for (i, opcode) in OPCODE_INFO_JUMPTABLE.iter().enumerate() { + for (i, opcode) in OPCODE_INFO.iter().enumerate() { if let Some(opcode) = opcode { assert_eq!( opcode.immediate_size(), @@ -787,7 +739,7 @@ mod tests { fn count_opcodes() { let mut opcode_num = 0; let mut eof_opcode_num = 0; - for opcode in OPCODE_INFO_JUMPTABLE.into_iter().flatten() { + for opcode in OPCODE_INFO.into_iter().flatten() { opcode_num += 1; if !opcode.is_disabled_in_eof() { eof_opcode_num += 1; @@ -815,7 +767,7 @@ mod tests { opcodes[*terminating as usize] = true; } - for (i, opcode) in OPCODE_INFO_JUMPTABLE.into_iter().enumerate() { + for (i, opcode) in OPCODE_INFO.into_iter().enumerate() { assert_eq!( opcode.map(|opcode| opcode.terminating).unwrap_or_default(), opcodes[i], 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/bytecode/src/utils.rs b/crates/bytecode/src/utils.rs new file mode 100644 index 0000000000..6b90c5e394 --- /dev/null +++ b/crates/bytecode/src/utils.rs @@ -0,0 +1,17 @@ +/// Read big-endian i16 from u8 pointer +/// +/// # Safety +/// +/// Pointer needs to point to at least 2 byte. +pub unsafe fn read_i16(ptr: *const u8) -> i16 { + i16::from_be_bytes(core::slice::from_raw_parts(ptr, 2).try_into().unwrap()) +} + +/// Read big-endian u16 from u8 pointer +/// +/// # Safety +/// +/// Pointer needs to point to at least 2 byte. +pub unsafe fn read_u16(ptr: *const u8) -> u16 { + u16::from_be_bytes(core::slice::from_raw_parts(ptr, 2).try_into().unwrap()) +} diff --git a/crates/inspector/src/customprinter.rs b/crates/inspector/src/customprinter.rs index 2a1abb8250..b4b0700dc8 100644 --- a/crates/inspector/src/customprinter.rs +++ b/crates/inspector/src/customprinter.rs @@ -3,7 +3,8 @@ use crate::{inspectors::GasInspector, Inspector}; use revm::{ - interpreter::{CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter, OpCode}, + bytecode::opcode::OpCode, + interpreter::{CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter}, primitives::{Address, U256}, EvmContext, EvmWiring, }; diff --git a/crates/inspector/src/eip3155.rs b/crates/inspector/src/eip3155.rs index 06be7a5261..5197e1ff07 100644 --- a/crates/inspector/src/eip3155.rs +++ b/crates/inspector/src/eip3155.rs @@ -1,9 +1,9 @@ use crate::{inspectors::GasInspector, Inspector}; use derive_where::derive_where; use revm::{ + bytecode::opcode::OpCode, interpreter::{ CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter, InterpreterResult, - OpCode, }, primitives::{hex, HashMap, B256, U256}, wiring::Transaction, diff --git a/crates/inspector/src/gas.rs b/crates/inspector/src/gas.rs index f9e505993d..31d887e31d 100644 --- a/crates/inspector/src/gas.rs +++ b/crates/inspector/src/gas.rs @@ -76,8 +76,8 @@ mod tests { use crate::inspector_handle_register; use database::BenchmarkDB; use revm::{ - bytecode::Bytecode, - interpreter::{opcode, Interpreter}, + bytecode::{opcode, Bytecode}, + interpreter::Interpreter, primitives::{address, Bytes, Log, TxKind}, wiring::EvmWiring as PrimitiveEvmWiring, wiring::{DefaultEthereumWiring, EthereumWiring}, diff --git a/crates/inspector/src/handler_register.rs b/crates/inspector/src/handler_register.rs index 5bfbe510dc..88e13f0e79 100644 --- a/crates/inspector/src/handler_register.rs +++ b/crates/inspector/src/handler_register.rs @@ -1,8 +1,9 @@ use crate::Inspector; use core::cell::RefCell; use revm::{ + bytecode::opcode, handler::register::EvmHandler, - interpreter::{opcode, opcode::DynInstruction, InstructionResult, Interpreter}, + interpreter::{table::DynInstruction, InstructionResult, Interpreter}, wiring::result::EVMResultGeneric, Context, EvmWiring, FrameOrResult, FrameResult, JournalEntry, }; @@ -265,9 +266,9 @@ mod tests { use crate::{inspector_handle_register, inspectors::NoOpInspector}; use database::BenchmarkDB; use revm::{ - bytecode::Bytecode, + bytecode::{opcode, Bytecode}, database_interface::EmptyDB, - interpreter::{opcode, CallInputs, CallOutcome, CreateInputs, CreateOutcome}, + interpreter::{CallInputs, CallOutcome, CreateInputs, CreateOutcome}, primitives::{address, Bytes, TxKind}, wiring::{DefaultEthereumWiring, EthereumWiring, EvmWiring as PrimitiveEvmWiring}, Evm, EvmContext, EvmWiring, 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 a9faa5c02a..cf4582912c 100644 --- a/crates/interpreter/src/instructions/control.rs +++ b/crates/interpreter/src/instructions/control.rs @@ -203,10 +203,8 @@ pub fn unknown(interpreter: &mut Interpreter, _host: &mut H) { #[cfg(test)] mod test { use super::*; - use crate::{ - opcode::{make_instruction_table, CALLF, JUMPF, NOP, RETF, RJUMP, RJUMPI, RJUMPV, STOP}, - 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}, Bytecode, @@ -220,9 +218,8 @@ mod test { fn rjump() { let table = make_instruction_table::, PragueSpec>(); let mut host = DummyHost::default(); - let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw(Bytes::from([ - RJUMP, 0x00, 0x02, STOP, STOP, - ]))); + let mut interp = + Interpreter::new_bytecode(Bytecode::LegacyRaw([RJUMP, 0x00, 0x02, STOP, STOP].into())); interp.is_eof = true; interp.gas = Gas::new(10000); @@ -234,9 +231,9 @@ mod test { fn rjumpi() { let table = make_instruction_table::, PragueSpec>(); let mut host = DummyHost::default(); - let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw(Bytes::from([ - RJUMPI, 0x00, 0x03, RJUMPI, 0x00, 0x01, STOP, STOP, - ]))); + let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw( + [RJUMPI, 0x00, 0x03, RJUMPI, 0x00, 0x01, STOP, STOP].into(), + )); interp.is_eof = true; interp.stack.push(U256::from(1)).unwrap(); interp.stack.push(U256::from(0)).unwrap(); @@ -254,21 +251,24 @@ mod test { fn rjumpv() { let table = make_instruction_table::, PragueSpec>(); let mut host = DummyHost::default(); - let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw(Bytes::from([ - RJUMPV, - 0x01, // max index, 0 and 1 - 0x00, // first x0001 - 0x01, - 0x00, // second 0x002 - 0x02, - NOP, - NOP, - NOP, - RJUMP, - 0xFF, - (-12i8) as u8, - STOP, - ]))); + let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw( + [ + RJUMPV, + 0x01, // max index, 0 and 1 + 0x00, // first x0001 + 0x01, + 0x00, // second 0x002 + 0x02, + NOP, + NOP, + NOP, + RJUMP, + 0xFF, + (-12i8) as u8, + STOP, + ] + .into(), + )); interp.is_eof = true; interp.gas = Gas::new(1000); diff --git a/crates/interpreter/src/instructions/data.rs b/crates/interpreter/src/instructions/data.rs index dcd332ac4a..a9257205b5 100644 --- a/crates/interpreter/src/instructions/data.rs +++ b/crates/interpreter/src/instructions/data.rs @@ -90,10 +90,8 @@ mod test { use wiring::DefaultEthereumWiring; use super::*; - use crate::{ - opcode::{make_instruction_table, DATACOPY, DATALOAD, DATALOADN, DATASIZE}, - 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 { let bytes = bytes!("ef000101000402000100010400000000800000fe"); diff --git a/crates/interpreter/src/instructions/stack.rs b/crates/interpreter/src/instructions/stack.rs index c7893d4310..4d2bf5881a 100644 --- a/crates/interpreter/src/instructions/stack.rs +++ b/crates/interpreter/src/instructions/stack.rs @@ -86,12 +86,9 @@ pub fn exchange(interpreter: &mut Interpreter, _host: &mut H) mod test { use super::*; - use crate::{ - opcode::{make_instruction_table, DUPN, EXCHANGE, SWAPN}, - DummyHost, Gas, InstructionResult, - }; + use crate::{table::make_instruction_table, DummyHost, Gas, InstructionResult}; + use bytecode::opcode::{DUPN, EXCHANGE, SWAPN}; use bytecode::Bytecode; - use primitives::Bytes; use specification::hardfork::PragueSpec; use wiring::DefaultEthereumWiring; @@ -99,9 +96,9 @@ mod test { fn dupn() { let table = make_instruction_table::, PragueSpec>(); let mut host = DummyHost::default(); - let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw(Bytes::from([ - DUPN, 0x00, DUPN, 0x01, DUPN, 0x02, - ]))); + let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw( + [DUPN, 0x00, DUPN, 0x01, DUPN, 0x02].into(), + )); interp.is_eof = true; interp.gas = Gas::new(10000); @@ -120,7 +117,7 @@ mod test { let table = make_instruction_table::, PragueSpec>(); let mut host = DummyHost::default(); let mut interp = - Interpreter::new_bytecode(Bytecode::LegacyRaw(Bytes::from([SWAPN, 0x00, SWAPN, 0x01]))); + Interpreter::new_bytecode(Bytecode::LegacyRaw([SWAPN, 0x00, SWAPN, 0x01].into())); interp.is_eof = true; interp.gas = Gas::new(10000); @@ -139,9 +136,8 @@ mod test { fn exchange() { let table = make_instruction_table::, PragueSpec>(); let mut host = DummyHost::default(); - let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw(Bytes::from([ - EXCHANGE, 0x00, EXCHANGE, 0x11, - ]))); + let mut interp = + Interpreter::new_bytecode(Bytecode::LegacyRaw([EXCHANGE, 0x00, EXCHANGE, 0x11].into())); interp.is_eof = true; interp.gas = Gas::new(10000); diff --git a/crates/interpreter/src/instructions/system.rs b/crates/interpreter/src/instructions/system.rs index 7daea5b16c..a4729af04c 100644 --- a/crates/interpreter/src/instructions/system.rs +++ b/crates/interpreter/src/instructions/system.rs @@ -185,10 +185,8 @@ pub fn gas(interpreter: &mut Interpreter, _host: &mut H) { #[cfg(test)] mod test { use super::*; - use crate::{ - opcode::{make_instruction_table, RETURNDATACOPY, RETURNDATALOAD}, - DummyHost, Gas, InstructionResult, - }; + use crate::{table::make_instruction_table, DummyHost, Gas, InstructionResult}; + use bytecode::opcode::{RETURNDATACOPY, RETURNDATALOAD}; use bytecode::Bytecode; use primitives::bytes; use specification::hardfork::PragueSpec; diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index d8e5e236a3..8198eec0f2 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -1,4 +1,3 @@ -pub mod analysis; mod contract; #[cfg(feature = "serde")] pub mod serde; @@ -469,7 +468,7 @@ pub fn resize_memory(memory: &mut SharedMemory, gas: &mut Gas, new_size: usize) #[cfg(test)] mod tests { use super::*; - use crate::{opcode::InstructionTable, DummyHost}; + use crate::{table::InstructionTable, DummyHost}; use specification::hardfork::CancunSpec; use wiring::DefaultEthereumWiring; @@ -479,14 +478,13 @@ mod tests { let mut host = crate::DummyHost::::default(); let table: &InstructionTable> = - &crate::opcode::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::opcode::make_instruction_table::< + &crate::table::make_instruction_table::< dyn Host, CancunSpec, >(); diff --git a/crates/interpreter/src/interpreter/contract.rs b/crates/interpreter/src/interpreter/contract.rs index 683be21cae..04bf4576e8 100644 --- a/crates/interpreter/src/interpreter/contract.rs +++ b/crates/interpreter/src/interpreter/contract.rs @@ -1,4 +1,3 @@ -use super::analysis::to_analysed; use crate::CallInputs; use bytecode::Bytecode; use primitives::{Address, Bytes, TxKind, B256, U256}; @@ -38,7 +37,7 @@ impl Contract { caller: Address, call_value: U256, ) -> Self { - let bytecode = to_analysed(bytecode); + let bytecode = bytecode.into_analyzed(); Self { input, diff --git a/crates/interpreter/src/lib.rs b/crates/interpreter/src/lib.rs index fe9ff6c8e3..e2821e06f5 100644 --- a/crates/interpreter/src/lib.rs +++ b/crates/interpreter/src/lib.rs @@ -24,7 +24,7 @@ mod instruction_result; pub mod instructions; pub mod interpreter; pub mod interpreter_action; -pub mod opcode; +pub mod table; // Reexport primary types. pub use function_stack::{FunctionReturnFrame, FunctionStack}; @@ -34,13 +34,13 @@ pub use host::{ }; pub use instruction_result::*; pub use interpreter::{ - analysis, num_words, Contract, Interpreter, InterpreterResult, SharedMemory, Stack, - EMPTY_SHARED_MEMORY, STACK_LIMIT, + num_words, Contract, Interpreter, InterpreterResult, SharedMemory, Stack, EMPTY_SHARED_MEMORY, + STACK_LIMIT, }; pub use interpreter_action::{ CallInputs, CallOutcome, CallScheme, CallValue, CreateInputs, CreateOutcome, EOFCreateInputs, EOFCreateKind, InterpreterAction, }; -pub use opcode::{Instruction, OpCode, OPCODE_INFO_JUMPTABLE}; pub use primitives::{MAX_CODE_SIZE, MAX_INITCODE_SIZE}; +pub use table::Instruction; pub use wiring::default::CreateScheme; diff --git a/crates/interpreter/src/opcode/tables.rs b/crates/interpreter/src/table.rs similarity index 98% rename from crates/interpreter/src/opcode/tables.rs rename to crates/interpreter/src/table.rs index d53a345d42..1dc4afaac7 100644 --- a/crates/interpreter/src/opcode/tables.rs +++ b/crates/interpreter/src/table.rs @@ -1,7 +1,6 @@ #![allow(clippy::wrong_self_convention)] -use super::instruction; -use crate::{instructions::control, 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 89f98c05b7..411e7df610 100644 --- a/crates/revm/benches/bench.rs +++ b/crates/revm/benches/bench.rs @@ -2,10 +2,10 @@ use criterion::{ criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, }; use database::BenchmarkDB; -use interpreter::{opcode::make_instruction_table, SharedMemory, EMPTY_SHARED_MEMORY}; +use interpreter::{table::make_instruction_table, SharedMemory, EMPTY_SHARED_MEMORY}; use revm::{ bytecode::Bytecode, - interpreter::{analysis::to_analysed, Contract, DummyHost, Interpreter}, + interpreter::{Contract, DummyHost, Interpreter}, primitives::{address, bytes, hex, Bytes, TxKind, U256}, specification::hardfork::BerlinSpec, wiring::EthereumWiring, @@ -35,7 +35,7 @@ fn analysis(c: &mut Criterion) { let mut evm = evm.modify().with_db(BenchmarkDB::new_bytecode(raw)).build(); bench_transact(&mut g, &mut evm); - let analysed = to_analysed(Bytecode::new_raw(contract_data)); + let analysed = Bytecode::new_raw(contract_data).into_analyzed(); let mut evm = evm .modify() .with_db(BenchmarkDB::new_bytecode(analysed)) @@ -102,7 +102,7 @@ fn bench_eval( g.bench_function("eval", |b| { let contract = Contract { input: evm.context.evm.env.tx.data.clone(), - bytecode: to_analysed(evm.context.evm.db.0.clone()), + bytecode: evm.context.evm.db.0.clone().into_analyzed(), ..Default::default() }; let mut shared_memory = SharedMemory::new(); @@ -123,7 +123,7 @@ fn bench_eval( } fn bytecode(s: &str) -> Bytecode { - to_analysed(Bytecode::new_raw(hex::decode(s).unwrap().into())) + Bytecode::new_raw(hex::decode(s).unwrap().into()).into_analyzed() } #[rustfmt::skip] diff --git a/crates/revm/src/context/evm_context.rs b/crates/revm/src/context/evm_context.rs index 22279a7dd8..58e154aedb 100644 --- a/crates/revm/src/context/evm_context.rs +++ b/crates/revm/src/context/evm_context.rs @@ -6,8 +6,8 @@ use database_interface::Database; use derive_where::derive_where; use interpreter::CallValue; use interpreter::{ - analysis::validate_eof, return_ok, CallInputs, Contract, CreateInputs, EOFCreateInputs, - EOFCreateKind, Gas, InstructionResult, Interpreter, InterpreterResult, + return_ok, CallInputs, Contract, CreateInputs, EOFCreateInputs, EOFCreateKind, Gas, + InstructionResult, Interpreter, InterpreterResult, }; use precompile::PrecompileErrors; use primitives::{keccak256, Address, Bytes, B256}; @@ -380,7 +380,7 @@ where return return_error(InstructionResult::InvalidEOFInitCode); }; - if validate_eof(&eof).is_err() { + if eof.validate().is_err() { // TODO (EOF) new error type. self.journaled_state.inc_nonce(inputs.caller); return return_error(InstructionResult::InvalidEOFInitCode); diff --git a/crates/revm/src/context/inner_evm_context.rs b/crates/revm/src/context/inner_evm_context.rs index 85770a110b..f276223f09 100644 --- a/crates/revm/src/context/inner_evm_context.rs +++ b/crates/revm/src/context/inner_evm_context.rs @@ -4,8 +4,8 @@ use crate::{journaled_state::JournaledState, JournalCheckpoint}; use bytecode::{Bytecode, Eof, EOF_MAGIC_BYTES, EOF_MAGIC_HASH}; use database_interface::Database; use interpreter::{ - analysis::to_analysed, gas, return_ok, AccountLoad, Eip7702CodeLoad, InstructionResult, - InterpreterResult, SStoreResult, SelfDestructResult, StateLoad, + gas, return_ok, AccountLoad, Eip7702CodeLoad, InstructionResult, InterpreterResult, + SStoreResult, SelfDestructResult, StateLoad, }; use primitives::{Address, Bytes, HashSet, B256, U256}; use specification::{ @@ -424,7 +424,7 @@ impl InnerEvmContext { let bytecode = match self.env.cfg.perf_analyse_created_bytecodes { AnalysisKind::Raw => Bytecode::new_legacy(interpreter_result.output.clone()), AnalysisKind::Analyse => { - to_analysed(Bytecode::new_legacy(interpreter_result.output.clone())) + Bytecode::new_legacy(interpreter_result.output.clone()).into_analyzed() } }; diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 6922d08cf8..1401134065 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -427,9 +427,11 @@ impl Evm<'_, EvmWiringT> { mod tests { use super::*; - use bytecode::Bytecode; + use bytecode::{ + opcode::{PUSH1, SSTORE}, + Bytecode, + }; use database::BenchmarkDB; - use interpreter::opcode::{PUSH1, SSTORE}; use primitives::{address, U256}; use specification::eip7702::{Authorization, RecoveredAuthorization, Signature}; use wiring::EthereumWiring; diff --git a/crates/revm/src/evm_wiring.rs b/crates/revm/src/evm_wiring.rs index 72f6a9ce23..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::opcode::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 60f5009184..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::{opcode::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 7907922afb..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::{ - opcode::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 1d48c2d736..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::{ - opcode::InstructionTables, return_ok, return_revert, 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/crates/specification/src/constantans.rs b/crates/specification/src/constantans.rs new file mode 100644 index 0000000000..7f545727c3 --- /dev/null +++ b/crates/specification/src/constantans.rs @@ -0,0 +1,2 @@ +/// EVM interpreter stack limit. +pub const STACK_LIMIT: usize = 1024; diff --git a/crates/specification/src/lib.rs b/crates/specification/src/lib.rs index 45fe59d601..bf327e7e27 100644 --- a/crates/specification/src/lib.rs +++ b/crates/specification/src/lib.rs @@ -5,6 +5,7 @@ #[cfg(not(feature = "std"))] extern crate alloc as std; +pub mod constantans; pub mod eip170; pub mod eip2; pub mod eip2930; 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}, From 0820b608949d9a1b5953ab810590f58ae2d0e177 Mon Sep 17 00:00:00 2001 From: rakita Date: Mon, 23 Sep 2024 17:25:56 +0200 Subject: [PATCH 2/3] cleanup --- crates/interpreter/src/host/results.rs | 0 crates/interpreter/src/host/trait.rs | 0 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 crates/interpreter/src/host/results.rs delete mode 100644 crates/interpreter/src/host/trait.rs diff --git a/crates/interpreter/src/host/results.rs b/crates/interpreter/src/host/results.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/crates/interpreter/src/host/trait.rs b/crates/interpreter/src/host/trait.rs deleted file mode 100644 index e69de29bb2..0000000000 From 2919191dc584ee6cde8a42bb3cfc2ca73f604b08 Mon Sep 17 00:00:00 2001 From: rakita Date: Mon, 23 Sep 2024 17:35:51 +0200 Subject: [PATCH 3/3] enable parse and std for bytecode --- Cargo.lock | 1 + bins/revme/Cargo.toml | 2 ++ crates/bytecode/src/eof/printer.rs | 4 ++-- crates/bytecode/src/opcode.rs | 2 +- crates/interpreter/src/gas/calc.rs | 10 +++++----- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8338fc0391..94b6e403ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3092,6 +3092,7 @@ dependencies = [ "microbench", "plain_hasher", "revm", + "revm-bytecode", "revm-database", "revm-inspector", "serde", diff --git a/bins/revme/Cargo.toml b/bins/revme/Cargo.toml index 4032a8ae09..072996142f 100644 --- a/bins/revme/Cargo.toml +++ b/bins/revme/Cargo.toml @@ -13,6 +13,8 @@ repository.workspace = true database.workspace = true revm = { workspace = true, features = ["std", "hashbrown", "c-kzg", "blst"] } inspector = { workspace = true, features = ["std", "serde-json"] } +# enable parse std and parse feature. +bytecode = { workspace = true, features = ["std", "parse"] } hash-db = "0.15" hex = "0.4" diff --git a/crates/bytecode/src/eof/printer.rs b/crates/bytecode/src/eof/printer.rs index 36ead724f6..f233084f0f 100644 --- a/crates/bytecode/src/eof/printer.rs +++ b/crates/bytecode/src/eof/printer.rs @@ -58,11 +58,11 @@ pub fn print(code: &[u8]) { #[cfg(test)] mod test { - use super::*; use primitives::hex; + #[cfg(feature = "std")] #[test] fn sanity_test() { - print(&hex!("6001e200ffff00")); + super::print(&hex!("6001e200ffff00")); } } diff --git a/crates/bytecode/src/opcode.rs b/crates/bytecode/src/opcode.rs index 1e42207f43..54c2a3321e 100644 --- a/crates/bytecode/src/opcode.rs +++ b/crates/bytecode/src/opcode.rs @@ -8,7 +8,7 @@ use core::{fmt, ptr::NonNull}; /// An EVM opcode. /// /// This is always a valid opcode, as declared in the [`opcode`][self] module or the -/// [`OPCODE_INFO_JUMPTABLE`] constant. +/// [`OPCODE_INFO`] constant. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct OpCode(u8); diff --git a/crates/interpreter/src/gas/calc.rs b/crates/interpreter/src/gas/calc.rs index 4af87c3d3b..9117df1e8e 100644 --- a/crates/interpreter/src/gas/calc.rs +++ b/crates/interpreter/src/gas/calc.rs @@ -279,14 +279,14 @@ pub const fn selfdestruct_cost(spec_id: SpecId, res: StateLoad u64 {