Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add more complex E2E test #12005

Merged
merged 2 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions crates/e2e-test-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ reth-node-builder = { workspace = true, features = ["test-utils"] }
reth-tokio-util.workspace = true
reth-stages-types.workspace = true
reth-network-peers.workspace = true
reth-engine-local.workspace = true

# rpc
jsonrpsee.workspace = true
url.workspace = true

# ethereum
alloy-primitives.workspace = true
Expand Down
51 changes: 38 additions & 13 deletions crates/e2e-test-utils/src/engine_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,22 @@ use reth::{
types::engine::{ForkchoiceState, PayloadStatusEnum},
},
};
use reth_chainspec::EthereumHardforks;
use reth_node_builder::BuiltPayload;
use reth_payload_builder::PayloadId;
use reth_rpc_layer::AuthClientService;
use std::marker::PhantomData;
use std::{marker::PhantomData, sync::Arc};

/// Helper for engine api operations
#[derive(Debug)]
pub struct EngineApiTestContext<E> {
pub struct EngineApiTestContext<E, ChainSpec> {
pub chain_spec: Arc<ChainSpec>,
pub canonical_stream: CanonStateNotificationStream,
pub engine_api_client: HttpClient<AuthClientService<HttpBackend>>,
pub _marker: PhantomData<E>,
}

impl<E: EngineTypes> EngineApiTestContext<E> {
impl<E: EngineTypes, ChainSpec: EthereumHardforks> EngineApiTestContext<E, ChainSpec> {
/// Retrieves a v3 payload from the engine api
pub async fn get_payload_v3(
&self,
Expand All @@ -51,18 +54,40 @@ impl<E: EngineTypes> EngineApiTestContext<E> {
) -> eyre::Result<B256>
where
E::ExecutionPayloadEnvelopeV3: From<E::BuiltPayload> + PayloadEnvelopeExt,
E::ExecutionPayloadEnvelopeV4: From<E::BuiltPayload> + PayloadEnvelopeExt,
{
// setup payload for submission
let envelope_v3: <E as EngineTypes>::ExecutionPayloadEnvelopeV3 = payload.into();

// submit payload to engine api
let submission = EngineApiClient::<E>::new_payload_v3(
&self.engine_api_client,
envelope_v3.execution_payload(),
versioned_hashes,
payload_builder_attributes.parent_beacon_block_root().unwrap(),
)
.await?;
let submission = if self
.chain_spec
.is_prague_active_at_timestamp(payload_builder_attributes.timestamp())
{
let requests = payload
.executed_block()
.unwrap()
.execution_outcome()
.requests
.first()
.unwrap()
.clone();
Comment on lines +64 to +71
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should likely track some improvement on this, it's not nice and i think we're doing it in some other place too

let envelope: <E as EngineTypes>::ExecutionPayloadEnvelopeV4 = payload.into();
EngineApiClient::<E>::new_payload_v4(
&self.engine_api_client,
envelope.execution_payload(),
versioned_hashes,
payload_builder_attributes.parent_beacon_block_root().unwrap(),
requests,
)
.await?
} else {
let envelope: <E as EngineTypes>::ExecutionPayloadEnvelopeV3 = payload.into();
EngineApiClient::<E>::new_payload_v3(
&self.engine_api_client,
envelope.execution_payload(),
versioned_hashes,
payload_builder_attributes.parent_beacon_block_root().unwrap(),
)
.await?
};

assert_eq!(submission.status, expected_status);

Expand Down
107 changes: 95 additions & 12 deletions crates/e2e-test-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ use reth::{
};
use reth_chainspec::{EthChainSpec, EthereumHardforks};
use reth_db::{test_utils::TempDatabase, DatabaseEnv};
use reth_engine_local::LocalPayloadAttributesBuilder;
use reth_node_builder::{
components::NodeComponentsBuilder, rpc::RethRpcAddOns, FullNodeTypesAdapter, Node, NodeAdapter,
NodeComponents, NodeTypesWithDBAdapter, NodeTypesWithEngine, RethFullAdapter,
components::NodeComponentsBuilder, rpc::RethRpcAddOns, EngineNodeLauncher,
FullNodeTypesAdapter, Node, NodeAdapter, NodeComponents, NodeTypesWithDBAdapter,
NodeTypesWithEngine, PayloadAttributesBuilder, PayloadTypes,
};
use reth_provider::providers::BlockchainProvider;
use reth_provider::providers::{BlockchainProvider, BlockchainProvider2};
use tracing::{span, Level};
use wallet::Wallet;

Expand Down Expand Up @@ -102,21 +104,102 @@ where
Ok((nodes, tasks, Wallet::default().with_chain_id(chain_spec.chain().into())))
}

/// Creates the initial setup with `num_nodes` started and interconnected.
pub async fn setup_engine<N>(
num_nodes: usize,
chain_spec: Arc<N::ChainSpec>,
is_dev: bool,
) -> eyre::Result<(
Vec<NodeHelperType<N, N::AddOns, BlockchainProvider2<NodeTypesWithDBAdapter<N, TmpDB>>>>,
TaskManager,
Wallet,
)>
where
N: Default
+ Node<TmpNodeAdapter<N, BlockchainProvider2<NodeTypesWithDBAdapter<N, TmpDB>>>>
+ NodeTypesWithEngine<ChainSpec: EthereumHardforks>,
N::ComponentsBuilder: NodeComponentsBuilder<
TmpNodeAdapter<N, BlockchainProvider2<NodeTypesWithDBAdapter<N, TmpDB>>>,
Components: NodeComponents<
TmpNodeAdapter<N, BlockchainProvider2<NodeTypesWithDBAdapter<N, TmpDB>>>,
Network: PeersHandleProvider,
>,
>,
N::AddOns: RethRpcAddOns<Adapter<N, BlockchainProvider2<NodeTypesWithDBAdapter<N, TmpDB>>>>,
LocalPayloadAttributesBuilder<N::ChainSpec>: PayloadAttributesBuilder<
<<N as NodeTypesWithEngine>::Engine as PayloadTypes>::PayloadAttributes,
>,
{
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<NodeTestContext<_, _>> = Vec::with_capacity(num_nodes);

for idx in 0..num_nodes {
let node_config = NodeConfig::new(chain_spec.clone())
.with_network(network_config.clone())
.with_unused_ports()
.with_rpc(RpcServerArgs::default().with_unused_ports().with_http())
.set_dev(is_dev);

let span = span!(Level::INFO, "node", idx);
let _enter = span.enter();
let node = N::default();
let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config.clone())
.testing_node(exec.clone())
.with_types_and_provider::<N, BlockchainProvider2<_>>()
.with_components(node.components_builder())
.with_add_ons(node.add_ons())
.launch_with_fn(|builder| {
let launcher = EngineNodeLauncher::new(
builder.task_executor().clone(),
builder.config().datadir(),
Default::default(),
);
builder.launch_with(launcher)
})
.await?;

let mut node = NodeTestContext::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 TmpNodeAdapter<N> = FullNodeTypesAdapter<
NodeTypesWithDBAdapter<N, TmpDB>,
BlockchainProvider<NodeTypesWithDBAdapter<N, TmpDB>>,
>;
type TmpNodeAdapter<N, Provider = BlockchainProvider<NodeTypesWithDBAdapter<N, TmpDB>>> =
FullNodeTypesAdapter<NodeTypesWithDBAdapter<N, TmpDB>, Provider>;

/// Type alias for a `NodeAdapter`
pub type Adapter<N> = NodeAdapter<
RethFullAdapter<TmpDB, N>,
<<N as Node<TmpNodeAdapter<N>>>::ComponentsBuilder as NodeComponentsBuilder<
RethFullAdapter<TmpDB, N>,
pub type Adapter<N, Provider = BlockchainProvider<NodeTypesWithDBAdapter<N, TmpDB>>> = NodeAdapter<
TmpNodeAdapter<N, Provider>,
<<N as Node<TmpNodeAdapter<N, Provider>>>::ComponentsBuilder as NodeComponentsBuilder<
TmpNodeAdapter<N, Provider>,
>>::Components,
>;

/// Type alias for a type of `NodeHelper`
pub type NodeHelperType<N, AO> = NodeTestContext<Adapter<N>, AO>;
pub type NodeHelperType<N, AO, Provider = BlockchainProvider<NodeTypesWithDBAdapter<N, TmpDB>>> =
NodeTestContext<Adapter<N, Provider>, AO>;
18 changes: 16 additions & 2 deletions crates/e2e-test-utils/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ use reth::{
},
};
use reth_chainspec::EthereumHardforks;
use reth_node_builder::{rpc::RethRpcAddOns, NodeTypesWithEngine};
use reth_node_builder::{rpc::RethRpcAddOns, NodeTypes, NodeTypesWithEngine};
use reth_stages_types::StageId;
use tokio_stream::StreamExt;
use url::Url;

use crate::{
engine_api::EngineApiTestContext, network::NetworkTestContext, payload::PayloadTestContext,
Expand All @@ -41,7 +42,10 @@ where
/// Context for testing network functionalities.
pub network: NetworkTestContext<Node::Network>,
/// Context for testing the Engine API.
pub engine_api: EngineApiTestContext<<Node::Types as NodeTypesWithEngine>::Engine>,
pub engine_api: EngineApiTestContext<
<Node::Types as NodeTypesWithEngine>::Engine,
<Node::Types as NodeTypes>::ChainSpec,
>,
/// Context for testing RPC features.
pub rpc: RpcTestContext<Node, AddOns::EthApi>,
}
Expand All @@ -63,6 +67,7 @@ where
payload: PayloadTestContext::new(builder).await?,
network: NetworkTestContext::new(node.network.clone()),
engine_api: EngineApiTestContext {
chain_spec: node.chain_spec(),
engine_api_client: node.auth_server_handle().http_client(),
canonical_stream: node.provider.canonical_state_stream(),
_marker: PhantomData::<Engine>,
Expand All @@ -89,6 +94,7 @@ where
) -> eyre::Result<Vec<(Engine::BuiltPayload, Engine::PayloadBuilderAttributes)>>
where
Engine::ExecutionPayloadEnvelopeV3: From<Engine::BuiltPayload> + PayloadEnvelopeExt,
Engine::ExecutionPayloadEnvelopeV4: From<Engine::BuiltPayload> + PayloadEnvelopeExt,
AddOns::EthApi: EthApiSpec + EthTransactions + TraceExt + FullEthApiTypes,
{
let mut chain = Vec::with_capacity(length as usize);
Expand Down Expand Up @@ -137,6 +143,8 @@ where
where
<Engine as EngineTypes>::ExecutionPayloadEnvelopeV3:
From<Engine::BuiltPayload> + PayloadEnvelopeExt,
<Engine as EngineTypes>::ExecutionPayloadEnvelopeV4:
From<Engine::BuiltPayload> + PayloadEnvelopeExt,
{
let (payload, eth_attr) = self.new_payload(attributes_generator).await?;

Expand Down Expand Up @@ -236,4 +244,10 @@ where
}
Ok(())
}

/// Returns the RPC URL.
pub fn rpc_url(&self) -> Url {
let addr = self.inner.rpc_server_handle().http_local_addr().unwrap();
format!("http://{}", addr).parse().unwrap()
}
}
15 changes: 14 additions & 1 deletion crates/e2e-test-utils/src/traits.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3;
use alloy_rpc_types::engine::ExecutionPayloadEnvelopeV4;
use op_alloy_rpc_types_engine::{OpExecutionPayloadEnvelopeV3, OpExecutionPayloadEnvelopeV4};
use reth::rpc::types::engine::{ExecutionPayloadEnvelopeV3, ExecutionPayloadV3};

/// The execution payload envelope type.
Expand All @@ -13,8 +14,20 @@ impl PayloadEnvelopeExt for OpExecutionPayloadEnvelopeV3 {
}
}

impl PayloadEnvelopeExt for OpExecutionPayloadEnvelopeV4 {
fn execution_payload(&self) -> ExecutionPayloadV3 {
self.execution_payload.clone()
}
}

impl PayloadEnvelopeExt for ExecutionPayloadEnvelopeV3 {
fn execution_payload(&self) -> ExecutionPayloadV3 {
self.execution_payload.clone()
}
}

impl PayloadEnvelopeExt for ExecutionPayloadEnvelopeV4 {
fn execution_payload(&self) -> ExecutionPayloadV3 {
self.execution_payload.clone()
}
}
3 changes: 3 additions & 0 deletions crates/ethereum/node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ alloy-genesis.workspace = true
tokio.workspace = true
serde_json.workspace = true
alloy-consensus.workspace = true
alloy-provider.workspace = true
rand.workspace = true
alloy-signer.workspace = true

[features]
default = []
Expand Down
Loading
Loading