diff --git a/crates/core/src/driver/mod.rs b/crates/core/src/driver/mod.rs index aed3856f..374e88a2 100644 --- a/crates/core/src/driver/mod.rs +++ b/crates/core/src/driver/mod.rs @@ -84,10 +84,28 @@ where task.json_output = Some(output.output.clone()); task.error = output.error; self.contracts.insert(output.input, output.output); + + if let Some(last_output) = self.contracts.values().last() { + if let Some(contracts) = &last_output.contracts { + for (file, contracts_map) in contracts { + for contract_name in contracts_map.keys() { + log::debug!( + "Compiled contract: {} from file: {}", + contract_name, + file + ); + } + } + } else { + log::warn!("Compiled contracts field is None"); + } + } + Report::compilation(span, T::config_id(), task); Ok(()) } Err(error) => { + log::error!("Failed to compile contract: {:?}", error.to_string()); task.error = Some(error.to_string()); Err(error) } @@ -99,13 +117,40 @@ where input: &Input, node: &T::Blockchain, ) -> anyhow::Result<(GethTrace, DiffMode)> { - let receipt = node.execute_transaction(input.legacy_transaction( - self.config.network_id, - 0, - &self.deployed_contracts, - )?)?; + log::trace!("Calling execute_input for input: {:?}", input); + + let nonce = node.fetch_add_nonce(input.caller)?; + + log::debug!( + "Nonce calculated on the execute contract, calculated nonce {}, for contract {}, having address {} on node: {}", + &nonce, + &input.instance, + &input.caller, + std::any::type_name::() + ); + + let tx = + match input.legacy_transaction(self.config.network_id, nonce, &self.deployed_contracts) + { + Ok(tx) => tx, + Err(err) => { + log::error!("Failed to construct legacy transaction: {:?}", err); + return Err(err); + } + }; + + log::trace!("Executing transaction for input: {:?}", input); + + let receipt = match node.execute_transaction(tx) { + Ok(receipt) => receipt, + Err(err) => { + log::error!("Failed to execute transaction: {:?}", err); + return Err(err); + } + }; log::trace!("Transaction receipt: {:?}", receipt); + let trace = node.trace_transaction(receipt.clone())?; log::trace!("Trace result: {:?}", trace); @@ -115,14 +160,28 @@ where } pub fn deploy_contracts(&mut self, input: &Input, node: &T::Blockchain) -> anyhow::Result<()> { + log::debug!( + "Deploying contracts {}, having address {} on node: {}", + &input.instance, + &input.caller, + std::any::type_name::() + ); for output in self.contracts.values() { let Some(contract_map) = &output.contracts else { - log::debug!("No contracts in output — skipping deployment for this input."); + log::debug!( + "No contracts in output — skipping deployment for this input {}", + &input.instance + ); continue; }; for contracts in contract_map.values() { for (contract_name, contract) in contracts { + log::debug!( + "Contract name is: {:?} and the input name is: {:?}", + &contract_name, + &input.instance + ); if contract_name != &input.instance { continue; } @@ -134,24 +193,47 @@ where .map(|b| b.object.clone()); let Some(code) = bytecode else { - anyhow::bail!("no bytecode for contract `{}`", contract_name); + log::error!("no bytecode for contract {}", contract_name); + continue; }; + let nonce = node.fetch_add_nonce(input.caller)?; + + log::debug!( + "Calculated nonce {}, for contract {}, having address {} on node: {}", + &nonce, + &input.instance, + &input.caller, + std::any::type_name::() + ); + let tx = TransactionRequest::default() .with_from(input.caller) .with_to(Address::ZERO) .with_input(Bytes::from(code.clone())) - .with_gas_price(20_000_000_000) - .with_gas_limit(20_000_000_000) + .with_gas_price(5_000_000) + .with_gas_limit(5_000_000) .with_chain_id(self.config.network_id) - .with_nonce(0); + .with_nonce(nonce); + + let receipt = match node.execute_transaction(tx) { + Ok(receipt) => receipt, + Err(err) => { + log::error!( + "Failed to execute transaction when deploying the contract: {:?}, {:?}", + &contract_name, + err + ); + return Err(err); + } + }; - let receipt = node.execute_transaction(tx)?; let Some(address) = receipt.contract_address else { - anyhow::bail!( - "contract `{}` deployment did not return an address", + log::error!( + "contract {} deployment did not return an address", contract_name ); + continue; }; self.deployed_contracts @@ -161,6 +243,8 @@ where } } + log::debug!("Available contracts: {:?}", self.deployed_contracts.keys()); + Ok(()) } } @@ -227,9 +311,11 @@ where for case in &self.metadata.cases { for input in &case.inputs { + log::debug!("Starting deploying contract {}", &input.instance); leader_state.deploy_contracts(input, self.leader_node)?; follower_state.deploy_contracts(input, self.follower_node)?; + log::debug!("Starting executing contract {}", &input.instance); let (_, leader_diff) = leader_state.execute_input(input, self.leader_node)?; let (_, follower_diff) = follower_state.execute_input(input, self.follower_node)?; diff --git a/crates/format/src/input.rs b/crates/format/src/input.rs index 0631db8c..7c9318de 100644 --- a/crates/format/src/input.rs +++ b/crates/format/src/input.rs @@ -118,8 +118,8 @@ impl Input { .with_to(to) .with_nonce(nonce) .with_chain_id(chain_id) - .with_gas_price(20_000_000_000) - .with_gas_limit(20_000_000_000)) + .with_gas_price(5_000_000) + .with_gas_limit(5_000_000)) } } diff --git a/crates/node-interaction/src/lib.rs b/crates/node-interaction/src/lib.rs index 8e17a266..2006d1bd 100644 --- a/crates/node-interaction/src/lib.rs +++ b/crates/node-interaction/src/lib.rs @@ -1,9 +1,11 @@ //! This crate implements all node interactions. +use alloy::primitives::Address; use alloy::rpc::types::trace::geth::{DiffMode, GethTrace}; use alloy::rpc::types::{TransactionReceipt, TransactionRequest}; use tokio_runtime::TO_TOKIO; +pub mod nonce; mod tokio_runtime; pub mod trace; pub mod transaction; @@ -21,4 +23,7 @@ pub trait EthereumNode { /// Returns the state diff of the transaction hash in the [TransactionReceipt]. fn state_diff(&self, transaction: TransactionReceipt) -> anyhow::Result; + + /// Returns the next available nonce for the given [Address]. + fn fetch_add_nonce(&self, address: Address) -> anyhow::Result; } diff --git a/crates/node-interaction/src/nonce.rs b/crates/node-interaction/src/nonce.rs new file mode 100644 index 00000000..53b73c9a --- /dev/null +++ b/crates/node-interaction/src/nonce.rs @@ -0,0 +1,55 @@ +use std::pin::Pin; + +use alloy::{ + primitives::Address, + providers::{Provider, ProviderBuilder}, +}; +use tokio::sync::oneshot; + +use crate::{TO_TOKIO, tokio_runtime::AsyncNodeInteraction}; + +pub type Task = Pin> + Send>>; + +pub(crate) struct Nonce { + sender: oneshot::Sender>, + task: Task, +} + +impl AsyncNodeInteraction for Nonce { + type Output = anyhow::Result; + + fn split( + self, + ) -> ( + std::pin::Pin + Send>>, + oneshot::Sender, + ) { + (self.task, self.sender) + } +} + +/// This is like `trace_transaction`, just for nonces. +pub fn fetch_onchain_nonce( + connection: String, + wallet: alloy::network::EthereumWallet, + address: Address, +) -> anyhow::Result { + let sender = TO_TOKIO.lock().unwrap().nonce_sender.clone(); + + let (tx, rx) = oneshot::channel(); + let task: Task = Box::pin(async move { + let provider = ProviderBuilder::new() + .wallet(wallet) + .connect(&connection) + .await?; + let onchain = provider.get_transaction_count(address).await?; + Ok(onchain) + }); + + sender + .blocking_send(Nonce { task, sender: tx }) + .expect("not in async context"); + + rx.blocking_recv() + .unwrap_or_else(|err| anyhow::bail!("nonce fetch failed: {err}")) +} diff --git a/crates/node-interaction/src/tokio_runtime.rs b/crates/node-interaction/src/tokio_runtime.rs index d58aa2fc..fccb31f4 100644 --- a/crates/node-interaction/src/tokio_runtime.rs +++ b/crates/node-interaction/src/tokio_runtime.rs @@ -10,6 +10,7 @@ use tokio::spawn; use tokio::sync::{mpsc, oneshot}; use tokio::task::JoinError; +use crate::nonce::Nonce; use crate::trace::Trace; use crate::transaction::Transaction; @@ -33,6 +34,7 @@ pub(crate) trait AsyncNodeInteraction: Send + 'static { pub(crate) struct TokioRuntime { pub(crate) transaction_sender: mpsc::Sender, pub(crate) trace_sender: mpsc::Sender, + pub(crate) nonce_sender: mpsc::Sender, } impl TokioRuntime { @@ -40,11 +42,13 @@ impl TokioRuntime { let rt = Runtime::new().expect("should be able to create the tokio runtime"); let (transaction_sender, transaction_receiver) = mpsc::channel::(1024); let (trace_sender, trace_receiver) = mpsc::channel::(1024); + let (nonce_sender, nonce_receiver) = mpsc::channel::(1024); thread::spawn(move || { rt.block_on(async move { let transaction_task = spawn(interaction::(transaction_receiver)); let trace_task = spawn(interaction::(trace_receiver)); + let nonce_task = spawn(interaction::(nonce_receiver)); if let Err(error) = transaction_task.await { log::error!("tokio transaction task failed: {error}"); @@ -52,12 +56,16 @@ impl TokioRuntime { if let Err(error) = trace_task.await { log::error!("tokio trace transaction task failed: {error}"); } + if let Err(error) = nonce_task.await { + log::error!("tokio nonce task failed: {error}"); + } }); }); Self { transaction_sender, trace_sender, + nonce_sender, } } } diff --git a/crates/node/src/geth.rs b/crates/node/src/geth.rs index bb945444..f35dea5f 100644 --- a/crates/node/src/geth.rs +++ b/crates/node/src/geth.rs @@ -5,13 +5,17 @@ use std::{ io::{BufRead, BufReader, Read, Write}, path::PathBuf, process::{Child, Command, Stdio}, - sync::atomic::{AtomicU32, Ordering}, + sync::{ + Mutex, + atomic::{AtomicU32, Ordering}, + }, thread, time::{Duration, Instant}, }; use alloy::{ network::EthereumWallet, + primitives::{Address, map::HashMap}, providers::{Provider, ProviderBuilder, ext::DebugApi}, rpc::types::{ TransactionReceipt, TransactionRequest, @@ -20,7 +24,8 @@ use alloy::{ }; use revive_dt_config::Arguments; use revive_dt_node_interaction::{ - EthereumNode, trace::trace_transaction, transaction::execute_transaction, + EthereumNode, nonce::fetch_onchain_nonce, trace::trace_transaction, + transaction::execute_transaction, }; use crate::Node; @@ -45,6 +50,7 @@ pub struct Instance { network_id: u64, start_timeout: u64, wallet: EthereumWallet, + nonces: Mutex>, } impl Instance { @@ -198,6 +204,19 @@ impl EthereumNode for Instance { _ => anyhow::bail!("expected a diff mode trace"), } } + + fn fetch_add_nonce(&self, address: Address) -> anyhow::Result { + let connection_string = self.connection_string.clone(); + let wallet = self.wallet.clone(); + + let onchain_nonce = fetch_onchain_nonce(connection_string, wallet, address)?; + + let mut nonces = self.nonces.lock().unwrap(); + let current = nonces.entry(address).or_insert(onchain_nonce); + let value = *current; + *current += 1; + Ok(value) + } } impl Node for Instance { @@ -216,6 +235,7 @@ impl Node for Instance { network_id: config.network_id, start_timeout: config.geth_start_timeout, wallet: config.wallet(), + nonces: Mutex::new(HashMap::new()), } } diff --git a/crates/node/src/kitchensink.rs b/crates/node/src/kitchensink.rs index b6007f45..0285d241 100644 --- a/crates/node/src/kitchensink.rs +++ b/crates/node/src/kitchensink.rs @@ -3,13 +3,17 @@ use std::{ io::BufRead, path::PathBuf, process::{Child, Command, Stdio}, - sync::atomic::{AtomicU32, Ordering}, + sync::{ + Mutex, + atomic::{AtomicU32, Ordering}, + }, time::Duration, }; use alloy::{ hex, network::EthereumWallet, + primitives::{Address, map::HashMap}, providers::{Provider, ProviderBuilder, ext::DebugApi}, rpc::types::{ TransactionReceipt, @@ -22,7 +26,8 @@ use sp_runtime::AccountId32; use revive_dt_config::Arguments; use revive_dt_node_interaction::{ - EthereumNode, trace::trace_transaction, transaction::execute_transaction, + EthereumNode, nonce::fetch_onchain_nonce, trace::trace_transaction, + transaction::execute_transaction, }; use crate::Node; @@ -39,6 +44,7 @@ pub struct KitchensinkNode { base_directory: PathBuf, process_substrate: Option, process_proxy: Option, + nonces: Mutex>, } impl KitchensinkNode { @@ -289,6 +295,19 @@ impl EthereumNode for KitchensinkNode { _ => anyhow::bail!("expected a diff mode trace"), } } + + fn fetch_add_nonce(&self, address: Address) -> anyhow::Result { + let url = self.rpc_url.clone(); + let wallet = self.wallet.clone(); + + let onchain_nonce = fetch_onchain_nonce(url, wallet, address)?; + + let mut nonces = self.nonces.lock().unwrap(); + let current = nonces.entry(address).or_insert(onchain_nonce); + let value = *current; + *current += 1; + Ok(value) + } } impl Node for KitchensinkNode { @@ -306,6 +325,7 @@ impl Node for KitchensinkNode { base_directory, process_substrate: None, process_proxy: None, + nonces: Mutex::new(HashMap::new()), } }