feat: add UX improvements on e2e testing (#7804)

This commit is contained in:
joshieDo
2024-04-23 15:30:38 +01:00
committed by GitHub
parent 9fd35f948c
commit b3db4cf56d
16 changed files with 343 additions and 221 deletions

5
Cargo.lock generated
View File

@ -6176,6 +6176,7 @@ dependencies = [
"reth-provider", "reth-provider",
"reth-prune", "reth-prune",
"reth-revm", "reth-revm",
"reth-rpc",
"reth-rpc-types", "reth-rpc-types",
"reth-rpc-types-compat", "reth-rpc-types-compat",
"reth-stages", "reth-stages",
@ -6451,16 +6452,19 @@ dependencies = [
"rand 0.8.5", "rand 0.8.5",
"reth", "reth",
"reth-db", "reth-db",
"reth-node-builder",
"reth-node-core", "reth-node-core",
"reth-node-ethereum", "reth-node-ethereum",
"reth-payload-builder", "reth-payload-builder",
"reth-primitives", "reth-primitives",
"reth-provider",
"reth-rpc", "reth-rpc",
"reth-tracing", "reth-tracing",
"secp256k1 0.27.0", "secp256k1 0.27.0",
"serde_json", "serde_json",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"tracing",
] ]
[[package]] [[package]]
@ -7039,6 +7043,7 @@ dependencies = [
"reqwest 0.11.27", "reqwest 0.11.27",
"reth", "reth",
"reth-basic-payload-builder", "reth-basic-payload-builder",
"reth-beacon-consensus",
"reth-db", "reth-db",
"reth-e2e-test-utils", "reth-e2e-test-utils",
"reth-evm", "reth-evm",

View File

@ -52,6 +52,7 @@ reth-blockchain-tree = { workspace = true, features = ["test-utils"] }
reth-db = { workspace = true, features = ["test-utils"] } reth-db = { workspace = true, features = ["test-utils"] }
reth-provider = { workspace = true, features = ["test-utils"] } reth-provider = { workspace = true, features = ["test-utils"] }
reth-rpc-types-compat.workspace = true reth-rpc-types-compat.workspace = true
reth-rpc.workspace = true
reth-tracing.workspace = true reth-tracing.workspace = true
reth-revm.workspace = true reth-revm.workspace = true
reth-downloaders.workspace = true reth-downloaders.workspace = true
@ -68,4 +69,6 @@ optimism = [
"reth-provider/optimism", "reth-provider/optimism",
"reth-blockchain-tree/optimism", "reth-blockchain-tree/optimism",
"reth-beacon-consensus-core/optimism", "reth-beacon-consensus-core/optimism",
"reth-revm/optimism",
"reth-rpc/optimism"
] ]

View File

@ -17,6 +17,8 @@ reth-tracing.workspace = true
reth-db.workspace = true reth-db.workspace = true
reth-rpc.workspace = true reth-rpc.workspace = true
reth-payload-builder = { workspace = true, features = ["test-utils"] } reth-payload-builder = { workspace = true, features = ["test-utils"] }
reth-provider.workspace = true
reth-node-builder.workspace = true
jsonrpsee.workspace = true jsonrpsee.workspace = true
@ -32,3 +34,4 @@ alloy-signer-wallet = { workspace = true, features = ["mnemonic"] }
alloy-rpc-types.workspace = true alloy-rpc-types.workspace = true
alloy-network.workspace = true alloy-network.workspace = true
alloy-consensus.workspace = true alloy-consensus.workspace = true
tracing.workspace = true

View File

@ -3,7 +3,10 @@ use jsonrpsee::http_client::HttpClient;
use reth::{ use reth::{
api::{EngineTypes, PayloadBuilderAttributes}, api::{EngineTypes, PayloadBuilderAttributes},
providers::CanonStateNotificationStream, providers::CanonStateNotificationStream,
rpc::{api::EngineApiClient, types::engine::ForkchoiceState}, rpc::{
api::EngineApiClient,
types::engine::{ForkchoiceState, PayloadStatusEnum},
},
}; };
use reth_payload_builder::PayloadId; use reth_payload_builder::PayloadId;
use reth_primitives::B256; use reth_primitives::B256;
@ -30,6 +33,7 @@ impl<E: EngineTypes + 'static> EngineApiHelper<E> {
&self, &self,
payload: E::BuiltPayload, payload: E::BuiltPayload,
payload_builder_attributes: E::PayloadBuilderAttributes, payload_builder_attributes: E::PayloadBuilderAttributes,
expected_status: PayloadStatusEnum,
) -> eyre::Result<B256> ) -> eyre::Result<B256>
where where
E::ExecutionPayloadV3: From<E::BuiltPayload> + PayloadEnvelopeExt, E::ExecutionPayloadV3: From<E::BuiltPayload> + PayloadEnvelopeExt,
@ -45,8 +49,10 @@ impl<E: EngineTypes + 'static> EngineApiHelper<E> {
payload_builder_attributes.parent_beacon_block_root().unwrap(), payload_builder_attributes.parent_beacon_block_root().unwrap(),
) )
.await?; .await?;
assert!(submission.is_valid(), "{}", submission);
Ok(submission.latest_valid_hash.unwrap()) assert!(submission.status == expected_status);
Ok(submission.latest_valid_hash.unwrap_or_default())
} }
/// Sends forkchoice update to the engine api /// Sends forkchoice update to the engine api
@ -64,4 +70,20 @@ impl<E: EngineTypes + 'static> EngineApiHelper<E> {
Ok(()) Ok(())
} }
/// Sends forkchoice update to the engine api with a zero finalized hash
pub async fn update_optimistic_forkchoice(&self, hash: B256) -> eyre::Result<()> {
EngineApiClient::<E>::fork_choice_updated_v2(
&self.engine_api_client,
ForkchoiceState {
head_block_hash: hash,
safe_block_hash: B256::ZERO,
finalized_block_hash: B256::ZERO,
},
None,
)
.await?;
Ok(())
}
} }

View File

@ -1,3 +1,22 @@
use node::NodeHelper;
use reth::{
args::{DiscoveryArgs, NetworkArgs, RpcServerArgs},
blockchain_tree::ShareableBlockchainTree,
builder::{NodeBuilder, NodeConfig, NodeHandle},
revm::EvmProcessorFactory,
tasks::TaskManager,
};
use reth_db::{test_utils::TempDatabase, DatabaseEnv};
use reth_node_builder::{
components::{NetworkBuilder, PayloadServiceBuilder, PoolBuilder},
FullNodeComponentsAdapter, FullNodeTypesAdapter, NodeTypes,
};
use reth_primitives::ChainSpec;
use reth_provider::providers::BlockchainProvider;
use std::sync::Arc;
use tracing::{span, Level};
use wallet::Wallet;
/// Wrapper type to create test nodes /// Wrapper type to create test nodes
pub mod node; pub mod node;
@ -15,3 +34,78 @@ mod engine_api;
/// Helper traits /// Helper traits
mod traits; mod traits;
/// Creates the initial setup with `num_nodes` started and interconnected.
pub async fn setup<N>(
num_nodes: usize,
chain_spec: Arc<ChainSpec>,
is_dev: bool,
) -> eyre::Result<(Vec<NodeHelperType<N>>, TaskManager, Wallet)>
where
N: Default + reth_node_builder::Node<TmpNodeAdapter<N>>,
N::PoolBuilder: PoolBuilder<TmpNodeAdapter<N>>,
N::NetworkBuilder: NetworkBuilder<TmpNodeAdapter<N>, TmpPool<N>>,
N::PayloadBuilder: PayloadServiceBuilder<TmpNodeAdapter<N>, TmpPool<N>>,
{
let tasks = TaskManager::current();
let exec = tasks.executor();
let network_config = NetworkArgs {
discovery: DiscoveryArgs { disable_discovery: true, ..DiscoveryArgs::default() },
..NetworkArgs::default()
};
// Create nodes and peer them
let mut nodes: Vec<NodeHelperType<N>> = Vec::with_capacity(num_nodes);
for idx in 0..num_nodes {
let mut node_config = NodeConfig::test()
.with_chain(chain_spec.clone())
.with_network(network_config.clone())
.with_unused_ports()
.with_rpc(RpcServerArgs::default().with_unused_ports().with_http());
if is_dev {
node_config = node_config.dev();
}
let span = span!(Level::INFO, "node", idx);
let _enter = span.enter();
let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config.clone())
.testing_node(exec.clone())
.node(Default::default())
.launch()
.await?;
let mut node = NodeHelper::new(node).await?;
// Connect each node in a chain.
if let Some(previous_node) = nodes.last_mut() {
previous_node.connect(&mut node).await;
}
// Connect last node with the first if there are more than two
if idx + 1 == num_nodes && num_nodes > 2 {
if let Some(first_node) = nodes.first_mut() {
node.connect(first_node).await;
}
}
nodes.push(node);
}
Ok((nodes, tasks, Wallet::default().with_chain_id(chain_spec.chain().into())))
}
// Type aliases
type TmpDB = Arc<TempDatabase<DatabaseEnv>>;
type EvmType<N> = EvmProcessorFactory<<N as NodeTypes>::Evm>;
type RethProvider<N> = BlockchainProvider<TmpDB, ShareableBlockchainTree<TmpDB, EvmType<N>>>;
type TmpPool<N> = <<N as reth_node_builder::Node<TmpNodeAdapter<N>>>::PoolBuilder as PoolBuilder<
TmpNodeAdapter<N>,
>>::Pool;
type TmpNodeAdapter<N> = FullNodeTypesAdapter<N, TmpDB, RethProvider<N>>;
/// Type alias for a type of NodeHelper
pub type NodeHelperType<N> = NodeHelper<FullNodeComponentsAdapter<TmpNodeAdapter<N>, TmpPool<N>>>;

View File

@ -4,21 +4,18 @@ use crate::{
}; };
use alloy_rpc_types::BlockNumberOrTag; use alloy_rpc_types::BlockNumberOrTag;
use eyre::Ok; use eyre::Ok;
use futures_util::Future;
use reth::{ use reth::{
api::{BuiltPayload, EngineTypes, FullNodeComponents, PayloadBuilderAttributes}, api::{BuiltPayload, EngineTypes, FullNodeComponents, PayloadBuilderAttributes},
builder::FullNode, builder::FullNode,
providers::{BlockReaderIdExt, CanonStateSubscriptions}, providers::{BlockReader, BlockReaderIdExt, CanonStateSubscriptions, StageCheckpointReader},
rpc::{ rpc::{
eth::{error::EthResult, EthTransactions}, eth::{error::EthResult, EthTransactions},
types::engine::PayloadAttributes, types::engine::PayloadStatusEnum,
}, },
}; };
use reth_payload_builder::EthPayloadBuilderAttributes; use reth_primitives::{stage::StageId, BlockHash, BlockNumber, Bytes, B256};
use reth_primitives::{Address, BlockNumber, Bytes, B256}; use std::{marker::PhantomData, pin::Pin};
use std::{
marker::PhantomData,
time::{SystemTime, UNIX_EPOCH},
};
use tokio_stream::StreamExt; use tokio_stream::StreamExt;
/// An helper struct to handle node actions /// An helper struct to handle node actions
@ -27,7 +24,7 @@ where
Node: FullNodeComponents, Node: FullNodeComponents,
{ {
pub inner: FullNode<Node>, pub inner: FullNode<Node>,
payload: PayloadHelper<Node::Engine>, pub payload: PayloadHelper<Node::Engine>,
pub network: NetworkHelper, pub network: NetworkHelper,
pub engine_api: EngineApiHelper<Node::Engine>, pub engine_api: EngineApiHelper<Node::Engine>,
} }
@ -52,12 +49,53 @@ where
}) })
} }
/// Advances the node forward pub async fn connect(&mut self, node: &mut NodeHelper<Node>) {
self.network.add_peer(node.network.record()).await;
node.network.add_peer(self.network.record()).await;
node.network.expect_session().await;
self.network.expect_session().await;
}
/// Advances the chain `length` blocks.
///
/// Returns the added chain as a Vec of block hashes.
pub async fn advance( pub async fn advance(
&mut self,
length: u64,
tx_generator: impl Fn() -> Pin<Box<dyn Future<Output = Bytes>>>,
attributes_generator: impl Fn(u64) -> <Node::Engine as EngineTypes>::PayloadBuilderAttributes
+ Copy,
) -> eyre::Result<
Vec<(
<Node::Engine as EngineTypes>::BuiltPayload,
<Node::Engine as EngineTypes>::PayloadBuilderAttributes,
)>,
>
where
<Node::Engine as EngineTypes>::ExecutionPayloadV3:
From<<Node::Engine as EngineTypes>::BuiltPayload> + PayloadEnvelopeExt,
{
let mut chain = Vec::with_capacity(length as usize);
for _ in 0..length {
let (payload, _) =
self.advance_block(tx_generator().await, attributes_generator).await?;
chain.push(payload);
}
Ok(chain)
}
/// Advances the node forward one block
pub async fn advance_block(
&mut self, &mut self,
raw_tx: Bytes, raw_tx: Bytes,
attributes_generator: impl Fn(u64) -> <Node::Engine as EngineTypes>::PayloadBuilderAttributes, attributes_generator: impl Fn(u64) -> <Node::Engine as EngineTypes>::PayloadBuilderAttributes,
) -> eyre::Result<(B256, B256)> ) -> eyre::Result<(
(
<Node::Engine as EngineTypes>::BuiltPayload,
<Node::Engine as EngineTypes>::PayloadBuilderAttributes,
),
B256,
)>
where where
<Node::Engine as EngineTypes>::ExecutionPayloadV3: <Node::Engine as EngineTypes>::ExecutionPayloadV3:
From<<Node::Engine as EngineTypes>::BuiltPayload> + PayloadEnvelopeExt, From<<Node::Engine as EngineTypes>::BuiltPayload> + PayloadEnvelopeExt,
@ -81,15 +119,54 @@ where
let payload = self.payload.expect_built_payload().await?; let payload = self.payload.expect_built_payload().await?;
// submit payload via engine api // submit payload via engine api
let block_number = payload.block().number; let block_hash = self
let block_hash = self.engine_api.submit_payload(payload, eth_attr.clone()).await?; .engine_api
.submit_payload(payload.clone(), eth_attr.clone(), PayloadStatusEnum::Valid)
.await?;
// trigger forkchoice update via engine api to commit the block to the blockchain // trigger forkchoice update via engine api to commit the block to the blockchain
self.engine_api.update_forkchoice(block_hash).await?; self.engine_api.update_forkchoice(block_hash).await?;
// assert the block has been committed to the blockchain // assert the block has been committed to the blockchain
self.assert_new_block(tx_hash, block_hash, block_number).await?; self.assert_new_block(tx_hash, block_hash, payload.block().number).await?;
Ok((block_hash, tx_hash)) Ok(((payload, eth_attr), tx_hash))
}
/// Waits for block to be available on node.
pub async fn wait_block(
&self,
number: BlockNumber,
expected_block_hash: BlockHash,
wait_finish_checkpoint: bool,
) -> eyre::Result<()> {
let mut check = !wait_finish_checkpoint;
loop {
tokio::time::sleep(std::time::Duration::from_millis(20)).await;
if !check && wait_finish_checkpoint {
if let Some(checkpoint) =
self.inner.provider.get_stage_checkpoint(StageId::Finish)?
{
if checkpoint.block_number >= number {
check = true
}
}
}
if check {
if let Some(latest_block) = self.inner.provider.block_by_number(number)? {
if latest_block.hash_slow() != expected_block_hash {
// TODO: only if its awaiting a reorg
continue
}
break
}
if wait_finish_checkpoint {
panic!("Finish checkpoint matches, but could not fetch block.");
}
}
}
Ok(())
} }
/// Injects a raw transaction into the node tx pool via RPC server /// Injects a raw transaction into the node tx pool via RPC server
@ -129,17 +206,3 @@ where
Ok(()) Ok(())
} }
} }
/// Helper function to create a new eth payload attributes
pub fn eth_payload_attributes() -> EthPayloadBuilderAttributes {
let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
let attributes = PayloadAttributes {
timestamp,
prev_randao: B256::ZERO,
suggested_fee_recipient: Address::ZERO,
withdrawals: Some(vec![]),
parent_beacon_block_root: Some(B256::ZERO),
};
EthPayloadBuilderAttributes::new(B256::ZERO, attributes)
}

View File

@ -7,7 +7,7 @@ use tokio_stream::wrappers::BroadcastStream;
pub struct PayloadHelper<E: EngineTypes + 'static> { pub struct PayloadHelper<E: EngineTypes + 'static> {
pub payload_event_stream: BroadcastStream<Events<E>>, pub payload_event_stream: BroadcastStream<Events<E>>,
payload_builder: PayloadBuilderHandle<E>, payload_builder: PayloadBuilderHandle<E>,
timestamp: u64, pub timestamp: u64,
} }
impl<E: EngineTypes + 'static> PayloadHelper<E> { impl<E: EngineTypes + 'static> PayloadHelper<E> {

View File

@ -1,11 +1,11 @@
use alloy_network::{eip2718::Encodable2718, EthereumSigner, TransactionBuilder}; use alloy_network::{eip2718::Encodable2718, EthereumSigner, TransactionBuilder};
use alloy_rpc_types::{TransactionInput, TransactionRequest}; use alloy_rpc_types::{TransactionInput, TransactionRequest};
use alloy_signer_wallet::{coins_bip39::English, LocalWallet, MnemonicBuilder}; use alloy_signer_wallet::{coins_bip39::English, LocalWallet, MnemonicBuilder};
use reth_primitives::{Address, Bytes, U256}; use reth_primitives::{hex, Address, Bytes, U256};
/// One of the accounts of the genesis allocations. /// One of the accounts of the genesis allocations.
pub struct Wallet { pub struct Wallet {
inner: LocalWallet, inner: LocalWallet,
nonce: u64, pub nonce: u64,
chain_id: u64, chain_id: u64,
} }
@ -27,6 +27,11 @@ impl Wallet {
self.tx(None).await self.tx(None).await
} }
pub async fn optimism_l1_block_info_tx(&mut self) -> Bytes {
let l1_block_info = Bytes::from_static(&hex!("7ef9015aa044bae9d41b8380d781187b426c6fe43df5fb2fb57bd4466ef6a701e1f01e015694deaddeaddeaddeaddeaddeaddeaddeaddead000194420000000000000000000000000000000000001580808408f0d18001b90104015d8eb900000000000000000000000000000000000000000000000000000000008057650000000000000000000000000000000000000000000000000000000063d96d10000000000000000000000000000000000000000000000000000000000009f35273d89754a1e0387b89520d989d3be9c37c1f32495a88faf1ea05c61121ab0d1900000000000000000000000000000000000000000000000000000000000000010000000000000000000000002d679b567db6187c0c8323fa982cfb88b74dbcc7000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240"));
self.tx(Some(l1_block_info)).await
}
/// Creates a transaction with data and signs it /// Creates a transaction with data and signs it
pub async fn tx(&mut self, data: Option<Bytes>) -> Bytes { pub async fn tx(&mut self, data: Option<Bytes>) -> Bytes {
let tx = TransactionRequest { let tx = TransactionRequest {

View File

@ -1,43 +1,27 @@
use futures_util::StreamExt; use crate::utils::EthNode;
use reth::{ use futures::StreamExt;
api::FullNodeComponents, use reth::rpc::eth::EthTransactions;
builder::{FullNode, NodeBuilder, NodeHandle}, use reth_e2e_test_utils::setup;
providers::CanonStateSubscriptions,
rpc::eth::EthTransactions,
tasks::TaskManager,
};
use reth_node_core::{args::RpcServerArgs, node_config::NodeConfig};
use reth_node_ethereum::EthereumNode;
use reth_primitives::{b256, hex, ChainSpec, Genesis}; use reth_primitives::{b256, hex, ChainSpec, Genesis};
use reth_provider::CanonStateSubscriptions;
use std::sync::Arc; use std::sync::Arc;
#[tokio::test] #[tokio::test]
async fn can_run_dev_node() -> eyre::Result<()> { async fn can_run_dev_node() -> eyre::Result<()> {
let tasks = TaskManager::current(); reth_tracing::init_test_tracing();
let (mut nodes, _tasks, _) = setup(1, custom_chain(), true).await?;
// create node config assert_chain_advances(nodes.pop().unwrap()).await;
let node_config = NodeConfig::test()
.dev()
.with_rpc(RpcServerArgs::default().with_http().with_unused_ports())
.with_chain(custom_chain());
let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config)
.testing_node(tasks.executor())
.node(EthereumNode::default())
.launch()
.await?;
assert_chain_advances(node).await;
Ok(()) Ok(())
} }
async fn assert_chain_advances<Node: FullNodeComponents>(mut node: FullNode<Node>) { async fn assert_chain_advances(mut node: EthNode) {
let mut notifications = node.provider.canonical_state_stream(); let mut notifications = node.inner.provider.canonical_state_stream();
// submit tx through rpc // submit tx through rpc
let raw_tx = hex!("02f876820a28808477359400847735940082520894ab0840c0e43688012c1adb0f5e3fc665188f83d28a029d394a5d630544000080c080a0a044076b7e67b5deecc63f61a8d7913fab86ca365b344b5759d1fe3563b4c39ea019eab979dd000da04dfc72bb0377c092d30fd9e1cab5ae487de49586cc8b0090"); let raw_tx = hex!("02f876820a28808477359400847735940082520894ab0840c0e43688012c1adb0f5e3fc665188f83d28a029d394a5d630544000080c080a0a044076b7e67b5deecc63f61a8d7913fab86ca365b344b5759d1fe3563b4c39ea019eab979dd000da04dfc72bb0377c092d30fd9e1cab5ae487de49586cc8b0090");
let eth_api = node.rpc_registry.eth_api(); let eth_api = node.inner.rpc_registry.eth_api();
let hash = eth_api.send_raw_transaction(raw_tx.into()).await.unwrap(); let hash = eth_api.send_raw_transaction(raw_tx.into()).await.unwrap();

View File

@ -4,7 +4,7 @@ use reth::{
builder::{NodeBuilder, NodeConfig, NodeHandle}, builder::{NodeBuilder, NodeConfig, NodeHandle},
tasks::TaskManager, tasks::TaskManager,
}; };
use reth_e2e_test_utils::{node::NodeHelper, wallet::Wallet}; use reth_e2e_test_utils::{node::NodeHelper, setup, wallet::Wallet};
use reth_node_ethereum::EthereumNode; use reth_node_ethereum::EthereumNode;
use reth_primitives::{ChainSpecBuilder, Genesis, MAINNET}; use reth_primitives::{ChainSpecBuilder, Genesis, MAINNET};
use std::sync::Arc; use std::sync::Arc;
@ -13,38 +13,24 @@ use std::sync::Arc;
async fn can_run_eth_node() -> eyre::Result<()> { async fn can_run_eth_node() -> eyre::Result<()> {
reth_tracing::init_test_tracing(); reth_tracing::init_test_tracing();
let exec = TaskManager::current(); let (mut nodes, _tasks, mut wallet) = setup::<EthereumNode>(
let exec = exec.executor(); 1,
Arc::new(
ChainSpecBuilder::default()
.chain(MAINNET.chain)
.genesis(serde_json::from_str(include_str!("../assets/genesis.json")).unwrap())
.cancun_activated()
.build(),
),
false,
)
.await?;
// Chain spec with test allocs let mut node = nodes.pop().unwrap();
let genesis: Genesis = serde_json::from_str(include_str!("../assets/genesis.json")).unwrap();
let chain_spec = Arc::new(
ChainSpecBuilder::default()
.chain(MAINNET.chain)
.genesis(genesis)
.cancun_activated()
.build(),
);
// Node setup
let node_config = NodeConfig::test()
.with_chain(chain_spec)
.with_unused_ports()
.with_rpc(RpcServerArgs::default().with_unused_ports().with_http());
let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config)
.testing_node(exec)
.node(EthereumNode::default())
.launch()
.await?;
let mut node = NodeHelper::new(node).await?;
// Configure wallet from test mnemonic and create dummy transfer tx
let mut wallet = Wallet::default();
let raw_tx = wallet.transfer_tx().await; let raw_tx = wallet.transfer_tx().await;
// make the node advance // make the node advance
node.advance(raw_tx, eth_payload_attributes).await?; node.advance_block(raw_tx, eth_payload_attributes).await?;
Ok(()) Ok(())
} }
@ -83,7 +69,7 @@ async fn can_run_eth_node_with_auth_engine_api_over_ipc() -> eyre::Result<()> {
let raw_tx = wallet.transfer_tx().await; let raw_tx = wallet.transfer_tx().await;
// make the node advance // make the node advance
node.advance(raw_tx, crate::utils::eth_payload_attributes).await?; node.advance_block(raw_tx, crate::utils::eth_payload_attributes).await?;
Ok(()) Ok(())
} }

View File

@ -1,71 +1,34 @@
use std::sync::Arc;
use crate::utils::eth_payload_attributes; use crate::utils::eth_payload_attributes;
use reth::{ use reth_e2e_test_utils::setup;
args::{DiscoveryArgs, NetworkArgs, RpcServerArgs},
builder::{NodeBuilder, NodeConfig, NodeHandle},
tasks::TaskManager,
};
use reth_e2e_test_utils::{node::NodeHelper, wallet::Wallet};
use reth_node_ethereum::EthereumNode; use reth_node_ethereum::EthereumNode;
use reth_primitives::{ChainSpecBuilder, Genesis, MAINNET}; use reth_primitives::{ChainSpecBuilder, MAINNET};
use std::sync::Arc;
#[tokio::test] #[tokio::test]
async fn can_sync() -> eyre::Result<()> { async fn can_sync() -> eyre::Result<()> {
reth_tracing::init_test_tracing(); reth_tracing::init_test_tracing();
let tasks = TaskManager::current(); let (mut nodes, _tasks, mut wallet) = setup::<EthereumNode>(
let exec = tasks.executor(); 2,
Arc::new(
ChainSpecBuilder::default()
.chain(MAINNET.chain)
.genesis(serde_json::from_str(include_str!("../assets/genesis.json")).unwrap())
.cancun_activated()
.build(),
),
false,
)
.await?;
let genesis: Genesis = serde_json::from_str(include_str!("../assets/genesis.json")).unwrap();
let chain_spec = Arc::new(
ChainSpecBuilder::default()
.chain(MAINNET.chain)
.genesis(genesis)
.cancun_activated()
.build(),
);
let network_config = NetworkArgs {
discovery: DiscoveryArgs { disable_discovery: true, ..DiscoveryArgs::default() },
..NetworkArgs::default()
};
let node_config = NodeConfig::test()
.with_chain(chain_spec)
.with_network(network_config)
.with_unused_ports()
.with_rpc(RpcServerArgs::default().with_unused_ports().with_http());
let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config.clone())
.testing_node(exec.clone())
.node(EthereumNode::default())
.launch()
.await?;
let mut first_node = NodeHelper::new(node.clone()).await?;
let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config)
.testing_node(exec)
.node(EthereumNode::default())
.launch()
.await?;
let mut second_node = NodeHelper::new(node).await?;
let mut wallet = Wallet::default();
let raw_tx = wallet.transfer_tx().await; let raw_tx = wallet.transfer_tx().await;
let mut second_node = nodes.pop().unwrap();
// Make them peer let mut first_node = nodes.pop().unwrap();
first_node.network.add_peer(second_node.network.record()).await;
second_node.network.add_peer(first_node.network.record()).await;
// Make sure they establish a new session
first_node.network.expect_session().await;
second_node.network.expect_session().await;
// Make the first node advance // Make the first node advance
let (block_hash, tx_hash) = first_node.advance(raw_tx.clone(), eth_payload_attributes).await?; let ((payload, _), tx_hash) =
first_node.advance_block(raw_tx.clone(), eth_payload_attributes).await?;
let block_hash = payload.block().hash();
// only send forkchoice update to second node // only send forkchoice update to second node
second_node.engine_api.update_forkchoice(block_hash).await?; second_node.engine_api.update_forkchoice(block_hash).await?;

View File

@ -1,7 +1,12 @@
use reth::rpc::types::engine::PayloadAttributes; use reth::rpc::types::engine::PayloadAttributes;
use reth_e2e_test_utils::NodeHelperType;
use reth_node_ethereum::EthereumNode;
use reth_payload_builder::EthPayloadBuilderAttributes; use reth_payload_builder::EthPayloadBuilderAttributes;
use reth_primitives::{Address, B256}; use reth_primitives::{Address, B256};
/// Ethereum Node Helper type
pub(crate) type EthNode = NodeHelperType<EthereumNode>;
/// Helper function to create a new eth payload attributes /// Helper function to create a new eth payload attributes
pub(crate) fn eth_payload_attributes(timestamp: u64) -> EthPayloadBuilderAttributes { pub(crate) fn eth_payload_attributes(timestamp: u64) -> EthPayloadBuilderAttributes {
let attributes = PayloadAttributes { let attributes = PayloadAttributes {

View File

@ -28,7 +28,7 @@ reth-network.workspace = true
reth-interfaces.workspace = true reth-interfaces.workspace = true
reth-evm.workspace = true reth-evm.workspace = true
reth-revm.workspace = true reth-revm.workspace = true
reth-beacon-consensus.workspace = true
revm.workspace = true revm.workspace = true
revm-primitives.workspace = true revm-primitives.workspace = true
@ -67,4 +67,5 @@ optimism = [
"reth-rpc/optimism", "reth-rpc/optimism",
"reth-revm/optimism", "reth-revm/optimism",
"reth-optimism-payload-builder/optimism", "reth-optimism-payload-builder/optimism",
"reth-beacon-consensus/optimism",
] ]

View File

@ -1,80 +1,28 @@
use crate::utils::{advance_chain, setup};
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::Mutex;
use crate::utils::optimism_payload_attributes;
use reth::{
args::{DiscoveryArgs, NetworkArgs, RpcServerArgs},
builder::{NodeBuilder, NodeConfig, NodeHandle},
tasks::TaskManager,
};
use reth_e2e_test_utils::{node::NodeHelper, wallet::Wallet};
use reth_node_optimism::node::OptimismNode;
use reth_primitives::{hex, Bytes, ChainSpecBuilder, Genesis, BASE_MAINNET};
#[tokio::test] #[tokio::test]
async fn can_sync() -> eyre::Result<()> { async fn can_sync() -> eyre::Result<()> {
reth_tracing::init_test_tracing(); reth_tracing::init_test_tracing();
let tasks = TaskManager::current(); let (mut nodes, _tasks, wallet) = setup(2).await?;
let exec = tasks.executor(); let wallet = Arc::new(Mutex::new(wallet));
let genesis: Genesis = serde_json::from_str(include_str!("../assets/genesis.json")).unwrap(); let second_node = nodes.pop().unwrap();
let chain_spec = Arc::new( let mut first_node = nodes.pop().unwrap();
ChainSpecBuilder::default()
.chain(BASE_MAINNET.chain)
.genesis(genesis)
.ecotone_activated()
.build(),
);
let mut wallet = Wallet::default().with_chain_id(chain_spec.chain.into());
let network_config = NetworkArgs { let tip: usize = 300;
discovery: DiscoveryArgs { disable_discovery: true, ..DiscoveryArgs::default() }, let tip_index: usize = tip - 1;
..NetworkArgs::default()
};
let node_config = NodeConfig::test() // On first node, create a chain up to block number 300a
.with_chain(chain_spec) let canonical_payload_chain = advance_chain(tip, &mut first_node, wallet.clone()).await?;
.with_network(network_config) let canonical_chain =
.with_unused_ports() canonical_payload_chain.iter().map(|p| p.0.block().hash()).collect::<Vec<_>>();
.with_rpc(RpcServerArgs::default().with_unused_ports().with_http());
let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config.clone()) // On second node, sync up to block number 300a
.testing_node(exec.clone()) second_node.engine_api.update_forkchoice(canonical_chain[tip_index]).await?;
.node(OptimismNode::default()) second_node.wait_block(tip as u64, canonical_chain[tip_index], true).await?;
.launch()
.await?;
let mut first_node = NodeHelper::new(node.clone()).await?;
let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config)
.testing_node(exec)
.node(OptimismNode::default())
.launch()
.await?;
let mut second_node = NodeHelper::new(node).await?;
// Make them peer
first_node.network.add_peer(second_node.network.record()).await;
second_node.network.add_peer(first_node.network.record()).await;
// Make sure they establish a new session
first_node.network.expect_session().await;
second_node.network.expect_session().await;
// Taken from optimism tests
let l1_block_info = Bytes::from_static(&hex!("7ef9015aa044bae9d41b8380d781187b426c6fe43df5fb2fb57bd4466ef6a701e1f01e015694deaddeaddeaddeaddeaddeaddeaddeaddead000194420000000000000000000000000000000000001580808408f0d18001b90104015d8eb900000000000000000000000000000000000000000000000000000000008057650000000000000000000000000000000000000000000000000000000063d96d10000000000000000000000000000000000000000000000000000000000009f35273d89754a1e0387b89520d989d3be9c37c1f32495a88faf1ea05c61121ab0d1900000000000000000000000000000000000000000000000000000000000000010000000000000000000000002d679b567db6187c0c8323fa982cfb88b74dbcc7000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240"));
// Make the first node advance
let raw_tx = wallet.tx(Some(l1_block_info)).await;
let (block_hash, tx_hash) =
first_node.advance(raw_tx.clone(), optimism_payload_attributes).await?;
// only send forkchoice update to second node
second_node.engine_api.update_forkchoice(block_hash).await?;
// expect second node advanced via p2p gossip
second_node.assert_new_block(tx_hash, block_hash, 1).await?;
Ok(()) Ok(())
} }

View File

@ -1,7 +1,45 @@
use reth::rpc::types::engine::PayloadAttributes; use reth::{rpc::types::engine::PayloadAttributes, tasks::TaskManager};
use reth_node_optimism::OptimismPayloadBuilderAttributes; use reth_e2e_test_utils::{wallet::Wallet, NodeHelperType};
use reth_node_optimism::{OptimismBuiltPayload, OptimismNode, OptimismPayloadBuilderAttributes};
use reth_payload_builder::EthPayloadBuilderAttributes; use reth_payload_builder::EthPayloadBuilderAttributes;
use reth_primitives::{Address, B256}; use reth_primitives::{Address, ChainSpecBuilder, Genesis, B256, BASE_MAINNET};
use std::sync::Arc;
use tokio::sync::Mutex;
/// Optimism Node Helper type
pub(crate) type OpNode = NodeHelperType<OptimismNode>;
pub(crate) async fn setup(num_nodes: usize) -> eyre::Result<(Vec<OpNode>, TaskManager, Wallet)> {
let genesis: Genesis = serde_json::from_str(include_str!("../assets/genesis.json")).unwrap();
reth_e2e_test_utils::setup(
num_nodes,
Arc::new(
ChainSpecBuilder::default()
.chain(BASE_MAINNET.chain)
.genesis(genesis)
.ecotone_activated()
.build(),
),
false,
)
.await
}
pub(crate) async fn advance_chain(
length: usize,
node: &mut OpNode,
wallet: Arc<Mutex<Wallet>>,
) -> eyre::Result<Vec<(OptimismBuiltPayload, OptimismPayloadBuilderAttributes)>> {
node.advance(
length as u64,
|| {
let wallet = wallet.clone();
Box::pin(async move { wallet.lock().await.optimism_l1_block_info_tx().await })
},
optimism_payload_attributes,
)
.await
}
/// Helper function to create a new eth payload attributes /// Helper function to create a new eth payload attributes
pub(crate) fn optimism_payload_attributes(timestamp: u64) -> OptimismPayloadBuilderAttributes { pub(crate) fn optimism_payload_attributes(timestamp: u64) -> OptimismPayloadBuilderAttributes {

View File

@ -2,7 +2,7 @@
use crate::{ use crate::{
AccountReader, BlockReaderIdExt, CanonStateSubscriptions, ChainSpecProvider, ChangeSetReader, AccountReader, BlockReaderIdExt, CanonStateSubscriptions, ChainSpecProvider, ChangeSetReader,
DatabaseProviderFactory, EvmEnvProvider, StateProviderFactory, DatabaseProviderFactory, EvmEnvProvider, StageCheckpointReader, StateProviderFactory,
}; };
use reth_db::database::Database; use reth_db::database::Database;
@ -16,6 +16,7 @@ pub trait FullProvider<DB: Database>:
+ ChainSpecProvider + ChainSpecProvider
+ ChangeSetReader + ChangeSetReader
+ CanonStateSubscriptions + CanonStateSubscriptions
+ StageCheckpointReader
+ Clone + Clone
+ Unpin + Unpin
+ 'static + 'static
@ -31,6 +32,7 @@ impl<T, DB: Database> FullProvider<DB> for T where
+ ChainSpecProvider + ChainSpecProvider
+ ChangeSetReader + ChangeSetReader
+ CanonStateSubscriptions + CanonStateSubscriptions
+ StageCheckpointReader
+ Clone + Clone
+ Unpin + Unpin
+ 'static + 'static