reth_payload_builder/traits.rs
1//! Trait abstractions used by the payload crate.
2
3use alloy_rpc_types::engine::PayloadId;
4use reth_chain_state::CanonStateNotification;
5use reth_payload_builder_primitives::PayloadBuilderError;
6use reth_payload_primitives::{BuiltPayload, PayloadAttributes, PayloadKind};
7use reth_primitives_traits::NodePrimitives;
8use std::future::Future;
9
10use crate::service::BuildNewPayload;
11
12/// A type that can build a payload.
13///
14/// This type is a [`Future`] that resolves when the job is done (e.g. complete, timed out) or it
15/// failed. It's not supposed to return the best payload built when it resolves, instead
16/// [`PayloadJob::best_payload`] should be used for that.
17///
18/// A `PayloadJob` must always be prepared to return the best payload built so far to ensure there
19/// is a valid payload to deliver to the CL, so it does not miss a slot, even if the payload is
20/// empty.
21///
22/// Note: A `PayloadJob` need to be cancel safe because it might be dropped after the CL has requested the payload via `engine_getPayloadV1` (see also [engine API docs](https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/paris.md#engine_getpayloadv1))
23pub trait PayloadJob: Future<Output = Result<(), PayloadBuilderError>> {
24 /// Represents the payload attributes type that is used to spawn this payload job.
25 type PayloadAttributes: PayloadAttributes + std::fmt::Debug;
26 /// Represents the future that resolves the block that's returned to the CL.
27 type ResolvePayloadFuture: Future<Output = Result<Self::BuiltPayload, PayloadBuilderError>>
28 + Send
29 + 'static;
30 /// Represents the built payload type that is returned to the CL.
31 type BuiltPayload: BuiltPayload + Clone + std::fmt::Debug;
32
33 /// Returns the best payload that has been built so far.
34 ///
35 /// Note: This is never called by the CL.
36 fn best_payload(&self) -> Result<Self::BuiltPayload, PayloadBuilderError>;
37
38 /// Returns the payload attributes for the payload being built.
39 fn payload_attributes(&self) -> Result<Self::PayloadAttributes, PayloadBuilderError>;
40
41 /// Returns the payload timestamp for the payload being built.
42 /// The default implementation allocates full attributes only to
43 /// extract the timestamp. Provide your own implementation if you
44 /// need performance here.
45 fn payload_timestamp(&self) -> Result<u64, PayloadBuilderError> {
46 Ok(self.payload_attributes()?.timestamp())
47 }
48
49 /// Called when the payload is requested by the CL.
50 ///
51 /// This is invoked on [`engine_getPayloadV2`](https://github.com/ethereum/execution-apis/blob/main/src/engine/shanghai.md#engine_getpayloadv2) and [`engine_getPayloadV1`](https://github.com/ethereum/execution-apis/blob/main/src/engine/paris.md#engine_getpayloadv1).
52 ///
53 /// The timeout for returning the payload to the CL is 1s, thus the future returned should
54 /// resolve in under 1 second.
55 ///
56 /// Ideally this is the best payload built so far, or an empty block without transactions, if
57 /// nothing has been built yet.
58 ///
59 /// According to the spec:
60 /// > Client software MAY stop the corresponding build process after serving this call.
61 ///
62 /// It is at the discretion of the implementer whether the build job should be kept alive or
63 /// terminated.
64 ///
65 /// If this returns [`KeepPayloadJobAlive::Yes`], then the [`PayloadJob`] will be polled
66 /// once more. If this returns [`KeepPayloadJobAlive::No`] then the [`PayloadJob`] will be
67 /// dropped after this call.
68 ///
69 /// # Cancellation safety
70 ///
71 /// The returned [`ResolvePayloadFuture`](Self::ResolvePayloadFuture) is not
72 /// cancellation-safe. Dropping it cancels resolving the payload and, when the corresponding
73 /// handle resolve call has removed the payload job, cancels the job identified by that
74 /// `payload_id`.
75 ///
76 /// The [`PayloadKind`] determines how the payload should be resolved in the
77 /// `ResolvePayloadFuture`. [`PayloadKind::Earliest`] should return the earliest available
78 /// payload (as fast as possible), e.g. racing an empty payload job against a pending job if
79 /// there's no payload available yet. [`PayloadKind::WaitForPending`] is allowed to wait
80 /// until a built payload is available.
81 fn resolve_kind(
82 &mut self,
83 kind: PayloadKind,
84 ) -> (Self::ResolvePayloadFuture, KeepPayloadJobAlive);
85
86 /// Resolves the payload as fast as possible.
87 ///
88 /// See also [`PayloadJob::resolve_kind`]
89 fn resolve(&mut self) -> (Self::ResolvePayloadFuture, KeepPayloadJobAlive) {
90 self.resolve_kind(PayloadKind::Earliest)
91 }
92}
93
94/// Whether the payload job should be kept alive or terminated after the payload was requested by
95/// the CL.
96#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub enum KeepPayloadJobAlive {
98 /// Keep the job alive.
99 Yes,
100 /// Terminate the job.
101 No,
102}
103
104/// A type that knows how to create new jobs for creating payloads.
105pub trait PayloadJobGenerator {
106 /// The type that manages the lifecycle of a payload.
107 ///
108 /// This type is a future that yields better payloads.
109 type Job: PayloadJob;
110
111 /// Creates the initial payload and a new [`PayloadJob`] that yields better payloads over time.
112 ///
113 /// This is called when the CL requests a new payload job via a fork choice update.
114 ///
115 /// # Note
116 ///
117 /// This is expected to initially build a new (empty) payload without transactions, so it can be
118 /// returned directly.
119 fn new_payload_job(
120 &self,
121 input: BuildNewPayload<<Self::Job as PayloadJob>::PayloadAttributes>,
122 id: PayloadId,
123 ) -> Result<Self::Job, PayloadBuilderError>;
124
125 /// Handles new chain state events
126 ///
127 /// This is intended for any logic that needs to be run when the chain state changes or used to
128 /// use the in memory state for the head block.
129 fn on_new_state<N: NodePrimitives>(&mut self, new_state: CanonStateNotification<N>) {
130 let _ = new_state;
131 }
132}