From 3e3c19e1e1d7553155ed64e33d881c2253774a26 Mon Sep 17 00:00:00 2001 From: Rodrigo Oliveri Date: Mon, 9 Dec 2024 21:06:33 -0300 Subject: [PATCH 01/12] Merge branch 'check-transactions-hanged-from-the-spammer' into check-transaction-spammer-count-diff --- Makefile | 12 ++++---- crates/common/types/blobs_bundle.rs | 4 +++ crates/networking/rpc/engine/payload.rs | 30 +++++++++++++++++--- crates/storage/store/engines/api.rs | 17 ++++++++++-- crates/storage/store/engines/in_memory.rs | 28 ++++++++++++++++--- crates/storage/store/engines/libmdbx.rs | 31 +++++++++++++++++---- crates/storage/store/engines/redb.rs | 34 +++++++++++++++++++---- crates/storage/store/storage.rs | 17 +++++++++++- 8 files changed, 146 insertions(+), 27 deletions(-) diff --git a/Makefile b/Makefile index 5412ccfeaf..b26314121f 100644 --- a/Makefile +++ b/Makefile @@ -58,18 +58,20 @@ checkout-ethereum-package: ethereum-package ## 📦 Checkout specific Ethereum p git fetch && \ git checkout $(ETHEREUM_PACKAGE_REVISION) +ENCLAVE ?= lambdanet + localnet: stop-localnet-silent build-image checkout-ethereum-package ## 🌐 Start local network - kurtosis run --enclave lambdanet ethereum-package --args-file test_data/network_params.yaml + kurtosis run --enclave $(ENCLAVE) ethereum-package --args-file test_data/network_params.yaml docker logs -f $$(docker ps -q --filter ancestor=ethrex) stop-localnet: ## 🛑 Stop local network - kurtosis enclave stop lambdanet - kurtosis enclave rm lambdanet --force + kurtosis enclave stop $(ENCLAVE) + kurtosis enclave rm $(ENCLAVE) --force stop-localnet-silent: @echo "Double checking local net is not already started..." - @kurtosis enclave stop lambdanet >/dev/null 2>&1 || true - @kurtosis enclave rm lambdanet --force >/dev/null 2>&1 || true + @kurtosis enclave stop $(ENCLAVE) >/dev/null 2>&1 || true + @kurtosis enclave rm $(ENCLAVE) --force >/dev/null 2>&1 || true HIVE_REVISION := f220e0c55fb222aaaffdf17d66aa0537cd16a67a # Shallow clones can't specify a single revision, but at least we avoid working diff --git a/crates/common/types/blobs_bundle.rs b/crates/common/types/blobs_bundle.rs index 9748924766..77c8ed9327 100644 --- a/crates/common/types/blobs_bundle.rs +++ b/crates/common/types/blobs_bundle.rs @@ -102,6 +102,10 @@ fn verify_blob_kzg_proof( } impl BlobsBundle { + pub fn empty() -> Self { + Self::default() + } + // In the future we might want to provide a new method that calculates the commitments and proofs using the following. #[cfg(feature = "c-kzg")] pub fn create_from_blobs(blobs: &Vec) -> Result { diff --git a/crates/networking/rpc/engine/payload.rs b/crates/networking/rpc/engine/payload.rs index 8f480c50c9..161e6e4e6f 100644 --- a/crates/networking/rpc/engine/payload.rs +++ b/crates/networking/rpc/engine/payload.rs @@ -355,23 +355,45 @@ impl GetPayloadRequest { fn handle(&self, context: RpcApiContext) -> Result { info!("Requested payload with id: {:#018x}", self.payload_id); - let Some(mut payload) = context.storage.get_payload(self.payload_id)? else { + + let payload = context.storage.get_payload(self.payload_id)?; + + let Some((mut payload_block, block_value, blobs_bundle, completed)) = payload else { return Err(RpcErr::UnknownPayload(format!( "Payload with id {:#018x} not found", self.payload_id ))); }; + // Check timestamp matches valid fork let chain_config = &context.storage.get_chain_config()?; - let current_fork = chain_config.get_fork(payload.header.timestamp); + let current_fork = chain_config.get_fork(payload_block.header.timestamp); info!("Current Fork: {:?}", current_fork); if current_fork != self.valid_fork() { return Err(RpcErr::UnsuportedFork(format!("{current_fork:?}"))); } - let (blobs_bundle, block_value) = build_payload(&mut payload, &context.storage) + if completed { + return serde_json::to_value(self.build_response( + ExecutionPayload::from_block(payload_block), + blobs_bundle, + block_value, + )) + .map_err(|error| RpcErr::Internal(error.to_string())); + } + + let (blobs_bundle, block_value) = build_payload(&mut payload_block, &context.storage) .map_err(|err| RpcErr::Internal(err.to_string()))?; - let execution_payload = ExecutionPayload::from_block(payload); + + context.storage.update_payload( + self.payload_id, + payload_block.clone(), + block_value, + blobs_bundle.clone(), + true, + )?; + + let execution_payload = ExecutionPayload::from_block(payload_block); serde_json::to_value(self.build_response(execution_payload, blobs_bundle, block_value)) .map_err(|error| RpcErr::Internal(error.to_string())) diff --git a/crates/storage/store/engines/api.rs b/crates/storage/store/engines/api.rs index cca2e7333f..c308456a4d 100644 --- a/crates/storage/store/engines/api.rs +++ b/crates/storage/store/engines/api.rs @@ -1,7 +1,8 @@ use bytes::Bytes; use ethereum_types::{H256, U256}; use ethrex_core::types::{ - Block, BlockBody, BlockHash, BlockHeader, BlockNumber, ChainConfig, Index, Receipt, Transaction, + BlobsBundle, Block, BlockBody, BlockHash, BlockHeader, BlockNumber, ChainConfig, Index, + Receipt, Transaction, }; use std::{fmt::Debug, panic::RefUnwindSafe}; @@ -216,5 +217,17 @@ pub trait StoreEngine: Debug + Send + Sync + RefUnwindSafe { fn add_payload(&self, payload_id: u64, block: Block) -> Result<(), StoreError>; - fn get_payload(&self, payload_id: u64) -> Result, StoreError>; + fn get_payload( + &self, + payload_id: u64, + ) -> Result, StoreError>; + + fn update_payload( + &self, + payload_id: u64, + block: Block, + block_value: U256, + blobs_bundle: BlobsBundle, + completed: bool, + ) -> Result<(), StoreError>; } diff --git a/crates/storage/store/engines/in_memory.rs b/crates/storage/store/engines/in_memory.rs index 4d4ed4dbb9..1abf296109 100644 --- a/crates/storage/store/engines/in_memory.rs +++ b/crates/storage/store/engines/in_memory.rs @@ -2,7 +2,7 @@ use crate::error::StoreError; use bytes::Bytes; use ethereum_types::{H256, U256}; use ethrex_core::types::{ - Block, BlockBody, BlockHash, BlockHeader, BlockNumber, ChainConfig, Index, Receipt, + BlobsBundle, Block, BlockBody, BlockHash, BlockHeader, BlockNumber, ChainConfig, Index, Receipt, }; use ethrex_trie::{InMemoryTrieDB, Trie}; use std::{ @@ -36,7 +36,7 @@ struct StoreInner { // TODO (#307): Remove TotalDifficulty. block_total_difficulties: HashMap, // Stores local blocks by payload id - payloads: HashMap, + payloads: HashMap, pending_blocks: HashMap, } @@ -341,13 +341,33 @@ impl StoreEngine for Store { } fn add_payload(&self, payload_id: u64, block: Block) -> Result<(), StoreError> { - self.inner().payloads.insert(payload_id, block); + self.inner().payloads.insert( + payload_id, + (block, U256::zero(), BlobsBundle::empty(), false), + ); Ok(()) } - fn get_payload(&self, payload_id: u64) -> Result, StoreError> { + fn get_payload( + &self, + payload_id: u64, + ) -> Result, StoreError> { Ok(self.inner().payloads.get(&payload_id).cloned()) } + + fn update_payload( + &self, + payload_id: u64, + block: Block, + block_value: U256, + blobs_bundle: BlobsBundle, + completed: bool, + ) -> Result<(), StoreError> { + self.inner() + .payloads + .insert(payload_id, (block, block_value, blobs_bundle, completed)); + Ok(()) + } } impl Debug for Store { diff --git a/crates/storage/store/engines/libmdbx.rs b/crates/storage/store/engines/libmdbx.rs index 47c661d687..b90716f49a 100644 --- a/crates/storage/store/engines/libmdbx.rs +++ b/crates/storage/store/engines/libmdbx.rs @@ -9,7 +9,8 @@ use anyhow::Result; use bytes::Bytes; use ethereum_types::{H256, U256}; use ethrex_core::types::{ - Block, BlockBody, BlockHash, BlockHeader, BlockNumber, ChainConfig, Index, Receipt, Transaction, + BlobsBundle, Block, BlockBody, BlockHash, BlockHeader, BlockNumber, ChainConfig, Index, + Receipt, Transaction, }; use ethrex_rlp::decode::RLPDecode; use ethrex_rlp::encode::RLPEncode; @@ -351,13 +352,33 @@ impl StoreEngine for Store { } fn add_payload(&self, payload_id: u64, block: Block) -> Result<(), StoreError> { - self.write::(payload_id, block.into()) + self.write::( + payload_id, + (block, U256::zero(), BlobsBundle::empty(), false).into(), + ) } - fn get_payload(&self, payload_id: u64) -> Result, StoreError> { + fn get_payload( + &self, + payload_id: u64, + ) -> Result, StoreError> { Ok(self.read::(payload_id)?.map(|b| b.to())) } + fn update_payload( + &self, + payload_id: u64, + block: Block, + block_value: U256, + blobs_bundle: BlobsBundle, + completed: bool, + ) -> std::result::Result<(), StoreError> { + self.write::( + payload_id, + (block, block_value, blobs_bundle, completed).into(), + ) + } + fn get_transaction_by_hash( &self, transaction_hash: H256, @@ -492,8 +513,8 @@ table!( // Local Blocks table!( - /// payload id to payload block table - ( Payloads ) u64 => BlockRLP + /// payload id to payload table + ( Payloads ) u64 => Rlp<(Block, U256, BlobsBundle, bool)> ); table!( diff --git a/crates/storage/store/engines/redb.rs b/crates/storage/store/engines/redb.rs index 1a7ab7f537..e033d8c1b4 100644 --- a/crates/storage/store/engines/redb.rs +++ b/crates/storage/store/engines/redb.rs @@ -1,10 +1,9 @@ use std::{borrow::Borrow, panic::RefUnwindSafe, sync::Arc}; use ethrex_core::types::BlockBody; -use ethrex_core::U256; use ethrex_core::{ - types::{Block, BlockHash, BlockHeader, BlockNumber, ChainConfig, Index, Receipt}, - H256, + types::{BlobsBundle, Block, BlockHash, BlockHeader, BlockNumber, ChainConfig, Index, Receipt}, + H256, U256, }; use ethrex_rlp::decode::RLPDecode; use ethrex_rlp::encode::RLPEncode; @@ -45,7 +44,8 @@ pub const STORAGE_TRIE_NODES_TABLE: MultimapTableDefinition<([u8; 32], [u8; 33]) MultimapTableDefinition::new("StorageTrieNodes"); const CHAIN_DATA_TABLE: TableDefinition> = TableDefinition::new("ChainData"); -const PAYLOADS_TABLE: TableDefinition = TableDefinition::new("Payloads"); +const PAYLOADS_TABLE: TableDefinition> = + TableDefinition::new("Payloads"); const PENDING_BLOCKS_TABLE: TableDefinition = TableDefinition::new("PendingBlocks"); const TRANSACTION_LOCATIONS_TABLE: MultimapTableDefinition< @@ -532,15 +532,37 @@ impl StoreEngine for RedBStore { self.write( PAYLOADS_TABLE, payload_id, - >::into(block), + <(Block, U256, BlobsBundle, bool) as Into>>::into( + (block, U256::zero(), BlobsBundle::empty(), false), + ), ) } - fn get_payload(&self, payload_id: u64) -> Result, StoreError> { + fn get_payload( + &self, + payload_id: u64, + ) -> Result, StoreError> { Ok(self .read(PAYLOADS_TABLE, payload_id)? .map(|b| b.value().to())) } + + fn update_payload( + &self, + payload_id: u64, + block: Block, + block_value: U256, + blobs_bundle: BlobsBundle, + completed: bool, + ) -> Result<(), StoreError> { + self.write( + PAYLOADS_TABLE, + payload_id, + <(Block, U256, BlobsBundle, bool) as Into>>::into( + (block, block_value, blobs_bundle, completed), + ), + ) + } } impl redb::Value for ChainDataIndex { diff --git a/crates/storage/store/storage.rs b/crates/storage/store/storage.rs index aa169b7771..1ffd2b3fa2 100644 --- a/crates/storage/store/storage.rs +++ b/crates/storage/store/storage.rs @@ -910,10 +910,25 @@ impl Store { self.engine.add_payload(payload_id, block) } - pub fn get_payload(&self, payload_id: u64) -> Result, StoreError> { + pub fn get_payload( + &self, + payload_id: u64, + ) -> Result, StoreError> { self.engine.get_payload(payload_id) } + pub fn update_payload( + &self, + payload_id: u64, + block: Block, + block_value: U256, + blobs_bundle: BlobsBundle, + completed: bool, + ) -> Result<(), StoreError> { + self.engine + .update_payload(payload_id, block, block_value, blobs_bundle, completed) + } + /// Creates a new state trie with an empty state root, for testing purposes only pub fn new_state_trie_for_test(&self) -> Trie { self.engine.open_state_trie(*EMPTY_TRIE_HASH) From cdad54bfcbcfad56082c9d66ce59587c1c5d0d39 Mon Sep 17 00:00:00 2001 From: Rodrigo Oliveri Date: Mon, 6 Jan 2025 18:18:06 -0300 Subject: [PATCH 02/12] Initial maxPriorityFeePerGas implementation based on the gasPrice one --- crates/networking/rpc/eth/max_priority_fee.rs | 100 ++++++++++++++++++ crates/networking/rpc/eth/mod.rs | 1 + crates/networking/rpc/rpc.rs | 46 +++++++- 3 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 crates/networking/rpc/eth/max_priority_fee.rs diff --git a/crates/networking/rpc/eth/max_priority_fee.rs b/crates/networking/rpc/eth/max_priority_fee.rs new file mode 100644 index 0000000000..04637cc12e --- /dev/null +++ b/crates/networking/rpc/eth/max_priority_fee.rs @@ -0,0 +1,100 @@ +use ethrex_blockchain::constants::MIN_GAS_LIMIT; +use tracing::error; + +use crate::utils::RpcErr; +use crate::{RpcApiContext, RpcHandler}; +use serde_json::Value; + +// TODO: This does not need a struct, +// but I'm leaving it like this for consistency +// with the other RPC endpoints. +// The handle function could simply be +// a function called 'estimate'. +#[derive(Debug, Clone)] +pub struct MaxPriorityFee; + +// TODO: Maybe these constants should be some kind of config. +// How many transactions to take as a price sample from a block. +const TXS_SAMPLE_SIZE: usize = 3; +// How many blocks we'll go back to calculate the estimate. +const BLOCK_RANGE_LOWER_BOUND_DEC: u64 = 20; + +impl RpcHandler for MaxPriorityFee { + fn parse(_: &Option>) -> Result { + Ok(MaxPriorityFee {}) + } + + // Disclaimer: + // This estimation is somewhat based on how currently go-ethereum does it. + // Reference: https://github.com/ethereum/go-ethereum/blob/368e16f39d6c7e5cce72a92ec289adbfbaed4854/eth/gasprice/gasprice.go#L153 + // Although it will (probably) not yield the same result. + // The idea here is to: + // - Take the last 20 blocks (100% arbitrary, this could be more or less blocks) + // - For each block, take the 3 txs with the lowest gas price (100% arbitrary) + // - Join every fetched tx into a single vec and sort it. + // - Return the one in the middle (what is also known as the 'median sample') + // The intuition here is that we're sampling already accepted transactions, + // fetched from recent blocks, so they should be real, representative values. + // This specific implementation probably is not the best way to do this + // but it works for now for a simple estimation, in the future + // we can look into more sophisticated estimation methods, if needed. + /// Estimate Gas Price based on already accepted transactions, + /// as per the spec, this will be returned in wei. + fn handle(&self, context: RpcApiContext) -> Result { + let latest_block_number = context.storage.get_latest_block_number()?; + let block_range_lower_bound = + latest_block_number.saturating_sub(BLOCK_RANGE_LOWER_BOUND_DEC); + // These are the blocks we'll use to estimate the price. + let block_range = block_range_lower_bound..=latest_block_number; + if block_range.is_empty() { + error!( + "Calculated block range from block {} \ + up to block {} for gas price estimation is empty", + block_range_lower_bound, latest_block_number + ); + return Err(RpcErr::Internal("Error calculating gas price".to_string())); + } + let mut results = vec![]; + // TODO: Estimating gas price involves querying multiple blocks + // and doing some calculations with each of them, let's consider + // caching this result, also we can have a specific DB method + // that returns a block range to not query them one-by-one. + for block_num in block_range { + let Some(block_body) = context.storage.get_block_body(block_num)? else { + error!("Block body for block number {block_num} is missing but is below the latest known block!"); + return Err(RpcErr::Internal( + "Error calculating gas price: missing data".to_string(), + )); + }; + let mut max_priority_fee_samples = block_body + .transactions + .into_iter() + .filter_map(|tx| tx.max_priority_fee()) + .collect::>(); + max_priority_fee_samples.sort(); + results.extend(max_priority_fee_samples.into_iter().take(TXS_SAMPLE_SIZE)); + } + results.sort(); + + let sample_gas = match results.get(results.len() / 2) { + Some(gas) => *gas, + None => { + // If we don't have enough samples, we'll return the base fee or the min gas limit as a default. + context + .storage + .get_block_header(latest_block_number) + .ok() + .flatten() + .and_then(|header| header.base_fee_per_gas) + .unwrap_or(MIN_GAS_LIMIT) + } + }; + + let gas_as_hex = format!("0x{:x}", sample_gas); + Ok(serde_json::Value::String(gas_as_hex)) + } +} + +#[cfg(test)] +mod tests { + } diff --git a/crates/networking/rpc/eth/mod.rs b/crates/networking/rpc/eth/mod.rs index 345a3cd0ea..a637d56a50 100644 --- a/crates/networking/rpc/eth/mod.rs +++ b/crates/networking/rpc/eth/mod.rs @@ -4,5 +4,6 @@ pub(crate) mod client; pub(crate) mod fee_market; pub(crate) mod filter; pub(crate) mod gas_price; +pub(crate) mod max_priority_fee; pub(crate) mod logs; pub(crate) mod transaction; diff --git a/crates/networking/rpc/rpc.rs b/crates/networking/rpc/rpc.rs index 2a9c9c623c..7044bc74f2 100644 --- a/crates/networking/rpc/rpc.rs +++ b/crates/networking/rpc/rpc.rs @@ -45,7 +45,7 @@ use std::{ time::Duration, }; use tokio::{net::TcpListener, sync::Mutex as TokioMutex}; -use tracing::info; +use tracing::{info, error}; use types::transaction::SendRawTransactionRequest; use utils::{ RpcErr, RpcErrorMetadata, RpcErrorResponse, RpcNamespace, RpcRequest, RpcRequestId, @@ -78,7 +78,48 @@ trait RpcHandler: Sized { fn call(req: &RpcRequest, context: RpcApiContext) -> Result { let request = Self::parse(&req.params)?; - request.handle(context) + let result = request.handle(context); + + match result { + Ok(res) => { + if ![ + "eth_getBlockByNumber", + "eth_getTransactionReceipt", + "eth_getBlockByHash", + ] + .contains(&req.method.as_str()) + { + // if req.method.as_str() == "engine_newPayloadV3" { + // info!( + // "RPC req method: {:?}, block_hash: {:?}", + // req.method, + // req.params.clone().unwrap()[0].get("blockHash") + // ); + // } else { + // info!( + // "RPC req method: {:?}, req params: {:?}", + // req.method, req.params + // ); + // } + + if req.method.as_str() == "engine_getPayloadV3" { + info!( + "RPC response parent hash: {:?}, blockhash: {:?}", + res["executionPayload"]["parentHash"], + res["executionPayload"]["blockHash"] + ); + } else { + info!("RPC response: {:?}", res); + } + }; + + Ok(res) + } + Err(e) => { + error!("RPC error: {:?}", e); + Err(e) + } + } } fn handle(&self, context: RpcApiContext) -> Result; @@ -241,6 +282,7 @@ pub fn map_eth_requests(req: &RpcRequest, context: RpcApiContext) -> Result SendRawTransactionRequest::call(req, context), "eth_getProof" => GetProofRequest::call(req, context), "eth_gasPrice" => GasPrice::call(req, context), + "eth_maxPriorityFeePerGas" => eth::max_priority_fee::MaxPriorityFee::call(req, context), unknown_eth_method => Err(RpcErr::MethodNotFound(unknown_eth_method.to_owned())), } } From d8b8569bd62fc497390a848c2061d53e292d8d13 Mon Sep 17 00:00:00 2001 From: Rodrigo Oliveri Date: Mon, 6 Jan 2025 18:24:58 -0300 Subject: [PATCH 03/12] Test the check of the 240 tx addition (80 acc x 3 txs) per block with assertoor --- .github/config/assertoor/el-stability-check.yaml | 13 +++++++------ .github/config/assertoor/network_params_blob.yaml | 2 +- test_data/network_params.yaml | 5 +++++ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/config/assertoor/el-stability-check.yaml b/.github/config/assertoor/el-stability-check.yaml index 85f3874cc7..99bb3b32dc 100644 --- a/.github/config/assertoor/el-stability-check.yaml +++ b/.github/config/assertoor/el-stability-check.yaml @@ -21,12 +21,6 @@ tasks: title: "Check if CL clients are synced" - name: check_execution_sync_status title: "Check if EL clients are synced" - - name: check_consensus_block_proposals - title: "Check the tx spammer is working as expected for block proposal with >= 50 transactions" - config: - minTransactionCount: 50 - configVars: - clientPattern: "1-ethrex-lighthouse" - name: run_task_matrix title: "Check block proposals from all client pairs" @@ -42,6 +36,13 @@ tasks: configVars: validatorNamePattern: "validatorPairName" +- name: check_consensus_block_proposals + title: "Check the tx spammer is working as expected for block proposal with >= 50 transactions" + timeout: 3m + config: + minTransactionCount: 240 + validatorNamePattern: "[\\d]-ethrex-lighthouse" + - name: run_tasks_concurrent title: "Check chain stability (reorgs and forks)" timeout: 7m diff --git a/.github/config/assertoor/network_params_blob.yaml b/.github/config/assertoor/network_params_blob.yaml index 80bb134ad8..ea298c7f9a 100644 --- a/.github/config/assertoor/network_params_blob.yaml +++ b/.github/config/assertoor/network_params_blob.yaml @@ -24,4 +24,4 @@ assertoor_params: run_block_proposal_check: false tests: - https://raw.githubusercontent.com/ethpandaops/assertoor/refs/heads/master/playbooks/stable/blob-transactions-test.yaml - - https://raw.githubusercontent.com/lambdaclass/ethrex/refs/heads/main/.github/config/assertoor/el-stability-check.yaml + - https://raw.githubusercontent.com/lambdaclass/ethrex/refs/heads/check-transaction-spammer-count-diff/.github/config/assertoor/el-stability-check.yaml diff --git a/test_data/network_params.yaml b/test_data/network_params.yaml index d28c474b37..68c40088d7 100644 --- a/test_data/network_params.yaml +++ b/test_data/network_params.yaml @@ -18,3 +18,8 @@ additional_services: - dora - el_forkmon - tx_spammer + +tx_spammer_params: + # A list of optional extra params that will be passed to the TX Spammer container for modifying its behaviour + tx_spammer_extra_args: ["--txcount=3", "--accounts=80"] + # Some taested seeds: 0x5a8e7b08fef94497, 0x6619e189b8a8b911, 0x52a0d7198393262e, use it as an extra argument for the tx_spammer, i.e: "--seed=0x5a8e7b08fef94497" \ No newline at end of file From c18ed3974e388f9135624d8e2f82d8d7b7fd4646 Mon Sep 17 00:00:00 2001 From: Rodrigo Oliveri Date: Tue, 7 Jan 2025 14:45:59 -0300 Subject: [PATCH 04/12] Removed unnedded diffs due to testing --- crates/networking/rpc/rpc.rs | 45 ++---------------------------------- 1 file changed, 2 insertions(+), 43 deletions(-) diff --git a/crates/networking/rpc/rpc.rs b/crates/networking/rpc/rpc.rs index 7044bc74f2..336d0d896f 100644 --- a/crates/networking/rpc/rpc.rs +++ b/crates/networking/rpc/rpc.rs @@ -45,7 +45,7 @@ use std::{ time::Duration, }; use tokio::{net::TcpListener, sync::Mutex as TokioMutex}; -use tracing::{info, error}; +use tracing::info; use types::transaction::SendRawTransactionRequest; use utils::{ RpcErr, RpcErrorMetadata, RpcErrorResponse, RpcNamespace, RpcRequest, RpcRequestId, @@ -78,48 +78,7 @@ trait RpcHandler: Sized { fn call(req: &RpcRequest, context: RpcApiContext) -> Result { let request = Self::parse(&req.params)?; - let result = request.handle(context); - - match result { - Ok(res) => { - if ![ - "eth_getBlockByNumber", - "eth_getTransactionReceipt", - "eth_getBlockByHash", - ] - .contains(&req.method.as_str()) - { - // if req.method.as_str() == "engine_newPayloadV3" { - // info!( - // "RPC req method: {:?}, block_hash: {:?}", - // req.method, - // req.params.clone().unwrap()[0].get("blockHash") - // ); - // } else { - // info!( - // "RPC req method: {:?}, req params: {:?}", - // req.method, req.params - // ); - // } - - if req.method.as_str() == "engine_getPayloadV3" { - info!( - "RPC response parent hash: {:?}, blockhash: {:?}", - res["executionPayload"]["parentHash"], - res["executionPayload"]["blockHash"] - ); - } else { - info!("RPC response: {:?}", res); - } - }; - - Ok(res) - } - Err(e) => { - error!("RPC error: {:?}", e); - Err(e) - } - } + request.handle(context) } fn handle(&self, context: RpcApiContext) -> Result; From 3c522435fb172c9cc88c829dc3347ae6c47c36c6 Mon Sep 17 00:00:00 2001 From: Rodrigo Oliveri Date: Tue, 7 Jan 2025 19:12:03 -0300 Subject: [PATCH 05/12] Initial enhanced implementation of max_priority_fee basing also gas_price in the same implementation --- crates/networking/rpc/eth/fee_calculator.rs | 82 +++++++++++++++++ crates/networking/rpc/eth/gas_price.rs | 90 +++++-------------- crates/networking/rpc/eth/max_priority_fee.rs | 79 ++-------------- crates/networking/rpc/eth/mod.rs | 6 +- test_data/network_params.yaml | 2 +- 5 files changed, 115 insertions(+), 144 deletions(-) create mode 100644 crates/networking/rpc/eth/fee_calculator.rs diff --git a/crates/networking/rpc/eth/fee_calculator.rs b/crates/networking/rpc/eth/fee_calculator.rs new file mode 100644 index 0000000000..45215da0ff --- /dev/null +++ b/crates/networking/rpc/eth/fee_calculator.rs @@ -0,0 +1,82 @@ +use ethrex_storage::Store; +use tracing::error; + +use crate::utils::RpcErr; + +// TODO: Maybe these constants should be some kind of config. +// How many transactions to take as a price sample from a block. +const TXS_SAMPLE_SIZE: usize = 3; +// How many blocks we'll go back to calculate the estimate. +const BLOCK_RANGE_LOWER_BOUND_DEC: u64 = 20; + +// The following comment is taken from the implementation of gas_price and is still valid, the logic was just moved here. + +// Disclaimer: + +// This estimation is somewhat based on how currently go-ethereum does it. +// Reference: https://github.com/ethereum/go-ethereum/blob/368e16f39d6c7e5cce72a92ec289adbfbaed4854/eth/gasprice/gasprice.go#L153 +// Although it will (probably) not yield the same result. +// The idea here is to: +// - Take the last 20 blocks (100% arbitrary, this could be more or less blocks) +// - For each block, take the 3 txs with the lowest gas price (100% arbitrary) +// - Join every fetched tx into a single vec and sort it. +// - Return the one in the middle (what is also known as the 'median sample') +// The intuition here is that we're sampling already accepted transactions, +// fetched from recent blocks, so they should be real, representative values. +// This specific implementation probably is not the best way to do this +// but it works for now for a simple estimation, in the future +// we can look into more sophisticated estimation methods, if needed. +/// Estimate Gas Price based on already accepted transactions, +/// as per the spec, this will be returned in wei. +pub fn estimate_gas_tip(storage: &Store) -> Result, RpcErr> { + let latest_block_number = storage.get_latest_block_number()?; + let block_range_lower_bound = + latest_block_number.saturating_sub(BLOCK_RANGE_LOWER_BOUND_DEC); + // These are the blocks we'll use to estimate the price. + let block_range = block_range_lower_bound..=latest_block_number; + if block_range.is_empty() { + error!( + "Calculated block range from block {} \ + up to block {} for gas price estimation is empty", + block_range_lower_bound, latest_block_number + ); + return Err(RpcErr::Internal("Error calculating gas price".to_string())); + } + let mut results = vec![]; + // TODO: Estimating gas price involves querying multiple blocks + // and doing some calculations with each of them, let's consider + // caching this result, also we can have a specific DB method + // that returns a block range to not query them one-by-one. + for block_num in block_range { + let Some(block_body) = storage.get_block_body(block_num)? else { + error!("Block body for block number {block_num} is missing but is below the latest known block!"); + return Err(RpcErr::Internal( + "Error calculating gas price: missing data".to_string(), + )); + }; + + let base_fee = + storage + .get_block_header(latest_block_number) + .ok() + .flatten() + .and_then(|header| header.base_fee_per_gas); + + // Previously we took the gas_price, now we take the effective_gas_tip and add the base_fee in the RPC + // call if needed. + let mut gas_tip_samples = block_body + .transactions + .into_iter() + .filter_map(|tx| tx.effective_gas_tip(base_fee)) + .collect::>(); + + gas_tip_samples.sort(); + results.extend(gas_tip_samples.into_iter().take(TXS_SAMPLE_SIZE)); + } + results.sort(); + + match results.get(results.len() / 2) { + None => return Ok(None), + Some(gas) => return Ok(Some(*gas)), + } +} \ No newline at end of file diff --git a/crates/networking/rpc/eth/gas_price.rs b/crates/networking/rpc/eth/gas_price.rs index b021cf95fe..03e091e4fe 100644 --- a/crates/networking/rpc/eth/gas_price.rs +++ b/crates/networking/rpc/eth/gas_price.rs @@ -1,5 +1,7 @@ +// Use fee_calculator mod in crates/networking/rpc/eth/ as gas_price + +use crate::eth::fee_calculator::estimate_gas_tip; use ethrex_blockchain::constants::MIN_GAS_LIMIT; -use tracing::error; use crate::utils::RpcErr; use crate::{RpcApiContext, RpcHandler}; @@ -13,84 +15,34 @@ use serde_json::Value; #[derive(Debug, Clone)] pub struct GasPrice; -// TODO: Maybe these constants should be some kind of config. -// How many transactions to take as a price sample from a block. -const TXS_SAMPLE_SIZE: usize = 3; -// How many blocks we'll go back to calculate the estimate. -const BLOCK_RANGE_LOWER_BOUND_DEC: u64 = 20; - impl RpcHandler for GasPrice { fn parse(_: &Option>) -> Result { Ok(GasPrice {}) } - // Disclaimer: - // This estimation is somewhat based on how currently go-ethereum does it. - // Reference: https://github.com/ethereum/go-ethereum/blob/368e16f39d6c7e5cce72a92ec289adbfbaed4854/eth/gasprice/gasprice.go#L153 - // Although it will (probably) not yield the same result. - // The idea here is to: - // - Take the last 20 blocks (100% arbitrary, this could be more or less blocks) - // - For each block, take the 3 txs with the lowest gas price (100% arbitrary) - // - Join every fetched tx into a single vec and sort it. - // - Return the one in the middle (what is also known as the 'median sample') - // The intuition here is that we're sampling already accepted transactions, - // fetched from recent blocks, so they should be real, representative values. - // This specific implementation probably is not the best way to do this - // but it works for now for a simple estimation, in the future - // we can look into more sophisticated estimation methods, if needed. - /// Estimate Gas Price based on already accepted transactions, - /// as per the spec, this will be returned in wei. fn handle(&self, context: RpcApiContext) -> Result { let latest_block_number = context.storage.get_latest_block_number()?; - let block_range_lower_bound = - latest_block_number.saturating_sub(BLOCK_RANGE_LOWER_BOUND_DEC); - // These are the blocks we'll use to estimate the price. - let block_range = block_range_lower_bound..=latest_block_number; - if block_range.is_empty() { - error!( - "Calculated block range from block {} \ - up to block {} for gas price estimation is empty", - block_range_lower_bound, latest_block_number - ); - return Err(RpcErr::Internal("Error calculating gas price".to_string())); - } - let mut results = vec![]; - // TODO: Estimating gas price involves querying multiple blocks - // and doing some calculations with each of them, let's consider - // caching this result, also we can have a specific DB method - // that returns a block range to not query them one-by-one. - for block_num in block_range { - let Some(block_body) = context.storage.get_block_body(block_num)? else { - error!("Block body for block number {block_num} is missing but is below the latest known block!"); - return Err(RpcErr::Internal( - "Error calculating gas price: missing data".to_string(), - )); - }; - let mut gas_price_samples = block_body - .transactions - .into_iter() - .map(|tx| tx.gas_price()) - .collect::>(); - gas_price_samples.sort(); - results.extend(gas_price_samples.into_iter().take(TXS_SAMPLE_SIZE)); - } - results.sort(); - let sample_gas = match results.get(results.len() / 2) { - Some(gas) => *gas, - None => { - // If we don't have enough samples, we'll return the base fee or the min gas limit as a default. - context - .storage - .get_block_header(latest_block_number) - .ok() - .flatten() - .and_then(|header| header.base_fee_per_gas) - .unwrap_or(MIN_GAS_LIMIT) - } + let estimated_gas_tip = estimate_gas_tip(&context.storage)?; + + let base_fee = context + .storage + .get_block_header(latest_block_number) + .ok() + .flatten() + .and_then(|header| header.base_fee_per_gas); + + // To complete the gas price, we need to add the base fee to the estimated gas. + // If we don't have the estimated gas, we'll use the base fee as the gas price. + // If we don't have the base fee, we'll use the minimum gas limit. + let gas_price = match (estimated_gas_tip, base_fee) { + (Some(gas_tip), Some(base_fee)) => gas_tip + base_fee, + (Some(gas_tip), None) => gas_tip, + (None, Some(base_fee)) => base_fee, + (None, None) => MIN_GAS_LIMIT, }; - let gas_as_hex = format!("0x{:x}", sample_gas); + let gas_as_hex = format!("0x{:x}", gas_price); Ok(serde_json::Value::String(gas_as_hex)) } } diff --git a/crates/networking/rpc/eth/max_priority_fee.rs b/crates/networking/rpc/eth/max_priority_fee.rs index 04637cc12e..1c4934fb55 100644 --- a/crates/networking/rpc/eth/max_priority_fee.rs +++ b/crates/networking/rpc/eth/max_priority_fee.rs @@ -1,5 +1,4 @@ -use ethrex_blockchain::constants::MIN_GAS_LIMIT; -use tracing::error; +use crate::eth::fee_calculator::estimate_gas_tip; use crate::utils::RpcErr; use crate::{RpcApiContext, RpcHandler}; @@ -13,88 +12,24 @@ use serde_json::Value; #[derive(Debug, Clone)] pub struct MaxPriorityFee; -// TODO: Maybe these constants should be some kind of config. -// How many transactions to take as a price sample from a block. -const TXS_SAMPLE_SIZE: usize = 3; -// How many blocks we'll go back to calculate the estimate. -const BLOCK_RANGE_LOWER_BOUND_DEC: u64 = 20; - impl RpcHandler for MaxPriorityFee { fn parse(_: &Option>) -> Result { Ok(MaxPriorityFee {}) } - // Disclaimer: - // This estimation is somewhat based on how currently go-ethereum does it. - // Reference: https://github.com/ethereum/go-ethereum/blob/368e16f39d6c7e5cce72a92ec289adbfbaed4854/eth/gasprice/gasprice.go#L153 - // Although it will (probably) not yield the same result. - // The idea here is to: - // - Take the last 20 blocks (100% arbitrary, this could be more or less blocks) - // - For each block, take the 3 txs with the lowest gas price (100% arbitrary) - // - Join every fetched tx into a single vec and sort it. - // - Return the one in the middle (what is also known as the 'median sample') - // The intuition here is that we're sampling already accepted transactions, - // fetched from recent blocks, so they should be real, representative values. - // This specific implementation probably is not the best way to do this - // but it works for now for a simple estimation, in the future - // we can look into more sophisticated estimation methods, if needed. - /// Estimate Gas Price based on already accepted transactions, - /// as per the spec, this will be returned in wei. fn handle(&self, context: RpcApiContext) -> Result { - let latest_block_number = context.storage.get_latest_block_number()?; - let block_range_lower_bound = - latest_block_number.saturating_sub(BLOCK_RANGE_LOWER_BOUND_DEC); - // These are the blocks we'll use to estimate the price. - let block_range = block_range_lower_bound..=latest_block_number; - if block_range.is_empty() { - error!( - "Calculated block range from block {} \ - up to block {} for gas price estimation is empty", - block_range_lower_bound, latest_block_number - ); - return Err(RpcErr::Internal("Error calculating gas price".to_string())); - } - let mut results = vec![]; - // TODO: Estimating gas price involves querying multiple blocks - // and doing some calculations with each of them, let's consider - // caching this result, also we can have a specific DB method - // that returns a block range to not query them one-by-one. - for block_num in block_range { - let Some(block_body) = context.storage.get_block_body(block_num)? else { - error!("Block body for block number {block_num} is missing but is below the latest known block!"); - return Err(RpcErr::Internal( - "Error calculating gas price: missing data".to_string(), - )); - }; - let mut max_priority_fee_samples = block_body - .transactions - .into_iter() - .filter_map(|tx| tx.max_priority_fee()) - .collect::>(); - max_priority_fee_samples.sort(); - results.extend(max_priority_fee_samples.into_iter().take(TXS_SAMPLE_SIZE)); - } - results.sort(); + let estimated_gas_tip = estimate_gas_tip(&context.storage)?; - let sample_gas = match results.get(results.len() / 2) { - Some(gas) => *gas, - None => { - // If we don't have enough samples, we'll return the base fee or the min gas limit as a default. - context - .storage - .get_block_header(latest_block_number) - .ok() - .flatten() - .and_then(|header| header.base_fee_per_gas) - .unwrap_or(MIN_GAS_LIMIT) - } + let gas_tip = match estimated_gas_tip { + Some(gas_tip) => gas_tip, + None => return Ok(serde_json::Value::Null), }; - let gas_as_hex = format!("0x{:x}", sample_gas); + let gas_as_hex = format!("0x{:x}", gas_tip); Ok(serde_json::Value::String(gas_as_hex)) } } #[cfg(test)] mod tests { - } +} diff --git a/crates/networking/rpc/eth/mod.rs b/crates/networking/rpc/eth/mod.rs index a637d56a50..aebd7fdadd 100644 --- a/crates/networking/rpc/eth/mod.rs +++ b/crates/networking/rpc/eth/mod.rs @@ -3,7 +3,9 @@ pub(crate) mod block; pub(crate) mod client; pub(crate) mod fee_market; pub(crate) mod filter; -pub(crate) mod gas_price; -pub(crate) mod max_priority_fee; pub(crate) mod logs; pub(crate) mod transaction; + +mod fee_calculator; +pub(crate) mod gas_price; +pub(crate) mod max_priority_fee; diff --git a/test_data/network_params.yaml b/test_data/network_params.yaml index 68c40088d7..c615a5d6a0 100644 --- a/test_data/network_params.yaml +++ b/test_data/network_params.yaml @@ -22,4 +22,4 @@ additional_services: tx_spammer_params: # A list of optional extra params that will be passed to the TX Spammer container for modifying its behaviour tx_spammer_extra_args: ["--txcount=3", "--accounts=80"] - # Some taested seeds: 0x5a8e7b08fef94497, 0x6619e189b8a8b911, 0x52a0d7198393262e, use it as an extra argument for the tx_spammer, i.e: "--seed=0x5a8e7b08fef94497" \ No newline at end of file + # Some tested seeds: 0x5a8e7b08fef94497, 0x6619e189b8a8b911, 0x52a0d7198393262e, use it as an extra argument for the tx_spammer, i.e: "--seed=0x5a8e7b08fef94497" From d160d200221992e58f605d08ea22bdf080a1b223 Mon Sep 17 00:00:00 2001 From: Rodrigo Oliveri Date: Tue, 7 Jan 2025 19:19:52 -0300 Subject: [PATCH 06/12] Fixing some lint issues and added a comment regarding gas_price results --- crates/networking/rpc/eth/fee_calculator.rs | 7 +++---- crates/networking/rpc/eth/gas_price.rs | 5 ++++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/networking/rpc/eth/fee_calculator.rs b/crates/networking/rpc/eth/fee_calculator.rs index 45215da0ff..e8f503ae78 100644 --- a/crates/networking/rpc/eth/fee_calculator.rs +++ b/crates/networking/rpc/eth/fee_calculator.rs @@ -12,7 +12,6 @@ const BLOCK_RANGE_LOWER_BOUND_DEC: u64 = 20; // The following comment is taken from the implementation of gas_price and is still valid, the logic was just moved here. // Disclaimer: - // This estimation is somewhat based on how currently go-ethereum does it. // Reference: https://github.com/ethereum/go-ethereum/blob/368e16f39d6c7e5cce72a92ec289adbfbaed4854/eth/gasprice/gasprice.go#L153 // Although it will (probably) not yield the same result. @@ -76,7 +75,7 @@ pub fn estimate_gas_tip(storage: &Store) -> Result, RpcErr> { results.sort(); match results.get(results.len() / 2) { - None => return Ok(None), - Some(gas) => return Ok(Some(*gas)), + None => Ok(None), + Some(gas) => Ok(Some(*gas)), } -} \ No newline at end of file +} diff --git a/crates/networking/rpc/eth/gas_price.rs b/crates/networking/rpc/eth/gas_price.rs index 03e091e4fe..91a9eee672 100644 --- a/crates/networking/rpc/eth/gas_price.rs +++ b/crates/networking/rpc/eth/gas_price.rs @@ -1,5 +1,7 @@ // Use fee_calculator mod in crates/networking/rpc/eth/ as gas_price +use std::cmp::max; + use crate::eth::fee_calculator::estimate_gas_tip; use ethrex_blockchain::constants::MIN_GAS_LIMIT; @@ -37,8 +39,9 @@ impl RpcHandler for GasPrice { // If we don't have the base fee, we'll use the minimum gas limit. let gas_price = match (estimated_gas_tip, base_fee) { (Some(gas_tip), Some(base_fee)) => gas_tip + base_fee, - (Some(gas_tip), None) => gas_tip, (None, Some(base_fee)) => base_fee, + // TODO: We might want to return null in this cases? + (Some(gas_tip), None) => max(gas_tip, MIN_GAS_LIMIT), (None, None) => MIN_GAS_LIMIT, }; From d8e9c8696818b304bbeaf1ddb4efbd3797543ca8 Mon Sep 17 00:00:00 2001 From: Rodrigo Oliveri Date: Tue, 7 Jan 2025 19:27:56 -0300 Subject: [PATCH 07/12] Fix blob github to add tx params --- .github/config/assertoor/network_params_blob.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/config/assertoor/network_params_blob.yaml b/.github/config/assertoor/network_params_blob.yaml index ea298c7f9a..a1fa335e5f 100644 --- a/.github/config/assertoor/network_params_blob.yaml +++ b/.github/config/assertoor/network_params_blob.yaml @@ -25,3 +25,7 @@ assertoor_params: tests: - https://raw.githubusercontent.com/ethpandaops/assertoor/refs/heads/master/playbooks/stable/blob-transactions-test.yaml - https://raw.githubusercontent.com/lambdaclass/ethrex/refs/heads/check-transaction-spammer-count-diff/.github/config/assertoor/el-stability-check.yaml + +tx_spammer_params: + # A list of optional extra params that will be passed to the TX Spammer container for modifying its behaviour + tx_spammer_extra_args: ["--txcount=3", "--accounts=80"] From 199e6fed5f2fa094e7df8c5f06a42d81554d7c9f Mon Sep 17 00:00:00 2001 From: Rodrigo Oliveri Date: Tue, 7 Jan 2025 20:08:25 -0300 Subject: [PATCH 08/12] Format --- crates/networking/rpc/eth/fee_calculator.rs | 16 +++++++--------- crates/networking/rpc/eth/max_priority_fee.rs | 3 +-- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/crates/networking/rpc/eth/fee_calculator.rs b/crates/networking/rpc/eth/fee_calculator.rs index e8f503ae78..e0305b6837 100644 --- a/crates/networking/rpc/eth/fee_calculator.rs +++ b/crates/networking/rpc/eth/fee_calculator.rs @@ -29,8 +29,7 @@ const BLOCK_RANGE_LOWER_BOUND_DEC: u64 = 20; /// as per the spec, this will be returned in wei. pub fn estimate_gas_tip(storage: &Store) -> Result, RpcErr> { let latest_block_number = storage.get_latest_block_number()?; - let block_range_lower_bound = - latest_block_number.saturating_sub(BLOCK_RANGE_LOWER_BOUND_DEC); + let block_range_lower_bound = latest_block_number.saturating_sub(BLOCK_RANGE_LOWER_BOUND_DEC); // These are the blocks we'll use to estimate the price. let block_range = block_range_lower_bound..=latest_block_number; if block_range.is_empty() { @@ -54,12 +53,11 @@ pub fn estimate_gas_tip(storage: &Store) -> Result, RpcErr> { )); }; - let base_fee = - storage - .get_block_header(latest_block_number) - .ok() - .flatten() - .and_then(|header| header.base_fee_per_gas); + let base_fee = storage + .get_block_header(latest_block_number) + .ok() + .flatten() + .and_then(|header| header.base_fee_per_gas); // Previously we took the gas_price, now we take the effective_gas_tip and add the base_fee in the RPC // call if needed. @@ -69,7 +67,7 @@ pub fn estimate_gas_tip(storage: &Store) -> Result, RpcErr> { .filter_map(|tx| tx.effective_gas_tip(base_fee)) .collect::>(); - gas_tip_samples.sort(); + gas_tip_samples.sort(); results.extend(gas_tip_samples.into_iter().take(TXS_SAMPLE_SIZE)); } results.sort(); diff --git a/crates/networking/rpc/eth/max_priority_fee.rs b/crates/networking/rpc/eth/max_priority_fee.rs index 1c4934fb55..5c6122974a 100644 --- a/crates/networking/rpc/eth/max_priority_fee.rs +++ b/crates/networking/rpc/eth/max_priority_fee.rs @@ -31,5 +31,4 @@ impl RpcHandler for MaxPriorityFee { } #[cfg(test)] -mod tests { -} +mod tests {} From 9b078dd6b7176c723e754af0662b36de17d2db69 Mon Sep 17 00:00:00 2001 From: Rodrigo Oliveri Date: Tue, 7 Jan 2025 21:00:52 -0300 Subject: [PATCH 09/12] Fixed the relation between max_priority_fee_per_gas and max_fee_per_gas in the eip1559 tx for test --- .github/config/assertoor/network_params_blob.yaml | 1 - crates/networking/rpc/eth/gas_price.rs | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/config/assertoor/network_params_blob.yaml b/.github/config/assertoor/network_params_blob.yaml index a1fa335e5f..4acd6ccf6b 100644 --- a/.github/config/assertoor/network_params_blob.yaml +++ b/.github/config/assertoor/network_params_blob.yaml @@ -27,5 +27,4 @@ assertoor_params: - https://raw.githubusercontent.com/lambdaclass/ethrex/refs/heads/check-transaction-spammer-count-diff/.github/config/assertoor/el-stability-check.yaml tx_spammer_params: - # A list of optional extra params that will be passed to the TX Spammer container for modifying its behaviour tx_spammer_extra_args: ["--txcount=3", "--accounts=80"] diff --git a/crates/networking/rpc/eth/gas_price.rs b/crates/networking/rpc/eth/gas_price.rs index 91a9eee672..3c15eccb3c 100644 --- a/crates/networking/rpc/eth/gas_price.rs +++ b/crates/networking/rpc/eth/gas_price.rs @@ -139,8 +139,8 @@ mod tests { Transaction::EIP1559Transaction(EIP1559Transaction { chain_id: 1, nonce, - max_fee_per_gas: nonce * BASE_PRICE_IN_WEI, - max_priority_fee_per_gas: (nonce * (10_u64.pow(9))).pow(2), + max_fee_per_gas: nonce * BASE_PRICE_IN_WEI * 2, + max_priority_fee_per_gas: nonce * BASE_PRICE_IN_WEI, gas_limit: 10000, to: TxKind::Create, value: 100.into(), From 9314cafb0d24ccbc8e3b4581c83789a8081a27b9 Mon Sep 17 00:00:00 2001 From: Rodrigo Oliveri Date: Tue, 7 Jan 2025 21:13:27 -0300 Subject: [PATCH 10/12] Removed old comment --- crates/networking/rpc/eth/gas_price.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/networking/rpc/eth/gas_price.rs b/crates/networking/rpc/eth/gas_price.rs index 3c15eccb3c..b859677b8d 100644 --- a/crates/networking/rpc/eth/gas_price.rs +++ b/crates/networking/rpc/eth/gas_price.rs @@ -1,5 +1,3 @@ -// Use fee_calculator mod in crates/networking/rpc/eth/ as gas_price - use std::cmp::max; use crate::eth::fee_calculator::estimate_gas_tip; From bfe8faffbc710ababe5e0cde5cde19c8d9c9fa08 Mon Sep 17 00:00:00 2001 From: Rodrigo Oliveri Date: Wed, 8 Jan 2025 15:35:52 -0300 Subject: [PATCH 11/12] Simplify and share test utilities across fee related tests --- crates/networking/rpc/eth/fee_calculator.rs | 41 +++ crates/networking/rpc/eth/gas_price.rs | 278 +++--------------- crates/networking/rpc/eth/max_priority_fee.rs | 104 ++++++- crates/networking/rpc/eth/mod.rs | 165 +++++++++++ 4 files changed, 355 insertions(+), 233 deletions(-) diff --git a/crates/networking/rpc/eth/fee_calculator.rs b/crates/networking/rpc/eth/fee_calculator.rs index e0305b6837..d5655dee49 100644 --- a/crates/networking/rpc/eth/fee_calculator.rs +++ b/crates/networking/rpc/eth/fee_calculator.rs @@ -77,3 +77,44 @@ pub fn estimate_gas_tip(storage: &Store) -> Result, RpcErr> { Some(gas) => Ok(Some(*gas)), } } + +// Tests for the estimate_gas_tip function. +#[cfg(test)] +mod tests { + use crate::eth::fee_calculator::estimate_gas_tip; + use crate::eth::test_utils::{ + add_eip1559_tx_blocks, add_legacy_tx_blocks, add_mixed_tx_blocks, setup_store, + BASE_PRICE_IN_WEI, + }; + + #[test] + fn test_for_legacy_txs() { + let storage = setup_store(); + add_legacy_tx_blocks(&storage, 20, 10); + let gas_tip = estimate_gas_tip(&storage).unwrap().unwrap(); + assert_eq!(gas_tip, BASE_PRICE_IN_WEI); + } + + #[test] + fn test_for_eip1559_txs() { + let storage = setup_store(); + add_eip1559_tx_blocks(&storage, 20, 10); + let gas_tip = estimate_gas_tip(&storage).unwrap().unwrap(); + assert_eq!(gas_tip, BASE_PRICE_IN_WEI); + } + + #[test] + fn test_for_mixed_txs() { + let storage = setup_store(); + add_mixed_tx_blocks(&storage, 20, 10); + let gas_tip = estimate_gas_tip(&storage).unwrap().unwrap(); + assert_eq!(gas_tip, BASE_PRICE_IN_WEI); + } + + #[test] + fn test_for_empty_blocks() { + let storage = setup_store(); + let gas_tip = estimate_gas_tip(&storage).unwrap(); + assert_eq!(gas_tip, None); + } +} diff --git a/crates/networking/rpc/eth/gas_price.rs b/crates/networking/rpc/eth/gas_price.rs index b859677b8d..fa43efbba1 100644 --- a/crates/networking/rpc/eth/gas_price.rs +++ b/crates/networking/rpc/eth/gas_price.rs @@ -1,7 +1,4 @@ -use std::cmp::max; - use crate::eth::fee_calculator::estimate_gas_tip; -use ethrex_blockchain::constants::MIN_GAS_LIMIT; use crate::utils::RpcErr; use crate::{RpcApiContext, RpcHandler}; @@ -34,13 +31,15 @@ impl RpcHandler for GasPrice { // To complete the gas price, we need to add the base fee to the estimated gas. // If we don't have the estimated gas, we'll use the base fee as the gas price. - // If we don't have the base fee, we'll use the minimum gas limit. + // If we don't have the base fee, we'll return an Error. let gas_price = match (estimated_gas_tip, base_fee) { (Some(gas_tip), Some(base_fee)) => gas_tip + base_fee, (None, Some(base_fee)) => base_fee, - // TODO: We might want to return null in this cases? - (Some(gas_tip), None) => max(gas_tip, MIN_GAS_LIMIT), - (None, None) => MIN_GAS_LIMIT, + (_, None) => { + return Err(RpcErr::Internal( + "Error calculating gas price: missing base_fee on block".to_string(), + )) + } }; let gas_as_hex = format!("0x{:x}", gas_price); @@ -51,240 +50,87 @@ impl RpcHandler for GasPrice { #[cfg(test)] mod tests { use super::GasPrice; + use crate::eth::test_utils::{ + add_eip1559_tx_blocks, add_legacy_tx_blocks, add_mixed_tx_blocks, setup_store, + BASE_PRICE_IN_WEI, + }; + use crate::{ map_http_requests, utils::{parse_json_hex, test_utils::example_p2p_node, RpcRequest}, RpcApiContext, RpcHandler, }; - use bytes::Bytes; - use ethrex_core::{ - types::{ - Block, BlockBody, BlockHeader, EIP1559Transaction, Genesis, LegacyTransaction, - Transaction, TxKind, - }, - Address, Bloom, H256, U256, - }; use ethrex_net::{sync::SyncManager, types::Node}; - use ethrex_storage::{EngineType, Store}; - use hex_literal::hex; use serde_json::json; - use std::{net::Ipv4Addr, str::FromStr, sync::Arc}; + use std::{net::Ipv4Addr, sync::Arc}; use tokio::sync::Mutex; - // Base price for each test transaction. - const BASE_PRICE_IN_WEI: u64 = 10_u64.pow(9); - fn test_header(block_num: u64) -> BlockHeader { - BlockHeader { - parent_hash: H256::from_str( - "0x1ac1bf1eef97dc6b03daba5af3b89881b7ae4bc1600dc434f450a9ec34d44999", - ) - .unwrap(), - ommers_hash: H256::from_str( - "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - ) - .unwrap(), - coinbase: Address::from_str("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba").unwrap(), - state_root: H256::from_str( - "0x9de6f95cb4ff4ef22a73705d6ba38c4b927c7bca9887ef5d24a734bb863218d9", - ) - .unwrap(), - transactions_root: H256::from_str( - "0x578602b2b7e3a3291c3eefca3a08bc13c0d194f9845a39b6f3bcf843d9fed79d", - ) - .unwrap(), - receipts_root: H256::from_str( - "0x035d56bac3f47246c5eed0e6642ca40dc262f9144b582f058bc23ded72aa72fa", - ) - .unwrap(), - logs_bloom: Bloom::from([0; 256]), - difficulty: U256::zero(), - number: block_num, - gas_limit: 0x016345785d8a0000, - gas_used: 0xa8de, - timestamp: 0x03e8, - extra_data: Bytes::new(), - prev_randao: H256::zero(), - nonce: 0x0000000000000000, - base_fee_per_gas: None, - withdrawals_root: Some( - H256::from_str( - "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - ) - .unwrap(), - ), - blob_gas_used: Some(0x00), - excess_blob_gas: Some(0x00), - parent_beacon_block_root: Some(H256::zero()), + + fn default_context() -> RpcApiContext { + RpcApiContext { + storage: setup_store(), + jwt_secret: Default::default(), + local_p2p_node: Node { + ip: std::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), + udp_port: Default::default(), + tcp_port: Default::default(), + node_id: Default::default(), + }, + active_filters: Default::default(), + syncer: Arc::new(Mutex::new(SyncManager::dummy())), } } - fn legacy_tx_for_test(nonce: u64) -> Transaction { - Transaction::LegacyTransaction(LegacyTransaction { - nonce, - gas_price: nonce * BASE_PRICE_IN_WEI, - gas: 10000, - to: TxKind::Create, - value: 100.into(), - data: Default::default(), - v: U256::from(0x1b), - r: U256::from_big_endian(&hex!( - "7e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37" - )), - s: U256::from_big_endian(&hex!( - "5f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509b" - )), - }) - } - fn eip1559_tx_for_test(nonce: u64) -> Transaction { - Transaction::EIP1559Transaction(EIP1559Transaction { - chain_id: 1, - nonce, - max_fee_per_gas: nonce * BASE_PRICE_IN_WEI * 2, - max_priority_fee_per_gas: nonce * BASE_PRICE_IN_WEI, - gas_limit: 10000, - to: TxKind::Create, - value: 100.into(), - data: Default::default(), - access_list: vec![], - signature_y_parity: true, - signature_r: U256::default(), - signature_s: U256::default(), - }) - } - fn setup_store() -> Store { - let genesis: &str = include_str!("../../../../test_data/genesis-l1.json"); - let genesis: Genesis = - serde_json::from_str(genesis).expect("Fatal: test config is invalid"); - let store = Store::new("test-store", EngineType::InMemory) - .expect("Fail to create in-memory db test"); - store.add_initial_state(genesis).unwrap(); - store - } + #[test] fn test_for_legacy_txs() { let context = default_context(); - for block_num in 1..100 { - let mut txs = vec![]; - for nonce in 1..=3 { - let legacy_tx = legacy_tx_for_test(nonce); - txs.push(legacy_tx) - } - let block_body = BlockBody { - transactions: txs, - ommers: Default::default(), - withdrawals: Default::default(), - }; - let block_header = test_header(block_num); - let block = Block::new(block_header.clone(), block_body); - context.storage.add_block(block).unwrap(); - context - .storage - .set_canonical_block(block_num, block_header.compute_block_hash()) - .unwrap(); - context - .storage - .update_latest_block_number(block_num) - .unwrap(); - } + + add_legacy_tx_blocks(&context.storage, 100, 10); + let gas_price = GasPrice {}; let response = gas_price.handle(context).unwrap(); let parsed_result = parse_json_hex(&response).unwrap(); - assert_eq!(parsed_result, 2000000000); + assert_eq!(parsed_result, 2 * BASE_PRICE_IN_WEI); } #[test] fn test_for_eip_1559_txs() { let context = default_context(); - for block_num in 1..100 { - let mut txs = vec![]; - for nonce in 1..=3 { - txs.push(eip1559_tx_for_test(nonce)); - } - let block_body = BlockBody { - transactions: txs, - ommers: Default::default(), - withdrawals: Default::default(), - }; - let block_header = test_header(block_num); - let block = Block::new(block_header.clone(), block_body); - context.storage.add_block(block).unwrap(); - context - .storage - .set_canonical_block(block_num, block_header.compute_block_hash()) - .unwrap(); - context - .storage - .update_latest_block_number(block_num) - .unwrap(); - } + + add_eip1559_tx_blocks(&context.storage, 100, 10); + let gas_price = GasPrice {}; let response = gas_price.handle(context).unwrap(); let parsed_result = parse_json_hex(&response).unwrap(); - assert_eq!(parsed_result, 2000000000); + assert_eq!(parsed_result, 2 * BASE_PRICE_IN_WEI); } #[test] fn test_with_mixed_transactions() { let context = default_context(); - for block_num in 1..100 { - let txs = vec![ - legacy_tx_for_test(1), - eip1559_tx_for_test(2), - legacy_tx_for_test(3), - eip1559_tx_for_test(4), - ]; - let block_body = BlockBody { - transactions: txs, - ommers: Default::default(), - withdrawals: Default::default(), - }; - let block_header = test_header(block_num); - let block = Block::new(block_header.clone(), block_body); - context.storage.add_block(block).unwrap(); - context - .storage - .set_canonical_block(block_num, block_header.compute_block_hash()) - .unwrap(); - context - .storage - .update_latest_block_number(block_num) - .unwrap(); - } + + add_mixed_tx_blocks(&context.storage, 100, 10); + let gas_price = GasPrice {}; let response = gas_price.handle(context).unwrap(); let parsed_result = parse_json_hex(&response).unwrap(); - assert_eq!(parsed_result, 2000000000); + assert_eq!(parsed_result, 2 * BASE_PRICE_IN_WEI); } #[test] fn test_with_not_enough_blocks_or_transactions() { let context = default_context(); - for block_num in 1..10 { - let txs = vec![legacy_tx_for_test(1)]; - let block_body = BlockBody { - transactions: txs, - ommers: Default::default(), - withdrawals: Default::default(), - }; - let block_header = test_header(block_num); - let block = Block::new(block_header.clone(), block_body); - context.storage.add_block(block).unwrap(); - context - .storage - .set_canonical_block(block_num, block_header.compute_block_hash()) - .unwrap(); - context - .storage - .update_latest_block_number(block_num) - .unwrap(); - } + + add_mixed_tx_blocks(&context.storage, 100, 0); + let gas_price = GasPrice {}; let response = gas_price.handle(context).unwrap(); let parsed_result = parse_json_hex(&response).unwrap(); - assert_eq!(parsed_result, 1000000000); + assert_eq!(parsed_result, BASE_PRICE_IN_WEI); } #[test] fn test_with_no_blocks_but_genesis() { let context = default_context(); let gas_price = GasPrice {}; - // genesis base fee is 1_000_000_000 - let expected_gas_price = 1_000_000_000; + // genesis base fee is = BASE_PRICE_IN_WEI + let expected_gas_price = BASE_PRICE_IN_WEI; let response = gas_price.handle(context).unwrap(); let parsed_result = parse_json_hex(&response).unwrap(); assert_eq!(parsed_result, expected_gas_price); @@ -302,41 +148,9 @@ mod tests { let mut context = default_context(); context.local_p2p_node = example_p2p_node(); - for block_num in 1..100 { - let txs = vec![legacy_tx_for_test(1)]; - let block_body = BlockBody { - transactions: txs, - ommers: Default::default(), - withdrawals: Default::default(), - }; - let block_header = test_header(block_num); - let block = Block::new(block_header.clone(), block_body); - context.storage.add_block(block).unwrap(); - context - .storage - .set_canonical_block(block_num, block_header.compute_block_hash()) - .unwrap(); - context - .storage - .update_latest_block_number(block_num) - .unwrap(); - } + add_legacy_tx_blocks(&context.storage, 100, 1); + let response = map_http_requests(&request, context).unwrap(); assert_eq!(response, expected_response) } - - fn default_context() -> RpcApiContext { - RpcApiContext { - storage: setup_store(), - jwt_secret: Default::default(), - local_p2p_node: Node { - ip: std::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), - udp_port: Default::default(), - tcp_port: Default::default(), - node_id: Default::default(), - }, - active_filters: Default::default(), - syncer: Arc::new(Mutex::new(SyncManager::dummy())), - } - } } diff --git a/crates/networking/rpc/eth/max_priority_fee.rs b/crates/networking/rpc/eth/max_priority_fee.rs index 5c6122974a..fbfabbaa68 100644 --- a/crates/networking/rpc/eth/max_priority_fee.rs +++ b/crates/networking/rpc/eth/max_priority_fee.rs @@ -31,4 +31,106 @@ impl RpcHandler for MaxPriorityFee { } #[cfg(test)] -mod tests {} +mod tests { + use super::MaxPriorityFee; + use crate::eth::test_utils::{ + add_eip1559_tx_blocks, add_legacy_tx_blocks, add_mixed_tx_blocks, setup_store, + BASE_PRICE_IN_WEI, + }; + + use crate::{ + map_http_requests, + utils::{parse_json_hex, test_utils::example_p2p_node, RpcRequest}, + RpcApiContext, RpcHandler, + }; + use ethrex_net::{sync::SyncManager, types::Node}; + use serde_json::{json, Value}; + use std::{net::Ipv4Addr, sync::Arc}; + use tokio::sync::Mutex; + + fn default_context() -> RpcApiContext { + RpcApiContext { + storage: setup_store(), + jwt_secret: Default::default(), + local_p2p_node: Node { + ip: std::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), + udp_port: Default::default(), + tcp_port: Default::default(), + node_id: Default::default(), + }, + active_filters: Default::default(), + syncer: Arc::new(Mutex::new(SyncManager::dummy())), + } + } + + #[test] + fn test_for_legacy_txs() { + let context = default_context(); + + add_legacy_tx_blocks(&context.storage, 100, 10); + + let gas_price = MaxPriorityFee {}; + let response = gas_price.handle(context).unwrap(); + let parsed_result = parse_json_hex(&response).unwrap(); + assert_eq!(parsed_result, BASE_PRICE_IN_WEI); + } + + #[test] + fn test_for_eip_1559_txs() { + let context = default_context(); + + add_eip1559_tx_blocks(&context.storage, 100, 10); + + let gas_price = MaxPriorityFee {}; + let response = gas_price.handle(context).unwrap(); + let parsed_result = parse_json_hex(&response).unwrap(); + assert_eq!(parsed_result, BASE_PRICE_IN_WEI); + } + #[test] + fn test_with_mixed_transactions() { + let context = default_context(); + + add_mixed_tx_blocks(&context.storage, 100, 10); + + let gas_price = MaxPriorityFee {}; + let response = gas_price.handle(context).unwrap(); + let parsed_result = parse_json_hex(&response).unwrap(); + assert_eq!(parsed_result, BASE_PRICE_IN_WEI); + } + #[test] + fn test_with_not_enough_blocks_or_transactions() { + let context = default_context(); + + add_mixed_tx_blocks(&context.storage, 100, 0); + + let gas_price = MaxPriorityFee {}; + let response = gas_price.handle(context).unwrap(); + assert_eq!(response, Value::Null); + } + #[test] + fn test_with_no_blocks_but_genesis() { + let context = default_context(); + let gas_price = MaxPriorityFee {}; + + let response = gas_price.handle(context).unwrap(); + assert_eq!(response, Value::Null); + } + #[test] + fn request_smoke_test() { + let raw_json = json!( + { + "jsonrpc":"2.0", + "method":"eth_maxPriorityFeePerGas", + "id":1 + }); + let expected_response = json!("0x3b9aca00"); + let request: RpcRequest = serde_json::from_value(raw_json).expect("Test json is not valid"); + let mut context = default_context(); + context.local_p2p_node = example_p2p_node(); + + add_eip1559_tx_blocks(&context.storage, 100, 3); + + let response = map_http_requests(&request, context).unwrap(); + assert_eq!(response, expected_response) + } +} diff --git a/crates/networking/rpc/eth/mod.rs b/crates/networking/rpc/eth/mod.rs index aebd7fdadd..7c83cacf54 100644 --- a/crates/networking/rpc/eth/mod.rs +++ b/crates/networking/rpc/eth/mod.rs @@ -9,3 +9,168 @@ pub(crate) mod transaction; mod fee_calculator; pub(crate) mod gas_price; pub(crate) mod max_priority_fee; + +#[cfg(test)] +pub mod test_utils { + use bytes::Bytes; + use ethrex_core::{ + types::{ + Block, BlockBody, BlockHeader, EIP1559Transaction, Genesis, LegacyTransaction, + Transaction, TxKind, + }, + Address, Bloom, H256, U256, + }; + use ethrex_storage::{EngineType, Store}; + use hex_literal::hex; + use std::str::FromStr; + + // Base price for each test transaction. + pub const BASE_PRICE_IN_WEI: u64 = 10_u64.pow(9); + + fn test_header(block_num: u64) -> BlockHeader { + BlockHeader { + parent_hash: H256::from_str( + "0x1ac1bf1eef97dc6b03daba5af3b89881b7ae4bc1600dc434f450a9ec34d44999", + ) + .unwrap(), + ommers_hash: H256::from_str( + "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + ) + .unwrap(), + coinbase: Address::from_str("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba").unwrap(), + state_root: H256::from_str( + "0x9de6f95cb4ff4ef22a73705d6ba38c4b927c7bca9887ef5d24a734bb863218d9", + ) + .unwrap(), + transactions_root: H256::from_str( + "0x578602b2b7e3a3291c3eefca3a08bc13c0d194f9845a39b6f3bcf843d9fed79d", + ) + .unwrap(), + receipts_root: H256::from_str( + "0x035d56bac3f47246c5eed0e6642ca40dc262f9144b582f058bc23ded72aa72fa", + ) + .unwrap(), + logs_bloom: Bloom::from([0; 256]), + difficulty: U256::zero(), + number: block_num, + gas_limit: 0x016345785d8a0000, + gas_used: 0xa8de, + timestamp: 0x03e8, + extra_data: Bytes::new(), + prev_randao: H256::zero(), + nonce: 0x0000000000000000, + base_fee_per_gas: Some(BASE_PRICE_IN_WEI), + withdrawals_root: Some( + H256::from_str( + "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + ) + .unwrap(), + ), + blob_gas_used: Some(0x00), + excess_blob_gas: Some(0x00), + parent_beacon_block_root: Some(H256::zero()), + } + } + + fn add_blocks_with_transactions( + storage: &Store, + block_count: u64, + txs_per_block: Vec, + ) { + for block_num in 1..=block_count { + let block_body = BlockBody { + transactions: txs_per_block.clone(), + ommers: Default::default(), + withdrawals: Default::default(), + }; + let block_header = test_header(block_num); + let block = Block::new(block_header.clone(), block_body); + storage.add_block(block).unwrap(); + storage + .set_canonical_block(block_num, block_header.compute_block_hash()) + .unwrap(); + storage.update_latest_block_number(block_num).unwrap(); + } + } + + fn legacy_tx_for_test(nonce: u64) -> Transaction { + Transaction::LegacyTransaction(LegacyTransaction { + nonce, + gas_price: nonce * BASE_PRICE_IN_WEI, + gas: 10000, + to: TxKind::Create, + value: 100.into(), + data: Default::default(), + v: U256::from(0x1b), + r: U256::from_big_endian(&hex!( + "7e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37" + )), + s: U256::from_big_endian(&hex!( + "5f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509b" + )), + }) + } + fn eip1559_tx_for_test(nonce: u64) -> Transaction { + Transaction::EIP1559Transaction(EIP1559Transaction { + chain_id: 1, + nonce, + max_fee_per_gas: nonce * BASE_PRICE_IN_WEI, + // This is less than gas_price in legacy txs because we should add base_fee to it + // base_fee is 10^9, so (nonce - 1) * 10^9 + base_fee equals the legacy gas_price + // for the same nonce. For consistency, we use the same value here. + max_priority_fee_per_gas: (nonce - 1) * BASE_PRICE_IN_WEI, + gas_limit: 10000, + to: TxKind::Create, + value: 100.into(), + data: Default::default(), + access_list: vec![], + signature_y_parity: true, + signature_r: U256::default(), + signature_s: U256::default(), + }) + } + + pub fn setup_store() -> Store { + let genesis: &str = include_str!("../../../../test_data/genesis-l1.json"); + let genesis: Genesis = + serde_json::from_str(genesis).expect("Fatal: test config is invalid"); + let store = Store::new("test-store", EngineType::InMemory) + .expect("Fail to create in-memory db test"); + store.add_initial_state(genesis).unwrap(); + store + } + + pub fn add_legacy_tx_blocks(storage: &Store, block_count: u64, tx_count: u64) { + for block_num in 1..=block_count { + let mut txs = vec![]; + for nonce in 1..=tx_count { + txs.push(legacy_tx_for_test(nonce)); + } + add_blocks_with_transactions(storage, block_num, txs); + } + } + + pub fn add_eip1559_tx_blocks(storage: &Store, block_count: u64, tx_count: u64) { + for block_num in 1..=block_count { + let mut txs = vec![]; + for nonce in 1..=tx_count { + txs.push(eip1559_tx_for_test(nonce)); + } + add_blocks_with_transactions(storage, block_num, txs); + } + } + + pub fn add_mixed_tx_blocks(storage: &Store, block_count: u64, tx_count: u64) { + for block_num in 1..=block_count { + let mut txs = vec![]; + for nonce in 1..=tx_count { + if nonce % 2 == 0 { + txs.push(legacy_tx_for_test(nonce)); + } else { + txs.push(eip1559_tx_for_test(nonce)); + } + } + add_blocks_with_transactions(storage, block_num, txs); + } + } +} From b6ecbca4b81ddd121c0aeb3066e59618a9b2d35b Mon Sep 17 00:00:00 2001 From: Rodrigo Oliveri Date: Wed, 8 Jan 2025 16:58:33 -0300 Subject: [PATCH 12/12] Point to main for the el-stability-check and fix a typo in a title --- .github/config/assertoor/el-stability-check.yaml | 2 +- .github/config/assertoor/network_params_blob.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/config/assertoor/el-stability-check.yaml b/.github/config/assertoor/el-stability-check.yaml index 99bb3b32dc..998814d764 100644 --- a/.github/config/assertoor/el-stability-check.yaml +++ b/.github/config/assertoor/el-stability-check.yaml @@ -37,7 +37,7 @@ tasks: validatorNamePattern: "validatorPairName" - name: check_consensus_block_proposals - title: "Check the tx spammer is working as expected for block proposal with >= 50 transactions" + title: "Check the tx spammer is working as expected for block proposal with >= 240 transactions" timeout: 3m config: minTransactionCount: 240 diff --git a/.github/config/assertoor/network_params_blob.yaml b/.github/config/assertoor/network_params_blob.yaml index 4acd6ccf6b..9983e779b1 100644 --- a/.github/config/assertoor/network_params_blob.yaml +++ b/.github/config/assertoor/network_params_blob.yaml @@ -24,7 +24,7 @@ assertoor_params: run_block_proposal_check: false tests: - https://raw.githubusercontent.com/ethpandaops/assertoor/refs/heads/master/playbooks/stable/blob-transactions-test.yaml - - https://raw.githubusercontent.com/lambdaclass/ethrex/refs/heads/check-transaction-spammer-count-diff/.github/config/assertoor/el-stability-check.yaml + - https://raw.githubusercontent.com/lambdaclass/ethrex/refs/heads/main/.github/config/assertoor/el-stability-check.yaml tx_spammer_params: tx_spammer_extra_args: ["--txcount=3", "--accounts=80"]