From 0849179114e7d8f62f6f601ae8c2d820ed0673a1 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 27 Nov 2024 23:18:25 +0400 Subject: [PATCH 1/7] wip --- crates/e2e-test-utils/src/lib.rs | 15 ++- crates/e2e-test-utils/src/node.rs | 47 ++++++++- crates/ethereum/node/tests/e2e/p2p.rs | 132 ++++++++---------------- crates/ethereum/node/tests/e2e/utils.rs | 115 ++++++++++++++++++++- crates/optimism/node/src/utils.rs | 10 +- 5 files changed, 215 insertions(+), 104 deletions(-) diff --git a/crates/e2e-test-utils/src/lib.rs b/crates/e2e-test-utils/src/lib.rs index 15065377fabf..b8a52e1ee2db 100644 --- a/crates/e2e-test-utils/src/lib.rs +++ b/crates/e2e-test-utils/src/lib.rs @@ -1,6 +1,7 @@ //! Utilities for end-to-end tests. use node::NodeTestContext; +use reth_provider::HeaderProvider; use reth_chainspec::EthChainSpec; use reth_db::{test_utils::TempDatabase, DatabaseEnv}; use reth_engine_local::LocalPayloadAttributesBuilder; @@ -53,7 +54,7 @@ pub async fn setup( chain_spec: Arc, is_dev: bool, attributes_generator: impl Fn(u64) -> <::Engine as PayloadTypes>::PayloadBuilderAttributes + Copy + 'static, -) -> eyre::Result<(Vec>, TaskManager, Wallet)> +) -> eyre::Result<(Vec>, TaskManager, Wallet)> where N: Default + Node> + NodeTypesForTree + NodeTypesWithEngine, N::ComponentsBuilder: NodeComponentsBuilder< @@ -115,7 +116,7 @@ pub async fn setup_engine( is_dev: bool, attributes_generator: impl Fn(u64) -> <::Engine as PayloadTypes>::PayloadBuilderAttributes + Copy + 'static, ) -> eyre::Result<( - Vec>>>, + Vec>>>, TaskManager, Wallet, )> @@ -183,6 +184,9 @@ where let mut node = NodeTestContext::new(node, attributes_generator).await?; + let first_block = node.inner.provider.sealed_header(0)?.unwrap().hash(); + node.engine_api.update_forkchoice(first_block, first_block).await?; + // Connect each node in a chain. if let Some(previous_node) = nodes.last_mut() { previous_node.connect(&mut node).await; @@ -203,7 +207,8 @@ where // Type aliases -type TmpDB = Arc>; +/// Testing database +pub type TmpDB = Arc>; type TmpNodeAdapter>> = FullNodeTypesAdapter, Provider>; @@ -216,5 +221,5 @@ pub type Adapter; /// Type alias for a type of `NodeHelper` -pub type NodeHelperType>> = - NodeTestContext, AO>; +pub type NodeHelperType>> = + NodeTestContext, >>::AddOns>; diff --git a/crates/e2e-test-utils/src/node.rs b/crates/e2e-test-utils/src/node.rs index b3eb641c1371..9957d34574c5 100644 --- a/crates/e2e-test-utils/src/node.rs +++ b/crates/e2e-test-utils/src/node.rs @@ -8,6 +8,7 @@ use alloy_rpc_types_engine::PayloadStatusEnum; use alloy_rpc_types_eth::BlockNumberOrTag; use eyre::Ok; use futures_util::Future; +use alloy_eips::BlockId; use reth_chainspec::EthereumHardforks; use reth_network_api::test_utils::PeersHandleProvider; use reth_node_api::{Block, EngineTypes, FullNodeComponents}; @@ -134,8 +135,8 @@ where Ok((self.payload.expect_built_payload().await?, eth_attr)) } - /// Advances the node forward one block - pub async fn advance_block( + /// Triggers payload building job and submits it to the engine. + pub async fn build_and_submit_payload( &mut self, ) -> eyre::Result<(Engine::BuiltPayload, Engine::PayloadBuilderAttributes)> where @@ -146,13 +147,27 @@ where { let (payload, eth_attr) = self.new_payload().await?; - let block_hash = self - .engine_api + self.engine_api .submit_payload(payload.clone(), eth_attr.clone(), PayloadStatusEnum::Valid) .await?; + Ok((payload, eth_attr)) + } + + /// Advances the node forward one block + pub async fn advance_block( + &mut self, + ) -> eyre::Result<(Engine::BuiltPayload, Engine::PayloadBuilderAttributes)> + where + ::ExecutionPayloadEnvelopeV3: + From + PayloadEnvelopeExt, + ::ExecutionPayloadEnvelopeV4: + From + PayloadEnvelopeExt, + { + let (payload, eth_attr) = self.build_and_submit_payload().await?; + // trigger forkchoice update via engine api to commit the block to the blockchain - self.engine_api.update_forkchoice(block_hash, block_hash).await?; + self.engine_api.update_forkchoice(payload.block().hash(), payload.block().hash()).await?; Ok((payload, eth_attr)) } @@ -238,6 +253,28 @@ where Ok(()) } + /// Sends FCU and waits for the node to sync to the given block. + pub async fn sync_to(&self, block: BlockHash) -> eyre::Result<()> { + self.engine_api.update_forkchoice(block, block).await?; + + let start = std::time::Instant::now(); + + while !self + .inner + .provider + .sealed_header_by_id(BlockId::Number(BlockNumberOrTag::Latest))? + .map_or(false, |h| { + h.hash() == block + }) + { + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + + assert!(start.elapsed() <= std::time::Duration::from_secs(10), "timed out"); + } + + Ok(()) + } + /// Returns the RPC URL. pub fn rpc_url(&self) -> Url { let addr = self.inner.rpc_server_handle().http_local_addr().unwrap(); diff --git a/crates/ethereum/node/tests/e2e/p2p.rs b/crates/ethereum/node/tests/e2e/p2p.rs index f8680f47ae3e..1723c4a8f36e 100644 --- a/crates/ethereum/node/tests/e2e/p2p.rs +++ b/crates/ethereum/node/tests/e2e/p2p.rs @@ -1,19 +1,11 @@ -use crate::utils::eth_payload_attributes; -use alloy_consensus::TxType; -use alloy_primitives::bytes; +use crate::utils::{advance_with_random_transactions, eth_payload_attributes}; use alloy_provider::{ - network::{ - Ethereum, EthereumWallet, NetworkWallet, TransactionBuilder, TransactionBuilder7702, - }, - Provider, ProviderBuilder, SendableTx, + Provider, ProviderBuilder, }; -use alloy_rpc_types_eth::TransactionRequest; -use alloy_signer::SignerSync; -use rand::{rngs::StdRng, seq::SliceRandom, Rng, SeedableRng}; +use rand::{rngs::StdRng,Rng, SeedableRng}; use reth_chainspec::{ChainSpecBuilder, MAINNET}; use reth_e2e_test_utils::{setup, setup_engine, transaction::TransactionTestContext}; use reth_node_ethereum::EthereumNode; -use revm::primitives::{AccessListItem, Authorization}; use std::sync::Arc; #[tokio::test] @@ -76,80 +68,12 @@ async fn e2e_test_send_transactions() -> eyre::Result<()> { .build(), ); - let (mut nodes, _tasks, wallet) = + let (mut nodes, _tasks, _) = setup_engine::(2, chain_spec.clone(), false, eth_payload_attributes).await?; let mut node = nodes.pop().unwrap(); - let signers = wallet.gen(); let provider = ProviderBuilder::new().with_recommended_fillers().on_http(node.rpc_url()); - // simple contract which writes to storage on any call - let dummy_bytecode = bytes!("6080604052348015600f57600080fd5b50602880601d6000396000f3fe4360a09081523360c0526040608081905260e08152902080805500fea164736f6c6343000810000a"); - let mut call_destinations = signers.iter().map(|s| s.address()).collect::>(); - - // Produce 100 random blocks with random transactions - for _ in 0..100 { - let tx_count = rng.gen_range(1..20); - - let mut pending = vec![]; - for _ in 0..tx_count { - let signer = signers.choose(&mut rng).unwrap(); - let tx_type = TxType::try_from(rng.gen_range(0..=4)).unwrap(); - - let mut tx = TransactionRequest::default().with_from(signer.address()); - - let should_create = - rng.gen::() && tx_type != TxType::Eip4844 && tx_type != TxType::Eip7702; - if should_create { - tx = tx.into_create().with_input(dummy_bytecode.clone()); - } else { - tx = tx.with_to(*call_destinations.choose(&mut rng).unwrap()).with_input( - (0..rng.gen_range(0..10000)).map(|_| rng.gen()).collect::>(), - ); - } - - if matches!(tx_type, TxType::Legacy | TxType::Eip2930) { - tx = tx.with_gas_price(provider.get_gas_price().await?); - } - - if rng.gen::() || tx_type == TxType::Eip2930 { - tx = tx.with_access_list( - vec![AccessListItem { - address: *call_destinations.choose(&mut rng).unwrap(), - storage_keys: (0..rng.gen_range(0..100)).map(|_| rng.gen()).collect(), - }] - .into(), - ); - } - - if tx_type == TxType::Eip7702 { - let signer = signers.choose(&mut rng).unwrap(); - let auth = Authorization { - chain_id: provider.get_chain_id().await?, - address: *call_destinations.choose(&mut rng).unwrap(), - nonce: provider.get_transaction_count(signer.address()).await?, - }; - let sig = signer.sign_hash_sync(&auth.signature_hash())?; - tx = tx.with_authorization_list(vec![auth.into_signed(sig)]) - } - - let SendableTx::Builder(tx) = provider.fill(tx).await? else { unreachable!() }; - let tx = - NetworkWallet::::sign_request(&EthereumWallet::new(signer.clone()), tx) - .await?; - - pending.push(provider.send_tx_envelope(tx).await?); - } - - let (payload, _) = node.advance_block().await?; - assert!(payload.block().raw_transactions().len() == tx_count); - - for pending in pending { - let receipt = pending.get_receipt().await?; - if let Some(address) = receipt.contract_address { - call_destinations.push(address); - } - } - } + advance_with_random_transactions(&mut node, 100, &mut rng, true).await?; let second_node = nodes.pop().unwrap(); let second_provider = @@ -159,15 +83,49 @@ async fn e2e_test_send_transactions() -> eyre::Result<()> { let head = provider.get_block_by_number(Default::default(), false.into()).await?.unwrap().header.hash; - second_node.engine_api.update_forkchoice(head, head).await?; - let start = std::time::Instant::now(); + second_node.sync_to(head).await?; - while provider.get_block_number().await? != second_provider.get_block_number().await? { - tokio::time::sleep(std::time::Duration::from_millis(100)).await; + Ok(()) +} + +#[tokio::test] +async fn test_long_reorg() -> eyre::Result<()> { + reth_tracing::init_test_tracing(); + + let seed: [u8; 32] = rand::thread_rng().gen(); + let mut rng = StdRng::from_seed(seed); + println!("Seed: {:?}", seed); + + let chain_spec = Arc::new( + ChainSpecBuilder::default() + .chain(MAINNET.chain) + .genesis(serde_json::from_str(include_str!("../assets/genesis.json")).unwrap()) + .cancun_activated() + .prague_activated() + .build(), + ); + + let (mut nodes, _tasks, _) = + setup_engine::(2, chain_spec.clone(), false, eth_payload_attributes).await?; + + let mut first_node = nodes.pop().unwrap(); + let mut second_node = nodes.pop().unwrap(); + + let first_priovider = ProviderBuilder::new().on_http(first_node.rpc_url()); + let second_provider = ProviderBuilder::new().on_http(second_node.rpc_url()); + + advance_with_random_transactions(&mut first_node, 100, &mut rng, false).await?; + let head = first_priovider.get_block_by_number(20.into(), false.into()).await?.unwrap().header.hash; + second_node.sync_to(head).await?; + + advance_with_random_transactions(&mut second_node, 40, &mut rng, true).await?; + let reorg_head = second_provider.get_block_by_number(60.into(), false.into()).await?.unwrap().header.hash; + first_node.sync_to(reorg_head).await?; - assert!(start.elapsed() <= std::time::Duration::from_secs(10), "timed out"); - } + advance_with_random_transactions(&mut second_node, 20, &mut rng, true).await?; + let canonical_head = second_provider.get_block_by_number(80.into(), false.into()).await?.unwrap().header.hash; + first_node.sync_to(canonical_head).await?; Ok(()) } diff --git a/crates/ethereum/node/tests/e2e/utils.rs b/crates/ethereum/node/tests/e2e/utils.rs index c3743de185f5..811eb4ab00de 100644 --- a/crates/ethereum/node/tests/e2e/utils.rs +++ b/crates/ethereum/node/tests/e2e/utils.rs @@ -1,6 +1,22 @@ -use alloy_primitives::{Address, B256}; +use alloy_eips::BlockNumberOrTag; +use alloy_primitives::{bytes, Address, B256}; +use alloy_provider::{ + network::{ + Ethereum, EthereumWallet, NetworkWallet, TransactionBuilder, TransactionBuilder7702, + }, + Provider, ProviderBuilder, SendableTx, +}; use alloy_rpc_types_engine::PayloadAttributes; +use alloy_rpc_types_eth::TransactionRequest; +use alloy_signer::SignerSync; +use rand::{seq::SliceRandom, Rng}; +use reth_e2e_test_utils::{wallet::Wallet, NodeHelperType, TmpDB}; +use reth_node_api::NodeTypesWithDBAdapter; +use reth_node_ethereum::EthereumNode; use reth_payload_builder::EthPayloadBuilderAttributes; +use reth_primitives::TxType; +use reth_provider::FullProvider; +use revm::primitives::{AccessListItem, Authorization}; /// Helper function to create a new eth payload attributes pub(crate) fn eth_payload_attributes(timestamp: u64) -> EthPayloadBuilderAttributes { @@ -13,3 +29,100 @@ pub(crate) fn eth_payload_attributes(timestamp: u64) -> EthPayloadBuilderAttribu }; EthPayloadBuilderAttributes::new(B256::ZERO, attributes) } + +/// Advances node by producing blocks with random transactions. +pub(crate) async fn advance_with_random_transactions( + node: &mut NodeHelperType, + num_blocks: usize, + rng: &mut impl Rng, + finalize: bool, +) -> eyre::Result<()> +where + Provider: FullProvider>, +{ + let provider = ProviderBuilder::new().with_recommended_fillers().on_http(node.rpc_url()); + let signers = Wallet::new(1).with_chain_id(provider.get_chain_id().await?).gen(); + + // simple contract which writes to storage on any call + let dummy_bytecode = bytes!("6080604052348015600f57600080fd5b50602880601d6000396000f3fe4360a09081523360c0526040608081905260e08152902080805500fea164736f6c6343000810000a"); + let mut call_destinations = signers.iter().map(|s| s.address()).collect::>(); + + for _ in 0..num_blocks { + let tx_count = rng.gen_range(1..20); + + let mut pending = vec![]; + for _ in 0..tx_count { + let signer = signers.choose(rng).unwrap(); + let tx_type = TxType::try_from(rng.gen_range(0..=4) as u64).unwrap(); + + let mut tx = TransactionRequest::default().with_from(signer.address()); + + let should_create = + rng.gen::() && tx_type != TxType::Eip4844 && tx_type != TxType::Eip7702; + if should_create { + tx = tx.into_create().with_input(dummy_bytecode.clone()); + } else { + tx = tx.with_to(*call_destinations.choose(rng).unwrap()).with_input( + (0..rng.gen_range(0..10000)).map(|_| rng.gen()).collect::>(), + ); + } + + if matches!(tx_type, TxType::Legacy | TxType::Eip2930) { + tx = tx.with_gas_price(provider.get_gas_price().await?); + } + + if rng.gen::() || tx_type == TxType::Eip2930 { + tx = tx.with_access_list( + vec![AccessListItem { + address: *call_destinations.choose(rng).unwrap(), + storage_keys: (0..rng.gen_range(0..100)).map(|_| rng.gen()).collect(), + }] + .into(), + ); + } + + if tx_type == TxType::Eip7702 { + let signer = signers.choose(rng).unwrap(); + let auth = Authorization { + chain_id: provider.get_chain_id().await?, + address: *call_destinations.choose(rng).unwrap(), + nonce: provider.get_transaction_count(signer.address()).await?, + }; + let sig = signer.sign_hash_sync(&auth.signature_hash())?; + tx = tx.with_authorization_list(vec![auth.into_signed(sig)]) + } + + let SendableTx::Builder(tx) = provider.fill(tx).await? else { unreachable!() }; + let tx = + NetworkWallet::::sign_request(&EthereumWallet::new(signer.clone()), tx) + .await?; + + pending.push(provider.send_tx_envelope(tx).await?); + } + + let (payload, _) = node.build_and_submit_payload().await?; + if finalize { + node.engine_api + .update_forkchoice(payload.block().hash(), payload.block().hash()) + .await?; + } else { + let last_safe = provider + .get_block_by_number(BlockNumberOrTag::Safe, false.into()) + .await? + .unwrap() + .header + .hash; + node.engine_api.update_forkchoice(last_safe, payload.block().hash()).await?; + } + assert_eq!(payload.block().raw_transactions().len(), tx_count); + + for pending in pending { + let receipt = pending.get_receipt().await?; + if let Some(address) = receipt.contract_address { + call_destinations.push(address); + } + } + } + + Ok(()) +} diff --git a/crates/optimism/node/src/utils.rs b/crates/optimism/node/src/utils.rs index e70e35031982..0e9330738482 100644 --- a/crates/optimism/node/src/utils.rs +++ b/crates/optimism/node/src/utils.rs @@ -1,10 +1,8 @@ -use crate::{node::OpAddOns, OpBuiltPayload, OpNode as OtherOpNode, OpPayloadBuilderAttributes}; +use crate::{OpBuiltPayload, OpNode as OtherOpNode, OpPayloadBuilderAttributes}; use alloy_genesis::Genesis; use alloy_primitives::{Address, B256}; -use alloy_rpc_types_engine::PayloadAttributes; -use reth_e2e_test_utils::{ - transaction::TransactionTestContext, wallet::Wallet, Adapter, NodeHelperType, -}; +use reth::{rpc::types::engine::PayloadAttributes, tasks::TaskManager}; +use reth_e2e_test_utils::{transaction::TransactionTestContext, wallet::Wallet, NodeHelperType}; use reth_optimism_chainspec::OpChainSpecBuilder; use reth_payload_builder::EthPayloadBuilderAttributes; use reth_tasks::TaskManager; @@ -12,7 +10,7 @@ use std::sync::Arc; use tokio::sync::Mutex; /// Optimism Node Helper type -pub(crate) type OpNode = NodeHelperType>>; +pub(crate) type OpNode = NodeHelperType; /// Creates the initial setup with `num_nodes` of the node config, started and connected. pub async fn setup(num_nodes: usize) -> eyre::Result<(Vec, TaskManager, Wallet)> { From bc9e9ef75d42ed82561f7cf8e856fd3637587ac6 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 28 Nov 2024 19:53:11 +0400 Subject: [PATCH 2/7] wip --- crates/e2e-test-utils/src/node.rs | 4 ++++ crates/ethereum/node/tests/e2e/p2p.rs | 12 ++++++++++-- crates/ethereum/node/tests/e2e/utils.rs | 17 +++++++++++++---- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/crates/e2e-test-utils/src/node.rs b/crates/e2e-test-utils/src/node.rs index 9957d34574c5..57d52a38b350 100644 --- a/crates/e2e-test-utils/src/node.rs +++ b/crates/e2e-test-utils/src/node.rs @@ -272,6 +272,10 @@ where assert!(start.elapsed() <= std::time::Duration::from_secs(10), "timed out"); } + // Hack to make sure that all components have time to process canonical state update. + // Othewise, this might result in e.g "nonce too low" errors when advancing chain further, making tests flaky. + tokio::time::sleep(std::time::Duration::from_millis(1000)).await; + Ok(()) } diff --git a/crates/ethereum/node/tests/e2e/p2p.rs b/crates/ethereum/node/tests/e2e/p2p.rs index 1723c4a8f36e..5298772449a7 100644 --- a/crates/ethereum/node/tests/e2e/p2p.rs +++ b/crates/ethereum/node/tests/e2e/p2p.rs @@ -115,14 +115,22 @@ async fn test_long_reorg() -> eyre::Result<()> { let first_priovider = ProviderBuilder::new().on_http(first_node.rpc_url()); let second_provider = ProviderBuilder::new().on_http(second_node.rpc_url()); + // Advance first node 100 blocks. advance_with_random_transactions(&mut first_node, 100, &mut rng, false).await?; - let head = first_priovider.get_block_by_number(20.into(), false.into()).await?.unwrap().header.hash; - second_node.sync_to(head).await?; + // Sync second node to 20th block. + let head = first_priovider.get_block_by_number(20.into(), false.into()).await?.unwrap(); + second_node.sync_to(head.header.hash).await?; + + // Produce a fork chain with blocks 21.40 + second_node.payload.timestamp = head.header.timestamp; advance_with_random_transactions(&mut second_node, 40, &mut rng, true).await?; + + // Reorg first node from 100th block to new 60th block. let reorg_head = second_provider.get_block_by_number(60.into(), false.into()).await?.unwrap().header.hash; first_node.sync_to(reorg_head).await?; + // Advance second node 20 blocks and ensure that first noce is able to follow it. advance_with_random_transactions(&mut second_node, 20, &mut rng, true).await?; let canonical_head = second_provider.get_block_by_number(80.into(), false.into()).await?.unwrap().header.hash; first_node.sync_to(canonical_head).await?; diff --git a/crates/ethereum/node/tests/e2e/utils.rs b/crates/ethereum/node/tests/e2e/utils.rs index 811eb4ab00de..3bf58d75dc8d 100644 --- a/crates/ethereum/node/tests/e2e/utils.rs +++ b/crates/ethereum/node/tests/e2e/utils.rs @@ -1,4 +1,4 @@ -use alloy_eips::BlockNumberOrTag; +use alloy_eips::{BlockId, BlockNumberOrTag}; use alloy_primitives::{bytes, Address, B256}; use alloy_provider::{ network::{ @@ -55,7 +55,9 @@ where let signer = signers.choose(rng).unwrap(); let tx_type = TxType::try_from(rng.gen_range(0..=4) as u64).unwrap(); - let mut tx = TransactionRequest::default().with_from(signer.address()); + let nonce = provider.get_transaction_count(signer.address()).block_id(BlockId::Number(BlockNumberOrTag::Pending)).await?; + + let mut tx = TransactionRequest::default().with_from(signer.address()).with_nonce(nonce); let should_create = rng.gen::() && tx_type != TxType::Eip4844 && tx_type != TxType::Eip7702; @@ -86,12 +88,20 @@ where let auth = Authorization { chain_id: provider.get_chain_id().await?, address: *call_destinations.choose(rng).unwrap(), - nonce: provider.get_transaction_count(signer.address()).await?, + nonce: provider.get_transaction_count(signer.address()).block_id(BlockId::Number(BlockNumberOrTag::Pending)).await?, }; let sig = signer.sign_hash_sync(&auth.signature_hash())?; tx = tx.with_authorization_list(vec![auth.into_signed(sig)]) } + let gas = if let Ok(gas) = provider.estimate_gas(&tx).block(BlockId::Number(BlockNumberOrTag::Pending)).await { + gas + } else { + 1_000_000 + }; + + tx.set_gas_limit(gas); + let SendableTx::Builder(tx) = provider.fill(tx).await? else { unreachable!() }; let tx = NetworkWallet::::sign_request(&EthereumWallet::new(signer.clone()), tx) @@ -114,7 +124,6 @@ where .hash; node.engine_api.update_forkchoice(last_safe, payload.block().hash()).await?; } - assert_eq!(payload.block().raw_transactions().len(), tx_count); for pending in pending { let receipt = pending.get_receipt().await?; From de115fa93effcfebb061c26fb544e03400eed518 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 28 Nov 2024 20:15:55 +0400 Subject: [PATCH 3/7] wip --- crates/e2e-test-utils/src/lib.rs | 6 +++--- crates/e2e-test-utils/src/node.rs | 19 ++++++++++++++----- crates/ethereum/node/tests/e2e/p2p.rs | 21 ++++++++++----------- crates/ethereum/node/tests/e2e/utils.rs | 23 +++++++++++++++-------- crates/optimism/node/src/utils.rs | 2 +- 5 files changed, 43 insertions(+), 28 deletions(-) diff --git a/crates/e2e-test-utils/src/lib.rs b/crates/e2e-test-utils/src/lib.rs index b8a52e1ee2db..8f8fb29c8409 100644 --- a/crates/e2e-test-utils/src/lib.rs +++ b/crates/e2e-test-utils/src/lib.rs @@ -1,7 +1,6 @@ //! Utilities for end-to-end tests. use node::NodeTestContext; -use reth_provider::HeaderProvider; use reth_chainspec::EthChainSpec; use reth_db::{test_utils::TempDatabase, DatabaseEnv}; use reth_engine_local::LocalPayloadAttributesBuilder; @@ -16,8 +15,9 @@ use reth_node_builder::{ }; use reth_node_core::args::{DiscoveryArgs, NetworkArgs, RpcServerArgs}; use reth_primitives::EthPrimitives; -use reth_provider::providers::{ - BlockchainProvider, BlockchainProvider2, NodeTypesForProvider, NodeTypesForTree, +use reth_provider::{ + providers::{BlockchainProvider, BlockchainProvider2, NodeTypesForProvider, NodeTypesForTree}, + HeaderProvider, }; use reth_rpc_server_types::RpcModuleSelection; use reth_tasks::TaskManager; diff --git a/crates/e2e-test-utils/src/node.rs b/crates/e2e-test-utils/src/node.rs index 57d52a38b350..88b142fb2a49 100644 --- a/crates/e2e-test-utils/src/node.rs +++ b/crates/e2e-test-utils/src/node.rs @@ -3,12 +3,12 @@ use crate::{ rpc::RpcTestContext, traits::PayloadEnvelopeExt, }; use alloy_consensus::BlockHeader; +use alloy_eips::BlockId; use alloy_primitives::{BlockHash, BlockNumber, Bytes, B256}; use alloy_rpc_types_engine::PayloadStatusEnum; use alloy_rpc_types_eth::BlockNumberOrTag; use eyre::Ok; use futures_util::Future; -use alloy_eips::BlockId; use reth_chainspec::EthereumHardforks; use reth_network_api::test_utils::PeersHandleProvider; use reth_node_api::{Block, EngineTypes, FullNodeComponents}; @@ -253,6 +253,16 @@ where Ok(()) } + /// Gets block hash by number. + pub fn block_hash(&self, number: u64) -> BlockHash { + self.inner + .provider + .sealed_header_by_number_or_tag(BlockNumberOrTag::Number(number)) + .unwrap() + .unwrap() + .hash() + } + /// Sends FCU and waits for the node to sync to the given block. pub async fn sync_to(&self, block: BlockHash) -> eyre::Result<()> { self.engine_api.update_forkchoice(block, block).await?; @@ -263,9 +273,7 @@ where .inner .provider .sealed_header_by_id(BlockId::Number(BlockNumberOrTag::Latest))? - .map_or(false, |h| { - h.hash() == block - }) + .map_or(false, |h| h.hash() == block) { tokio::time::sleep(std::time::Duration::from_millis(100)).await; @@ -273,7 +281,8 @@ where } // Hack to make sure that all components have time to process canonical state update. - // Othewise, this might result in e.g "nonce too low" errors when advancing chain further, making tests flaky. + // Otherwise, this might result in e.g "nonce too low" errors when advancing chain further, + // making tests flaky. tokio::time::sleep(std::time::Duration::from_millis(1000)).await; Ok(()) diff --git a/crates/ethereum/node/tests/e2e/p2p.rs b/crates/ethereum/node/tests/e2e/p2p.rs index 5298772449a7..908f0a86bea6 100644 --- a/crates/ethereum/node/tests/e2e/p2p.rs +++ b/crates/ethereum/node/tests/e2e/p2p.rs @@ -1,8 +1,6 @@ use crate::utils::{advance_with_random_transactions, eth_payload_attributes}; -use alloy_provider::{ - Provider, ProviderBuilder, -}; -use rand::{rngs::StdRng,Rng, SeedableRng}; +use alloy_provider::{Provider, ProviderBuilder}; +use rand::{rngs::StdRng, Rng, SeedableRng}; use reth_chainspec::{ChainSpecBuilder, MAINNET}; use reth_e2e_test_utils::{setup, setup_engine, transaction::TransactionTestContext}; use reth_node_ethereum::EthereumNode; @@ -112,14 +110,13 @@ async fn test_long_reorg() -> eyre::Result<()> { let mut first_node = nodes.pop().unwrap(); let mut second_node = nodes.pop().unwrap(); - let first_priovider = ProviderBuilder::new().on_http(first_node.rpc_url()); - let second_provider = ProviderBuilder::new().on_http(second_node.rpc_url()); + let first_provider = ProviderBuilder::new().on_http(first_node.rpc_url()); // Advance first node 100 blocks. advance_with_random_transactions(&mut first_node, 100, &mut rng, false).await?; // Sync second node to 20th block. - let head = first_priovider.get_block_by_number(20.into(), false.into()).await?.unwrap(); + let head = first_provider.get_block_by_number(20.into(), false.into()).await?.unwrap(); second_node.sync_to(head.header.hash).await?; // Produce a fork chain with blocks 21.40 @@ -127,13 +124,15 @@ async fn test_long_reorg() -> eyre::Result<()> { advance_with_random_transactions(&mut second_node, 40, &mut rng, true).await?; // Reorg first node from 100th block to new 60th block. - let reorg_head = second_provider.get_block_by_number(60.into(), false.into()).await?.unwrap().header.hash; - first_node.sync_to(reorg_head).await?; + first_node.sync_to(second_node.block_hash(60)).await?; // Advance second node 20 blocks and ensure that first noce is able to follow it. advance_with_random_transactions(&mut second_node, 20, &mut rng, true).await?; - let canonical_head = second_provider.get_block_by_number(80.into(), false.into()).await?.unwrap().header.hash; - first_node.sync_to(canonical_head).await?; + first_node.sync_to(second_node.block_hash(80)).await?; + + // Ensure that it works the other way around too. + advance_with_random_transactions(&mut first_node, 20, &mut rng, true).await?; + second_node.sync_to(first_node.block_hash(100)).await?; Ok(()) } diff --git a/crates/ethereum/node/tests/e2e/utils.rs b/crates/ethereum/node/tests/e2e/utils.rs index 3bf58d75dc8d..ee451b8f3c5b 100644 --- a/crates/ethereum/node/tests/e2e/utils.rs +++ b/crates/ethereum/node/tests/e2e/utils.rs @@ -55,9 +55,13 @@ where let signer = signers.choose(rng).unwrap(); let tx_type = TxType::try_from(rng.gen_range(0..=4) as u64).unwrap(); - let nonce = provider.get_transaction_count(signer.address()).block_id(BlockId::Number(BlockNumberOrTag::Pending)).await?; + let nonce = provider + .get_transaction_count(signer.address()) + .block_id(BlockId::Number(BlockNumberOrTag::Pending)) + .await?; - let mut tx = TransactionRequest::default().with_from(signer.address()).with_nonce(nonce); + let mut tx = + TransactionRequest::default().with_from(signer.address()).with_nonce(nonce); let should_create = rng.gen::() && tx_type != TxType::Eip4844 && tx_type != TxType::Eip7702; @@ -88,17 +92,20 @@ where let auth = Authorization { chain_id: provider.get_chain_id().await?, address: *call_destinations.choose(rng).unwrap(), - nonce: provider.get_transaction_count(signer.address()).block_id(BlockId::Number(BlockNumberOrTag::Pending)).await?, + nonce: provider + .get_transaction_count(signer.address()) + .block_id(BlockId::Number(BlockNumberOrTag::Pending)) + .await?, }; let sig = signer.sign_hash_sync(&auth.signature_hash())?; tx = tx.with_authorization_list(vec![auth.into_signed(sig)]) } - let gas = if let Ok(gas) = provider.estimate_gas(&tx).block(BlockId::Number(BlockNumberOrTag::Pending)).await { - gas - } else { - 1_000_000 - }; + let gas = provider + .estimate_gas(&tx) + .block(BlockId::Number(BlockNumberOrTag::Pending)) + .await + .unwrap_or(1_000_000); tx.set_gas_limit(gas); diff --git a/crates/optimism/node/src/utils.rs b/crates/optimism/node/src/utils.rs index 0e9330738482..9cadcdcf7a15 100644 --- a/crates/optimism/node/src/utils.rs +++ b/crates/optimism/node/src/utils.rs @@ -1,7 +1,7 @@ use crate::{OpBuiltPayload, OpNode as OtherOpNode, OpPayloadBuilderAttributes}; use alloy_genesis::Genesis; use alloy_primitives::{Address, B256}; -use reth::{rpc::types::engine::PayloadAttributes, tasks::TaskManager}; +use alloy_rpc_types_engine::PayloadAttributes; use reth_e2e_test_utils::{transaction::TransactionTestContext, wallet::Wallet, NodeHelperType}; use reth_optimism_chainspec::OpChainSpecBuilder; use reth_payload_builder::EthPayloadBuilderAttributes; From 87216bb95a82ad5f95d18b67ba37cf44e1220047 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 28 Nov 2024 20:26:29 +0400 Subject: [PATCH 4/7] fix --- crates/e2e-test-utils/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/e2e-test-utils/src/lib.rs b/crates/e2e-test-utils/src/lib.rs index 8f8fb29c8409..85053894a751 100644 --- a/crates/e2e-test-utils/src/lib.rs +++ b/crates/e2e-test-utils/src/lib.rs @@ -184,8 +184,8 @@ where let mut node = NodeTestContext::new(node, attributes_generator).await?; - let first_block = node.inner.provider.sealed_header(0)?.unwrap().hash(); - node.engine_api.update_forkchoice(first_block, first_block).await?; + let genesis = node.block_hash(0); + node.engine_api.update_forkchoice(genesis, genesis).await?; // Connect each node in a chain. if let Some(previous_node) = nodes.last_mut() { From 1ea4aa59f9fb0947d2d2a6223ad35b816309a38b Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 28 Nov 2024 20:27:57 +0400 Subject: [PATCH 5/7] fix --- crates/ethereum/node/tests/e2e/p2p.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ethereum/node/tests/e2e/p2p.rs b/crates/ethereum/node/tests/e2e/p2p.rs index 908f0a86bea6..2d56d0643461 100644 --- a/crates/ethereum/node/tests/e2e/p2p.rs +++ b/crates/ethereum/node/tests/e2e/p2p.rs @@ -119,7 +119,7 @@ async fn test_long_reorg() -> eyre::Result<()> { let head = first_provider.get_block_by_number(20.into(), false.into()).await?.unwrap(); second_node.sync_to(head.header.hash).await?; - // Produce a fork chain with blocks 21.40 + // Produce a fork chain with blocks 21.60 second_node.payload.timestamp = head.header.timestamp; advance_with_random_transactions(&mut second_node, 40, &mut rng, true).await?; From c56f25a768332ca7f5b78806af77306be3967a76 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 28 Nov 2024 20:28:20 +0400 Subject: [PATCH 6/7] fix --- crates/ethereum/node/tests/e2e/p2p.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ethereum/node/tests/e2e/p2p.rs b/crates/ethereum/node/tests/e2e/p2p.rs index 2d56d0643461..343521ef8eb7 100644 --- a/crates/ethereum/node/tests/e2e/p2p.rs +++ b/crates/ethereum/node/tests/e2e/p2p.rs @@ -126,7 +126,7 @@ async fn test_long_reorg() -> eyre::Result<()> { // Reorg first node from 100th block to new 60th block. first_node.sync_to(second_node.block_hash(60)).await?; - // Advance second node 20 blocks and ensure that first noce is able to follow it. + // Advance second node 20 blocks and ensure that first node is able to follow it. advance_with_random_transactions(&mut second_node, 20, &mut rng, true).await?; first_node.sync_to(second_node.block_hash(80)).await?; From 502a6d5a146066cc599571abe7aec149bcd98ce6 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 28 Nov 2024 23:26:22 +0400 Subject: [PATCH 7/7] fix --- crates/e2e-test-utils/src/lib.rs | 5 ++--- crates/e2e-test-utils/src/node.rs | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/e2e-test-utils/src/lib.rs b/crates/e2e-test-utils/src/lib.rs index 85053894a751..72d912d6b54f 100644 --- a/crates/e2e-test-utils/src/lib.rs +++ b/crates/e2e-test-utils/src/lib.rs @@ -15,9 +15,8 @@ use reth_node_builder::{ }; use reth_node_core::args::{DiscoveryArgs, NetworkArgs, RpcServerArgs}; use reth_primitives::EthPrimitives; -use reth_provider::{ - providers::{BlockchainProvider, BlockchainProvider2, NodeTypesForProvider, NodeTypesForTree}, - HeaderProvider, +use reth_provider::providers::{ + BlockchainProvider, BlockchainProvider2, NodeTypesForProvider, NodeTypesForTree, }; use reth_rpc_server_types::RpcModuleSelection; use reth_tasks::TaskManager; diff --git a/crates/e2e-test-utils/src/node.rs b/crates/e2e-test-utils/src/node.rs index 88b142fb2a49..dcd24df5c7a2 100644 --- a/crates/e2e-test-utils/src/node.rs +++ b/crates/e2e-test-utils/src/node.rs @@ -269,11 +269,11 @@ where let start = std::time::Instant::now(); - while !self + while self .inner .provider .sealed_header_by_id(BlockId::Number(BlockNumberOrTag::Latest))? - .map_or(false, |h| h.hash() == block) + .is_none_or(|h| h.hash() != block) { tokio::time::sleep(std::time::Duration::from_millis(100)).await;