diff --git a/Cargo.lock b/Cargo.lock index f8dd20e..e9bfead 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,6 +14,17 @@ dependencies = [ "opaque-debug 0.3.0", ] +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "0.7.18" @@ -29,6 +40,12 @@ version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1485d4d2cc45e7b201ee3767015c96faa5904387c9d87c6efdd0fb511f12d305" +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + [[package]] name = "arrayvec" version = "0.7.2" @@ -885,9 +902,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +checksum = "ab30e97ab6aacfe635fad58f22c2bb06c8b685f7421eb1e064a729e2a5f481fa" dependencies = [ "futures-channel", "futures-core", @@ -900,9 +917,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +checksum = "2bfc52cbddcfd745bf1740338492bb0bd83d76c67b445f91c5fb29fae29ecaa1" dependencies = [ "futures-core", "futures-sink", @@ -910,15 +927,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" +checksum = "d2acedae88d38235936c3922476b10fced7b2b68136f5e3c03c2d5be348a1115" [[package]] name = "futures-executor" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +checksum = "1d11aa21b5b587a64682c0094c2bdd4df0076c5324961a40cc3abd7f37930528" dependencies = [ "futures-core", "futures-task", @@ -944,9 +961,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +checksum = "0db9cce532b0eae2ccf2766ab246f114b56b9cf6d445e00c2549fbc100ca045d" dependencies = [ "proc-macro2", "quote", @@ -955,15 +972,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" +checksum = "ca0bae1fe9752cf7fd9b0064c674ae63f97b37bc714d745cbde0afb7ec4e6765" [[package]] name = "futures-task" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" +checksum = "842fc63b931f4056a24d59de13fb1272134ce261816e063e634ad0c15cdc5306" [[package]] name = "futures-timer" @@ -977,9 +994,9 @@ dependencies = [ [[package]] name = "futures-util" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +checksum = "f0828a5471e340229c11c77ca80017937ce3c58cb788a17e5f1c2d5c485a9577" dependencies = [ "futures-channel", "futures-core", @@ -1131,6 +1148,9 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] [[package]] name = "hashers" @@ -1563,6 +1583,9 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] [[package]] name = "libc" @@ -1575,11 +1598,14 @@ name = "lightclient" version = "0.1.0" dependencies = [ "blst", + "bytes", "ethers", "eyre", + "futures", "hex", "jsonrpsee", "reqwest", + "revm", "serde", "ssz-rs", "tokio", @@ -1652,6 +1678,20 @@ dependencies = [ "tempfile", ] +[[package]] +name = "num" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.3" @@ -1663,6 +1703,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-complex" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" +dependencies = [ + "num-traits", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -1673,6 +1722,29 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -1692,6 +1764,27 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "once_cell" version = "1.13.0" @@ -2127,6 +2220,39 @@ dependencies = [ "winreg", ] +[[package]] +name = "revm" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab402ae244afdd560b2aa710df5af557bc791b1cca6fec174ced0cf781e13134" +dependencies = [ + "arrayref", + "auto_impl", + "bytes", + "hashbrown", + "num_enum", + "primitive-types", + "revm_precompiles", + "rlp", + "sha3", +] + +[[package]] +name = "revm_precompiles" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af88e7e9feb30cc4ed64645f09b966e84a1f6be56551ce5f1691105def45705d" +dependencies = [ + "bytes", + "num", + "primitive-types", + "ripemd", + "secp256k1", + "sha2 0.10.2", + "sha3", + "substrate-bn", +] + [[package]] name = "rfc6979" version = "0.3.0" @@ -2322,6 +2448,24 @@ dependencies = [ "zeroize", ] +[[package]] +name = "secp256k1" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ece73253dd9e1fb540ff324eae554113a31c25fb598d22fd13b088a6a03f90d" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7058dc8eaf3f2810d7828680320acda0b25a288f6d288e19278e249bbf74226b" +dependencies = [ + "cc", +] + [[package]] name = "security-framework" version = "2.6.1" @@ -2603,6 +2747,19 @@ dependencies = [ "syn", ] +[[package]] +name = "substrate-bn" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c" +dependencies = [ + "byteorder", + "crunchy", + "lazy_static", + "rand", + "rustc-hex", +] + [[package]] name = "subtle" version = "2.4.1" diff --git a/Cargo.toml b/Cargo.toml index 938bc95..d108745 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,6 @@ ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs" } blst = "0.3.10" ethers = "0.17.0" jsonrpsee = { version = "0.15.1", features = ["full"] } +revm = "1.9.0" +bytes = "1.2.1" +futures = "0.3.23" diff --git a/src/client/client.rs b/src/client/client.rs index b96ccbf..c9b4fd8 100644 --- a/src/client/client.rs +++ b/src/client/client.rs @@ -4,6 +4,7 @@ use eyre::Result; use crate::consensus::types::Header; use crate::consensus::ConsensusClient; use crate::execution::ExecutionClient; +use crate::execution::evm::Evm; pub struct Client { consensus: ConsensusClient, @@ -29,6 +30,12 @@ impl Client { self.consensus.sync().await } + pub async fn call(&mut self, to: &Address, calldata: &Vec, value: U256) -> Result> { + let payload = self.consensus.get_execution_payload().await?; + let mut evm = Evm::new(self.execution.clone(), payload); + evm.call(to, calldata, value) + } + pub async fn get_balance(&mut self, address: &Address) -> Result { let payload = self.consensus.get_execution_payload().await?; let account = self.execution.get_account(&address, None, &payload).await?; diff --git a/src/execution/evm.rs b/src/execution/evm.rs new file mode 100644 index 0000000..38eeaf1 --- /dev/null +++ b/src/execution/evm.rs @@ -0,0 +1,118 @@ +use std::str::FromStr; + +use bytes::Bytes; +use eyre::Result; +use ethers::prelude::{U256, H256, H160, Address}; +use revm::{Database, Bytecode, AccountInfo, EVM, Env, TransactTo, TransactOut}; +use futures::executor::block_on; + +use crate::consensus::types::ExecutionPayload; +use super::ExecutionClient; + +pub struct Evm { + evm: EVM +} + +impl Evm { + pub fn new(execution: ExecutionClient, payload: ExecutionPayload) -> Self { + let mut evm: EVM = EVM::new(); + let db = ProofDB::new(execution, payload); + evm.database(db); + + Evm { evm } + } + + pub fn call(&mut self, to: &Address, calldata: &Vec, value: U256) -> Result> { + + let mut env = Env::default(); + let mut tx = revm::TxEnv::default(); + tx.transact_to = TransactTo::Call(*to); + tx.data = Bytes::from(calldata.clone()); + tx.value = value; + env.tx = tx; + + self.evm.env = env; + + let output = self.evm.transact().1; + + if let Some(err) = &self.evm.db.as_ref().unwrap().error { + return Err(eyre::eyre!(err.clone())); + } + + match output { + TransactOut::None => Err(eyre::eyre!("Invalid Call")), + TransactOut::Create(..) => Err(eyre::eyre!("Invalid Call")), + TransactOut::Call(bytes) => Ok(bytes.to_vec()), + } + } +} + +struct ProofDB { + execution: ExecutionClient, + payload: ExecutionPayload, + error: Option, +} + +impl ProofDB { + pub fn new(execution: ExecutionClient, payload: ExecutionPayload) -> Self { + ProofDB { + execution, + payload, + error: None, + } + } +} + +impl Database for ProofDB { + fn basic(&mut self, address: H160) -> AccountInfo { + + self.error = None; + + if is_precompile(&address) { + return AccountInfo::default() + } + + let account_future = self.execution.get_account(&address, None, &self.payload); + let account_res = block_on(account_future); + if account_res.is_err() { + self.error = Some(account_res.as_ref().err().unwrap().to_string()) + } + + let account = account_res.unwrap(); + + let bytecode_future = self.execution.get_code(&address, &self.payload); + let bytecode_res = block_on(bytecode_future); + if bytecode_res.is_err() { + self.error = Some(bytecode_res.as_ref().err().unwrap().to_string()) + } + + let bytecode = bytecode_res.unwrap(); + let bytecode = Bytecode::new_raw(Bytes::from(bytecode)); + + AccountInfo::new(account.balance, account.nonce.as_u64(), bytecode) + } + + fn block_hash(&mut self, _number: U256) -> H256 { + H256::default() + } + + fn storage(&mut self, address: H160, slot: U256) -> U256 { + let slots = [slot]; + let account_future = self.execution.get_account(&address, Some(&slots), &self.payload); + let account_res = block_on(account_future); + if account_res.is_err() { + self.error = Some(account_res.as_ref().err().unwrap().to_string()); + } + + let account = account_res.unwrap(); + *account.slots.get(&slot).unwrap() + } + + fn code_by_hash(&mut self, _code_hash: H256) -> Bytecode { + panic!("should never be called"); + } +} + +fn is_precompile(address: &Address) -> bool { + address.le(&Address::from_str("0x0000000000000000000000000000000000000009").unwrap()) +} diff --git a/src/execution/execution.rs b/src/execution/execution.rs index 3812148..92158e4 100644 --- a/src/execution/execution.rs +++ b/src/execution/execution.rs @@ -12,6 +12,7 @@ use super::types::Account; use crate::common::utils::hex_str_to_bytes; use crate::consensus::types::ExecutionPayload; +#[derive(Clone)] pub struct ExecutionClient { rpc: Rpc, } diff --git a/src/execution/mod.rs b/src/execution/mod.rs index cd0f3c4..e451d20 100644 --- a/src/execution/mod.rs +++ b/src/execution/mod.rs @@ -1,4 +1,5 @@ pub mod types; +pub mod evm; mod execution; pub use execution::*; diff --git a/src/execution/rpc.rs b/src/execution/rpc.rs index 1c5220f..b4226b9 100644 --- a/src/execution/rpc.rs +++ b/src/execution/rpc.rs @@ -11,6 +11,7 @@ use crate::common::utils::{address_to_hex_string, hex_str_to_bytes, u64_to_hex_s use super::types::Proof; +#[derive(Clone)] pub struct Rpc { rpc: String, } diff --git a/src/main.rs b/src/main.rs index 7aba45d..094bc70 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,8 @@ use eyre::Result; use client::Client; +use crate::common::utils::hex_str_to_bytes; + pub mod client; pub mod common; pub mod consensus; @@ -28,10 +30,15 @@ async fn main() -> Result<()> { let code = client.get_code(&address).await?; let storage_value = client.get_storage_at(&address, U256::from(0)).await?; + let owner_calldata = hex_str_to_bytes("0x8da5cb5b")?; + let value = U256::from(0); + let owner = client.call(&address, &owner_calldata, value).await?; + println!("balance: {}", balance); println!("nonce: {}", nonce); println!("code: 0x{}...", hex::encode(code[..5].to_vec())); println!("value at slot 0: 0x{:x}", storage_value); + println!("result of calling owner() on address: {}", hex::encode(owner)); Ok(()) }