From ec96410546d7e8fb71c1187140e194a29350540f Mon Sep 17 00:00:00 2001 From: Omar Abdulla Date: Sun, 28 Sep 2025 16:52:43 +0300 Subject: [PATCH] Implement a solution for the pre-fund account limit --- Cargo.lock | 1 + crates/config/src/lib.rs | 153 ++++++++++++++++++++++++++++- crates/core/src/main.rs | 1 + crates/core/src/pool.rs | 10 +- crates/node-interaction/src/lib.rs | 3 + crates/node/Cargo.toml | 1 + crates/node/src/geth.rs | 4 + crates/node/src/lighthouse_geth.rs | 83 ++++++++++------ crates/node/src/substrate.rs | 4 + 9 files changed, 228 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bae4906..97233fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4618,6 +4618,7 @@ version = "0.1.0" dependencies = [ "alloy", "anyhow", + "futures", "revive-common", "revive-dt-common", "revive-dt-config", diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index b1551bc..6621c0d 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -236,6 +236,78 @@ pub struct TestExecutionContext { pub report_configuration: ReportConfiguration, } +#[derive(Clone, Debug, Parser, Serialize)] +pub struct BenchmarkingContext { + /// The working directory that the program will use for all of the temporary artifacts needed at + /// runtime. + /// + /// If not specified, then a temporary directory will be created and used by the program for all + /// temporary artifacts. + #[clap( + short, + long, + default_value = "", + value_hint = ValueHint::DirPath, + )] + pub working_directory: WorkingDirectoryConfiguration, + + /// The set of platforms that the differential tests should run on. + #[arg( + short = 'p', + long = "platform", + default_values = ["geth-evm-solc", "revive-dev-node-polkavm-resolc"] + )] + pub platforms: Vec, + + /// A list of test corpus JSON files to be tested. + #[arg(long = "corpus", short)] + pub corpus: Vec, + + /// Configuration parameters for the solc compiler. + #[clap(flatten, next_help_heading = "Solc Configuration")] + pub solc_configuration: SolcConfiguration, + + /// Configuration parameters for the resolc compiler. + #[clap(flatten, next_help_heading = "Resolc Configuration")] + pub resolc_configuration: ResolcConfiguration, + + /// Configuration parameters for the geth node. + #[clap(flatten, next_help_heading = "Geth Configuration")] + pub geth_configuration: GethConfiguration, + + /// Configuration parameters for the lighthouse node. + #[clap(flatten, next_help_heading = "Lighthouse Configuration")] + pub lighthouse_configuration: KurtosisConfiguration, + + /// Configuration parameters for the Kitchensink. + #[clap(flatten, next_help_heading = "Kitchensink Configuration")] + pub kitchensink_configuration: KitchensinkConfiguration, + + /// Configuration parameters for the Revive Dev Node. + #[clap(flatten, next_help_heading = "Revive Dev Node Configuration")] + pub revive_dev_node_configuration: ReviveDevNodeConfiguration, + + /// Configuration parameters for the Eth Rpc. + #[clap(flatten, next_help_heading = "Eth RPC Configuration")] + pub eth_rpc_configuration: EthRpcConfiguration, + + /// Configuration parameters for the wallet. + #[clap(flatten, next_help_heading = "Wallet Configuration")] + pub wallet_configuration: WalletConfiguration, + + /// Configuration parameters for concurrency. + #[clap(flatten, next_help_heading = "Concurrency Configuration")] + pub concurrency_configuration: ConcurrencyConfiguration, + + /// Configuration parameters for the compilers and compilation. + #[clap(flatten, next_help_heading = "Compilation Configuration")] + pub compilation_configuration: CompilationConfiguration, + + /// Configuration parameters for the report. + #[clap(flatten, next_help_heading = "Report Configuration")] + pub report_configuration: ReportConfiguration, +} + impl Default for TestExecutionContext { fn default() -> Self { Self::parse_from(["execution-context"]) @@ -320,6 +392,84 @@ impl AsRef for TestExecutionContext { } } +impl Default for BenchmarkingContext { + fn default() -> Self { + Self::parse_from(["execution-context"]) + } +} + +impl AsRef for BenchmarkingContext { + fn as_ref(&self) -> &WorkingDirectoryConfiguration { + &self.working_directory + } +} + +impl AsRef for BenchmarkingContext { + fn as_ref(&self) -> &SolcConfiguration { + &self.solc_configuration + } +} + +impl AsRef for BenchmarkingContext { + fn as_ref(&self) -> &ResolcConfiguration { + &self.resolc_configuration + } +} + +impl AsRef for BenchmarkingContext { + fn as_ref(&self) -> &GethConfiguration { + &self.geth_configuration + } +} + +impl AsRef for BenchmarkingContext { + fn as_ref(&self) -> &KurtosisConfiguration { + &self.lighthouse_configuration + } +} + +impl AsRef for BenchmarkingContext { + fn as_ref(&self) -> &KitchensinkConfiguration { + &self.kitchensink_configuration + } +} + +impl AsRef for BenchmarkingContext { + fn as_ref(&self) -> &ReviveDevNodeConfiguration { + &self.revive_dev_node_configuration + } +} + +impl AsRef for BenchmarkingContext { + fn as_ref(&self) -> &EthRpcConfiguration { + &self.eth_rpc_configuration + } +} + +impl AsRef for BenchmarkingContext { + fn as_ref(&self) -> &WalletConfiguration { + &self.wallet_configuration + } +} + +impl AsRef for BenchmarkingContext { + fn as_ref(&self) -> &ConcurrencyConfiguration { + &self.concurrency_configuration + } +} + +impl AsRef for BenchmarkingContext { + fn as_ref(&self) -> &CompilationConfiguration { + &self.compilation_configuration + } +} + +impl AsRef for BenchmarkingContext { + fn as_ref(&self) -> &ReportConfiguration { + &self.report_configuration + } +} + /// A set of configuration parameters for Solc. #[derive(Clone, Debug, Parser, Serialize)] pub struct SolcConfiguration { @@ -499,7 +649,7 @@ pub struct WalletConfiguration { /// This argument controls which private keys the nodes should have access to and be added to /// its wallet signers. With a value of N, private keys (0, N] will be added to the signer set /// of the node. - #[clap(long = "wallet.additional-keys", default_value_t = 200)] + #[clap(long = "wallet.additional-keys", default_value_t = 100_000)] additional_keys: usize, /// The wallet object that will be used. @@ -629,6 +779,7 @@ impl AsRef for WorkingDirectoryConfiguration { impl Default for WorkingDirectoryConfiguration { fn default() -> Self { TempDir::new() + .map(|tempdir| dbg!(tempdir.dont_delete_on_drop())) .map(Arc::new) .map(Self::TemporaryDirectory) .expect("Failed to create the temporary directory") diff --git a/crates/core/src/main.rs b/crates/core/src/main.rs index 3d569ff..5406f52 100644 --- a/crates/core/src/main.rs +++ b/crates/core/src/main.rs @@ -147,6 +147,7 @@ async fn run_driver( let mut nodes = Vec::<(&dyn Platform, NodePool)>::new(); for platform in platforms.into_iter() { let pool = NodePool::new(Context::ExecuteTests(Box::new(context.clone())), platform) + .await .inspect_err(|err| { error!( ?err, diff --git a/crates/core/src/pool.rs b/crates/core/src/pool.rs index 06fb2be..afaa791 100644 --- a/crates/core/src/pool.rs +++ b/crates/core/src/pool.rs @@ -16,7 +16,7 @@ pub struct NodePool { impl NodePool { /// Create a new Pool. This will start as many nodes as there are workers in `config`. - pub fn new(context: Context, platform: &dyn Platform) -> anyhow::Result { + pub async fn new(context: Context, platform: &dyn Platform) -> anyhow::Result { let concurrency_configuration = AsRef::::as_ref(&context); let nodes = concurrency_configuration.number_of_nodes; @@ -38,6 +38,14 @@ impl NodePool { ); } + let pre_transactions_tasks = nodes + .iter_mut() + .map(|node| node.pre_transactions()) + .collect::>(); + futures::future::try_join_all(pre_transactions_tasks) + .await + .context("Failed to run the pre-transactions task")?; + Ok(Self { nodes, next: Default::default(), diff --git a/crates/node-interaction/src/lib.rs b/crates/node-interaction/src/lib.rs index 57b0b67..86804dc 100644 --- a/crates/node-interaction/src/lib.rs +++ b/crates/node-interaction/src/lib.rs @@ -14,6 +14,9 @@ use revive_dt_format::traits::ResolverApi; /// An interface for all interactions with Ethereum compatible nodes. #[allow(clippy::type_complexity)] pub trait EthereumNode { + /// A function to run post spawning the nodes and before any transactions are run on the node. + fn pre_transactions(&mut self) -> Pin> + '_>>; + fn id(&self) -> usize; /// Returns the nodes connection string. diff --git a/crates/node/Cargo.toml b/crates/node/Cargo.toml index d482621..9703794 100644 --- a/crates/node/Cargo.toml +++ b/crates/node/Cargo.toml @@ -11,6 +11,7 @@ rust-version.workspace = true [dependencies] anyhow = { workspace = true } alloy = { workspace = true } +futures = { workspace = true } tracing = { workspace = true } tokio = { workspace = true } diff --git a/crates/node/src/geth.rs b/crates/node/src/geth.rs index 57e299a..bf498e4 100644 --- a/crates/node/src/geth.rs +++ b/crates/node/src/geth.rs @@ -266,6 +266,10 @@ impl GethNode { } impl EthereumNode for GethNode { + fn pre_transactions(&mut self) -> Pin> + '_>> { + Box::pin(async move { Ok(()) }) + } + fn id(&self) -> usize { self.id as _ } diff --git a/crates/node/src/lighthouse_geth.rs b/crates/node/src/lighthouse_geth.rs index 7029c68..c9cf287 100644 --- a/crates/node/src/lighthouse_geth.rs +++ b/crates/node/src/lighthouse_geth.rs @@ -39,8 +39,10 @@ use alloy::{ EIP1186AccountProofResponse, TransactionRequest, trace::geth::{DiffMode, GethDebugTracingOptions, PreStateConfig, PreStateFrame}, }, + signers::local::PrivateKeySigner, }; use anyhow::Context as _; +use futures::future; use revive_common::EVMVersion; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_with::serde_as; @@ -91,6 +93,9 @@ pub struct LighthouseGethNode { /* Spawned Processes */ process: Option, + /* Prefunded Account Information */ + prefunded_account_private_key: PrivateKeySigner, + /* Provider Related Fields */ wallet: Arc, nonce_manager: CachedNonceManager, @@ -160,6 +165,9 @@ impl LighthouseGethNode { /* Spawned Processes */ process: None, + /* Prefunded Account Information */ + prefunded_account_private_key: PrivateKeySigner::random(), + /* Provider Related Fields */ wallet: wallet.clone(), nonce_manager: Default::default(), @@ -168,7 +176,7 @@ impl LighthouseGethNode { } /// Create the node directory and call `geth init` to configure the genesis. - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] + #[instrument(level = "info", skip_all, fields(lighthouse_node_id = self.id))] fn init(&mut self, _: Genesis) -> anyhow::Result<&mut Self> { self.init_directories() .context("Failed to initialize the directories of the Lighthouse Geth node.")?; @@ -228,14 +236,8 @@ impl LighthouseGethNode { num_validator_keys_per_node: 64, genesis_delay: 10, prefunded_accounts: { - let map = NetworkWallet::::signer_addresses(&self.wallet) - .map(|address| { - ( - address, - GenesisAccount::default() - .with_balance(INITIAL_BALANCE.try_into().unwrap()), - ) - }) + let map = std::iter::once(self.prefunded_account_private_key.address()) + .map(|address| (address, GenesisAccount::default().with_balance(U256::MAX))) .collect::>(); serde_json::to_string(&map).unwrap() }, @@ -261,7 +263,7 @@ impl LighthouseGethNode { } /// Spawn the go-ethereum node child process. - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] + #[instrument(level = "info", skip_all, fields(lighthouse_node_id = self.id))] fn spawn_process(&mut self) -> anyhow::Result<&mut Self> { let process = Process::new( None, @@ -346,9 +348,30 @@ impl LighthouseGethNode { .await .context("Failed to create the provider for Kurtosis") } + + /// Funds all of the accounts in the Ethereum wallet from the initially funded account. + async fn fund_all_accounts(&self) -> anyhow::Result<()> { + let tasks = NetworkWallet::::signer_addresses(self.wallet.as_ref()) + .enumerate() + .map(|(nonce, address)| async move { + let transaction = TransactionRequest::default() + .from(self.prefunded_account_private_key.address()) + .to(address) + .nonce(nonce as _) + .value(INITIAL_BALANCE.try_into().unwrap()); + self.execute_transaction(transaction).await + }) + .collect::>(); + future::try_join_all(tasks).await?; + Ok(()) + } } impl EthereumNode for LighthouseGethNode { + fn pre_transactions(&mut self) -> Pin> + '_>> { + Box::pin(async move { self.fund_all_accounts().await }) + } + fn id(&self) -> usize { self.id as _ } @@ -360,7 +383,7 @@ impl EthereumNode for LighthouseGethNode { #[instrument( level = "info", skip_all, - fields(geth_node_id = self.id, connection_string = self.connection_string), + fields(lighthouse_node_id = self.id, connection_string = self.connection_string), err, )] fn execute_transaction( @@ -407,7 +430,7 @@ impl EthereumNode for LighthouseGethNode { let provider = Arc::new(provider); poll( Self::RECEIPT_POLLING_DURATION, - PollingWaitBehavior::Constant(Duration::from_millis(200)), + PollingWaitBehavior::Constant(Duration::from_millis(500)), move || { let provider = provider.clone(); async move { @@ -433,7 +456,7 @@ impl EthereumNode for LighthouseGethNode { }) } - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] + #[instrument(level = "info", skip_all, fields(lighthouse_node_id = self.id))] fn trace_transaction( &self, tx_hash: TxHash, @@ -473,7 +496,7 @@ impl EthereumNode for LighthouseGethNode { }) } - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] + #[instrument(level = "info", skip_all, fields(lighthouse_node_id = self.id))] fn state_diff( &self, tx_hash: TxHash, @@ -497,7 +520,7 @@ impl EthereumNode for LighthouseGethNode { }) } - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] + #[instrument(level = "info", skip_all, fields(lighthouse_node_id = self.id))] fn balance_of( &self, address: Address, @@ -512,7 +535,7 @@ impl EthereumNode for LighthouseGethNode { }) } - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] + #[instrument(level = "info", skip_all, fields(lighthouse_node_id = self.id))] fn latest_state_proof( &self, address: Address, @@ -529,7 +552,7 @@ impl EthereumNode for LighthouseGethNode { }) } - // #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] + // #[instrument(level = "info", skip_all, fields(lighthouse_node_id = self.id))] fn resolver( &self, ) -> Pin>> + '_>> { @@ -553,14 +576,14 @@ pub struct LighthouseGethNodeResolver, P: Provider, P: Provider> ResolverApi for LighthouseGethNodeResolver { - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] + #[instrument(level = "info", skip_all, fields(lighthouse_node_id = self.id))] fn chain_id( &self, ) -> Pin> + '_>> { Box::pin(async move { self.provider.get_chain_id().await.map_err(Into::into) }) } - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] + #[instrument(level = "info", skip_all, fields(lighthouse_node_id = self.id))] fn transaction_gas_price( &self, tx_hash: TxHash, @@ -574,7 +597,7 @@ impl, P: Provider> ResolverApi }) } - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] + #[instrument(level = "info", skip_all, fields(lighthouse_node_id = self.id))] fn block_gas_limit( &self, number: BlockNumberOrTag, @@ -589,7 +612,7 @@ impl, P: Provider> ResolverApi }) } - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] + #[instrument(level = "info", skip_all, fields(lighthouse_node_id = self.id))] fn block_coinbase( &self, number: BlockNumberOrTag, @@ -604,7 +627,7 @@ impl, P: Provider> ResolverApi }) } - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] + #[instrument(level = "info", skip_all, fields(lighthouse_node_id = self.id))] fn block_difficulty( &self, number: BlockNumberOrTag, @@ -619,7 +642,7 @@ impl, P: Provider> ResolverApi }) } - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] + #[instrument(level = "info", skip_all, fields(lighthouse_node_id = self.id))] fn block_base_fee( &self, number: BlockNumberOrTag, @@ -639,7 +662,7 @@ impl, P: Provider> ResolverApi }) } - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] + #[instrument(level = "info", skip_all, fields(lighthouse_node_id = self.id))] fn block_hash( &self, number: BlockNumberOrTag, @@ -654,7 +677,7 @@ impl, P: Provider> ResolverApi }) } - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] + #[instrument(level = "info", skip_all, fields(lighthouse_node_id = self.id))] fn block_timestamp( &self, number: BlockNumberOrTag, @@ -669,14 +692,14 @@ impl, P: Provider> ResolverApi }) } - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] + #[instrument(level = "info", skip_all, fields(lighthouse_node_id = self.id))] fn last_block_number(&self) -> Pin> + '_>> { Box::pin(async move { self.provider.get_block_number().await.map_err(Into::into) }) } } impl Node for LighthouseGethNode { - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] + #[instrument(level = "info", skip_all, fields(lighthouse_node_id = self.id))] fn shutdown(&mut self) -> anyhow::Result<()> { if !Command::new(self.kurtosis_binary_path.as_path()) .arg("enclave") @@ -699,13 +722,13 @@ impl Node for LighthouseGethNode { Ok(()) } - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] + #[instrument(level = "info", skip_all, fields(lighthouse_node_id = self.id))] fn spawn(&mut self, genesis: Genesis) -> anyhow::Result<()> { self.init(genesis)?.spawn_process()?; Ok(()) } - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] + #[instrument(level = "info", skip_all, fields(lighthouse_node_id = self.id))] fn version(&self) -> anyhow::Result { let output = Command::new(&self.kurtosis_binary_path) .arg("version") @@ -722,7 +745,7 @@ impl Node for LighthouseGethNode { } impl Drop for LighthouseGethNode { - #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))] + #[instrument(level = "info", skip_all, fields(lighthouse_node_id = self.id))] fn drop(&mut self) { self.shutdown().expect("Failed to shutdown") } diff --git a/crates/node/src/substrate.rs b/crates/node/src/substrate.rs index b76c46c..f4e61b8 100644 --- a/crates/node/src/substrate.rs +++ b/crates/node/src/substrate.rs @@ -356,6 +356,10 @@ impl SubstrateNode { } impl EthereumNode for SubstrateNode { + fn pre_transactions(&mut self) -> Pin> + '_>> { + Box::pin(async move { Ok(()) }) + } + fn id(&self) -> usize { self.id as _ }