diff --git a/Cargo.toml b/Cargo.toml index 5703601..5b52113 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,51 +22,55 @@ path = "src/cached_proxy.rs" [lib] name = "dvf_libs" path = "lib/lib.rs" - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tracing = "0.1.34" -tracing-subscriber = { version = "0.3.11", default-features = true, features = ["env-filter", "fmt"]} -reqwest = { version = "0.11", features = ["json", "blocking"] } -tokio = { version = "1", features = ["full"] } -serde = { version = "1.0.144", features = ["derive"] } -serde_json = "1.0.85" -substring = "1.4.5" -clap = { version = "3.1.6", features = ["derive"]} +actix-web = "4.7.0" +alloy = { version = "0.6.4", features = ["full"] } +alloy-chains = { version = "0.1.47", features = ["serde"] } +alloy-dyn-abi = { version = "0.8.12", features = ["eip712"] } +alloy-json-abi = "0.8.12" +alloy-node-bindings = "0.7.0" +alloy-rpc-types = "0.6.4" +alloy-rpc-types-trace = "0.6.4" +alloy-signer = { version = "0.6.4", features = ["eip712"] } +alloy-signer-ledger = "0.6.4" +alloy-signer-local = "0.6.4" +async-trait = "0.1.69" +bigint = "1" +bytes = "1.4.0" +clap = { version = "3.1.6", features = ["derive"] } colored = "1.0.0" +console = "0.15.7" +dirs-next = "2.0.0" +dotenv = "0.15.0" +foundry-block-explorers = "0.9.0" +foundry-compilers = "0.11.6" +foundry-compilers-core = "0.12.3" +hex = "0.4" +indicatif = "0.17.6" prettytable-rs = "0.10.0" +rand = "0.8.5" regex = "1" -bigint = "1" -tiny-keccak = { version = "2.0.0", features = ["sha3", "keccak"] } -hex = "0.4" -ethers = { version = "2.0.8", features = ["solc"] } -ethers-contract = "2.0.8" -ethers-core = { version = "2.0.8" } -ethers-etherscan = { version = "2.0.8" } -ethers-providers = "2.0.8" -ethers-signers = {version = "2.0.8", features = ["ledger", "yubi", "yubihsm"]} -ethers-solc = "2.0.8" +reqwest = { version = "0.11", features = ["json", "blocking"] } +reth-trie = { git = "https://github.com/paradigmxyz/reth", tag = "v1.1.2"} +ruint = "1.12.3" +rustc-hex = "2.1.0" +scanf = "1.2.1" semver = { version = "1.0.17", features = ["serde"] } -toml = "0.7.4" -dirs-next = "2.0.0" -zip = "0.6.6" +serde = { version = "1.0.144", features = ["derive"] } +serde_json = "1.0.85" +sha3 = "0.10.8" +substring = "1.4.5" tempfile = "3.6.0" -bytes = "1.4.0" -async-trait = "0.1.69" thiserror = "1.0.40" -sha3 = "0.10.8" -rustc-hex = "2.1.0" -indicatif = "0.17.6" -console = "0.15.7" -actix-web = "4.7.0" -scanf = "1.2.1" -dotenv = "0.15.0" time = "0.3.36" -foundry-compilers = "0.11.6" -alloy = { version = "0.6.4", features = ["full"] } -alloy-rpc-types = "0.6.4" -alloy-rpc-types-trace = "0.6.4" +tiny-keccak = { version = "2.0.0", features = ["sha3", "keccak"] } +tokio = { version = "1", features = ["full"] } +toml = "0.7.4" +tracing = "0.1.34" +tracing-subscriber = { version = "0.3.11", default-features = true, features = ["env-filter", "fmt"] } +zip = "0.6.6" [dev-dependencies] assert_cmd = "2.0.12" diff --git a/lib/bytecode_verification/compare_bytecodes.rs b/lib/bytecode_verification/compare_bytecodes.rs index d53b611..fdcf389 100644 --- a/lib/bytecode_verification/compare_bytecodes.rs +++ b/lib/bytecode_verification/compare_bytecodes.rs @@ -1,4 +1,4 @@ -use alloy::dyn_abi::{JsonAbiExt}; +use alloy::dyn_abi::JsonAbiExt; use foundry_compilers::artifacts::BytecodeHash; use tracing::{debug, info}; @@ -273,8 +273,8 @@ impl CompareInitCode { for (arg, value) in project_info.constructor_args.iter_mut().zip(decoded_args) { let encoded_value = value.abi_encode_packed(); let formatted_value = format!("0x{}", hex::encode(&encoded_value)); - - let sol_type = value.as_type().expect(format!("Unable to find constructor argument type for {}", arg.name).as_str()); + + let sol_type = value.as_type().unwrap_or_else(|| panic!("Unable to find constructor argument type for {}", arg.name)); arg.value = formatted_value; arg.type_string = sol_type.sol_type_name().to_string() @@ -289,7 +289,6 @@ impl CompareInitCode { mod tests { use crate::types::ConstructorArg; use alloy::json_abi::{Constructor, Param, StateMutability}; - use ethers::abi::ParamType; use semver::Version; use std::collections::HashMap; @@ -359,18 +358,18 @@ mod tests { ConstructorArg { name: "arg1".to_string(), value: "1".to_string(), - type_string: "uint256".to_string(), + type_string: "address".to_string(), }, ConstructorArg { name: "arg2".to_string(), value: "2".to_string(), - type_string: "uint128".to_string(), + type_string: "address".to_string(), }, ]; let constructor_inputs: Vec = vec![ - Param::parse("uint256 arg1").unwrap(), - Param::parse("uint128 arg2").unwrap() + Param::parse("address arg1").unwrap(), + Param::parse("address arg2").unwrap() ]; let constructor = Constructor { diff --git a/lib/bytecode_verification/parse_json.rs b/lib/bytecode_verification/parse_json.rs index 95e39f9..3adc442 100644 --- a/lib/bytecode_verification/parse_json.rs +++ b/lib/bytecode_verification/parse_json.rs @@ -869,14 +869,12 @@ impl ProjectInfo { if value["nodeType"] == "FunctionCall" && value["expression"]["name"] == "keccak256" { if let Some(arguments) = value.get("arguments") { if !arguments.as_array().unwrap().is_empty() { - let mut slot = U256::from_str( - arguments[0]["typeDescriptions"]["typeIdentifier"] - .as_str() - .unwrap() - .replace("t_stringliteral_", "") - .as_str(), - ) - .unwrap(); + let mut hex_wo_prefix = arguments[0]["typeDescriptions"]["typeIdentifier"] + .as_str() + .unwrap() + .replace("t_stringliteral_", ""); + hex_wo_prefix.insert_str(0, "0x"); + let mut slot = U256::from_str(hex_wo_prefix.as_str()).unwrap(); if let Some(binary_op) = binary_op { slot -= U256::from(binary_op); } @@ -1399,9 +1397,9 @@ impl ProjectInfo { Ok(read_dir) => { for build_info_file in read_dir.flatten() { let bi: BuildInfo = BuildInfo::read(&build_info_file.path())?; - if bi.output.contracts.values().flatten().find(|(name, _)| { - name == &contract_name - }).is_some() { + if bi.output.contracts.values().flatten().any(|(name, _)| { + name == contract_name + }) { build_infos.push(bi); } } diff --git a/lib/bytecode_verification/verify_bytecode.rs b/lib/bytecode_verification/verify_bytecode.rs index da0b39b..d71e359 100644 --- a/lib/bytecode_verification/verify_bytecode.rs +++ b/lib/bytecode_verification/verify_bytecode.rs @@ -3,7 +3,7 @@ use std::io::prelude::*; use std::path::Path; use colored::Colorize; -use ethers::abi::Address; +use alloy::primitives::Address; use prettytable::Table; use tracing::debug; diff --git a/lib/dvf/abstract_wallet.rs b/lib/dvf/abstract_wallet.rs index 21ddd2f..a2a5730 100644 --- a/lib/dvf/abstract_wallet.rs +++ b/lib/dvf/abstract_wallet.rs @@ -1,18 +1,21 @@ use async_trait::async_trait; -use ethers::signers::{LocalWallet, Signer}; -use ethers::types::Signature; -use ethers_core::types::transaction::eip2718::TypedTransaction; -use ethers_core::types::transaction::eip712::Eip712; -use ethers_core::types::Address; -use ethers_signers::Ledger; -use ethers_signers::LedgerError; -use ethers_signers::WalletError; + +use alloy::signers::{Signer, Error as SignerError}; +use alloy::signers::local::{PrivateKeySigner, LocalSignerError}; +use alloy_signer_ledger::{LedgerSigner, LedgerError}; +use alloy::primitives::{Address, PrimitiveSignature as Signature, B256, ChainId}; +use alloy::consensus::SignableTransaction; +use alloy::network::TxSigner; +use alloy::dyn_abi::eip712::TypedData; +use alloy::sol_types::{Eip712Domain, SolStruct}; + use thiserror::Error; #[derive(Error, Debug)] pub enum AbstractError { LedgerError(LedgerError), - WalletError(WalletError), + WalletError(LocalSignerError), + GeneralError(SignerError), } impl From for AbstractError { @@ -21,91 +24,127 @@ impl From for AbstractError { } } -impl From for AbstractError { - fn from(error: WalletError) -> Self { +impl From for AbstractError { + fn from(error: LocalSignerError) -> Self { AbstractError::WalletError(error) } } +impl From for AbstractError { + fn from(error: SignerError) -> Self { + AbstractError::GeneralError(error) + } +} + impl std::fmt::Display for AbstractError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { AbstractError::LedgerError(e) => write!(f, "{:?}", e), AbstractError::WalletError(e) => write!(f, "{:?}", e), + AbstractError::GeneralError(e) => write!(f, "{:?}", e), } } } #[derive(Debug)] pub enum AbstractWallet { - Ledger(Ledger), - LocalWallet(LocalWallet), + Ledger(LedgerSigner), + LocalWallet(PrivateKeySigner), +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TxSigner for AbstractWallet { + + fn address(&self) -> Address { + match self { + AbstractWallet::Ledger(ledger) => Signer::address(ledger), + AbstractWallet::LocalWallet(localwallet) => localwallet.address(), + } + } + + #[inline] + async fn sign_transaction( + &self, + tx: &mut dyn SignableTransaction, + ) -> Result { //@audit how to turn a typed transaction into a signableTx? + match self { + AbstractWallet::Ledger(ledger) => ledger + .sign_transaction(tx) + .await, + AbstractWallet::LocalWallet(localwallet) => localwallet + .sign_transaction(tx) + .await, + } + } } #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl Signer for AbstractWallet { - type Error = AbstractError; - async fn sign_message>( + async fn sign_hash(&self, _hash: &B256) -> Result { + Err(alloy_signer::Error::UnsupportedOperation( + alloy_signer::UnsupportedSignerOperation::SignHash, + )) + } + + async fn sign_message( &self, - message: S, - ) -> Result { + message: &[u8], + ) -> Result { match self { AbstractWallet::Ledger(ledger) => ledger .sign_message(message) - .await - .map_err(AbstractError::from), + .await, AbstractWallet::LocalWallet(localwallet) => localwallet .sign_message(message) - .await - .map_err(AbstractError::from), + .await, } } - /// Signs the transaction - async fn sign_transaction( + #[inline] + async fn sign_typed_data( &self, - message: &TypedTransaction, - ) -> Result { + payload: &T, + domain: &Eip712Domain, + ) -> Result { match self { AbstractWallet::Ledger(ledger) => ledger - .sign_transaction(message) - .await - .map_err(AbstractError::from), + .sign_typed_data(payload, domain) + .await, + // .map_err(AbstractError::from), AbstractWallet::LocalWallet(localwallet) => localwallet - .sign_transaction(message) - .await - .map_err(AbstractError::from), + .sign_typed_data(payload, domain) + .await, + // .map_err(AbstractError::from), } } - async fn sign_typed_data( - &self, - payload: &T, - ) -> Result { + #[inline] + async fn sign_dynamic_typed_data(&self, payload: &TypedData) -> Result { match self { AbstractWallet::Ledger(ledger) => ledger - .sign_typed_data(payload) - .await - .map_err(AbstractError::from), + .sign_dynamic_typed_data(payload) + .await, + // .map_err(AbstractError::from), AbstractWallet::LocalWallet(localwallet) => localwallet - .sign_typed_data(payload) - .await - .map_err(AbstractError::from), + .sign_dynamic_typed_data(payload) + .await, + // .map_err(AbstractError::from), } } /// Returns the signer's Ethereum Address fn address(&self) -> Address { match self { - AbstractWallet::Ledger(ledger) => ledger.address(), + AbstractWallet::Ledger(ledger) => Signer::address(ledger), AbstractWallet::LocalWallet(localwallet) => localwallet.address(), } } /// Returns the signer's chain id - fn chain_id(&self) -> u64 { + fn chain_id(&self) -> Option { match self { AbstractWallet::Ledger(ledger) => ledger.chain_id(), AbstractWallet::LocalWallet(localwallet) => localwallet.chain_id(), @@ -113,14 +152,15 @@ impl Signer for AbstractWallet { } /// Sets the signer's chain id - fn with_chain_id>(self, chain_id: T) -> Self { + fn set_chain_id(&mut self, chain_id: Option) { match self { AbstractWallet::Ledger(ledger) => { - AbstractWallet::Ledger(ledger.with_chain_id(chain_id)) + ledger.set_chain_id(chain_id) } AbstractWallet::LocalWallet(localwallet) => { - AbstractWallet::LocalWallet(localwallet.with_chain_id(chain_id)) + localwallet.set_chain_id(chain_id) } } } } + diff --git a/lib/dvf/config.rs b/lib/dvf/config.rs index 971ee87..1160f1e 100644 --- a/lib/dvf/config.rs +++ b/lib/dvf/config.rs @@ -9,14 +9,14 @@ use std::str::FromStr; use clap::ArgMatches; use dirs_next::home_dir; -use ethers::types::H160; -use ethers_core::rand::thread_rng; -use ethers_core::types::Address; -use ethers_core::types::Chain; -use ethers_signers::HDPath; -use ethers_signers::Ledger; -use ethers_signers::LocalWallet; -use ethers_signers::Signer; + +use alloy::primitives::Address; +use alloy_chains::NamedChain; + +use alloy::signers::Signer; +use alloy::signers::local::PrivateKeySigner; //LOCALWALLET +use alloy_signer_ledger::{LedgerSigner, HDPath}; + use reqwest::blocking::Client; use serde::{Deserialize, Serialize}; use tempfile::{tempdir, NamedTempFile}; @@ -101,7 +101,7 @@ pub struct DVFConfig { #[serde(skip_serializing)] pub active_chain_id: Option, #[serde(default, skip_serializing)] - active_chain: Option, + active_chain: Option, } fn default_max_blocks() -> u64 { @@ -210,22 +210,22 @@ impl DVFConfig { pub fn get_abstract_wallet(&self, chain_id: u64) -> Result { let wallet = match &self.signer { - None => AbstractWallet::LocalWallet(LocalWallet::new(&mut thread_rng())), + None => AbstractWallet::LocalWallet(PrivateKeySigner::random()), Some(signer) => { let temp_wallet = match &signer.wallet_type { DVFWalletType::SecretKey(sk) => AbstractWallet::LocalWallet( sk.secret_key - .parse::() + .parse::() .map_err(|_| { ValidationError::Error("Could not parse private key.".to_string()) })? - .with_chain_id(chain_id), + .with_chain_id(Option::Some(chain_id)), ), DVFWalletType::Ledger(ledger_config) => { let rt = tokio::runtime::Runtime::new().unwrap(); AbstractWallet::Ledger( - rt.block_on(Ledger::new(ledger_config.get_hd_path(), chain_id))?, + rt.block_on(LedgerSigner::new(ledger_config.get_hd_path(), Option::Some(chain_id)))?, ) } }; @@ -386,12 +386,12 @@ impl DVFConfig { } } - let mut trusted_signers: Vec = vec![]; + let mut trusted_signers: Vec
= vec![]; first_item = true; println!(); println!("{}", "STEP 3".green()); loop { - let mut signer = H160::zero(); + let mut signer = Address::default(); if first_item { println!( "Please provide a trusted signer or hit {} to go to the next step.", @@ -563,7 +563,7 @@ impl DVFConfig { break; } - let mut address = H160::zero(); + let mut address = Address::default(); if sscanf!(&input, "{}", address).is_ok() { loop { println!("Please enter your signing choice:"); @@ -726,7 +726,7 @@ impl DVFConfig { } Some(_) => self.active_chain_id = Some(chain_id), } - self.active_chain = Chain::try_from(chain_id).ok(); + self.active_chain = NamedChain::try_from(chain_id).ok(); let rpc_chain_id = web3::get_eth_chain_id(self)?; if rpc_chain_id != chain_id { @@ -740,7 +740,7 @@ impl DVFConfig { Ok(()) } - pub fn get_chain(&self) -> Chain { + pub fn get_chain(&self) -> NamedChain { self.active_chain.unwrap() } diff --git a/lib/dvf/mod.rs b/lib/dvf/mod.rs index 56e72bb..e470aef 100644 --- a/lib/dvf/mod.rs +++ b/lib/dvf/mod.rs @@ -1,4 +1,4 @@ pub mod abstract_wallet; pub mod config; pub mod parse; -pub mod registry; +pub mod registry; \ No newline at end of file diff --git a/lib/dvf/parse.rs b/lib/dvf/parse.rs index bc702ff..3c15454 100644 --- a/lib/dvf/parse.rs +++ b/lib/dvf/parse.rs @@ -8,16 +8,22 @@ use std::num::ParseIntError; use std::path::Path; use std::str::FromStr; +use ruint; + use crate::bytecode_verification::parse_json::ProjectInfo; use crate::utils::pretty::convert_bytes_to_i256; use crate::utils::pretty::PrettyPrinter; use clap::ArgMatches; -use ethers::abi::ethabi::ethereum_types::FromStrRadixErr; -use ethers::signers::Signer; -use ethers::solc::error::SolcError; -use ethers::types::Address; -use ethers::types::{Bytes, H256, U256}; -use ethers_signers::{LedgerError, WalletError}; + +use foundry_compilers; + +use alloy_signer_ledger::LedgerError; +use alloy_signer_local::LocalSignerError; +use alloy::signers::Signer; +use alloy_dyn_abi; +use alloy::primitives::{Address, B256, U256, Bytes, PrimitiveSignature}; + + use reqwest; use semver::Version; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -47,6 +53,7 @@ impl From for ValidationError { match error { AbstractError::LedgerError(e) => ValidationError::from(e), AbstractError::WalletError(e) => ValidationError::from(e), + AbstractError::GeneralError(e) => ValidationError::from(e), } } } @@ -57,15 +64,15 @@ impl From for ValidationError { } } -impl From for ValidationError { - fn from(error: ethers::utils::hex::FromHexError) -> Self { - ValidationError::Error(format!("Error Decoding Hex: {}", error)) +impl From for ValidationError { + fn from(error: alloy_dyn_abi::Error) -> Self { + ValidationError::Error(format!("Alloy Dyn Abi Error: {}", error)) } } -impl From for ValidationError { - fn from(error: ethers::abi::Error) -> Self { - ValidationError::Error(format!("ABI Error: {}", error)) +impl From for ValidationError { + fn from(error: ruint::ParseError) -> Self { + ValidationError::Error(format!("Uint Parse Error: {}", error)) } } @@ -75,20 +82,26 @@ impl From for ValidationError { } } -impl From for ValidationError { - fn from(error: FromStrRadixErr) -> Self { - ValidationError::Error(format!("Error converting from hex: {}", error)) +impl From for ValidationError { + fn from(error: alloy::signers::Error) -> Self { + ValidationError::Error(format!("Signer Error: {}", error)) + } +} + +impl From for ValidationError { + fn from(error: alloy::hex::FromHexError) -> Self { + ValidationError::Error(format!("Alloy Hex Parse Error: {}", error)) } } -impl From for ValidationError { - fn from(error: SolcError) -> Self { - ValidationError::Error(format!("Error in solc: {}", error)) +impl From for ValidationError { + fn from(error: hex::FromHexError) -> Self { + ValidationError::Error(format!("Hex Parse Error: {}", error)) } } -impl From for ValidationError { - fn from(error: WalletError) -> Self { +impl From for ValidationError { + fn from(error: LocalSignerError) -> Self { ValidationError::Error(format!("Error in with Local Wallet: {}", error)) } } @@ -287,14 +300,14 @@ impl DVFStorageEntry { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct DVFEventOccurrence { - pub topics: Vec, + pub topics: Vec, pub data: Bytes, } #[derive(Serialize, Deserialize, Debug, Clone)] pub struct DVFEventEntry { pub sig: String, // Event signature, e.g. "Transfer(address,address,uint256)" - pub topic0: H256, // Event Topic 0 + pub topic0: B256, // Event Topic 0 pub occurrences: Vec, // Where the event has legitimately occurred } @@ -365,7 +378,7 @@ impl DumpedDVF { let critical_storage_variables: Vec = vec![]; let critical_events: Vec = vec![]; let constructor_args: Vec = vec![]; - let implementation_address = matches.value_of("implementation").map(|_| Address::zero()); + let implementation_address = matches.value_of("implementation").map(|_| Address::default()); let implementation_name = matches.value_of("implementation").map(|x| x.to_string()); let dumped = DumpedDVF { version: CURRENT_VERSION, @@ -527,13 +540,15 @@ impl CompleteDVF { match &self.signature { Some(sig) => match &sig.sig_data { Some(sig_data) => { - let signature = ethers::types::Signature::from_str(sig_data).unwrap(); - let sig_message = ethers::types::RecoveryMessage::from(self.get_sig_message()?); - debug!("sig_message: {}", self.get_sig_message()?); + // let signature = PrimitiveSignature::from_str(sig_data).unwrap(); + let signature: PrimitiveSignature = serde_json::from_str(sig_data).unwrap(); + let sig_message = self.get_sig_message()?; debug!("sig_message: {:?}", sig_message); - let rec_address = signature.recover(sig_message).map_err(|_| { - ValidationError::Error(String::from("Error. Signature validation failed.")) - })?; + let rec_address = signature.recover_address_from_msg(sig_message).map_err( + |_| { + ValidationError::Error(String::from("Error. Signature validation failed.")) + } + )?; debug!("Provided Address: {:?}", &sig.signer); debug!("Recovered address {:?}", rec_address); if sig.signer != rec_address { @@ -653,11 +668,12 @@ impl CompleteDVF { // Chain ID should not matter here let wallet = config.get_abstract_wallet(1)?; let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(wallet.sign_message(to_be_signed_data))? + rt.block_on(wallet.sign_message(to_be_signed_data.as_bytes()))? } }; if let Some(sig) = self.signature.as_mut() { - sig.sig_data = Some("0x".to_string() + &signature.to_string()); + let signature_str = serde_json::to_string(&signature).unwrap(); + sig.sig_data = Some(signature_str); }; Ok(()) } diff --git a/lib/dvf/registry.rs b/lib/dvf/registry.rs index d4aca0f..0966114 100644 --- a/lib/dvf/registry.rs +++ b/lib/dvf/registry.rs @@ -4,7 +4,7 @@ use std::fs; use std::path::Path; use std::path::PathBuf; -use ethers_core::types::Address; +use alloy::primitives::Address; use tracing::debug; use crate::dvf::config::DVFConfig; diff --git a/lib/state/contract_state.rs b/lib/state/contract_state.rs index 066fac7..973d3f2 100644 --- a/lib/state/contract_state.rs +++ b/lib/state/contract_state.rs @@ -172,10 +172,8 @@ impl<'a> ContractState<'a> { } if log.op == "CALL" || log.op == "STATICCALL" { - let mut address_bytes = [0u8; 32]; - stack[stack.len() - 2].to_big_endian(&mut address_bytes); - let mut a = Address::from([0; 20]); - a.assign_from_slice(&address_bytes[12..]); + let address_bytes = stack[stack.len() - 2].to_be_bytes::<32>(); + let a = Address::from_slice(&address_bytes[12..]); depth_to_address.insert(log.depth + 1, a); } @@ -212,7 +210,7 @@ impl<'a> ContractState<'a> { if length_in_bytes > U256::from(32_u64) && length_in_bytes < U256::from(usize::MAX / 2) { - let usize_str_length = length_in_bytes.as_usize() * 2 + 2; + let usize_str_length = usize::try_from(length_in_bytes).unwrap() * 2 + 2; assert!(sha3_input.len() == usize_str_length); key = Some(sha3_input[2..usize_str_length - 64].to_string()); index = U256::from_str_radix(&sha3_input[usize_str_length - 64..], 16)?; @@ -294,7 +292,7 @@ impl<'a> ContractState<'a> { } for unused_part in unused_parts { - let crit_var = DVFStorageEntry { + let crit_var: DVFStorageEntry = DVFStorageEntry { slot: unused_part.slot, offset: unused_part.offset, var_name: String::from("unknown"), @@ -448,7 +446,7 @@ impl<'a> ContractState<'a> { )?); } let mut current_slot = match self.is_dynamic_array(&state_variable.var_type) { - true => U256::from(hash_u256(&state_variable.slot)), + true => U256::from_be_slice(hash_u256(&state_variable.slot).as_slice()), false => state_variable.slot, }; for i in 0..num { @@ -468,7 +466,7 @@ impl<'a> ContractState<'a> { current_offset = 0; // Check if we need to skip one slot } else if current_offset + base_num_bytes + base_num_bytes > 32 { - current_slot = current_slot.add(U256::one()); + current_slot = current_slot.add(U256::from(1)); current_offset = 0; } else { current_offset += base_num_bytes; @@ -581,19 +579,19 @@ impl<'a> ContractState<'a> { snapshot, table, )?); - let mut string_length = U256::from_big_endian(&snapshot.get_slot( + let mut string_length = U256::from_be_slice(&snapshot.get_slot( &length_var.slot, length_var.offset, 32, )); // We skip the -1 as we round down anyway - string_length /= U256([2, 0, 0, 0]); - let mut string_index = U256::zero(); - let mut current_slot = U256::from(hash_u256(&state_variable.slot)); + string_length /= U256::from_limbs([2, 0, 0, 0]); + let mut string_index = U256::ZERO; + let mut current_slot = U256::from_be_slice(hash_u256(&state_variable.slot).as_slice()); let mut raw_string: Vec = vec![]; - let u256_32 = U256([32, 0, 0, 0]); + let u256_32 = U256::from_limbs([32, 0, 0, 0]); loop { - let value_length = cmp::min(string_length.as_usize(), 32); + let value_length = cmp::min(string_length.as_limbs()[0] as usize, 32); //@note take the least significant limbs let value = snapshot.get_slot_and_mark(¤t_slot, 32 - value_length, value_length); raw_string.extend_from_slice(&value); @@ -613,8 +611,8 @@ impl<'a> ContractState<'a> { break; } string_length -= u256_32; - string_index += U256::one(); - current_slot += U256::one(); + string_index += U256::from(1); + current_slot += U256::from(1); } let mut full_string = String::new(); if Self::is_string(&state_variable.var_type) { diff --git a/lib/utils/pretty.rs b/lib/utils/pretty.rs index c42530e..ed02c2b 100644 --- a/lib/utils/pretty.rs +++ b/lib/utils/pretty.rs @@ -2,12 +2,10 @@ use std::collections::HashMap; use std::ops::BitAnd; use std::str::FromStr; -use ethers::abi::ethabi::param_type::Writer; -use ethers::abi::Log; -use ethers::abi::Token; -use ethers::abi::{Event, ParamType}; -use ethers::types::{Address, Sign, I256, U256}; -use ethers_etherscan::Client; +use alloy::json_abi::Event; +use alloy::primitives::{Address, U256, I256, Sign}; + +use alloy_dyn_abi::{DecodedEvent, DynSolValue}; use prettytable::Table; use serde::Deserialize; use tracing::debug; @@ -37,7 +35,6 @@ pub struct ResolvedAddress { #[derive(Debug)] pub struct PrettyPrinter { ns: HashMap, - client: Option, } const KNOWN_ADDRS: &str = include_str!("../../addresses/known.json"); @@ -64,30 +61,30 @@ impl PrettyPrinter { ns.extend(registry.collect_name_resolution(chain_id)); } debug!("Name Resolution has {} entries.", ns.keys().len()); - PrettyPrinter { ns, client: None } + PrettyPrinter { ns } } - // Based on: https://docs.rs/ethabi/latest/src/ethabi/signature.rs.html - // and https://docs.rs/ethabi/latest/src/ethabi/event.rs.html pub fn event_to_string(event: &Event) -> String { - let name = &event.name; - let params: Vec = event.inputs.iter().map(|p| p.kind.clone()).collect(); - let types = params - .iter() - .map(Writer::write) - .collect::>() - .join(","); - format!("{name}({types})") + event.signature() } - pub fn pretty_event_params(&self, log: &Log, newlines: bool) -> String { + pub fn pretty_event_params(&self, abi_event: &Event, decoded_event: &DecodedEvent, newlines: bool) -> String { let mut decoded_params: Vec = vec![]; - for param in &log.params { + let mut next_index = 0; + let mut next_body = 0; + for param in &abi_event.inputs { + let current_val: &DynSolValue = if param.indexed { + next_index += 1; + &decoded_event.indexed[next_index - 1] + } else { + next_body += 1; + &decoded_event.body[next_body - 1] + }; decoded_params.push(format!( "{} = {}", ¶m.name, Self::insert_newline_every_n_chars( - &self.pretty_token(¶m.value), + &self.pretty_token(current_val), CHARS_PER_LINE, param.name.len() + 4 ), @@ -100,35 +97,36 @@ impl PrettyPrinter { } } - pub fn pretty_event(&self, event: &Event, log: &Log, newlines: bool) -> String { - let name = &event.name; - let params = self.pretty_event_params(log, newlines); - format!("{name}{params}") - } - - pub fn pretty_token(&self, token: &Token) -> String { - match token { - Token::Address(addr) => self.pretty_address(addr, false, false), - Token::FixedBytes(fbytes) => format!("0x{}", hex::encode(fbytes)), - Token::Bytes(bytes) => format!("0x{}", hex::encode(bytes)), - Token::Int(int) => Self::pretty_int(&I256::from_raw(*int)), - Token::Uint(uint) => Self::pretty_uint(uint), - Token::Bool(b) => Self::pretty_bool(*b), - Token::String(s) => s.clone(), - Token::FixedArray(arr) | Token::Array(arr) => { + pub fn pretty_token(&self, dyn_val: &DynSolValue) -> String { + match dyn_val { + DynSolValue::Address(addr) => self.pretty_address(addr, false, false), + DynSolValue::FixedBytes(fbytes, _num_bytes) => format!("0x{}", hex::encode(fbytes)), + DynSolValue::Bytes(bytes) => format!("0x{}", hex::encode(bytes)), + DynSolValue::Int(int, _num_bits) => Self::pretty_int(int), + DynSolValue::Uint(uint, _num_bits) => Self::pretty_uint(uint), + DynSolValue::Bool(b) => Self::pretty_bool(*b), + DynSolValue::String(s) => s.clone(), + DynSolValue::FixedArray(arr) | DynSolValue::Array(arr) => { let decoded: Vec = arr.iter().map(|a| self.pretty_token(a)).collect(); format!("[{}]", decoded.join(", ")) } - Token::Tuple(arr) => { + DynSolValue::Tuple(arr) => { let decoded: Vec = arr.iter().map(|a| self.pretty_token(a)).collect(); format!("({})", decoded.join(", ")) } + DynSolValue::Function(func) => { + format!("function {:?}", func) + } + DynSolValue::CustomStruct { name: _name, prop_names: _prop_name, tuple } => { + let decoded: Vec = tuple.iter().map(|a| self.pretty_token(a)).collect(); + format!("({})", decoded.join(", ")) + } } } fn pretty_uint(u256: &U256) -> String { // TODO Timestamps - if u256 == &U256::max_value() { + if u256 == &U256::MAX { return String::from("uint256 Max Value"); } let u256_str = u256.to_string(); @@ -179,11 +177,11 @@ impl PrettyPrinter { match resolved.address_type { AddressType::Contract | AddressType::Token | AddressType::Registry => { if long { - if let Some(client) = self.client.as_ref() { - format!("{}\nLink:\n{}", resolved.name, client.address_url(*a)) - } else { - resolved.name.clone() - } + format!( + "{}\nLink:\nhttps://etherscan.io/address/{:?}", + resolved.name, + a, + ) } else { resolved.name.clone() } @@ -244,11 +242,10 @@ impl PrettyPrinter { let i256 = convert_bytes_to_i256(value, var_type); return Self::pretty_int(&i256); } else if ContractState::is_uint(var_type) { - let u256 = U256::from_big_endian(value); + let u256 = U256::from_be_slice(value); return Self::pretty_uint(&u256); } else if ContractState::is_address(var_type) { - let mut a = Address::zero(); - a.assign_from_slice(&value[value.len() - 20..]); + let a = Address::from_slice(&value[value.len() - 20..]); return self.pretty_address(&a, long, leave_empty); } else if ContractState::is_bool(var_type) { let last_byte: u8 = *value.last().unwrap(); @@ -372,10 +369,10 @@ pub fn convert_bytes_to_i256(bytes: &[u8], var_type: &str) -> I256 { for i in 0..num_used_bytes { full_bytes[i + num_unused_bytes] = bytes[bytes.len() - num_used_bytes + i]; } - let u256 = U256::from_big_endian(&full_bytes); + let u256 = U256::from_be_slice(&full_bytes); I256::from_raw(u256) } else { - let u256 = U256::from_big_endian(bytes); + let u256 = U256::from_be_slice(bytes); I256::from_raw(u256) } } @@ -387,46 +384,44 @@ mod tests { #[test] fn test_convert_bytes_to_i256() { let b = vec![0xff]; - assert_eq!(I256::minus_one(), convert_bytes_to_i256(&b, "t_int8")); + assert_eq!(I256::MINUS_ONE, convert_bytes_to_i256(&b, "t_int8")); let b2 = vec![0xff, 0xff]; - assert_eq!(I256::minus_one(), convert_bytes_to_i256(&b2, "t_int16")); + assert_eq!(I256::MINUS_ONE, convert_bytes_to_i256(&b2, "t_int16")); let b3 = vec![0xff, 0xfe]; assert_eq!( - I256::minus_one() + I256::minus_one(), + I256::MINUS_ONE + I256::MINUS_ONE, convert_bytes_to_i256(&b3, "t_int16") ); let b4 = vec![0, 0xff, 0xfe]; assert_eq!( - I256::minus_one() + I256::minus_one(), + I256::MINUS_ONE + I256::MINUS_ONE, convert_bytes_to_i256(&b4, "t_int16") ); let b5 = vec![0x00]; - assert_eq!(I256::zero(), convert_bytes_to_i256(&b5, "t_int8")); + assert_eq!(I256::ZERO, convert_bytes_to_i256(&b5, "t_int8")); let b6 = vec![0x00, 0x00]; - assert_eq!(I256::zero(), convert_bytes_to_i256(&b6, "t_int16")); + assert_eq!(I256::ZERO, convert_bytes_to_i256(&b6, "t_int16")); let b7 = vec![0x00, 0x01]; - assert_eq!(I256::one(), convert_bytes_to_i256(&b7, "t_int16")); + assert_eq!(I256::ONE, convert_bytes_to_i256(&b7, "t_int16")); let b8 = vec![0, 0x00, 0x02]; assert_eq!( - I256::one() + I256::one(), + I256::ONE + I256::ONE, convert_bytes_to_i256(&b8, "t_int16") ); let b9 = vec![0u8; 32]; - assert_eq!(I256::zero(), convert_bytes_to_i256(&b9, "t_int256")); + assert_eq!(I256::ZERO, convert_bytes_to_i256(&b9, "t_int256")); for i in vec![ - I256::zero(), - I256::one(), - I256::minus_one(), - I256::max_value(), - I256::min_value(), + I256::ZERO, + I256::ONE, + I256::MINUS_ONE, + I256::MAX, + I256::MAX, ] { - let mut bytes = [0u8; 32]; - i.to_big_endian(&mut bytes); + let bytes: [u8; 32] = i.to_be_bytes(); assert_eq!(i, convert_bytes_to_i256(&bytes.to_vec(), "t_int256")); } - for i in vec![I256::zero(), I256::one(), I256::minus_one()] { - let mut bytes = [0u8; 32]; - i.to_big_endian(&mut bytes); + for i in vec![I256::ZERO, I256::ONE, I256::MINUS_ONE] { + let bytes: [u8; 32] = i.to_be_bytes(); assert_eq!(i, convert_bytes_to_i256(&bytes.to_vec(), "t_int128")); } let b10 = vec![0x80]; diff --git a/lib/web3.rs b/lib/web3.rs index 04404e4..f70ed5d 100644 --- a/lib/web3.rs +++ b/lib/web3.rs @@ -1,5 +1,5 @@ use std::cmp::Ordering; -use std::collections::{BTreeMap, HashMap, HashSet}; +use std::collections::{HashMap, HashSet}; use std::fmt; use std::io::Read; use std::str::FromStr; @@ -17,14 +17,68 @@ use tracing::{debug, info}; use crate::dvf::config::DVFConfig; use crate::dvf::parse::ValidationError; -use alloy::primitives::{Address, B256, U256}; +use alloy::primitives::{Address, B256, U256, Bytes}; use alloy_rpc_types_trace::parity::{Action, TraceOutput, LocalizedTransactionTrace}; -use alloy_rpc_types_trace::geth::{CallFrame, DefaultFrame, DiffMode}; -use alloy::rpc::types::{TransactionReceipt, Log, Block, Transaction}; +use alloy_rpc_types_trace::geth::{CallFrame, DefaultFrame, StructLog, DiffMode}; +use alloy::rpc::types::{TransactionReceipt, Log, Block, Transaction, EIP1186AccountProofResponse}; + +use reth_trie::root; const NUM_STORAGE_QUERIES: u64 = 32; const LARGE_BLOCK_RANGE: u64 = 100000; +mod pathological_rpc_deserde { + use serde::{self, Deserialize}; + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: super::Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + u64::from_str_radix(s.trim_start_matches("0x"), 16).map_err(serde::de::Error::custom) + } +} + +// @note Some rpc returns gas in hex string +// Copy pasted the alloy DefaultFrame with customized deserde impl +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct IntermediateDefaultFrame { + /// Whether the transaction failed + pub failed: bool, + /// How much gas was used. + #[serde(deserialize_with = "pathological_rpc_deserde::deserialize")] + pub gas: u64, + /// Output of the transaction + pub return_value: Bytes, + /// Recorded traces of the transaction + pub struct_logs: Vec, +} + +impl From for TraceWithAddress { + + fn from(x: IntermediateTraceWithAddress) -> Self { + let df = DefaultFrame { + failed: x.trace.failed, + gas: x.trace.gas, + return_value: x.trace.return_value, + struct_logs: x.trace.struct_logs, + }; + TraceWithAddress { + trace: df, + address: x.address, + tx_id: x.tx_id, + } + } +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct IntermediateTraceWithAddress { + pub trace: IntermediateDefaultFrame, + pub address: Address, + pub tx_id: String, +} + #[derive(Debug, Serialize, Deserialize, Clone)] pub struct TraceWithAddress { pub trace: DefaultFrame, @@ -155,7 +209,7 @@ fn extract_create_call_frame( ) -> Result { if call_frame.typ.starts_with("CREATE") && call_frame.error.is_none() - && call_frame.to.as_ref().and_then(|to| Some(to)) == Some(address) + && call_frame.to.as_ref() == Some(address) { return Ok(call_frame.clone()); } @@ -241,7 +295,7 @@ fn extract_create_addresses_from_call_frame( is_first: bool, ) -> Result<(), ValidationError> { if !is_first && call_frame.typ.starts_with("CREATE") { - let rec = call_frame.to.as_ref().and_then(|to| Some(to)); + let rec = call_frame.to.as_ref(); match rec { Some(addr) => addresses.push(*addr), None => { @@ -521,6 +575,28 @@ pub fn get_block_number_for_tx( )) } +// Not every rpc supports eth_getAccount. +// So we have to retrieve the account by querying an empty storage proof +pub fn get_eth_account_at_block(config: &DVFConfig, account: &Address, block: u64) -> Result { + let block_hex = format!("{:#x}", block); + let request_body = json!({ + "jsonrpc": "2.0", + "method": "eth_getProof", + "params": [ + account, + [], // slots + block_hex, + ], + "id": 1 + }); + + let result = send_blocking_web3_post(config, &request_body)?; + + let proof_resp: EIP1186AccountProofResponse = serde_json::from_value(result)?; + + Ok(proof_resp.storage_hash) +} + fn get_deployment_tx_from_blockscout( config: &DVFConfig, address: &Address, @@ -743,7 +819,7 @@ pub fn get_all_txs_for_contract( // Ignores reverting executions fn call_frame_contains(call_frame: &CallFrame, address: &Address) -> bool { if call_frame.error.is_none() - && call_frame.to.as_ref().and_then(|to| Some(to)) == Some(address) + && call_frame.to.as_ref() == Some(address) { return true; } @@ -1262,9 +1338,16 @@ impl StorageSnapshot { // And diffs for all txs if let Ok(all_diffs) = get_many_diff_traces(config, &tx_hashes) { // And compute snapshot from there - Self::snapshot_from_diff_traces(&all_diffs, address) + let snapshot = Self::snapshot_from_diff_traces(&all_diffs, address); + // verify snapshot with account storage merkle root + Self::validate_snapshot_with_mpt_root(config, &snapshot, address, init_block_num); + snapshot + } else { - Self::snapshot_from_tx_ids(config, address, &tx_hashes)? + let snapshot = Self::snapshot_from_tx_ids(config, address, &tx_hashes)?; + // verify snapshot with account storage merkle root + Self::validate_snapshot_with_mpt_root(config, &snapshot, address, init_block_num); + snapshot } }; debug!("Storage Snapshot: {:?}", snapshot); @@ -1275,6 +1358,25 @@ impl StorageSnapshot { }) } + // Reconstruct and verify the account storage root + pub fn validate_snapshot_with_mpt_root(config: &DVFConfig, snapshot: &HashMap, address: &Address, block_num: u64) { + // retrieve account info from rpc + let account_storage_root = get_eth_account_at_block(config, address, block_num).unwrap(); + + // snapshot type casting + let snapshot: HashMap = snapshot + .iter() + .map( + |(k, v)| + (B256::from(*k), U256::from_be_slice(v.as_slice())) + ) + .collect(); + + let reconstructed_root = root::storage_root_unhashed(snapshot); + + assert_eq!(reconstructed_root, account_storage_root); + } + fn init_unused_parts(snapshot: &HashMap) -> HashMap { let mut unused_parts: HashMap = HashMap::new(); for slot in snapshot.keys() { @@ -1703,6 +1805,7 @@ impl StorageSnapshot { #[cfg(test)] mod tests { use std::str::FromStr; + use reth_trie::root; use super::*; use env_logger; @@ -1780,22 +1883,57 @@ mod tests { ); } + #[test] + fn test_validate_snapshot_with_merkle_root() { + init(); + let address = Address::from_str("0x27dab51C2c5B6AF23DF64143c61ffCFa36F35E6d").unwrap(); + let mut config = match DVFConfig::from_env(None) { + Ok(config) => config, + Err(err) => { + println!("{}", err); + assert!(false); + return; + } + }; + config.set_chain_id(1).unwrap(); + + let init_block_num = get_eth_block_number(&config).unwrap() - 1; + + let account_storage_root = get_eth_account_at_block(&config, &address, init_block_num).unwrap(); + + let snapshot: HashMap, [u8; 32]> = get_eth_storage_snapshot(&config, &address, init_block_num).unwrap(); + let snapshot: HashMap = snapshot + .into_iter() + .map( + |(k, v)| + (B256::from(k), U256::from_be_slice(v.as_slice())) + ) + .collect(); + + let reconstructed_root = root::storage_root_unhashed(snapshot); + + println!("expected: {:?}", account_storage_root); + println!("reconstructed: {:?}", reconstructed_root); + + assert_eq!(account_storage_root, reconstructed_root); + } + #[test] fn test_snapshot_get() { init(); let slots: Vec = vec![ U256::from_str_radix( - "0x0000000000000000000000000000000000000000000000000000000000000002", + "0000000000000000000000000000000000000000000000000000000000000002", 16, ) .unwrap(), U256::from_str_radix( - "0x0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", 16, ) .unwrap(), U256::from_str_radix( - "0x000000000000000000000000000000000000000000000000000000000000000a", + "000000000000000000000000000000000000000000000000000000000000000a", 16, ) .unwrap(), @@ -1819,7 +1957,7 @@ mod tests { assert_eq!( snapshot.get_slot( &U256::from_str_radix( - "0x0000000000000000000000000000000000000000000000000000000000000123", + "0000000000000000000000000000000000000000000000000000000000000123", 16 ) .unwrap(), @@ -1831,7 +1969,7 @@ mod tests { assert_eq!( snapshot.get_slot( &U256::from_str_radix( - "0x0000000000000000000000000000000000000000000000000000000000000123", + "0000000000000000000000000000000000000000000000000000000000000123", 16 ) .unwrap(), @@ -1843,7 +1981,7 @@ mod tests { assert_eq!( snapshot.get_slot( &U256::from_str_radix( - "0x0000000000000000000000000000000000000000000000000000000000000123", + "0000000000000000000000000000000000000000000000000000000000000123", 16 ) .unwrap(), diff --git a/src/cached_proxy.rs b/src/cached_proxy.rs index 5989be8..7a3169c 100644 --- a/src/cached_proxy.rs +++ b/src/cached_proxy.rs @@ -1,6 +1,6 @@ use actix_web::{web, web::get, web::post, App, HttpRequest, HttpServer, Responder}; use clap::{Arg, Command}; -use ethers::utils::keccak256; +use alloy::primitives::keccak256; use reqwest::{header::HeaderMap, Url}; use serde_json::Value; use std::fs::{create_dir_all, File}; diff --git a/src/dvf.rs b/src/dvf.rs index 6b696d3..4150ce2 100644 --- a/src/dvf.rs +++ b/src/dvf.rs @@ -4,6 +4,7 @@ use std::path::{Path, PathBuf}; use std::process::exit; use std::str::FromStr; +use alloy_dyn_abi::EventExt; use clap::{value_parser, Arg, ArgAction, ArgMatches, Command, SubCommand}; use colored::Colorize; use console::style; @@ -17,8 +18,8 @@ use dvf_libs::state::contract_state::ContractState; use dvf_libs::state::forge_inspect::{self, StateVariable, TypeDescription}; use dvf_libs::utils::pretty::PrettyPrinter; use dvf_libs::web3; -use ethers::abi::{Event, RawLog}; -use ethers::types::{Address, H256}; +use alloy::json_abi::Event; +use alloy::primitives::{Address, B256}; use indicatif::ProgressBar; use prettytable::{row, Table}; use scanf::sscanf; @@ -206,13 +207,14 @@ fn validate_dvf( #[allow(clippy::needless_range_loop)] for i in 0..seen_events.len() { - if seen_events[i].topics != critical_event.occurrences[i].topics { + let log_inner = &seen_events[i].inner; + if log_inner.topics() != critical_event.occurrences[i].topics { return Err(ValidationError::Invalid(format!( "Mismatching topics for event occurrence {} of {}.", i, critical_event.sig ))); } - if seen_events[i].data != critical_event.occurrences[i].data { + if log_inner.data.data != critical_event.occurrences[i].data { return Err(ValidationError::Invalid(format!( "Mismatching data for event occurrence {} of {}.", i, critical_event.sig @@ -290,7 +292,7 @@ fn is_valid_path(val: &str) -> Result<(), String> { fn is_valid_address(val: &str) -> Result<(), String> { match Address::from_str(val) { Ok(a) => { - if a != Address::zero() { + if a != Address::ZERO { Ok(()) } else { Err(String::from("Zero is not a valid address.")) @@ -992,11 +994,11 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { let all_events = match &imp_project_info { None => project_info.events.clone(), Some(imp_project) => { - let mut set_of_sigs: HashSet = HashSet::new(); + let mut set_of_sigs: HashSet = HashSet::new(); let mut res: Vec = vec![]; for eventlist in [&project_info.events, &imp_project.events] { for event in eventlist { - let sig = event.signature(); + let sig = event.selector(); if set_of_sigs.contains(&sig) { info!( "Warning. Event {} omitted, as it is already known.", @@ -1019,16 +1021,17 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { for abi_event in &all_events { let sig = PrettyPrinter::event_to_string(abi_event); debug!("Found the following event: {}", sig); - let topic0 = abi_event.signature(); + let topic0 = abi_event.selector(); debug!("Topic0: {:?}", topic0); let mut table_head = false; // Collect Occurrences let mut occurrences: Vec = vec![]; for seen_event in &seen_events { - if seen_event.topics.first() == Some(&topic0) { - let decoded_log = abi_event.parse_log(RawLog::from(seen_event.clone()))?; - let pretty_event = pretty_printer.pretty_event_params(&decoded_log, true); + if seen_event.topic0() == Some(&topic0) { + let log_inner = &seen_event.inner; + let decoded_event = abi_event.decode_log(log_inner, true)?; + let pretty_event = pretty_printer.pretty_event_params(abi_event, &decoded_event, true); // Add Event Name to table if !table_head { @@ -1039,8 +1042,8 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { event_table.add_row(row![format!("- {}", pretty_event)]); let occurrence = parse::DVFEventOccurrence { - topics: seen_event.topics.clone(), - data: seen_event.data.clone(), + topics: log_inner.data.topics().to_vec(), + data: log_inner.data.data.clone(), }; occurrences.push(occurrence); covered_events += 1; @@ -1061,20 +1064,21 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { seen_events.len(), covered_events ); - let used_topics_0: HashSet = - all_events.iter().map(|e| e.signature()).collect(); - let all_topics_0: HashSet = seen_events + let used_topics_0: HashSet = + all_events.iter().map(|e| e.selector()).collect(); + let all_topics_0: HashSet = seen_events .iter() - .map(|e| *e.topics.first().unwrap()) + .map(|e| *e.topic0().unwrap()) .collect(); for unused_topic in all_topics_0.difference(&used_topics_0) { // Collect Occurrences let mut occurrences: Vec = vec![]; for seen_event in &seen_events { - if seen_event.topics.first() == Some(unused_topic) { + let log_inner = &seen_event.inner; + if seen_event.topic0() == Some(unused_topic) { let occurrence = parse::DVFEventOccurrence { - topics: seen_event.topics.clone(), - data: seen_event.data.clone(), + topics: log_inner.data.topics().to_vec(), + data: log_inner.data.data.clone(), }; occurrences.push(occurrence); } @@ -1361,14 +1365,15 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { let num_shared = std::cmp::min(seen_events.len(), critical_event.occurrences.len()); #[allow(clippy::needless_range_loop)] for i in 0..num_shared { - if seen_events[i].topics != critical_event.occurrences[i].topics { + let log_innner = &seen_events[i].inner; + if log_innner.topics() != critical_event.occurrences[i].topics { println!( "Mismatching topics for event occurrence {} of {}.", i, critical_event.sig ); replace_events = true; } - if seen_events[i].data != critical_event.occurrences[i].data { + if log_innner.data.data != critical_event.occurrences[i].data { println!( "Mismatching data for event occurrence {} of {}.", i, critical_event.sig @@ -1380,9 +1385,10 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { // Collect Occurrences let mut occurrences: Vec = vec![]; for seen_event in &seen_events { + let log_inner = &seen_event.inner; let occurrence = parse::DVFEventOccurrence { - topics: seen_event.topics.clone(), - data: seen_event.data.clone(), + topics: log_inner.data.topics().to_vec(), + data: log_inner.data.data.clone(), }; occurrences.push(occurrence); } diff --git a/src/fetch.rs b/src/fetch.rs index 2af1527..e612dbe 100644 --- a/src/fetch.rs +++ b/src/fetch.rs @@ -13,11 +13,11 @@ use clap::ArgMatches; use clap::{App, Arg, ArgAction}; use dvf_libs::dvf::config::DVFConfig; use dvf_libs::dvf::parse::{ValidationError, CURRENT_VERSION}; -use ethers_core::abi::Address; -use ethers_etherscan::contract::SourceCodeEntry; -use ethers_etherscan::errors::EtherscanError; -use ethers_etherscan::Client; -use ethers_solc::artifacts::Settings; +use alloy::primitives::Address; +use foundry_block_explorers::contract::{SourceCodeEntry, SourceCodeMetadata}; +use foundry_block_explorers::Client; +use foundry_block_explorers::errors::EtherscanError; +use foundry_compilers::artifacts::Settings; use semver::Version; use tokio::runtime::Runtime; use toml::Table; @@ -355,17 +355,17 @@ fn fetch(matches: &ArgMatches) -> Result<(), ValidationError> { // Write sources // Check what kind of Etherscan Response we have (Single file, Multi file, ...) match &metadata.items[0].source_code { - ethers_etherscan::contract::SourceCodeMetadata::SourceCode(source_str) => { + SourceCodeMetadata::SourceCode(source_str) => { let sol_path = foundry_path .join("src") .join(&metadata.items[0].contract_name) .with_extension("sol"); fs::write(sol_path, source_str).expect("Unable to write file"); } - ethers_etherscan::contract::SourceCodeMetadata::Sources(sources) => { + SourceCodeMetadata::Sources(sources) => { parse_sources(sources, foundry_path); } - ethers_etherscan::contract::SourceCodeMetadata::Metadata { + SourceCodeMetadata::Metadata { sources, settings, .. } => { // Parse settings diff --git a/tests/test_bytecode_only_e2e.rs b/tests/test_bytecode_only_e2e.rs index afd1918..3430862 100644 --- a/tests/test_bytecode_only_e2e.rs +++ b/tests/test_bytecode_only_e2e.rs @@ -4,12 +4,12 @@ mod tests { use assert_cmd::Command; use dvf_libs::dvf::config::DVFConfig; use dvf_libs::utils::pretty::ResolvedAddress; - use ethers::types::Address; use std::collections::HashMap; - use std::str::FromStr; use std::thread::sleep; use std::time::Duration; use tempfile::TempDir; + use std::str::FromStr; + use alloy::primitives::Address; #[test] fn test_invalid_bytecode() { diff --git a/tests/test_decoding.rs b/tests/test_decoding.rs index 74781c8..b166d32 100644 --- a/tests/test_decoding.rs +++ b/tests/test_decoding.rs @@ -10,7 +10,7 @@ mod tests { use dvf_libs::state::contract_state::ContractState; use dvf_libs::state::forge_inspect; use dvf_libs::utils::pretty::PrettyPrinter; - use dvf_libs::web3::{StorageSnapshot, TraceWithAddress}; + use dvf_libs::web3::{StorageSnapshot, TraceWithAddress, IntermediateTraceWithAddress}; use prettytable::Table; fn generate_results( @@ -58,7 +58,8 @@ mod tests { let path = format!("./tests/data/trace_{}.json", contract_name); println!("Reading {}", path); let trace_str = fs::read_to_string(&path).unwrap(); - let trace_w_a: TraceWithAddress = serde_json::from_str(&trace_str).unwrap(); + let trace_w_a: IntermediateTraceWithAddress = serde_json::from_str(&trace_str).unwrap(); + let trace_w_a: TraceWithAddress = trace_w_a.into(); let empty_config = DVFConfig::default(); let mut snapshot = diff --git a/tests/test_end_to_end.rs b/tests/test_end_to_end.rs index 83e5b1e..d6e9416 100644 --- a/tests/test_end_to_end.rs +++ b/tests/test_end_to_end.rs @@ -4,7 +4,6 @@ mod tests { use assert_cmd::Command; use dvf_libs::dvf::config::DVFConfig; use dvf_libs::dvf::parse::CompleteDVF; - use ethers_core::utils::{Anvil, AnvilInstance}; use std::fs::metadata; use std::fs::File; use std::fs::OpenOptions; @@ -16,6 +15,7 @@ mod tests { use std::thread::sleep; use std::time::Duration; use tempfile::NamedTempFile; + use alloy_node_bindings::{AnvilInstance, Anvil}; #[derive(PartialEq, Clone)] enum LocalClientType {