kona_engine/task_queue/tasks/insert/
task.rs

1//! A task to insert an unsafe payload into the execution engine.
2
3use crate::{
4    EngineClient, EngineState, EngineTaskExt, InsertTaskError, SynchronizeTask,
5    state::EngineSyncStateUpdate,
6};
7use alloy_eips::eip7685::EMPTY_REQUESTS_HASH;
8use alloy_provider::ext::EngineApi;
9use alloy_rpc_types_engine::{
10    CancunPayloadFields, ExecutionPayloadInputV2, PayloadStatusEnum, PraguePayloadFields,
11};
12use async_trait::async_trait;
13use kona_genesis::RollupConfig;
14use kona_protocol::L2BlockInfo;
15use op_alloy_consensus::OpBlock;
16use op_alloy_provider::ext::engine::OpEngineApi;
17use op_alloy_rpc_types_engine::{
18    OpExecutionPayload, OpExecutionPayloadEnvelope, OpExecutionPayloadSidecar,
19};
20use std::{sync::Arc, time::Instant};
21
22/// The task to insert a payload into the execution engine.
23#[derive(Debug, Clone)]
24pub struct InsertTask {
25    /// The engine client.
26    client: Arc<EngineClient>,
27    /// The rollup config.
28    rollup_config: Arc<RollupConfig>,
29    /// The network payload envelope.
30    envelope: OpExecutionPayloadEnvelope,
31    /// If the payload is safe this is true.
32    /// A payload is safe if it is derived from a safe block.
33    is_payload_safe: bool,
34}
35
36impl InsertTask {
37    /// Creates a new insert task.
38    pub const fn new(
39        client: Arc<EngineClient>,
40        rollup_config: Arc<RollupConfig>,
41        envelope: OpExecutionPayloadEnvelope,
42        is_attributes_derived: bool,
43    ) -> Self {
44        Self { client, rollup_config, envelope, is_payload_safe: is_attributes_derived }
45    }
46
47    /// Checks the response of the `engine_newPayload` call.
48    const fn check_new_payload_status(&self, status: &PayloadStatusEnum) -> bool {
49        matches!(status, PayloadStatusEnum::Valid | PayloadStatusEnum::Syncing)
50    }
51}
52
53#[async_trait]
54impl EngineTaskExt for InsertTask {
55    type Output = ();
56
57    type Error = InsertTaskError;
58
59    async fn execute(&self, state: &mut EngineState) -> Result<(), InsertTaskError> {
60        let time_start = Instant::now();
61
62        // Insert the new payload.
63        // Form the new unsafe block ref from the execution payload.
64        let parent_beacon_block_root = self.envelope.parent_beacon_block_root.unwrap_or_default();
65        let insert_time_start = Instant::now();
66        let (response, block): (_, OpBlock) = match self.envelope.execution_payload.clone() {
67            OpExecutionPayload::V1(payload) => (
68                self.client.new_payload_v1(payload).await,
69                self.envelope
70                    .execution_payload
71                    .clone()
72                    .try_into_block()
73                    .map_err(InsertTaskError::FromBlockError)?,
74            ),
75            OpExecutionPayload::V2(payload) => {
76                let payload_input = ExecutionPayloadInputV2 {
77                    execution_payload: payload.payload_inner,
78                    withdrawals: Some(payload.withdrawals),
79                };
80                (
81                    self.client.new_payload_v2(payload_input).await,
82                    self.envelope
83                        .execution_payload
84                        .clone()
85                        .try_into_block()
86                        .map_err(InsertTaskError::FromBlockError)?,
87                )
88            }
89            OpExecutionPayload::V3(payload) => (
90                self.client.new_payload_v3(payload, parent_beacon_block_root).await,
91                self.envelope
92                    .execution_payload
93                    .clone()
94                    .try_into_block_with_sidecar(&OpExecutionPayloadSidecar::v3(
95                        CancunPayloadFields::new(parent_beacon_block_root, vec![]),
96                    ))
97                    .map_err(InsertTaskError::FromBlockError)?,
98            ),
99            OpExecutionPayload::V4(payload) => (
100                self.client.new_payload_v4(payload, parent_beacon_block_root).await,
101                self.envelope
102                    .execution_payload
103                    .clone()
104                    .try_into_block_with_sidecar(&OpExecutionPayloadSidecar::v4(
105                        CancunPayloadFields::new(parent_beacon_block_root, vec![]),
106                        PraguePayloadFields::new(EMPTY_REQUESTS_HASH),
107                    ))
108                    .map_err(InsertTaskError::FromBlockError)?,
109            ),
110        };
111
112        // Check the `engine_newPayload` response.
113        let response = match response {
114            Ok(resp) => resp,
115            Err(e) => return Err(InsertTaskError::InsertFailed(e)),
116        };
117        if !self.check_new_payload_status(&response.status) {
118            return Err(InsertTaskError::UnexpectedPayloadStatus(response.status));
119        }
120        let insert_duration = insert_time_start.elapsed();
121
122        let new_unsafe_ref =
123            L2BlockInfo::from_block_and_genesis(&block, &self.rollup_config.genesis)
124                .map_err(InsertTaskError::L2BlockInfoConstruction)?;
125
126        // Send a FCU to canonicalize the imported block.
127        SynchronizeTask::new(
128            Arc::clone(&self.client),
129            self.rollup_config.clone(),
130            EngineSyncStateUpdate {
131                cross_unsafe_head: Some(new_unsafe_ref),
132                unsafe_head: Some(new_unsafe_ref),
133                local_safe_head: self.is_payload_safe.then_some(new_unsafe_ref),
134                safe_head: self.is_payload_safe.then_some(new_unsafe_ref),
135                ..Default::default()
136            },
137        )
138        .execute(state)
139        .await?;
140
141        let total_duration = time_start.elapsed();
142
143        info!(
144            target: "engine",
145            hash = %new_unsafe_ref.block_info.hash,
146            number = new_unsafe_ref.block_info.number,
147            total_duration = ?total_duration,
148            insert_duration = ?insert_duration,
149            "Inserted new unsafe block"
150        );
151
152        Ok(())
153    }
154}