reth_e2e_test_utils/
engine_api.rs

1use crate::traits::PayloadEnvelopeExt;
2use alloy_primitives::B256;
3use alloy_rpc_types_engine::{ForkchoiceState, PayloadStatusEnum};
4use jsonrpsee::{
5    core::client::ClientT,
6    http_client::{transport::HttpBackend, HttpClient},
7};
8use reth_chainspec::EthereumHardforks;
9use reth_node_api::EngineTypes;
10use reth_node_builder::BuiltPayload;
11use reth_payload_builder::PayloadId;
12use reth_payload_primitives::PayloadBuilderAttributes;
13use reth_provider::CanonStateNotificationStream;
14use reth_rpc_api::EngineApiClient;
15use reth_rpc_layer::AuthClientService;
16use std::{marker::PhantomData, sync::Arc};
17
18/// Helper for engine api operations
19#[derive(Debug)]
20pub struct EngineApiTestContext<E, ChainSpec> {
21    pub chain_spec: Arc<ChainSpec>,
22    pub canonical_stream: CanonStateNotificationStream,
23    pub engine_api_client: HttpClient<AuthClientService<HttpBackend>>,
24    pub _marker: PhantomData<E>,
25}
26
27impl<E: EngineTypes, ChainSpec: EthereumHardforks> EngineApiTestContext<E, ChainSpec> {
28    /// Retrieves a v3 payload from the engine api
29    pub async fn get_payload_v3(
30        &self,
31        payload_id: PayloadId,
32    ) -> eyre::Result<E::ExecutionPayloadEnvelopeV3> {
33        Ok(EngineApiClient::<E>::get_payload_v3(&self.engine_api_client, payload_id).await?)
34    }
35
36    /// Retrieves a v3 payload from the engine api as serde value
37    pub async fn get_payload_v3_value(
38        &self,
39        payload_id: PayloadId,
40    ) -> eyre::Result<serde_json::Value> {
41        Ok(self.engine_api_client.request("engine_getPayloadV3", (payload_id,)).await?)
42    }
43
44    /// Submits a payload to the engine api
45    pub async fn submit_payload(
46        &self,
47        payload: E::BuiltPayload,
48        payload_builder_attributes: E::PayloadBuilderAttributes,
49        expected_status: PayloadStatusEnum,
50    ) -> eyre::Result<B256>
51    where
52        E::ExecutionPayloadEnvelopeV3: From<E::BuiltPayload> + PayloadEnvelopeExt,
53        E::ExecutionPayloadEnvelopeV4: From<E::BuiltPayload> + PayloadEnvelopeExt,
54    {
55        let versioned_hashes =
56            payload.block().blob_versioned_hashes_iter().copied().collect::<Vec<_>>();
57        // submit payload to engine api
58        let submission = if self
59            .chain_spec
60            .is_prague_active_at_timestamp(payload_builder_attributes.timestamp())
61        {
62            let requests = payload.requests().unwrap();
63            let envelope: <E as EngineTypes>::ExecutionPayloadEnvelopeV4 = payload.into();
64            EngineApiClient::<E>::new_payload_v4(
65                &self.engine_api_client,
66                envelope.execution_payload(),
67                versioned_hashes,
68                payload_builder_attributes.parent_beacon_block_root().unwrap(),
69                requests,
70            )
71            .await?
72        } else {
73            let envelope: <E as EngineTypes>::ExecutionPayloadEnvelopeV3 = payload.into();
74            EngineApiClient::<E>::new_payload_v3(
75                &self.engine_api_client,
76                envelope.execution_payload(),
77                versioned_hashes,
78                payload_builder_attributes.parent_beacon_block_root().unwrap(),
79            )
80            .await?
81        };
82
83        assert_eq!(submission.status.as_str(), expected_status.as_str());
84
85        Ok(submission.latest_valid_hash.unwrap_or_default())
86    }
87
88    /// Sends forkchoice update to the engine api
89    pub async fn update_forkchoice(&self, current_head: B256, new_head: B256) -> eyre::Result<()> {
90        EngineApiClient::<E>::fork_choice_updated_v2(
91            &self.engine_api_client,
92            ForkchoiceState {
93                head_block_hash: new_head,
94                safe_block_hash: current_head,
95                finalized_block_hash: current_head,
96            },
97            None,
98        )
99        .await?;
100        Ok(())
101    }
102
103    /// Sends forkchoice update to the engine api with a zero finalized hash
104    pub async fn update_optimistic_forkchoice(&self, hash: B256) -> eyre::Result<()> {
105        EngineApiClient::<E>::fork_choice_updated_v2(
106            &self.engine_api_client,
107            ForkchoiceState {
108                head_block_hash: hash,
109                safe_block_hash: B256::ZERO,
110                finalized_block_hash: B256::ZERO,
111            },
112            None,
113        )
114        .await?;
115
116        Ok(())
117    }
118}