reth_e2e_test_utils/
engine_api.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use crate::traits::PayloadEnvelopeExt;
use alloy_primitives::B256;
use alloy_rpc_types_engine::{ForkchoiceState, PayloadStatusEnum};
use jsonrpsee::{
    core::client::ClientT,
    http_client::{transport::HttpBackend, HttpClient},
};
use reth_chainspec::EthereumHardforks;
use reth_node_api::EngineTypes;
use reth_node_builder::BuiltPayload;
use reth_payload_builder::PayloadId;
use reth_payload_primitives::PayloadBuilderAttributes;
use reth_provider::CanonStateNotificationStream;
use reth_rpc_api::EngineApiClient;
use reth_rpc_layer::AuthClientService;
use std::{marker::PhantomData, sync::Arc};

/// Helper for engine api operations
#[derive(Debug)]
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, ChainSpec: EthereumHardforks> EngineApiTestContext<E, ChainSpec> {
    /// Retrieves a v3 payload from the engine api
    pub async fn get_payload_v3(
        &self,
        payload_id: PayloadId,
    ) -> eyre::Result<E::ExecutionPayloadEnvelopeV3> {
        Ok(EngineApiClient::<E>::get_payload_v3(&self.engine_api_client, payload_id).await?)
    }

    /// Retrieves a v3 payload from the engine api as serde value
    pub async fn get_payload_v3_value(
        &self,
        payload_id: PayloadId,
    ) -> eyre::Result<serde_json::Value> {
        Ok(self.engine_api_client.request("engine_getPayloadV3", (payload_id,)).await?)
    }

    /// Submits a payload to the engine api
    pub async fn submit_payload(
        &self,
        payload: E::BuiltPayload,
        payload_builder_attributes: E::PayloadBuilderAttributes,
        expected_status: PayloadStatusEnum,
    ) -> eyre::Result<B256>
    where
        E::ExecutionPayloadEnvelopeV3: From<E::BuiltPayload> + PayloadEnvelopeExt,
        E::ExecutionPayloadEnvelopeV4: From<E::BuiltPayload> + PayloadEnvelopeExt,
    {
        let versioned_hashes =
            payload.block().blob_versioned_hashes_iter().copied().collect::<Vec<_>>();
        // submit payload to engine api
        let submission = if self
            .chain_spec
            .is_prague_active_at_timestamp(payload_builder_attributes.timestamp())
        {
            let requests = payload.requests().unwrap();
            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.as_str(), expected_status.as_str());

        Ok(submission.latest_valid_hash.unwrap_or_default())
    }

    /// Sends forkchoice update to the engine api
    pub async fn update_forkchoice(&self, current_head: B256, new_head: B256) -> eyre::Result<()> {
        EngineApiClient::<E>::fork_choice_updated_v2(
            &self.engine_api_client,
            ForkchoiceState {
                head_block_hash: new_head,
                safe_block_hash: current_head,
                finalized_block_hash: current_head,
            },
            None,
        )
        .await?;
        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(())
    }
}