diff --git a/Cargo.lock b/Cargo.lock index c2cbc6b96e..ed70fd6df6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -327,6 +327,15 @@ dependencies = [ "serde", ] +[[package]] +name = "alloy-type-conversions" +version = "0.1.0" +dependencies = [ + "cfx-types", + "rand 0.8.5", + "revm-primitives 5.0.0", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -1166,6 +1175,7 @@ dependencies = [ name = "cfx-executor" version = "2.0.2" dependencies = [ + "alloy-type-conversions", "bls-signatures", "byteorder", "c-kzg", @@ -1200,6 +1210,7 @@ dependencies = [ "pow-types", "primitives", "rayon", + "revm-interpreter 6.0.0", "rlp 0.4.6", "rlp_derive", "rustc-hex", @@ -2924,6 +2935,16 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +[[package]] +name = "dynamic_host_macro" +version = "0.1.0" +source = "git+https://github.com/Conflux-Chain/revm.git?branch=dev#25e76c3cdb66ba39b15af0dfaaa50eb73bfd9174" +dependencies = [ + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 1.0.109", +] + [[package]] name = "ecdsa" version = "0.16.9" @@ -5935,6 +5956,48 @@ dependencies = [ "indexmap 1.9.2", ] +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2 1.0.85", + "quote 1.0.36", + "syn 2.0.66", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "1.0.12" @@ -6811,7 +6874,7 @@ dependencies = [ "auto_impl", "cfg-if 1.0.0", "dyn-clone", - "revm-interpreter", + "revm-interpreter 4.0.0", "revm-precompile", "serde", "serde_json", @@ -6823,7 +6886,19 @@ version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d322f2730cd300e99d271a1704a2dfb8973d832428f5aa282aaa40e2473b5eec" dependencies = [ - "revm-primitives", + "revm-primitives 3.1.1", + "serde", +] + +[[package]] +name = "revm-interpreter" +version = "6.0.0" +source = "git+https://github.com/Conflux-Chain/revm.git?branch=dev#25e76c3cdb66ba39b15af0dfaaa50eb73bfd9174" +dependencies = [ + "dynamic_host_macro", + "paste", + "phf", + "revm-primitives 5.0.0", "serde", ] @@ -6837,7 +6912,7 @@ dependencies = [ "c-kzg", "k256", "once_cell", - "revm-primitives", + "revm-primitives 3.1.1", "ripemd", "secp256k1 0.28.2", "sha2 0.10.6", @@ -6865,6 +6940,26 @@ dependencies = [ "serde", ] +[[package]] +name = "revm-primitives" +version = "5.0.0" +source = "git+https://github.com/Conflux-Chain/revm.git?branch=dev#25e76c3cdb66ba39b15af0dfaaa50eb73bfd9174" +dependencies = [ + "alloy-primitives", + "auto_impl", + "bitflags 2.5.0", + "bitvec 1.0.1", + "c-kzg", + "cfg-if 1.0.0", + "derive_more", + "dyn-clone", + "enumn", + "hashbrown 0.14.3", + "hex 0.4.3", + "once_cell", + "serde", +] + [[package]] name = "rfc6979" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 12ed2dd34c..28aa818008 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ members = [ "crates/dbs/storage", "crates/cfxcore/vm-interpreter", "crates/cfxcore/vm-types", + "crates/util/alloy-type-conversions", "crates/util/cfx-vm-tracer-derive", "crates/util/dag", "crates/util/delegate", @@ -147,6 +148,7 @@ cfx-rpc-eth-types = { path = "./crates/rpc/rpc-eth-types" } cfx-rpc-primitives = { path = "./crates/rpc/rpc-primitives" } cfx-rpc-cfx-types = { path = "./crates/rpc/rpc-cfx-types" } cfx-parity-trace-types = { path = "./crates/cfxcore/parity-trace-types" } +alloy-type-conversions = { path = "./crates/util/alloy-type-conversions" } serde = { version = "1.0", features = ["derive", "alloc"] } serde_json = "1.0" @@ -174,5 +176,10 @@ alloy-primitives = "0.7.2" alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "4e22b9e" } alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "4e22b9e" } revm = "8.0" +# revm-interpreter = "6.0" +# revm-primitives = "5.0" +revm-interpreter = { git = "https://github.com/Conflux-Chain/revm.git", branch = "dev"} +revm-primitives = { git = "https://github.com/Conflux-Chain/revm.git", branch = "dev"} + bls-signatures = { git = "https://github.com/Conflux-Chain/bls-signatures.git", rev = "fb52187df92d27c365642cb7e7b2aaf60437cf9c", default-features = false, features = ["multicore", "blst"] } diff --git a/crates/cfxcore/executor/Cargo.toml b/crates/cfxcore/executor/Cargo.toml index f15dfc8303..b5042854b3 100644 --- a/crates/cfxcore/executor/Cargo.toml +++ b/crates/cfxcore/executor/Cargo.toml @@ -54,6 +54,8 @@ c-kzg = { version = "1.0.2", default-features = false} once_cell = "1.19" rayon = { workspace = true } cfx-parity-trace-types = { workspace = true } +revm-interpreter = { workspace = true } +alloy-type-conversions = { workspace = true } [dev-dependencies] cfx-statedb = { workspace = true, features = ["testonly_code"]} diff --git a/crates/cfxcore/executor/src/context.rs b/crates/cfxcore/executor/src/context/mod.rs similarity index 99% rename from crates/cfxcore/executor/src/context.rs rename to crates/cfxcore/executor/src/context/mod.rs index a8f683cc74..33ff6ce58e 100644 --- a/crates/cfxcore/executor/src/context.rs +++ b/crates/cfxcore/executor/src/context/mod.rs @@ -2,6 +2,10 @@ // Conflux is free software and distributed under GNU General Public License. // See http://www.gnu.org/licenses/ +mod revm; + +pub(crate) use revm::EvmHost; + // Transaction execution environment. use crate::{ executive::contract_address, diff --git a/crates/cfxcore/executor/src/context/revm.rs b/crates/cfxcore/executor/src/context/revm.rs new file mode 100644 index 0000000000..2307c8cf17 --- /dev/null +++ b/crates/cfxcore/executor/src/context/revm.rs @@ -0,0 +1,285 @@ +#![allow(unused, dead_code)] + +use super::Context; +use alloy_type_conversions::*; +use cfx_statedb::Result as DbResult; +use cfx_types::AddressSpaceUtil; +use cfx_vm_types::Context as ContextTrait; +use revm_interpreter::primitives::{Address, Bytes, Env, Log, B256, U256}; + +pub(crate) struct EvmHost<'a> { + context: Context<'a>, + error: DbResult<()>, +} + +impl<'a> EvmHost<'a> { + pub fn new(context: Context<'a>) -> Self { + Self { + context, + error: Ok(()), + } + } + + pub fn take_db_error(&mut self) -> DbResult<()> { + std::mem::replace(&mut self.error, Ok(())) + } +} + +fn unwrap_db_error(e: cfx_vm_types::Error) -> cfx_statedb::Error { + match e { + cfx_vm_types::Error::StateDbError(e) => e.0, + _ => unreachable!(), + } +} + +const COLD: bool = true; + +// pub trait HostProvider {} + +// impl<'a> HostProvider for EvmHost<'a> {} + +// impl revm_interpreter::Host for dyn HostProvider { +// fn env(&self) -> &Env { +// todo!() +// } + +// fn env_mut(&mut self) -> &mut Env { +// todo!() +// } + +// fn load_account(&mut self, address: Address) -> +// Option { todo!() +// } + +// fn block_hash(&mut self, number: U256) -> Option { +// todo!() +// } + +// fn balance(&mut self, address: Address) -> Option<(U256, bool)> { +// todo!() +// } + +// fn code(&mut self, address: Address) -> Option<(Bytes, bool)> { +// todo!() +// } + +// fn code_hash(&mut self, address: Address) -> Option<(B256, bool)> { +// todo!() +// } + +// fn sload(&mut self, address: Address, index: U256) -> Option<(U256, +// bool)> { todo!() +// } + +// fn sstore(&mut self, address: Address, index: U256, value: U256) -> +// Option { todo!() +// } + +// fn tload(&mut self, address: Address, index: U256) -> U256 { +// todo!() +// } + +// fn tstore(&mut self, address: Address, index: U256, value: U256) { +// todo!() +// } + +// fn log(&mut self, log: Log) { +// todo!() +// } + +// fn selfdestruct(&mut self, address: Address, target: Address) -> +// Option { todo!() +// } +// } + +impl<'a> revm_interpreter::Host for EvmHost<'a> { + fn env(&self) -> &Env { todo!() } + + fn env_mut(&mut self) -> &mut Env { todo!() } + + fn load_account( + &mut self, address: Address, + ) -> Option { + match self + .context + .exists_and_not_null(&from_alloy_address(address)) + { + Ok(exists) => Some(revm_interpreter::LoadAccountResult { + is_cold: COLD, + is_empty: !exists, + }), + Err(e) => { + self.error = Err(unwrap_db_error(e)); + None + } + } + } + + fn block_hash(&mut self, number: U256) -> Option { + match self.context.blockhash(&from_alloy_u256(number)) { + Ok(hash) => Some(to_alloy_h256(hash)), + Err(e) => { + self.error = Err(unwrap_db_error(e)); + None + } + } + } + + fn balance(&mut self, address: Address) -> Option<(U256, bool)> { + match self.context.balance(&from_alloy_address(address)) { + Ok(balance) => Some((to_alloy_u256(balance), COLD)), + Err(e) => { + self.error = Err(unwrap_db_error(e)); + None + } + } + } + + fn code(&mut self, address: Address) -> Option<(Bytes, bool)> { + match self.context.extcode(&from_alloy_address(address)) { + Ok(None) => Some((Bytes::new(), COLD)), + Ok(Some(code)) => Some((Bytes::copy_from_slice(&**code), COLD)), + Err(e) => { + self.error = Err(unwrap_db_error(e)); + None + } + } + } + + fn code_hash(&mut self, address: Address) -> Option<(B256, bool)> { + match self.context.extcodehash(&from_alloy_address(address)) { + Ok(hash) => Some((to_alloy_h256(hash), COLD)), + Err(e) => { + self.error = Err(unwrap_db_error(e)); + None + } + } + } + + fn sload(&mut self, address: Address, index: U256) -> Option<(U256, bool)> { + let receiver = + from_alloy_address(address).with_space(self.context.space); + let key = index.to_be_bytes::<32>(); + match self.context.state.storage_at(&receiver, &key) { + Ok(value) => Some((to_alloy_u256(value), COLD)), + Err(e) => { + self.error = Err(e); + None + } + } + } + + fn sstore( + &mut self, address: Address, index: U256, value: U256, + ) -> Option { + // TODO: who checks static flag in revm? + todo!() + } + + fn tload(&mut self, address: Address, index: U256) -> U256 { todo!() } + + fn tstore(&mut self, address: Address, index: U256, value: U256) { todo!() } + + fn log(&mut self, log: Log) { todo!() } + + fn selfdestruct( + &mut self, address: Address, target: Address, + ) -> Option { + todo!() + } +} + +// impl<'a> revm_interpreter::Host for EvmHost<'a> { +// fn env(&self) -> &Env { todo!() } + +// fn env_mut(&mut self) -> &mut Env { todo!() } + +// fn load_account( +// &mut self, address: Address, +// ) -> Option { +// match self.context.exists_and_not_null(&from_alloy_address(address)) +// { Ok(exists) => { +// Some(revm_interpreter::LoadAccountResult { is_cold: COLD, +// is_empty: !exists }) + +// } +// Err(e) => { +// self.error = Err(unwrap_db_error(e)); +// None +// } +// } +// } + +// fn block_hash(&mut self, number: U256) -> Option { +// match self.context.blockhash(&from_alloy_u256(number)) { +// Ok(hash) => { +// Some(to_alloy_h256(hash)) +// } +// Err(e) => { +// self.error = Err(unwrap_db_error(e)); +// None +// } +// } +// } + +// fn balance(&mut self, address: Address) -> Option<(U256, bool)> { +// match self.context.balance(&from_alloy_address(address)) { +// Ok(balance) => Some((to_alloy_u256(balance), COLD)), +// Err(e) => { +// self.error = Err(unwrap_db_error(e)); +// None +// } +// } +// } + +// fn code(&mut self, address: Address) -> Option<(Bytes, bool)> { match +// self.context.extcode(&from_alloy_address(address)) { Ok(None) => +// Some((Bytes::new(), COLD)), Ok(Some(code)) => +// Some((Bytes::copy_from_slice(&**code), COLD)), Err(e) => { +// self.error = Err(unwrap_db_error(e)); +// None +// } +// }} + +// fn code_hash(&mut self, address: Address) -> Option<(B256, bool)> { +// match self.context.extcodehash(&from_alloy_address(address)) { +// Ok(hash) => Some((to_alloy_h256(hash), COLD)), +// Err(e) => { +// self.error = Err(unwrap_db_error(e)); +// None +// } +// } +// } + +// fn sload(&mut self, address: Address, index: U256) -> Option<(U256, +// bool)> { let receiver = +// from_alloy_address(address).with_space(self.context.space); let key = +// index.to_be_bytes::<32>(); match +// self.context.state.storage_at(&receiver, &key) { Ok(value) => +// Some((to_alloy_u256(value), COLD)), Err(e) => { +// self.error = Err(e); +// None +// } +// } +// } + +// fn sstore( +// &mut self, address: Address, index: U256, value: U256, +// ) -> Option { +// // TODO: who checks static flag in revm? +// todo!() +// } + +// fn tload(&mut self, address: Address, index: U256) -> U256 { todo!() } + +// fn tstore(&mut self, address: Address, index: U256, value: U256) { +// todo!() } + +// fn log(&mut self, log: Log) { todo!() } + +// fn selfdestruct( +// &mut self, address: Address, target: Address, +// ) -> Option { +// todo!() +// } +// } diff --git a/crates/cfxcore/executor/src/lib.rs b/crates/cfxcore/executor/src/lib.rs index b898c52409..771da11046 100644 --- a/crates/cfxcore/executor/src/lib.rs +++ b/crates/cfxcore/executor/src/lib.rs @@ -60,5 +60,7 @@ pub mod spec; /// meaningful database interfaces for the execution. pub mod state; +pub mod revm_wrapper; + pub use internal_contract::{InternalContractMap, InternalContractTrait}; pub use observer as executive_observer; diff --git a/crates/cfxcore/executor/src/revm_wrapper/mod.rs b/crates/cfxcore/executor/src/revm_wrapper/mod.rs new file mode 100644 index 0000000000..3175caa242 --- /dev/null +++ b/crates/cfxcore/executor/src/revm_wrapper/mod.rs @@ -0,0 +1,86 @@ +#![allow(unused)] + +use crate::{ + context::{Context, EvmHost}, + stack::{Executable, ExecutableOutcome}, +}; + +use cfx_statedb::Result as DbResult; +use cfx_types::U256; +use cfx_vm_interpreter::FinalizationResult; +use cfx_vm_types::{self as vm, ActionParams}; +use revm_interpreter::{ + opcode::{make_instruction_table, InstructionTable}, + primitives::{specification::LatestSpec, HaltReason, SuccessReason}, + CallInputs, Host, Interpreter, InterpreterAction, InterpreterResult, + SharedMemory, SuccessOrHalt, +}; +use vm::ReturnData; + +const INSTUCTION_TABLE: InstructionTable = + make_instruction_table::(); + +impl Executable for Interpreter { + fn execute<'c>( + mut self: Box, context: Context<'c>, + ) -> DbResult { + let mut revm_context = EvmHost::new(context); + let shared_memory = SharedMemory::new(); + + let context = &mut revm_context as &mut dyn Host; + + // TODO: inspect shared_memory + let action = self.run(shared_memory, &INSTUCTION_TABLE, context); + + revm_context.take_db_error()?; + + todo!() + } +} + +fn adapt_action(action: InterpreterAction) -> ExecutableOutcome { + match action { + InterpreterAction::Call { inputs } => todo!(), + InterpreterAction::Create { inputs } => todo!(), + InterpreterAction::EOFCreate { inputs } => todo!(), + InterpreterAction::Return { result } => todo!(), + InterpreterAction::None => todo!("What should I do here"), + } +} + +fn adapt_frame_call(inputs: Box) -> ActionParams { todo!() } + +fn adapt_frame_create(inputs: Box) -> ActionParams { todo!() } + +fn adapt_frame_return( + inputs: InterpreterResult, +) -> vm::Result { + let gas_left: U256 = inputs.gas.remaining().into(); + let return_data = + >>::from(inputs.output.0.into()); + + match SuccessOrHalt::from(inputs.result) { + SuccessOrHalt::Success(SuccessReason::Stop | SuccessReason::Return) => { + Ok(FinalizationResult { + gas_left, + apply_state: true, + return_data, + }) + } + SuccessOrHalt::Success(SuccessReason::SelfDestruct) => todo!(), + SuccessOrHalt::Success(SuccessReason::EofReturnContract) => todo!(), + SuccessOrHalt::Revert => Ok(FinalizationResult { + gas_left, + apply_state: false, + return_data, + }), + SuccessOrHalt::Halt(halt_reason) => Err(adapt_vm_error(halt_reason)), + // If FatalExternalError happen, db error should have halted the + // execution + SuccessOrHalt::FatalExternalError => unreachable!(), + // Interpreter internal result should not throw out + SuccessOrHalt::Internal(_) => unreachable!(), + } +} + +fn adapt_vm_error(halt_reason: HaltReason) -> vm::Error { todo!() } diff --git a/crates/cfxcore/executor/src/stack/executable.rs b/crates/cfxcore/executor/src/stack/executable.rs index a1dc3a19d3..e62f569163 100644 --- a/crates/cfxcore/executor/src/stack/executable.rs +++ b/crates/cfxcore/executor/src/stack/executable.rs @@ -22,7 +22,7 @@ use cfx_vm_types::{ /// 2. After the completion of a frame's execution, an `Executable` may /// be created by the its caller frame's `Resumer` (implementing the `Resumable` /// trait) based on the execution results. -pub trait Executable: Send { +pub trait Executable { fn execute( self: Box, context: Context, ) -> DbResult; diff --git a/crates/util/alloy-type-conversions/Cargo.toml b/crates/util/alloy-type-conversions/Cargo.toml new file mode 100644 index 0000000000..1832edffd6 --- /dev/null +++ b/crates/util/alloy-type-conversions/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "alloy-type-conversions" +version = "0.1.0" +edition = "2021" + +[dependencies] +cfx-types = { workspace = true } +revm-primitives = { workspace = true } + +[dev-dependencies] +rand = "0.8" \ No newline at end of file diff --git a/crates/util/alloy-type-conversions/src/lib.rs b/crates/util/alloy-type-conversions/src/lib.rs new file mode 100644 index 0000000000..5fb899a350 --- /dev/null +++ b/crates/util/alloy-type-conversions/src/lib.rs @@ -0,0 +1,106 @@ +extern crate cfx_types; +extern crate revm_primitives as alloy_types; + +pub fn from_alloy_address(address: alloy_types::Address) -> cfx_types::Address { + cfx_types::H160(address.0 .0) +} + +pub fn to_alloy_address(address: cfx_types::Address) -> alloy_types::Address { + alloy_types::Address(alloy_types::FixedBytes(address.0)) +} + +pub fn from_alloy_u256(value: alloy_types::U256) -> cfx_types::U256 { + // SAFETY: `alloy_types::U256` has a single field of type `[u64; 4]` with + // `repr(transparent)`. + let dwords = + unsafe { std::mem::transmute::(value) }; + cfx_types::U256(dwords) +} + +pub fn to_alloy_u256(value: cfx_types::U256) -> alloy_types::U256 { + // SAFETY: `alloy_types::U256` has a single field of type `[u64; 4]` with + // `repr(transparent)`. + unsafe { std::mem::transmute::<[u64; 4], alloy_types::U256>(value.0) } +} + +pub fn from_alloy_h256(value: alloy_types::B256) -> cfx_types::H256 { + cfx_types::H256(value.0) +} + +pub fn to_alloy_h256(value: cfx_types::H256) -> alloy_types::B256 { + alloy_types::FixedBytes(value.0) +} + +#[cfg(test)] +mod tests { + use super::*; + use rand::Rng; + + #[test] + fn test_address_conversions() { + let mut rng = rand::thread_rng(); + for _ in 0..1000 { + let random_bytes: [u8; 20] = rng.gen(); + let alloy_address = + alloy_types::Address(alloy_types::FixedBytes(random_bytes)); + let cfx_address = cfx_types::H160(random_bytes); + + assert_eq!(from_alloy_address(alloy_address), cfx_address); + assert_eq!(to_alloy_address(cfx_address), alloy_address); + } + } + + #[test] + fn test_u256_conversions() { + let mut rng = rand::thread_rng(); + for _ in 0..1000 { + let random_bytes: [u8; 32] = rng.gen(); + let alloy_u256 = alloy_types::U256::from_be_bytes(random_bytes); + let cfx_u256 = cfx_types::U256::from(random_bytes); + + assert_eq!(from_alloy_u256(alloy_u256), cfx_u256); + assert_eq!(to_alloy_u256(cfx_u256), alloy_u256); + } + } + + #[test] + fn test_h256_conversions() { + let mut rng = rand::thread_rng(); + for _ in 0..1000 { + let random_bytes: [u8; 32] = rng.gen(); + let alloy_h256 = alloy_types::B256::from(random_bytes); + let cfx_h256 = cfx_types::H256(random_bytes); + + assert_eq!(from_alloy_h256(alloy_h256), cfx_h256); + assert_eq!(to_alloy_h256(cfx_h256), alloy_h256); + } + } + + #[test] + fn test_roundtrip_conversions() { + let mut rng = rand::thread_rng(); + for _ in 0..1000 { + // Address roundtrip + let random_address_bytes: [u8; 20] = rng.gen(); + let original_address = alloy_types::Address( + alloy_types::FixedBytes(random_address_bytes), + ); + let roundtrip_address = + to_alloy_address(from_alloy_address(original_address)); + assert_eq!(original_address, roundtrip_address); + + // U256 roundtrip + let random_u256_bytes: [u8; 32] = rng.gen(); + let original_u256 = + alloy_types::U256::from_be_bytes(random_u256_bytes); + let roundtrip_u256 = to_alloy_u256(from_alloy_u256(original_u256)); + assert_eq!(original_u256, roundtrip_u256); + + // H256 roundtrip + let random_h256_bytes: [u8; 32] = rng.gen(); + let original_h256 = alloy_types::B256::from(random_h256_bytes); + let roundtrip_h256 = to_alloy_h256(from_alloy_h256(original_h256)); + assert_eq!(original_h256, roundtrip_h256); + } + } +}