From 78ac7ee38113bb44b14cf495cf9c653dcf5f0edb Mon Sep 17 00:00:00 2001 From: Omar Date: Thu, 4 Dec 2025 16:19:48 +0300 Subject: [PATCH] Fix the Fallback Gas Limiter (#217) * Add code to disable the fallback gas filler * Allow benchmarks driver to await tx receipts * Improve the transaction submission logic * Update Python Script to process Geth benchmarks --- crates/config/src/lib.rs | 17 +++ .../src/differential_benchmarks/driver.rs | 91 ++++++++----- .../differential_benchmarks/entry_point.rs | 1 + .../src/differential_benchmarks/watcher.rs | 17 ++- crates/core/src/lib.rs | 18 ++- crates/node/src/node_implementations/geth.rs | 8 +- .../node_implementations/lighthouse_geth.rs | 9 +- .../src/node_implementations/substrate.rs | 12 +- .../src/node_implementations/zombienet.rs | 14 +- .../src/provider_utils/fallback_gas_filler.rs | 40 +++++- scripts/print_benchmark_metrics_csv.py | 127 ++++++++++++++---- 11 files changed, 269 insertions(+), 85 deletions(-) diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index a12e724..344910a 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -375,6 +375,23 @@ pub struct BenchmarkingContext { #[arg(short = 'r', long = "default-repetition-count", default_value_t = 1000)] pub default_repetition_count: usize, + /// This transaction controls whether the benchmarking driver should await for transactions to + /// be included in a block before moving on to the next transaction in the sequence or not. + /// + /// This behavior is useful in certain cases and not so useful in others. For example, in some + /// repetition block if there's some kind of relationship between txs n and n+1 (for example a + /// mint then a transfer) then you would want to wait for the minting to happen and then move on + /// to the transfers. On the other hand, if there's no relationship between the transactions n + /// and n+1 (e.g., mint and another mint of a different token) then awaiting the first mint to + /// be included in a block might not seem necessary. + /// + /// By default, this behavior is set to false to allow the benchmarking framework to saturate + /// the node's mempool as quickly as possible. However, as explained above, there are cases + /// where it's needed and certain workloads where failure to provide this argument would lead to + /// inaccurate results. + #[arg(long)] + pub await_transaction_inclusion: bool, + /// Configuration parameters for the corpus files to use. #[clap(flatten, next_help_heading = "Corpus Configuration")] pub corpus_configuration: CorpusConfiguration, diff --git a/crates/core/src/differential_benchmarks/driver.rs b/crates/core/src/differential_benchmarks/driver.rs index 5dcc2fb..cc7ffad 100644 --- a/crates/core/src/differential_benchmarks/driver.rs +++ b/crates/core/src/differential_benchmarks/driver.rs @@ -1,6 +1,5 @@ use std::{ collections::HashMap, - ops::ControlFlow, sync::{ Arc, atomic::{AtomicUsize, Ordering}, @@ -13,6 +12,7 @@ use alloy::{ json_abi::JsonAbi, network::{Ethereum, TransactionBuilder}, primitives::{Address, TxHash, U256}, + providers::Provider, rpc::types::{ TransactionReceipt, TransactionRequest, trace::geth::{ @@ -22,12 +22,9 @@ use alloy::{ }, }; use anyhow::{Context as _, Result, bail}; -use futures::TryFutureExt; +use futures::{FutureExt as _, TryFutureExt}; use indexmap::IndexMap; -use revive_dt_common::{ - futures::{PollingWaitBehavior, poll}, - types::PrivateKeyAllocator, -}; +use revive_dt_common::types::PrivateKeyAllocator; use revive_dt_format::{ metadata::{ContractInstance, ContractPathAndIdent}, steps::{ @@ -37,7 +34,7 @@ use revive_dt_format::{ traits::{ResolutionContext, ResolverApi}, }; use tokio::sync::{Mutex, OnceCell, mpsc::UnboundedSender}; -use tracing::{Instrument, Span, debug, error, field::display, info, info_span, instrument}; +use tracing::{Span, debug, error, field::display, info, instrument}; use crate::{ differential_benchmarks::{ExecutionState, WatcherEvent}, @@ -73,6 +70,10 @@ pub struct Driver<'a, I> { /// The number of steps that were executed on the driver. steps_executed: usize, + /// This function controls if the driver should wait for transactions to be included in a block + /// or not before proceeding forward. + await_transaction_inclusion: bool, + /// This is the queue of steps that are to be executed by the driver for this test case. Each /// time `execute_step` is called one of the steps is executed. steps_iterator: I, @@ -89,6 +90,7 @@ where private_key_allocator: Arc>, cached_compiler: &CachedCompiler<'a>, watcher_tx: UnboundedSender, + await_transaction_inclusion: bool, steps: I, ) -> Result { let mut this = Driver { @@ -104,6 +106,7 @@ where execution_state: ExecutionState::empty(), steps_executed: 0, steps_iterator: steps, + await_transaction_inclusion, watcher_tx, }; this.init_execution_state(cached_compiler) @@ -166,7 +169,7 @@ where code, ); let receipt = self - .execute_transaction(tx, None) + .execute_transaction(tx, None, Duration::from_secs(5 * 60)) .and_then(|(_, receipt_fut)| receipt_fut) .await .inspect_err(|err| { @@ -365,7 +368,30 @@ where let tx = step .as_transaction(self.resolver.as_ref(), self.default_resolution_context()) .await?; - Ok(self.execute_transaction(tx, Some(step_path)).await?.0) + + let (tx_hash, receipt_future) = self + .execute_transaction(tx.clone(), Some(step_path), Duration::from_secs(30 * 60)) + .await?; + if self.await_transaction_inclusion { + let receipt = receipt_future + .await + .context("Failed while waiting for transaction inclusion in block")?; + + if !receipt.status() { + error!( + ?tx, + tx.hash = %receipt.transaction_hash, + ?receipt, + "Encountered a failing benchmark transaction" + ); + bail!( + "Encountered a failing transaction in benchmarks: {}", + receipt.transaction_hash + ) + } + } + + Ok(tx_hash) } } } @@ -466,6 +492,7 @@ where .collect::>(); steps.into_iter() }, + await_transaction_inclusion: self.await_transaction_inclusion, watcher_tx: self.watcher_tx.clone(), }) .map(|driver| driver.execute_all()); @@ -632,7 +659,7 @@ where }; let receipt = match self - .execute_transaction(tx, step_path) + .execute_transaction(tx, step_path, Duration::from_secs(5 * 60)) .and_then(|(_, receipt_fut)| receipt_fut) .await { @@ -677,18 +704,33 @@ where #[instrument( level = "info", skip_all, - fields(driver_id = self.driver_id, transaction_hash = tracing::field::Empty) + fields( + driver_id = self.driver_id, + transaction = ?transaction, + transaction_hash = tracing::field::Empty + ), + err(Debug) )] async fn execute_transaction( &self, transaction: TransactionRequest, step_path: Option<&StepPath>, + receipt_wait_duration: Duration, ) -> anyhow::Result<(TxHash, impl Future>)> { let node = self.platform_information.node; - let transaction_hash = node - .submit_transaction(transaction) + let provider = node.provider().await.context("Creating provider failed")?; + + let pending_transaction_builder = provider + .send_transaction(transaction) .await .context("Failed to submit transaction")?; + + let transaction_hash = *pending_transaction_builder.tx_hash(); + let receipt_future = pending_transaction_builder + .with_timeout(Some(receipt_wait_duration)) + .with_required_confirmations(2) + .get_receipt() + .map(|res| res.context("Failed to get the receipt of the transaction")); Span::current().record("transaction_hash", display(transaction_hash)); info!("Submitted transaction"); @@ -701,28 +743,7 @@ where .context("Failed to send the transaction hash to the watcher")?; }; - Ok((transaction_hash, async move { - info!("Starting to poll for transaction receipt"); - poll( - Duration::from_secs(30 * 60), - PollingWaitBehavior::Constant(Duration::from_secs(1)), - || { - async move { - match node.get_receipt(transaction_hash).await { - Ok(receipt) => { - info!("Polling succeeded, receipt found"); - Ok(ControlFlow::Break(receipt)) - } - Err(_) => Ok(ControlFlow::Continue(())), - } - } - .instrument(info_span!("Polling for receipt")) - }, - ) - .instrument(info_span!("Polling for receipt", %transaction_hash)) - .await - .inspect(|_| info!("Found the transaction receipt")) - })) + Ok((transaction_hash, receipt_future)) } // endregion:Transaction Execution } diff --git a/crates/core/src/differential_benchmarks/entry_point.rs b/crates/core/src/differential_benchmarks/entry_point.rs index 17b897b..fdaa5af 100644 --- a/crates/core/src/differential_benchmarks/entry_point.rs +++ b/crates/core/src/differential_benchmarks/entry_point.rs @@ -160,6 +160,7 @@ pub async fn handle_differential_benchmarks( private_key_allocator, cached_compiler.as_ref(), watcher_tx.clone(), + context.await_transaction_inclusion, test_definition .case .steps_iterator_for_benchmarks(context.default_repetition_count) diff --git a/crates/core/src/differential_benchmarks/watcher.rs b/crates/core/src/differential_benchmarks/watcher.rs index 2d96920..129067b 100644 --- a/crates/core/src/differential_benchmarks/watcher.rs +++ b/crates/core/src/differential_benchmarks/watcher.rs @@ -139,23 +139,18 @@ impl Watcher { break; } - info!( - block_number = block.ethereum_block_information.block_number, - block_tx_count = block.ethereum_block_information.transaction_hashes.len(), - remaining_transactions = watch_for_transaction_hashes.read().await.len(), - "Observed a block" - ); - // Remove all of the transaction hashes observed in this block from the txs we // are currently watching for. let mut watch_for_transaction_hashes = watch_for_transaction_hashes.write().await; + let mut relevant_transactions_observed = 0; for tx_hash in block.ethereum_block_information.transaction_hashes.iter() { let Some((step_path, submission_time)) = watch_for_transaction_hashes.remove(tx_hash) else { continue; }; + relevant_transactions_observed += 1; let transaction_information = TransactionInformation { transaction_hash: *tx_hash, submission_timestamp: submission_time @@ -172,6 +167,14 @@ impl Watcher { ) .expect("Can't fail") } + + info!( + block_number = block.ethereum_block_information.block_number, + block_tx_count = block.ethereum_block_information.transaction_hashes.len(), + relevant_transactions_observed, + remaining_transactions = watch_for_transaction_hashes.len(), + "Observed a block" + ); } info!("Watcher's Block Watching Task Finished"); diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 12c8b1b..2628302 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -91,7 +91,8 @@ impl Platform for GethEvmSolcPlatform { let genesis_configuration = AsRef::::as_ref(&context); let genesis = genesis_configuration.genesis()?.clone(); Ok(thread::spawn(move || { - let node = GethNode::new(context); + let use_fallback_gas_filler = matches!(context, Context::Test(..)); + let node = GethNode::new(context, use_fallback_gas_filler); let node = spawn_node::(node, genesis)?; Ok(Box::new(node) as Box<_>) })) @@ -145,7 +146,8 @@ impl Platform for LighthouseGethEvmSolcPlatform { let genesis_configuration = AsRef::::as_ref(&context); let genesis = genesis_configuration.genesis()?.clone(); Ok(thread::spawn(move || { - let node = LighthouseGethNode::new(context); + let use_fallback_gas_filler = matches!(context, Context::Test(..)); + let node = LighthouseGethNode::new(context, use_fallback_gas_filler); let node = spawn_node::(node, genesis)?; Ok(Box::new(node) as Box<_>) })) @@ -206,12 +208,14 @@ impl Platform for ReviveDevNodePolkavmResolcPlatform { let genesis = genesis_configuration.genesis()?.clone(); Ok(thread::spawn(move || { + let use_fallback_gas_filler = matches!(context, Context::Test(..)); let node = SubstrateNode::new( revive_dev_node_path, SubstrateNode::REVIVE_DEV_NODE_EXPORT_CHAINSPEC_COMMAND, Some(revive_dev_node_consensus), context, ð_rpc_connection_strings, + use_fallback_gas_filler, ); let node = spawn_node(node, genesis)?; Ok(Box::new(node) as Box<_>) @@ -274,12 +278,14 @@ impl Platform for ReviveDevNodeRevmSolcPlatform { let genesis = genesis_configuration.genesis()?.clone(); Ok(thread::spawn(move || { + let use_fallback_gas_filler = matches!(context, Context::Test(..)); let node = SubstrateNode::new( revive_dev_node_path, SubstrateNode::REVIVE_DEV_NODE_EXPORT_CHAINSPEC_COMMAND, Some(revive_dev_node_consensus), context, ð_rpc_connection_strings, + use_fallback_gas_filler, ); let node = spawn_node(node, genesis)?; Ok(Box::new(node) as Box<_>) @@ -338,7 +344,9 @@ impl Platform for ZombienetPolkavmResolcPlatform { .clone(); let genesis = genesis_configuration.genesis()?.clone(); Ok(thread::spawn(move || { - let node = ZombienetNode::new(polkadot_parachain_path, context); + let use_fallback_gas_filler = matches!(context, Context::Test(..)); + let node = + ZombienetNode::new(polkadot_parachain_path, context, use_fallback_gas_filler); let node = spawn_node(node, genesis)?; Ok(Box::new(node) as Box<_>) })) @@ -395,7 +403,9 @@ impl Platform for ZombienetRevmSolcPlatform { .clone(); let genesis = genesis_configuration.genesis()?.clone(); Ok(thread::spawn(move || { - let node = ZombienetNode::new(polkadot_parachain_path, context); + let use_fallback_gas_filler = matches!(context, Context::Test(..)); + let node = + ZombienetNode::new(polkadot_parachain_path, context, use_fallback_gas_filler); let node = spawn_node(node, genesis)?; Ok(Box::new(node) as Box<_>) })) diff --git a/crates/node/src/node_implementations/geth.rs b/crates/node/src/node_implementations/geth.rs index b58b357..67673a3 100644 --- a/crates/node/src/node_implementations/geth.rs +++ b/crates/node/src/node_implementations/geth.rs @@ -76,6 +76,7 @@ pub struct GethNode { wallet: Arc, nonce_manager: CachedNonceManager, provider: OnceCell>>, + use_fallback_gas_filler: bool, } impl GethNode { @@ -100,6 +101,7 @@ impl GethNode { + AsRef + AsRef + Clone, + use_fallback_gas_filler: bool, ) -> Self { let working_directory_configuration = AsRef::::as_ref(&context); @@ -126,6 +128,7 @@ impl GethNode { wallet: wallet.clone(), nonce_manager: Default::default(), provider: Default::default(), + use_fallback_gas_filler, } } @@ -246,7 +249,8 @@ impl GethNode { .get_or_try_init(|| async move { construct_concurrency_limited_provider::( self.connection_string.as_str(), - FallbackGasFiller::default(), + FallbackGasFiller::default() + .with_use_fallback_gas_filler(self.use_fallback_gas_filler), ChainIdFiller::new(Some(CHAIN_ID)), NonceFiller::new(self.nonce_manager.clone()), self.wallet.clone(), @@ -742,7 +746,7 @@ mod tests { fn new_node() -> (TestExecutionContext, GethNode) { let context = test_config(); - let mut node = GethNode::new(&context); + let mut node = GethNode::new(&context, true); node.init(context.genesis_configuration.genesis().unwrap().clone()) .expect("Failed to initialize the node") .spawn_process() diff --git a/crates/node/src/node_implementations/lighthouse_geth.rs b/crates/node/src/node_implementations/lighthouse_geth.rs index bcd265a..e672ea2 100644 --- a/crates/node/src/node_implementations/lighthouse_geth.rs +++ b/crates/node/src/node_implementations/lighthouse_geth.rs @@ -106,6 +106,8 @@ pub struct LighthouseGethNode { persistent_http_provider: OnceCell>>, persistent_ws_provider: OnceCell>>, + + use_fallback_gas_filler: bool, } impl LighthouseGethNode { @@ -127,6 +129,7 @@ impl LighthouseGethNode { + AsRef + AsRef + Clone, + use_fallback_gas_filler: bool, ) -> Self { let working_directory_configuration = AsRef::::as_ref(&context); @@ -176,6 +179,7 @@ impl LighthouseGethNode { nonce_manager: Default::default(), persistent_http_provider: OnceCell::const_new(), persistent_ws_provider: OnceCell::const_new(), + use_fallback_gas_filler, } } @@ -374,7 +378,8 @@ impl LighthouseGethNode { .get_or_try_init(|| async move { construct_concurrency_limited_provider::( self.ws_connection_string.as_str(), - FallbackGasFiller::default(), + FallbackGasFiller::default() + .with_use_fallback_gas_filler(self.use_fallback_gas_filler), ChainIdFiller::new(Some(CHAIN_ID)), NonceFiller::new(self.nonce_manager.clone()), self.wallet.clone(), @@ -1152,7 +1157,7 @@ mod tests { let _guard = NODE_START_MUTEX.lock().unwrap(); let context = test_config(); - let mut node = LighthouseGethNode::new(&context); + let mut node = LighthouseGethNode::new(&context, true); node.init(context.genesis_configuration.genesis().unwrap().clone()) .expect("Failed to initialize the node") .spawn_process() diff --git a/crates/node/src/node_implementations/substrate.rs b/crates/node/src/node_implementations/substrate.rs index 30b6553..3e128dc 100644 --- a/crates/node/src/node_implementations/substrate.rs +++ b/crates/node/src/node_implementations/substrate.rs @@ -79,6 +79,7 @@ pub struct SubstrateNode { nonce_manager: CachedNonceManager, provider: OnceCell>>, consensus: Option, + use_fallback_gas_filler: bool, } impl SubstrateNode { @@ -105,6 +106,7 @@ impl SubstrateNode { + AsRef + AsRef, existing_connection_strings: &[String], + use_fallback_gas_filler: bool, ) -> Self { let working_directory_path = AsRef::::as_ref(&context).as_path(); @@ -137,6 +139,7 @@ impl SubstrateNode { nonce_manager: Default::default(), provider: Default::default(), consensus, + use_fallback_gas_filler, } } @@ -324,7 +327,12 @@ impl SubstrateNode { .get_or_try_init(|| async move { construct_concurrency_limited_provider::( self.rpc_url.as_str(), - FallbackGasFiller::new(u64::MAX, 50_000_000_000, 1_000_000_000), + FallbackGasFiller::new( + u64::MAX, + 50_000_000_000, + 1_000_000_000, + self.use_fallback_gas_filler, + ), ChainIdFiller::new(Some(CHAIN_ID)), NonceFiller::new(self.nonce_manager.clone()), self.wallet.clone(), @@ -825,6 +833,7 @@ mod tests { None, &context, &[], + true, ); node.init(context.genesis_configuration.genesis().unwrap().clone()) .expect("Failed to initialize the node") @@ -896,6 +905,7 @@ mod tests { None, &context, &[], + true, ); // Call `init()` diff --git a/crates/node/src/node_implementations/zombienet.rs b/crates/node/src/node_implementations/zombienet.rs index aa2be88..76e8722 100644 --- a/crates/node/src/node_implementations/zombienet.rs +++ b/crates/node/src/node_implementations/zombienet.rs @@ -114,6 +114,8 @@ pub struct ZombienetNode { nonce_manager: CachedNonceManager, provider: OnceCell>>, + + use_fallback_gas_filler: bool, } impl ZombienetNode { @@ -137,6 +139,7 @@ impl ZombienetNode { context: impl AsRef + AsRef + AsRef, + use_fallback_gas_filler: bool, ) -> Self { let eth_proxy_binary = AsRef::::as_ref(&context) .path @@ -164,6 +167,7 @@ impl ZombienetNode { connection_string: String::new(), node_rpc_port: None, provider: Default::default(), + use_fallback_gas_filler, } } @@ -330,7 +334,12 @@ impl ZombienetNode { .get_or_try_init(|| async move { construct_concurrency_limited_provider::( self.connection_string.as_str(), - FallbackGasFiller::new(u64::MAX, 5_000_000_000, 1_000_000_000), + FallbackGasFiller::new( + u64::MAX, + 5_000_000_000, + 1_000_000_000, + self.use_fallback_gas_filler, + ), ChainIdFiller::default(), // TODO: use CHAIN_ID constant NonceFiller::new(self.nonce_manager.clone()), self.wallet.clone(), @@ -823,6 +832,7 @@ mod tests { let mut node = ZombienetNode::new( context.polkadot_parachain_configuration.path.clone(), &context, + true, ); let genesis = context.genesis_configuration.genesis().unwrap().clone(); node.init(genesis).unwrap(); @@ -936,6 +946,7 @@ mod tests { let node = ZombienetNode::new( context.polkadot_parachain_configuration.path.clone(), &context, + true, ); // Act @@ -956,6 +967,7 @@ mod tests { let node = ZombienetNode::new( context.polkadot_parachain_configuration.path.clone(), &context, + true, ); // Act diff --git a/crates/node/src/provider_utils/fallback_gas_filler.rs b/crates/node/src/provider_utils/fallback_gas_filler.rs index 8b6f0df..c7f209f 100644 --- a/crates/node/src/provider_utils/fallback_gas_filler.rs +++ b/crates/node/src/provider_utils/fallback_gas_filler.rs @@ -4,7 +4,7 @@ use alloy::{ Provider, SendableTx, fillers::{GasFiller, TxFiller}, }, - transports::TransportResult, + transports::{TransportError, TransportResult}, }; // Percentage padding applied to estimated gas (e.g. 120 = 20% padding) @@ -17,6 +17,7 @@ pub struct FallbackGasFiller { default_gas_limit: u64, default_max_fee_per_gas: u128, default_priority_fee: u128, + use_fallback_gas_filler: bool, } impl FallbackGasFiller { @@ -24,19 +25,41 @@ impl FallbackGasFiller { default_gas_limit: u64, default_max_fee_per_gas: u128, default_priority_fee: u128, + use_fallback_gas_filler: bool, ) -> Self { Self { inner: GasFiller, default_gas_limit, default_max_fee_per_gas, default_priority_fee, + use_fallback_gas_filler, } } + + pub fn with_default_gas_limit(mut self, default_gas_limit: u64) -> Self { + self.default_gas_limit = default_gas_limit; + self + } + + pub fn with_default_max_fee_per_gas(mut self, default_max_fee_per_gas: u128) -> Self { + self.default_max_fee_per_gas = default_max_fee_per_gas; + self + } + + pub fn with_default_priority_fee(mut self, default_priority_fee: u128) -> Self { + self.default_priority_fee = default_priority_fee; + self + } + + pub fn with_use_fallback_gas_filler(mut self, use_fallback_gas_filler: bool) -> Self { + self.use_fallback_gas_filler = use_fallback_gas_filler; + self + } } impl Default for FallbackGasFiller { fn default() -> Self { - FallbackGasFiller::new(25_000_000, 1_000_000_000, 1_000_000_000) + FallbackGasFiller::new(25_000_000, 1_000_000_000, 1_000_000_000, true) } } @@ -64,7 +87,12 @@ where Ok(fill) => Ok(Some(fill)), Err(err) => { tracing::debug!(error = ?err, "Gas Provider Estimation Failed, using fallback"); - Ok(None) + + if !self.use_fallback_gas_filler { + Err(err) + } else { + Ok(None) + } } } } @@ -86,13 +114,17 @@ where } } Ok(tx) - } else { + } else if self.use_fallback_gas_filler { if let Some(builder) = tx.as_mut_builder() { builder.set_gas_limit(self.default_gas_limit); builder.set_max_fee_per_gas(self.default_max_fee_per_gas); builder.set_max_priority_fee_per_gas(self.default_priority_fee); } Ok(tx) + } else { + Err(TransportError::UnsupportedFeature( + "Fallback gas filler is disabled and we're attempting to do a gas estimate on a failing transaction", + )) } } } diff --git a/scripts/print_benchmark_metrics_csv.py b/scripts/print_benchmark_metrics_csv.py index e4258ae..933dfce 100644 --- a/scripts/print_benchmark_metrics_csv.py +++ b/scripts/print_benchmark_metrics_csv.py @@ -28,7 +28,7 @@ from __future__ import annotations import json import sys import csv -from typing import List, Mapping, TypedDict +from typing import List, Mapping, TypedDict, no_type_check class EthereumMinedBlockInformation(TypedDict): @@ -69,7 +69,43 @@ class MinedBlockInformation(TypedDict): """Block-level information for a mined block with both EVM and optional Substrate fields.""" ethereum_block_information: EthereumMinedBlockInformation - substrate_block_information: SubstrateMinedBlockInformation + substrate_block_information: SubstrateMinedBlockInformation | None + + +def substrate_block_information_ref_time( + block: SubstrateMinedBlockInformation | None, +) -> int | None: + if block is None: + return None + else: + return block["ref_time"] + + +def substrate_block_information_max_ref_time( + block: SubstrateMinedBlockInformation | None, +) -> int | None: + if block is None: + return None + else: + return block["max_ref_time"] + + +def substrate_block_information_proof_size( + block: SubstrateMinedBlockInformation | None, +) -> int | None: + if block is None: + return None + else: + return block["proof_size"] + + +def substrate_block_information_max_proof_size( + block: SubstrateMinedBlockInformation | None, +) -> int | None: + if block is None: + return None + else: + return block["max_proof_size"] class Metric(TypedDict): @@ -100,8 +136,19 @@ class Metrics(TypedDict): transaction_per_second: Metric gas_per_second: Metric gas_block_fullness: Metric - ref_time_block_fullness: Metric - proof_size_block_fullness: Metric + ref_time_block_fullness: Metric | None + proof_size_block_fullness: Metric | None + + +@no_type_check +def metrics_raw_item( + metrics: Metrics, name: str, target: str, index: int +) -> int | None: + l: list[int] = metrics.get(name, dict()).get("raw", dict()).get(target, dict()) + try: + return l[index] + except: + return None class ExecutionReport(TypedDict): @@ -144,12 +191,15 @@ BlockInformation = TypedDict( "Transaction Count": int, "TPS": int | None, "GPS": int | None, - "Ref Time": int, - "Max Ref Time": int, - "Block Fullness Ref Time": int, - "Proof Size": int, - "Max Proof Size": int, - "Block Fullness Proof Size": int, + "Gas Mined": int, + "Block Gas Limit": int, + "Block Fullness Gas": float, + "Ref Time": int | None, + "Max Ref Time": int | None, + "Block Fullness Ref Time": int | None, + "Proof Size": int | None, + "Max Proof Size": int | None, + "Block Fullness Proof Size": int | None, }, ) """A typed dictionary used to hold all of the block information""" @@ -175,7 +225,7 @@ def main() -> None: report: ReportRoot = load_report(report_path) # TODO: Remove this in the future, but for now, the target is fixed. - target: str = "revive-dev-node-revm-solc" + target: str = sys.argv[2] csv_writer = csv.writer(sys.stdout) @@ -188,6 +238,12 @@ def main() -> None: resolved_blocks: list[BlockInformation] = [] for i, block_information in enumerate(blocks_information): + mined_gas: int = block_information["ethereum_block_information"][ + "mined_gas" + ] + block_gas_limit: int = block_information[ + "ethereum_block_information" + ]["block_gas_limit"] resolved_blocks.append( { "Block Number": block_information[ @@ -216,24 +272,37 @@ def main() -> None: "raw" ][target][i - 1] ), - "Ref Time": block_information[ - "substrate_block_information" - ]["ref_time"], - "Max Ref Time": block_information[ - "substrate_block_information" - ]["max_ref_time"], - "Block Fullness Ref Time": execution_report["metrics"][ - "ref_time_block_fullness" - ]["raw"][target][i], - "Proof Size": block_information[ - "substrate_block_information" - ]["proof_size"], - "Max Proof Size": block_information[ - "substrate_block_information" - ]["max_proof_size"], - "Block Fullness Proof Size": execution_report["metrics"][ - "proof_size_block_fullness" - ]["raw"][target][i], + "Gas Mined": block_information[ + "ethereum_block_information" + ]["mined_gas"], + "Block Gas Limit": block_information[ + "ethereum_block_information" + ]["block_gas_limit"], + "Block Fullness Gas": mined_gas / block_gas_limit, + "Ref Time": substrate_block_information_ref_time( + block_information["substrate_block_information"] + ), + "Max Ref Time": substrate_block_information_max_ref_time( + block_information["substrate_block_information"] + ), + "Block Fullness Ref Time": metrics_raw_item( + execution_report["metrics"], + "ref_time_block_fullness", + target, + i, + ), + "Proof Size": substrate_block_information_proof_size( + block_information["substrate_block_information"] + ), + "Max Proof Size": substrate_block_information_max_proof_size( + block_information["substrate_block_information"] + ), + "Block Fullness Proof Size": metrics_raw_item( + execution_report["metrics"], + "proof_size_block_fullness", + target, + i, + ), } )