Skip to content

Commit

Permalink
chore(e2e): refactor e2e tests (#7773)
Browse files Browse the repository at this point in the history
  • Loading branch information
joshieDo committed Apr 20, 2024
1 parent 66d5ecb commit 3750edd
Show file tree
Hide file tree
Showing 23 changed files with 400 additions and 125 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jobs:
run: |
cargo nextest run \
--locked --features "asm-keccak ${{ matrix.network }}" \
--workspace --exclude examples --exclude ef-tests node-e2e-tests \
--workspace --exclude examples --exclude ef-tests node-ethereum \
-E "kind(test)"
sync:
Expand Down
64 changes: 37 additions & 27 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ members = [
"crates/consensus/beacon-core/",
"crates/consensus/common/",
"crates/ethereum-forks/",
"crates/e2e-test-utils/",
"crates/etl/",
"crates/evm/",
"crates/ethereum/evm",
Expand Down Expand Up @@ -51,7 +52,6 @@ members = [
"crates/optimism/node/",
"crates/node-core/",
"crates/node/api/",
"crates/node-e2e-tests/",
"crates/stages/",
"crates/stages-api",
"crates/static-file/",
Expand Down Expand Up @@ -212,6 +212,7 @@ reth-db = { path = "crates/storage/db" }
reth-discv4 = { path = "crates/net/discv4" }
reth-discv5 = { path = "crates/net/discv5" }
reth-dns-discovery = { path = "crates/net/dns" }
reth-e2e-test-utils = { path = "crates/e2e-test-utils" }
reth-engine-primitives = { path = "crates/engine-primitives" }
reth-ethereum-engine-primitives = { path = "crates/ethereum/engine-primitives" }
reth-node-builder = { path = "crates/node-builder" }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
[package]
name = "node-e2e-tests"
version = "0.0.0"
publish = false
name = "reth-e2e-test-utils"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true


[dependencies]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,56 +1,57 @@
use crate::traits::PayloadEnvelopeExt;
use jsonrpsee::http_client::HttpClient;
use reth::{
api::{EngineTypes, PayloadBuilderAttributes},
providers::CanonStateNotificationStream,
rpc::{
api::EngineApiClient,
types::engine::{ExecutionPayloadEnvelopeV3, ForkchoiceState},
},
rpc::{api::EngineApiClient, types::engine::ForkchoiceState},
};
use reth_node_ethereum::EthEngineTypes;
use reth_payload_builder::{EthBuiltPayload, EthPayloadBuilderAttributes, PayloadId};
use reth_payload_builder::PayloadId;
use reth_primitives::B256;
use std::marker::PhantomData;

/// Helper for engine api operations
pub struct EngineApiHelper {
pub struct EngineApiHelper<E> {
pub canonical_stream: CanonStateNotificationStream,
pub engine_api_client: HttpClient,
pub _marker: PhantomData<E>,
}

impl EngineApiHelper {
impl<E: EngineTypes + 'static> EngineApiHelper<E> {
/// Retrieves a v3 payload from the engine api
pub async fn get_payload_v3(
&self,
payload_id: PayloadId,
) -> eyre::Result<ExecutionPayloadEnvelopeV3> {
Ok(EngineApiClient::<EthEngineTypes>::get_payload_v3(&self.engine_api_client, payload_id)
.await?)
) -> eyre::Result<E::ExecutionPayloadV3> {
Ok(EngineApiClient::<E>::get_payload_v3(&self.engine_api_client, payload_id).await?)
}

/// Submits a payload to the engine api
pub async fn submit_payload(
&self,
payload: EthBuiltPayload,
eth_attr: EthPayloadBuilderAttributes,
) -> eyre::Result<B256> {
payload: E::BuiltPayload,
payload_builder_attributes: E::PayloadBuilderAttributes,
) -> eyre::Result<B256>
where
E::ExecutionPayloadV3: From<E::BuiltPayload> + PayloadEnvelopeExt,
{
// setup payload for submission
let envelope_v3 = ExecutionPayloadEnvelopeV3::from(payload);
let payload_v3 = envelope_v3.execution_payload;
let envelope_v3: <E as EngineTypes>::ExecutionPayloadV3 = payload.into();

// submit payload to engine api
let submission = EngineApiClient::<EthEngineTypes>::new_payload_v3(
let submission = EngineApiClient::<E>::new_payload_v3(
&self.engine_api_client,
payload_v3,
envelope_v3.execution_payload(),
vec![],
eth_attr.parent_beacon_block_root.unwrap(),
payload_builder_attributes.parent_beacon_block_root().unwrap(),
)
.await?;
assert!(submission.is_valid());
assert!(submission.is_valid(), "{}", submission);
Ok(submission.latest_valid_hash.unwrap())
}

/// Sends forkchoice update to the engine api
pub async fn update_forkchoice(&self, hash: B256) -> eyre::Result<()> {
EngineApiClient::<EthEngineTypes>::fork_choice_updated_v2(
EngineApiClient::<E>::fork_choice_updated_v2(
&self.engine_api_client,
ForkchoiceState {
head_block_hash: hash,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ mod network;

/// Helper for engine api operations
mod engine_api;

/// Helper traits
mod traits;
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,37 +1,40 @@
use crate::{engine_api::EngineApiHelper, network::NetworkHelper, payload::PayloadHelper};
use crate::{
engine_api::EngineApiHelper, network::NetworkHelper, payload::PayloadHelper,
traits::PayloadEnvelopeExt,
};
use alloy_rpc_types::BlockNumberOrTag;
use eyre::Ok;
use reth::{
api::FullNodeComponents,
api::{BuiltPayload, EngineTypes, FullNodeComponents, PayloadBuilderAttributes},
builder::FullNode,
providers::{BlockReaderIdExt, CanonStateSubscriptions},
rpc::{
eth::{error::EthResult, EthTransactions},
types::engine::PayloadAttributes,
},
};

use reth_node_ethereum::EthEngineTypes;
use reth_payload_builder::EthPayloadBuilderAttributes;
use reth_primitives::{Address, Bytes, B256};

use std::time::{SystemTime, UNIX_EPOCH};
use reth_primitives::{Address, BlockNumber, Bytes, B256};
use std::{
marker::PhantomData,
time::{SystemTime, UNIX_EPOCH},
};
use tokio_stream::StreamExt;

/// An helper struct to handle node actions
pub struct NodeHelper<Node>
where
Node: FullNodeComponents<Engine = EthEngineTypes>,
Node: FullNodeComponents,
{
pub inner: FullNode<Node>,
payload: PayloadHelper<Node::Engine>,
pub network: NetworkHelper,
pub engine_api: EngineApiHelper,
pub engine_api: EngineApiHelper<Node::Engine>,
}

impl<Node> NodeHelper<Node>
where
Node: FullNodeComponents<Engine = EthEngineTypes>,
Node: FullNodeComponents,
{
/// Creates a new test node
pub async fn new(node: FullNode<Node>) -> eyre::Result<Self> {
Expand All @@ -44,17 +47,26 @@ where
engine_api: EngineApiHelper {
engine_api_client: node.auth_server_handle().http_client(),
canonical_stream: node.provider.canonical_state_stream(),
_marker: PhantomData::<Node::Engine>,
},
})
}

/// Advances the node forward
pub async fn advance(&mut self, raw_tx: Bytes) -> eyre::Result<(B256, B256)> {
pub async fn advance(
&mut self,
raw_tx: Bytes,
attributes_generator: impl Fn(u64) -> <Node::Engine as EngineTypes>::PayloadBuilderAttributes,
) -> eyre::Result<(B256, B256)>
where
<Node::Engine as EngineTypes>::ExecutionPayloadV3:
From<<Node::Engine as EngineTypes>::BuiltPayload> + PayloadEnvelopeExt,
{
// push tx into pool via RPC server
let tx_hash = self.inject_tx(raw_tx).await?;

// trigger new payload building draining the pool
let eth_attr = self.payload.new_payload().await.unwrap();
let eth_attr = self.payload.new_payload(attributes_generator).await.unwrap();

// first event is the payload attributes
self.payload.expect_attr_event(eth_attr.clone()).await?;
Expand All @@ -69,13 +81,14 @@ where
let payload = self.payload.expect_built_payload().await?;

// submit payload via engine api
let block_number = payload.block().number;
let block_hash = self.engine_api.submit_payload(payload, eth_attr.clone()).await?;

// trigger forkchoice update via engine api to commit the block to the blockchain
self.engine_api.update_forkchoice(block_hash).await?;

// assert the block has been committed to the blockchain
self.assert_new_block(tx_hash, block_hash).await?;
self.assert_new_block(tx_hash, block_hash, block_number).await?;
Ok((block_hash, tx_hash))
}

Expand All @@ -91,21 +104,28 @@ where
&mut self,
tip_tx_hash: B256,
block_hash: B256,
block_number: BlockNumber,
) -> eyre::Result<()> {
// get head block from notifications stream and verify the tx has been pushed to the
// pool is actually present in the canonical block
let head = self.engine_api.canonical_stream.next().await.unwrap();
let tx = head.tip().transactions().next();
assert_eq!(tx.unwrap().hash().as_slice(), tip_tx_hash.as_slice());

// wait for the block to commit
tokio::time::sleep(std::time::Duration::from_secs(1)).await;

// make sure the block hash we submitted via FCU engine api is the new latest block
// using an RPC call
let latest_block =
self.inner.provider.block_by_number_or_tag(BlockNumberOrTag::Latest)?.unwrap();
assert_eq!(latest_block.hash_slow(), block_hash);
loop {
// wait for the block to commit
tokio::time::sleep(std::time::Duration::from_millis(20)).await;
if let Some(latest_block) =
self.inner.provider.block_by_number_or_tag(BlockNumberOrTag::Latest)?
{
if latest_block.number == block_number {
// make sure the block hash we submitted via FCU engine api is the new latest
// block using an RPC call
assert_eq!(latest_block.hash_slow(), block_hash);
break
}
}
}
Ok(())
}
}
Expand Down
Loading

0 comments on commit 3750edd

Please sign in to comment.