From 7cf492d5257d94ba0b57f26c5ef0615c65946f7b Mon Sep 17 00:00:00 2001 From: greged93 <82421016+greged93@users.noreply.github.com> Date: Fri, 20 Sep 2024 09:12:14 +0200 Subject: [PATCH] feat(payload): abstract payload builder in trait (#10965) Co-authored-by: Matthias Seitz --- Cargo.lock | 11 +- crates/consensus/beacon/src/engine/message.rs | 2 +- crates/consensus/beacon/src/engine/mod.rs | 2 +- crates/e2e-test-utils/Cargo.toml | 1 + crates/e2e-test-utils/src/payload.rs | 3 +- crates/engine/local/src/service.rs | 2 +- crates/engine/tree/src/tree/mod.rs | 2 +- crates/ethereum/payload/src/lib.rs | 6 +- crates/node/builder/Cargo.toml | 1 + crates/node/builder/src/launch/engine.rs | 1 + crates/optimism/payload/src/builder.rs | 3 +- crates/payload/basic/src/lib.rs | 5 +- crates/payload/builder/Cargo.toml | 5 +- crates/payload/builder/src/error.rs | 58 -------- crates/payload/builder/src/lib.rs | 7 +- crates/payload/builder/src/noop.rs | 2 +- crates/payload/builder/src/service.rs | 135 +++++++++--------- crates/payload/builder/src/test_utils.rs | 8 +- crates/payload/builder/src/traits.rs | 3 +- crates/payload/primitives/Cargo.toml | 6 +- crates/payload/primitives/src/error.rs | 6 + .../{builder => primitives}/src/events.rs | 8 +- crates/payload/primitives/src/lib.rs | 8 +- crates/payload/primitives/src/traits.rs | 49 +++++++ crates/rpc/rpc-engine-api/src/error.rs | 3 +- examples/custom-engine-types/src/main.rs | 2 +- .../custom-payload-builder/src/generator.rs | 2 +- examples/custom-payload-builder/src/job.rs | 2 +- 28 files changed, 169 insertions(+), 174 deletions(-) rename crates/payload/{builder => primitives}/src/events.rs (93%) diff --git a/Cargo.lock b/Cargo.lock index 0d7816c337db..e1a575bb3158 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6903,6 +6903,7 @@ dependencies = [ "reth-node-builder", "reth-node-ethereum", "reth-payload-builder", + "reth-payload-primitives", "reth-primitives", "reth-provider", "reth-rpc", @@ -7748,6 +7749,7 @@ dependencies = [ "reth-node-events", "reth-node-metrics", "reth-payload-builder", + "reth-payload-primitives", "reth-payload-validator", "reth-primitives", "reth-provider", @@ -8118,20 +8120,17 @@ name = "reth-payload-builder" version = "1.0.7" dependencies = [ "alloy-primitives", + "async-trait", "futures-util", "metrics", - "pin-project", "reth-chain-state", - "reth-errors", "reth-ethereum-engine-primitives", "reth-metrics", "reth-payload-primitives", "reth-primitives", "reth-provider", "reth-rpc-types", - "reth-transaction-pool", "revm", - "thiserror", "tokio", "tokio-stream", "tracing", @@ -8142,6 +8141,8 @@ name = "reth-payload-primitives" version = "1.0.7" dependencies = [ "alloy-primitives", + "async-trait", + "pin-project", "reth-chain-state", "reth-chainspec", "reth-errors", @@ -8151,6 +8152,8 @@ dependencies = [ "serde", "thiserror", "tokio", + "tokio-stream", + "tracing", ] [[package]] diff --git a/crates/consensus/beacon/src/engine/message.rs b/crates/consensus/beacon/src/engine/message.rs index 04f79b2d4ddc..435f8a313d85 100644 --- a/crates/consensus/beacon/src/engine/message.rs +++ b/crates/consensus/beacon/src/engine/message.rs @@ -2,7 +2,7 @@ use crate::engine::{error::BeaconOnNewPayloadError, forkchoice::ForkchoiceStatus use futures::{future::Either, FutureExt}; use reth_engine_primitives::EngineTypes; use reth_errors::RethResult; -use reth_payload_builder::error::PayloadBuilderError; +use reth_payload_primitives::PayloadBuilderError; use reth_rpc_types::engine::{ CancunPayloadFields, ExecutionPayload, ForkChoiceUpdateResult, ForkchoiceState, ForkchoiceUpdateError, ForkchoiceUpdated, PayloadId, PayloadStatus, PayloadStatusEnum, diff --git a/crates/consensus/beacon/src/engine/mod.rs b/crates/consensus/beacon/src/engine/mod.rs index 415b95c5a79f..8a785aa9ea22 100644 --- a/crates/consensus/beacon/src/engine/mod.rs +++ b/crates/consensus/beacon/src/engine/mod.rs @@ -13,7 +13,7 @@ use reth_network_p2p::{ }; use reth_node_types::NodeTypesWithEngine; use reth_payload_builder::PayloadBuilderHandle; -use reth_payload_primitives::{PayloadAttributes, PayloadBuilderAttributes}; +use reth_payload_primitives::{PayloadAttributes, PayloadBuilder, PayloadBuilderAttributes}; use reth_payload_validator::ExecutionPayloadValidator; use reth_primitives::{ constants::EPOCH_SLOTS, BlockNumHash, Head, Header, SealedBlock, SealedHeader, diff --git a/crates/e2e-test-utils/Cargo.toml b/crates/e2e-test-utils/Cargo.toml index 36f525533a76..1c3b0eb0df9d 100644 --- a/crates/e2e-test-utils/Cargo.toml +++ b/crates/e2e-test-utils/Cargo.toml @@ -19,6 +19,7 @@ reth-db = { workspace = true, features = ["test-utils"] } reth-rpc.workspace = true reth-rpc-layer.workspace = true reth-payload-builder = { workspace = true, features = ["test-utils"] } +reth-payload-primitives.workspace = true reth-provider.workspace = true reth-node-builder = { workspace = true, features = ["test-utils"] } reth-tokio-util.workspace = true diff --git a/crates/e2e-test-utils/src/payload.rs b/crates/e2e-test-utils/src/payload.rs index 150c5a5ca6a1..e2f1445e7849 100644 --- a/crates/e2e-test-utils/src/payload.rs +++ b/crates/e2e-test-utils/src/payload.rs @@ -1,6 +1,7 @@ use futures_util::StreamExt; use reth::api::{BuiltPayload, EngineTypes, PayloadBuilderAttributes}; -use reth_payload_builder::{Events, PayloadBuilderHandle, PayloadId}; +use reth_payload_builder::{PayloadBuilderHandle, PayloadId}; +use reth_payload_primitives::{Events, PayloadBuilder}; use tokio_stream::wrappers::BroadcastStream; /// Helper for payload operations diff --git a/crates/engine/local/src/service.rs b/crates/engine/local/src/service.rs index 765737bbad5d..f9d2bd2aacb0 100644 --- a/crates/engine/local/src/service.rs +++ b/crates/engine/local/src/service.rs @@ -12,7 +12,7 @@ use reth_beacon_consensus::EngineNodeTypes; use reth_engine_tree::persistence::PersistenceHandle; use reth_payload_builder::PayloadBuilderHandle; use reth_payload_primitives::{ - BuiltPayload, PayloadAttributesBuilder, PayloadBuilderAttributes, PayloadTypes, + BuiltPayload, PayloadAttributesBuilder, PayloadBuilder, PayloadBuilderAttributes, PayloadTypes, }; use reth_provider::ProviderFactory; use reth_prune::PrunerWithFactory; diff --git a/crates/engine/tree/src/tree/mod.rs b/crates/engine/tree/src/tree/mod.rs index 05600a8984bb..dc71d2d75ef1 100644 --- a/crates/engine/tree/src/tree/mod.rs +++ b/crates/engine/tree/src/tree/mod.rs @@ -23,7 +23,7 @@ use reth_engine_primitives::EngineTypes; use reth_errors::{ConsensusError, ProviderResult}; use reth_evm::execute::{BlockExecutorProvider, Executor}; use reth_payload_builder::PayloadBuilderHandle; -use reth_payload_primitives::{PayloadAttributes, PayloadBuilderAttributes}; +use reth_payload_primitives::{PayloadAttributes, PayloadBuilder, PayloadBuilderAttributes}; use reth_payload_validator::ExecutionPayloadValidator; use reth_primitives::{ Block, GotExpected, Header, SealedBlock, SealedBlockWithSenders, SealedHeader, diff --git a/crates/ethereum/payload/src/lib.rs b/crates/ethereum/payload/src/lib.rs index 9cd7981ff367..0a013495b331 100644 --- a/crates/ethereum/payload/src/lib.rs +++ b/crates/ethereum/payload/src/lib.rs @@ -25,10 +25,8 @@ use reth_evm::{ }; use reth_evm_ethereum::{eip6110::parse_deposits_from_receipts, EthEvmConfig}; use reth_execution_types::ExecutionOutcome; -use reth_payload_builder::{ - error::PayloadBuilderError, EthBuiltPayload, EthPayloadBuilderAttributes, -}; -use reth_payload_primitives::PayloadBuilderAttributes; +use reth_payload_builder::{EthBuiltPayload, EthPayloadBuilderAttributes}; +use reth_payload_primitives::{PayloadBuilderAttributes, PayloadBuilderError}; use reth_primitives::{ constants::{eip4844::MAX_DATA_GAS_PER_BLOCK, BEACON_NONCE}, eip4844::calculate_excess_blob_gas, diff --git a/crates/node/builder/Cargo.toml b/crates/node/builder/Cargo.toml index 57dfd882c5f7..65b165617ff9 100644 --- a/crates/node/builder/Cargo.toml +++ b/crates/node/builder/Cargo.toml @@ -40,6 +40,7 @@ reth-node-core.workspace = true reth-node-events.workspace = true reth-node-metrics.workspace = true reth-payload-builder.workspace = true +reth-payload-primitives.workspace = true reth-payload-validator.workspace = true reth-primitives.workspace = true reth-provider.workspace = true diff --git a/crates/node/builder/src/launch/engine.rs b/crates/node/builder/src/launch/engine.rs index 353c0d5f9431..3bceef594971 100644 --- a/crates/node/builder/src/launch/engine.rs +++ b/crates/node/builder/src/launch/engine.rs @@ -31,6 +31,7 @@ use reth_node_core::{ version::{CARGO_PKG_VERSION, CLIENT_CODE, NAME_CLIENT, VERGEN_GIT_SHA}, }; use reth_node_events::{cl::ConsensusLayerHealthEvents, node}; +use reth_payload_primitives::PayloadBuilder; use reth_provider::providers::BlockchainProvider2; use reth_rpc_engine_api::{capabilities::EngineCapabilities, EngineApi}; use reth_rpc_types::{engine::ClientVersionV1, WithOtherFields}; diff --git a/crates/optimism/payload/src/builder.rs b/crates/optimism/payload/src/builder.rs index 5d302a3902ee..2da5c6ad9dba 100644 --- a/crates/optimism/payload/src/builder.rs +++ b/crates/optimism/payload/src/builder.rs @@ -13,8 +13,7 @@ use reth_evm::{ NextBlockEnvAttributes, }; use reth_execution_types::ExecutionOutcome; -use reth_payload_builder::error::PayloadBuilderError; -use reth_payload_primitives::PayloadBuilderAttributes; +use reth_payload_primitives::{PayloadBuilderAttributes, PayloadBuilderError}; use reth_primitives::{ constants::BEACON_NONCE, eip4844::calculate_excess_blob_gas, diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index 6a03c35c1954..5117e44e348d 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -14,10 +14,9 @@ use futures_core::ready; use futures_util::FutureExt; use reth_chainspec::{ChainSpec, EthereumHardforks}; use reth_payload_builder::{ - database::CachedReads, error::PayloadBuilderError, KeepPayloadJobAlive, PayloadId, PayloadJob, - PayloadJobGenerator, + database::CachedReads, KeepPayloadJobAlive, PayloadId, PayloadJob, PayloadJobGenerator, }; -use reth_payload_primitives::{BuiltPayload, PayloadBuilderAttributes}; +use reth_payload_primitives::{BuiltPayload, PayloadBuilderAttributes, PayloadBuilderError}; use reth_primitives::{ constants::{EMPTY_WITHDRAWALS, RETH_CLIENT_VERSION, SLOT_DURATION}, proofs, BlockNumberOrTag, SealedBlock, Withdrawals, diff --git a/crates/payload/builder/Cargo.toml b/crates/payload/builder/Cargo.toml index d2b0d82e8217..c96b9ae3d6a3 100644 --- a/crates/payload/builder/Cargo.toml +++ b/crates/payload/builder/Cargo.toml @@ -15,8 +15,6 @@ workspace = true # reth reth-primitives.workspace = true reth-rpc-types.workspace = true -reth-transaction-pool.workspace = true -reth-errors.workspace = true reth-provider.workspace = true reth-payload-primitives.workspace = true reth-ethereum-engine-primitives.workspace = true @@ -26,17 +24,16 @@ reth-chain-state = { workspace = true, optional = true } alloy-primitives.workspace = true # async +async-trait.workspace = true tokio = { workspace = true, features = ["sync"] } tokio-stream.workspace = true futures-util.workspace = true -pin-project.workspace = true # metrics reth-metrics.workspace = true metrics.workspace = true # misc -thiserror.workspace = true tracing.workspace = true [dev-dependencies] diff --git a/crates/payload/builder/src/error.rs b/crates/payload/builder/src/error.rs index 2ad7b287eea8..e69de29bb2d1 100644 --- a/crates/payload/builder/src/error.rs +++ b/crates/payload/builder/src/error.rs @@ -1,58 +0,0 @@ -//! Error types emitted by types or implementations of this crate. - -use alloy_primitives::B256; -use reth_errors::{ProviderError, RethError}; -use reth_primitives::revm_primitives::EVMError; -use reth_transaction_pool::BlobStoreError; -use tokio::sync::oneshot; - -/// Possible error variants during payload building. -#[derive(Debug, thiserror::Error)] -pub enum PayloadBuilderError { - /// Thrown when the parent block is missing. - #[error("missing parent block {0}")] - MissingParentBlock(B256), - /// An oneshot channels has been closed. - #[error("sender has been dropped")] - ChannelClosed, - /// If there's no payload to resolve. - #[error("missing payload")] - MissingPayload, - /// Error occurring in the blob store. - #[error(transparent)] - BlobStore(#[from] BlobStoreError), - /// Other internal error - #[error(transparent)] - Internal(#[from] RethError), - /// Unrecoverable error during evm execution. - #[error("evm execution error: {0}")] - EvmExecutionError(EVMError), - /// Thrown if the payload requests withdrawals before Shanghai activation. - #[error("withdrawals set before Shanghai activation")] - WithdrawalsBeforeShanghai, - /// Any other payload building errors. - #[error(transparent)] - Other(Box), -} - -impl PayloadBuilderError { - /// Create a new error from a boxed error. - pub fn other(error: E) -> Self - where - E: std::error::Error + Send + Sync + 'static, - { - Self::Other(Box::new(error)) - } -} - -impl From for PayloadBuilderError { - fn from(error: ProviderError) -> Self { - Self::Internal(RethError::Provider(error)) - } -} - -impl From for PayloadBuilderError { - fn from(_: oneshot::error::RecvError) -> Self { - Self::ChannelClosed - } -} diff --git a/crates/payload/builder/src/lib.rs b/crates/payload/builder/src/lib.rs index fa78e912cfcd..64f5f93db370 100644 --- a/crates/payload/builder/src/lib.rs +++ b/crates/payload/builder/src/lib.rs @@ -27,8 +27,7 @@ //! use std::future::Future; //! use std::pin::Pin; //! use std::task::{Context, Poll}; -//! use reth_payload_builder::{EthBuiltPayload, KeepPayloadJobAlive, EthPayloadBuilderAttributes, PayloadJob, PayloadJobGenerator}; -//! use reth_payload_builder::error::PayloadBuilderError; +//! use reth_payload_builder::{EthBuiltPayload, PayloadBuilderError, KeepPayloadJobAlive, EthPayloadBuilderAttributes, PayloadJob, PayloadJobGenerator}; //! use reth_primitives::{Block, Header, U256}; //! //! /// The generator type that creates new jobs that builds empty blocks. @@ -102,8 +101,6 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] pub mod database; -pub mod error; -mod events; mod metrics; mod service; mod traits; @@ -113,7 +110,7 @@ pub mod noop; #[cfg(any(test, feature = "test-utils"))] pub mod test_utils; -pub use events::{Events, PayloadEvents}; +pub use reth_payload_primitives::PayloadBuilderError; pub use reth_rpc_types::engine::PayloadId; pub use service::{ PayloadBuilderHandle, PayloadBuilderService, PayloadServiceCommand, PayloadStore, diff --git a/crates/payload/builder/src/noop.rs b/crates/payload/builder/src/noop.rs index 3fe036cc1b16..06da7dcfada1 100644 --- a/crates/payload/builder/src/noop.rs +++ b/crates/payload/builder/src/noop.rs @@ -20,7 +20,7 @@ pub struct NoopPayloadBuilderService { impl NoopPayloadBuilderService where - T: PayloadTypes + 'static, + T: PayloadTypes, { /// Creates a new [`NoopPayloadBuilderService`]. pub fn new() -> (Self, PayloadBuilderHandle) { diff --git a/crates/payload/builder/src/service.rs b/crates/payload/builder/src/service.rs index 7f22cef99017..e5c88f554e08 100644 --- a/crates/payload/builder/src/service.rs +++ b/crates/payload/builder/src/service.rs @@ -4,14 +4,14 @@ //! Once a new payload is created, it is continuously updated. use crate::{ - error::PayloadBuilderError, - events::{Events, PayloadEvents}, - metrics::PayloadBuilderServiceMetrics, - traits::PayloadJobGenerator, - KeepPayloadJobAlive, PayloadJob, + metrics::PayloadBuilderServiceMetrics, traits::PayloadJobGenerator, KeepPayloadJobAlive, + PayloadJob, }; use futures_util::{future::FutureExt, Stream, StreamExt}; -use reth_payload_primitives::{BuiltPayload, PayloadBuilderAttributes, PayloadTypes}; +use reth_payload_primitives::{ + BuiltPayload, Events, PayloadBuilder, PayloadBuilderAttributes, PayloadBuilderError, + PayloadEvents, PayloadTypes, +}; use reth_provider::CanonStateNotification; use reth_rpc_types::engine::PayloadId; use std::{ @@ -22,7 +22,7 @@ use std::{ }; use tokio::sync::{ broadcast, mpsc, - oneshot::{self, error::RecvError}, + oneshot::{self, Receiver}, }; use tokio_stream::wrappers::UnboundedReceiverStream; use tracing::{debug, info, trace, warn}; @@ -39,7 +39,7 @@ pub struct PayloadStore { impl PayloadStore where - T: PayloadTypes + 'static, + T: PayloadTypes, { /// Resolves the payload job and returns the best payload that has been built so far. /// @@ -102,37 +102,18 @@ pub struct PayloadBuilderHandle { // === impl PayloadBuilderHandle === -impl PayloadBuilderHandle +#[async_trait::async_trait] +impl PayloadBuilder for PayloadBuilderHandle where - T: PayloadTypes + 'static, + T: PayloadTypes, { - /// Creates a new payload builder handle for the given channel. - /// - /// Note: this is only used internally by the [`PayloadBuilderService`] to manage the payload - /// building flow See [`PayloadBuilderService::poll`] for implementation details. - pub const fn new(to_service: mpsc::UnboundedSender>) -> Self { - Self { to_service } - } - - /// Resolves the payload job and returns the best payload that has been built so far. - /// - /// Note: depending on the installed [`PayloadJobGenerator`], this may or may not terminate the - /// job, See [`PayloadJob::resolve`]. - async fn resolve(&self, id: PayloadId) -> Option> { - let (tx, rx) = oneshot::channel(); - self.to_service.send(PayloadServiceCommand::Resolve(id, tx)).ok()?; - match rx.await.transpose()? { - Ok(fut) => Some(fut.await), - Err(e) => Some(Err(e.into())), - } - } + type PayloadType = T; + type Error = PayloadBuilderError; - /// Sends a message to the service to start building a new payload for the given payload - /// attributes and returns a future that resolves to the payload. - pub async fn send_and_resolve_payload( + async fn send_and_resolve_payload( &self, - attr: T::PayloadBuilderAttributes, - ) -> Result, PayloadBuilderError> { + attr: ::PayloadBuilderAttributes, + ) -> Result::BuiltPayload>, Self::Error> { let rx = self.send_new_payload(attr); let id = rx.await??; @@ -141,64 +122,78 @@ where rx.await?.ok_or(PayloadBuilderError::MissingPayload) } - /// Returns the best payload for the given identifier. - /// /// Note: this does not resolve the job if it's still in progress. - pub async fn best_payload( + async fn best_payload( &self, id: PayloadId, - ) -> Option> { + ) -> Option::BuiltPayload, Self::Error>> { let (tx, rx) = oneshot::channel(); self.to_service.send(PayloadServiceCommand::BestPayload(id, tx)).ok()?; rx.await.ok()? } - /// Returns the payload attributes associated with the given identifier. - /// - /// Note: this returns the attributes of the payload and does not resolve the job. - async fn payload_attributes( + fn send_new_payload( &self, - id: PayloadId, - ) -> Option> { - let (tx, rx) = oneshot::channel(); - self.to_service.send(PayloadServiceCommand::PayloadAttributes(id, tx)).ok()?; - rx.await.ok()? - } - - /// Sends a message to the service to start building a new payload for the given payload. - /// - /// This is the same as [`PayloadBuilderHandle::new_payload`] but does not wait for the result - /// and returns the receiver instead - pub fn send_new_payload( - &self, - attr: T::PayloadBuilderAttributes, - ) -> oneshot::Receiver> { + attr: ::PayloadBuilderAttributes, + ) -> Receiver> { let (tx, rx) = oneshot::channel(); let _ = self.to_service.send(PayloadServiceCommand::BuildNewPayload(attr, tx)); rx } - /// Starts building a new payload for the given payload attributes. - /// - /// Returns the identifier of the payload. - /// /// Note: if there's already payload in progress with same identifier, it will be returned. - pub async fn new_payload( + async fn new_payload( &self, - attr: T::PayloadBuilderAttributes, - ) -> Result { + attr: ::PayloadBuilderAttributes, + ) -> Result { self.send_new_payload(attr).await? } - /// Sends a message to the service to subscribe to payload events. - /// Returns a receiver that will receive them. - pub async fn subscribe(&self) -> Result, RecvError> { + async fn subscribe(&self) -> Result, Self::Error> { let (tx, rx) = oneshot::channel(); let _ = self.to_service.send(PayloadServiceCommand::Subscribe(tx)); Ok(PayloadEvents { receiver: rx.await? }) } } +impl PayloadBuilderHandle +where + T: PayloadTypes, +{ + /// Creates a new payload builder handle for the given channel. + /// + /// Note: this is only used internally by the [`PayloadBuilderService`] to manage the payload + /// building flow See [`PayloadBuilderService::poll`] for implementation details. + pub const fn new(to_service: mpsc::UnboundedSender>) -> Self { + Self { to_service } + } + + /// Resolves the payload job and returns the best payload that has been built so far. + /// + /// Note: depending on the installed [`PayloadJobGenerator`], this may or may not terminate the + /// job, See [`PayloadJob::resolve`]. + async fn resolve(&self, id: PayloadId) -> Option> { + let (tx, rx) = oneshot::channel(); + self.to_service.send(PayloadServiceCommand::Resolve(id, tx)).ok()?; + match rx.await.transpose()? { + Ok(fut) => Some(fut.await), + Err(e) => Some(Err(e.into())), + } + } + + /// Returns the payload attributes associated with the given identifier. + /// + /// Note: this returns the attributes of the payload and does not resolve the job. + async fn payload_attributes( + &self, + id: PayloadId, + ) -> Option> { + let (tx, rx) = oneshot::channel(); + self.to_service.send(PayloadServiceCommand::PayloadAttributes(id, tx)).ok()?; + rx.await.ok()? + } +} + impl Clone for PayloadBuilderHandle where T: PayloadTypes, @@ -246,7 +241,7 @@ const PAYLOAD_EVENTS_BUFFER_SIZE: usize = 20; impl PayloadBuilderService where - T: PayloadTypes + 'static, + T: PayloadTypes, Gen: PayloadJobGenerator, Gen::Job: PayloadJob, ::BuiltPayload: Into, @@ -360,7 +355,7 @@ where impl Future for PayloadBuilderService where - T: PayloadTypes + 'static, + T: PayloadTypes, Gen: PayloadJobGenerator + Unpin + 'static, ::Job: Unpin + 'static, St: Stream + Send + Unpin + 'static, diff --git a/crates/payload/builder/src/test_utils.rs b/crates/payload/builder/src/test_utils.rs index 63d5516da8d3..55b9b84f45ea 100644 --- a/crates/payload/builder/src/test_utils.rs +++ b/crates/payload/builder/src/test_utils.rs @@ -1,13 +1,13 @@ //! Utils for testing purposes. use crate::{ - error::PayloadBuilderError, traits::KeepPayloadJobAlive, EthBuiltPayload, - EthPayloadBuilderAttributes, PayloadBuilderHandle, PayloadBuilderService, PayloadJob, - PayloadJobGenerator, + traits::KeepPayloadJobAlive, EthBuiltPayload, EthPayloadBuilderAttributes, + PayloadBuilderHandle, PayloadBuilderService, PayloadJob, PayloadJobGenerator, }; + use alloy_primitives::U256; use reth_chain_state::ExecutedBlock; -use reth_payload_primitives::PayloadTypes; +use reth_payload_primitives::{PayloadBuilderError, PayloadTypes}; use reth_primitives::Block; use reth_provider::CanonStateNotification; use std::{ diff --git a/crates/payload/builder/src/traits.rs b/crates/payload/builder/src/traits.rs index 895670d8b0ea..8d448eeff5ad 100644 --- a/crates/payload/builder/src/traits.rs +++ b/crates/payload/builder/src/traits.rs @@ -1,7 +1,6 @@ //! Trait abstractions used by the payload crate. -use crate::error::PayloadBuilderError; -use reth_payload_primitives::{BuiltPayload, PayloadBuilderAttributes}; +use reth_payload_primitives::{BuiltPayload, PayloadBuilderAttributes, PayloadBuilderError}; use reth_provider::CanonStateNotification; use std::future::Future; diff --git a/crates/payload/primitives/Cargo.toml b/crates/payload/primitives/Cargo.toml index 76d55d28dd87..33c38cab92b2 100644 --- a/crates/payload/primitives/Cargo.toml +++ b/crates/payload/primitives/Cargo.toml @@ -24,8 +24,12 @@ reth-chain-state.workspace = true alloy-primitives.workspace = true # async +async-trait.workspace = true tokio = { workspace = true, features = ["sync"] } +tokio-stream.workspace = true +pin-project.workspace = true # misc +serde.workspace = true thiserror.workspace = true -serde.workspace = true \ No newline at end of file +tracing.workspace = true diff --git a/crates/payload/primitives/src/error.rs b/crates/payload/primitives/src/error.rs index b8b278b4db49..0b8113f67dc2 100644 --- a/crates/payload/primitives/src/error.rs +++ b/crates/payload/primitives/src/error.rs @@ -15,6 +15,12 @@ pub enum PayloadBuilderError { /// An oneshot channels has been closed. #[error("sender has been dropped")] ChannelClosed, + /// If there's no payload to resolve. + #[error("missing payload")] + MissingPayload, + /// Build cancelled + #[error("build outcome cancelled")] + BuildOutcomeCancelled, /// Error occurring in the blob store. #[error(transparent)] BlobStore(#[from] BlobStoreError), diff --git a/crates/payload/builder/src/events.rs b/crates/payload/primitives/src/events.rs similarity index 93% rename from crates/payload/builder/src/events.rs rename to crates/payload/primitives/src/events.rs index 33e6021bf494..3fb3813adb1e 100644 --- a/crates/payload/builder/src/events.rs +++ b/crates/payload/primitives/src/events.rs @@ -1,4 +1,4 @@ -use reth_payload_primitives::PayloadTypes; +use crate::PayloadTypes; use std::{ pin::Pin, task::{ready, Context, Poll}, @@ -29,7 +29,7 @@ pub struct PayloadEvents { pub receiver: broadcast::Receiver>, } -impl PayloadEvents { +impl PayloadEvents { /// Convert this receiver into a stream of `PayloadEvents`. pub fn into_stream(self) -> BroadcastStream> { BroadcastStream::new(self.receiver) @@ -60,7 +60,7 @@ pub struct BuiltPayloadStream { st: BroadcastStream>, } -impl Stream for BuiltPayloadStream { +impl Stream for BuiltPayloadStream { type Item = T::BuiltPayload; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { @@ -90,7 +90,7 @@ pub struct PayloadAttributeStream { st: BroadcastStream>, } -impl Stream for PayloadAttributeStream { +impl Stream for PayloadAttributeStream { type Item = T::PayloadBuilderAttributes; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { diff --git a/crates/payload/primitives/src/lib.rs b/crates/payload/primitives/src/lib.rs index 0b1e6d8b3a5f..b6168f5621ae 100644 --- a/crates/payload/primitives/src/lib.rs +++ b/crates/payload/primitives/src/lib.rs @@ -12,11 +12,15 @@ mod error; pub use error::{EngineObjectValidationError, PayloadBuilderError, VersionSpecificValidationError}; +mod events; +pub use crate::events::{Events, PayloadEvents}; + /// Contains traits to abstract over payload attributes types and default implementations of the /// [`PayloadAttributes`] trait for ethereum mainnet and optimism types. mod traits; pub use traits::{ - BuiltPayload, PayloadAttributes, PayloadAttributesBuilder, PayloadBuilderAttributes, + BuiltPayload, PayloadAttributes, PayloadAttributesBuilder, PayloadBuilder, + PayloadBuilderAttributes, }; mod payload; @@ -24,7 +28,7 @@ pub use payload::PayloadOrAttributes; use reth_chainspec::{ChainSpec, EthereumHardforks}; /// The types that are used by the engine API. -pub trait PayloadTypes: Send + Sync + Unpin + core::fmt::Debug + Clone { +pub trait PayloadTypes: Send + Sync + Unpin + core::fmt::Debug + Clone + 'static { /// The built payload type. type BuiltPayload: BuiltPayload + Clone + Unpin; diff --git a/crates/payload/primitives/src/traits.rs b/crates/payload/primitives/src/traits.rs index 24c5219ba16a..60210efe7603 100644 --- a/crates/payload/primitives/src/traits.rs +++ b/crates/payload/primitives/src/traits.rs @@ -1,5 +1,6 @@ use crate::{ validate_version_specific_fields, EngineApiMessageVersion, EngineObjectValidationError, + PayloadBuilderError, PayloadEvents, PayloadTypes, }; use alloy_primitives::{Address, B256, U256}; use reth_chain_state::ExecutedBlock; @@ -10,6 +11,54 @@ use reth_rpc_types::{ optimism::OptimismPayloadAttributes, Withdrawal, }; +use std::{future::Future, pin::Pin}; +use tokio::sync::oneshot; + +pub(crate) type PayloadFuture

= + Pin> + Send + Sync>>; + +/// A type that can request, subscribe to and resolve payloads. +#[async_trait::async_trait] +pub trait PayloadBuilder: Send + Unpin { + /// The Payload type for the builder. + type PayloadType: PayloadTypes; + /// The error type returned by the builder. + type Error; + + /// Sends a message to the service to start building a new payload for the given payload + /// attributes and returns a future that resolves to the payload. + async fn send_and_resolve_payload( + &self, + attr: ::PayloadBuilderAttributes, + ) -> Result::BuiltPayload>, Self::Error>; + + /// Returns the best payload for the given identifier. + async fn best_payload( + &self, + id: PayloadId, + ) -> Option::BuiltPayload, Self::Error>>; + + /// Sends a message to the service to start building a new payload for the given payload. + /// + /// This is the same as [`PayloadBuilder::new_payload`] but does not wait for the result + /// and returns the receiver instead + fn send_new_payload( + &self, + attr: ::PayloadBuilderAttributes, + ) -> oneshot::Receiver>; + + /// Starts building a new payload for the given payload attributes. + /// + /// Returns the identifier of the payload. + async fn new_payload( + &self, + attr: ::PayloadBuilderAttributes, + ) -> Result; + + /// Sends a message to the service to subscribe to payload events. + /// Returns a receiver that will receive them. + async fn subscribe(&self) -> Result, Self::Error>; +} /// Represents a built payload type that contains a built [`SealedBlock`] and can be converted into /// engine API execution payloads. diff --git a/crates/rpc/rpc-engine-api/src/error.rs b/crates/rpc/rpc-engine-api/src/error.rs index 7fd5a112a1c6..b0ba93d6e455 100644 --- a/crates/rpc/rpc-engine-api/src/error.rs +++ b/crates/rpc/rpc-engine-api/src/error.rs @@ -3,8 +3,7 @@ use jsonrpsee_types::error::{ INTERNAL_ERROR_CODE, INVALID_PARAMS_CODE, INVALID_PARAMS_MSG, SERVER_ERROR_MSG, }; use reth_beacon_consensus::{BeaconForkChoiceUpdateError, BeaconOnNewPayloadError}; -use reth_payload_builder::error::PayloadBuilderError; -use reth_payload_primitives::EngineObjectValidationError; +use reth_payload_primitives::{EngineObjectValidationError, PayloadBuilderError}; use reth_rpc_types::ToRpcError; use thiserror::Error; diff --git a/examples/custom-engine-types/src/main.rs b/examples/custom-engine-types/src/main.rs index b6bccd2beb1d..3829c4df0b64 100644 --- a/examples/custom-engine-types/src/main.rs +++ b/examples/custom-engine-types/src/main.rs @@ -52,7 +52,7 @@ use reth_node_ethereum::{ EthEvmConfig, }; use reth_payload_builder::{ - error::PayloadBuilderError, EthBuiltPayload, EthPayloadBuilderAttributes, PayloadBuilderHandle, + EthBuiltPayload, EthPayloadBuilderAttributes, PayloadBuilderError, PayloadBuilderHandle, PayloadBuilderService, }; use reth_primitives::{Address, Withdrawals, B256}; diff --git a/examples/custom-payload-builder/src/generator.rs b/examples/custom-payload-builder/src/generator.rs index a220315cb427..6531de0bcc76 100644 --- a/examples/custom-payload-builder/src/generator.rs +++ b/examples/custom-payload-builder/src/generator.rs @@ -7,7 +7,7 @@ use reth::{ use reth_basic_payload_builder::{BasicPayloadJobGeneratorConfig, PayloadBuilder, PayloadConfig}; use reth_chainspec::ChainSpec; use reth_node_api::PayloadBuilderAttributes; -use reth_payload_builder::{error::PayloadBuilderError, PayloadJobGenerator}; +use reth_payload_builder::{PayloadBuilderError, PayloadJobGenerator}; use reth_primitives::{BlockNumberOrTag, Bytes}; use std::sync::Arc; diff --git a/examples/custom-payload-builder/src/job.rs b/examples/custom-payload-builder/src/job.rs index 154e5165e827..26b594be94b8 100644 --- a/examples/custom-payload-builder/src/job.rs +++ b/examples/custom-payload-builder/src/job.rs @@ -3,7 +3,7 @@ use reth::{ providers::StateProviderFactory, tasks::TaskSpawner, transaction_pool::TransactionPool, }; use reth_basic_payload_builder::{PayloadBuilder, PayloadConfig}; -use reth_payload_builder::{error::PayloadBuilderError, KeepPayloadJobAlive, PayloadJob}; +use reth_payload_builder::{KeepPayloadJobAlive, PayloadBuilderError, PayloadJob}; use std::{ pin::Pin,