mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 19:09:54 +00:00
feat: add empty payload building support (#2346)
This commit is contained in:
@ -15,7 +15,9 @@ use reth_payload_builder::{
|
|||||||
};
|
};
|
||||||
use reth_primitives::{
|
use reth_primitives::{
|
||||||
bytes::{Bytes, BytesMut},
|
bytes::{Bytes, BytesMut},
|
||||||
constants::{RETH_CLIENT_VERSION, SLOT_DURATION},
|
constants::{
|
||||||
|
EMPTY_RECEIPTS, EMPTY_TRANSACTIONS, EMPTY_WITHDRAWALS, RETH_CLIENT_VERSION, SLOT_DURATION,
|
||||||
|
},
|
||||||
proofs, Block, BlockId, BlockNumberOrTag, ChainSpec, Header, IntoRecoveredTransaction, Receipt,
|
proofs, Block, BlockId, BlockNumberOrTag, ChainSpec, Header, IntoRecoveredTransaction, Receipt,
|
||||||
SealedBlock, EMPTY_OMMER_ROOT, U256,
|
SealedBlock, EMPTY_OMMER_ROOT, U256,
|
||||||
};
|
};
|
||||||
@ -333,9 +335,17 @@ where
|
|||||||
Pool: TransactionPool + Unpin + 'static,
|
Pool: TransactionPool + Unpin + 'static,
|
||||||
Tasks: TaskSpawner + Clone + 'static,
|
Tasks: TaskSpawner + Clone + 'static,
|
||||||
{
|
{
|
||||||
fn best_payload(&self) -> Arc<BuiltPayload> {
|
fn best_payload(&self) -> Result<Arc<BuiltPayload>, PayloadBuilderError> {
|
||||||
// TODO if still not set, initialize empty block
|
if let Some(ref payload) = self.best_payload {
|
||||||
self.best_payload.clone().unwrap()
|
return Ok(payload.clone())
|
||||||
|
}
|
||||||
|
// No payload has been built yet, but we need to return something that the CL then can
|
||||||
|
// deliver, so we need to return an empty payload.
|
||||||
|
//
|
||||||
|
// Note: it is assumed that this is unlikely to happen, as the payload job is started right
|
||||||
|
// away and the first full block should have been built by the time CL is requesting the
|
||||||
|
// payload.
|
||||||
|
build_empty_payload(&self.client, self.config.clone()).map(Arc::new)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,6 +367,8 @@ impl Future for PendingPayload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A marker that can be used to cancel a job.
|
/// A marker that can be used to cancel a job.
|
||||||
|
///
|
||||||
|
/// If dropped, it will set the `cancelled` flag to true.
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
struct Cancelled(Arc<AtomicBool>);
|
struct Cancelled(Arc<AtomicBool>);
|
||||||
|
|
||||||
@ -575,6 +587,65 @@ fn build_payload<Pool, Client>(
|
|||||||
let _ = to_job.send(try_build(client, pool, config, cancel, best_payload));
|
let _ = to_job.send(try_build(client, pool, config, cancel, best_payload));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Builds an empty payload without any transactions.
|
||||||
|
fn build_empty_payload<Client>(
|
||||||
|
client: &Client,
|
||||||
|
config: PayloadConfig,
|
||||||
|
) -> Result<BuiltPayload, PayloadBuilderError>
|
||||||
|
where
|
||||||
|
Client: StateProviderFactory,
|
||||||
|
{
|
||||||
|
// TODO this needs to access the _pending_ state of the parent block hash
|
||||||
|
let _state = client.latest()?;
|
||||||
|
|
||||||
|
let PayloadConfig {
|
||||||
|
initialized_block_env,
|
||||||
|
parent_block,
|
||||||
|
extra_data,
|
||||||
|
attributes,
|
||||||
|
initialized_cfg,
|
||||||
|
..
|
||||||
|
} = config;
|
||||||
|
|
||||||
|
let base_fee = initialized_block_env.basefee.to::<u64>();
|
||||||
|
let block_gas_limit: u64 = initialized_block_env.gas_limit.try_into().unwrap_or(u64::MAX);
|
||||||
|
|
||||||
|
let mut withdrawals_root = None;
|
||||||
|
let mut withdrawals = None;
|
||||||
|
|
||||||
|
if initialized_cfg.spec_id >= SpecId::SHANGHAI {
|
||||||
|
withdrawals_root = Some(EMPTY_WITHDRAWALS);
|
||||||
|
// set withdrawals
|
||||||
|
withdrawals = Some(attributes.withdrawals);
|
||||||
|
}
|
||||||
|
|
||||||
|
let header = Header {
|
||||||
|
parent_hash: parent_block.hash,
|
||||||
|
ommers_hash: EMPTY_OMMER_ROOT,
|
||||||
|
beneficiary: initialized_block_env.coinbase,
|
||||||
|
// TODO compute state root
|
||||||
|
state_root: Default::default(),
|
||||||
|
transactions_root: EMPTY_TRANSACTIONS,
|
||||||
|
withdrawals_root,
|
||||||
|
receipts_root: EMPTY_RECEIPTS,
|
||||||
|
logs_bloom: Default::default(),
|
||||||
|
timestamp: attributes.timestamp,
|
||||||
|
mix_hash: attributes.prev_randao,
|
||||||
|
nonce: 0,
|
||||||
|
base_fee_per_gas: Some(base_fee),
|
||||||
|
number: parent_block.number + 1,
|
||||||
|
gas_limit: block_gas_limit,
|
||||||
|
difficulty: U256::ZERO,
|
||||||
|
gas_used: 0,
|
||||||
|
extra_data: extra_data.into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let block = Block { header, body: vec![], ommers: vec![], withdrawals };
|
||||||
|
let sealed_block = block.seal_slow();
|
||||||
|
|
||||||
|
Ok(BuiltPayload::new(attributes.id, sealed_block, U256::ZERO))
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks if the new payload is better than the current best.
|
/// Checks if the new payload is better than the current best.
|
||||||
///
|
///
|
||||||
/// This compares the total fees of the blocks, higher is better.
|
/// This compares the total fees of the blocks, higher is better.
|
||||||
|
|||||||
@ -29,7 +29,10 @@ pub struct PayloadStore {
|
|||||||
|
|
||||||
impl PayloadStore {
|
impl PayloadStore {
|
||||||
/// Returns the best payload for the given identifier.
|
/// Returns the best payload for the given identifier.
|
||||||
pub async fn get_payload(&self, id: PayloadId) -> Option<Arc<BuiltPayload>> {
|
pub async fn get_payload(
|
||||||
|
&self,
|
||||||
|
id: PayloadId,
|
||||||
|
) -> Option<Result<Arc<BuiltPayload>, PayloadBuilderError>> {
|
||||||
self.inner.get_payload(id).await
|
self.inner.get_payload(id).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,7 +56,10 @@ pub struct PayloadBuilderHandle {
|
|||||||
|
|
||||||
impl PayloadBuilderHandle {
|
impl PayloadBuilderHandle {
|
||||||
/// Returns the best payload for the given identifier.
|
/// Returns the best payload for the given identifier.
|
||||||
pub async fn get_payload(&self, id: PayloadId) -> Option<Arc<BuiltPayload>> {
|
pub async fn get_payload(
|
||||||
|
&self,
|
||||||
|
id: PayloadId,
|
||||||
|
) -> Option<Result<Arc<BuiltPayload>, PayloadBuilderError>> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
self.to_service.send(PayloadServiceCommand::GetPayload(id, tx)).ok()?;
|
self.to_service.send(PayloadServiceCommand::GetPayload(id, tx)).ok()?;
|
||||||
rx.await.ok()?
|
rx.await.ok()?
|
||||||
@ -136,7 +142,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the best payload for the given identifier.
|
/// Returns the best payload for the given identifier.
|
||||||
fn get_payload(&self, id: PayloadId) -> Option<Arc<BuiltPayload>> {
|
fn get_payload(&self, id: PayloadId) -> Option<Result<Arc<BuiltPayload>, PayloadBuilderError>> {
|
||||||
self.payload_jobs.iter().find(|(_, job_id)| *job_id == id).map(|(j, _)| j.best_payload())
|
self.payload_jobs.iter().find(|(_, job_id)| *job_id == id).map(|(j, _)| j.best_payload())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -238,5 +244,5 @@ enum PayloadServiceCommand {
|
|||||||
oneshot::Sender<Result<PayloadId, PayloadBuilderError>>,
|
oneshot::Sender<Result<PayloadId, PayloadBuilderError>>,
|
||||||
),
|
),
|
||||||
/// Get the current payload.
|
/// Get the current payload.
|
||||||
GetPayload(PayloadId, oneshot::Sender<Option<Arc<BuiltPayload>>>),
|
GetPayload(PayloadId, oneshot::Sender<Option<Result<Arc<BuiltPayload>, PayloadBuilderError>>>),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,11 +56,11 @@ impl Stream for TestPayloadJob {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PayloadJob for TestPayloadJob {
|
impl PayloadJob for TestPayloadJob {
|
||||||
fn best_payload(&self) -> Arc<BuiltPayload> {
|
fn best_payload(&self) -> Result<Arc<BuiltPayload>, PayloadBuilderError> {
|
||||||
Arc::new(BuiltPayload::new(
|
Ok(Arc::new(BuiltPayload::new(
|
||||||
self.attr.payload_id(),
|
self.attr.payload_id(),
|
||||||
Block::default().seal_slow(),
|
Block::default().seal_slow(),
|
||||||
U256::ZERO,
|
U256::ZERO,
|
||||||
))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,9 +9,10 @@ use std::sync::Arc;
|
|||||||
///
|
///
|
||||||
/// This type is a Stream that yields better payloads.
|
/// This type is a Stream that yields better payloads.
|
||||||
///
|
///
|
||||||
/// Note: PaylodJob need to be cancel safe.
|
/// A PayloadJob must always be prepared to return the best payload built so far to make there's a
|
||||||
|
/// valid payload to deliver to the CL, so it does not miss a slot, even if the payload is empty.
|
||||||
///
|
///
|
||||||
/// TODO convert this into a future?
|
/// 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 <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/paris.md#engine_getpayloadv1>
|
||||||
pub trait PayloadJob:
|
pub trait PayloadJob:
|
||||||
TryStream<Ok = Arc<BuiltPayload>, Error = PayloadBuilderError> + Send + Sync
|
TryStream<Ok = Arc<BuiltPayload>, Error = PayloadBuilderError> + Send + Sync
|
||||||
{
|
{
|
||||||
@ -19,7 +20,7 @@ pub trait PayloadJob:
|
|||||||
///
|
///
|
||||||
/// Note: this is expected to be an empty block without transaction if nothing has been built
|
/// Note: this is expected to be an empty block without transaction if nothing has been built
|
||||||
/// yet.
|
/// yet.
|
||||||
fn best_payload(&self) -> Arc<BuiltPayload>;
|
fn best_payload(&self) -> Result<Arc<BuiltPayload>, PayloadBuilderError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type that knows how to create new jobs for creating payloads.
|
/// A type that knows how to create new jobs for creating payloads.
|
||||||
@ -31,6 +32,8 @@ pub trait PayloadJobGenerator: Send + Sync {
|
|||||||
|
|
||||||
/// Creates the initial payload and a new [PayloadJob] that yields better payloads.
|
/// Creates the initial payload and a new [PayloadJob] that yields better payloads.
|
||||||
///
|
///
|
||||||
|
/// This is called when the CL requests a new payload job via a fork choice update.
|
||||||
|
///
|
||||||
/// Note: this is expected to build a new (empty) payload without transactions, so it can be
|
/// Note: this is expected to build a new (empty) payload without transactions, so it can be
|
||||||
/// returned directly. when asked for
|
/// returned directly. when asked for
|
||||||
fn new_payload_job(
|
fn new_payload_job(
|
||||||
|
|||||||
@ -125,11 +125,12 @@ where
|
|||||||
/// Note:
|
/// Note:
|
||||||
/// > Client software MAY stop the corresponding build process after serving this call.
|
/// > Client software MAY stop the corresponding build process after serving this call.
|
||||||
pub async fn get_payload_v1(&self, payload_id: PayloadId) -> EngineApiResult<ExecutionPayload> {
|
pub async fn get_payload_v1(&self, payload_id: PayloadId) -> EngineApiResult<ExecutionPayload> {
|
||||||
self.payload_store
|
Ok(self
|
||||||
|
.payload_store
|
||||||
.get_payload(payload_id)
|
.get_payload(payload_id)
|
||||||
.await
|
.await
|
||||||
.map(|payload| (*payload).clone().into_v1_payload())
|
.ok_or(EngineApiError::UnknownPayload)?
|
||||||
.ok_or(EngineApiError::UnknownPayload)
|
.map(|payload| (*payload).clone().into_v1_payload())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the most recent version of the payload that is available in the corresponding
|
/// Returns the most recent version of the payload that is available in the corresponding
|
||||||
@ -143,11 +144,12 @@ where
|
|||||||
&self,
|
&self,
|
||||||
payload_id: PayloadId,
|
payload_id: PayloadId,
|
||||||
) -> EngineApiResult<ExecutionPayloadEnvelope> {
|
) -> EngineApiResult<ExecutionPayloadEnvelope> {
|
||||||
self.payload_store
|
Ok(self
|
||||||
|
.payload_store
|
||||||
.get_payload(payload_id)
|
.get_payload(payload_id)
|
||||||
.await
|
.await
|
||||||
.map(|payload| (*payload).clone().into_v2_payload())
|
.ok_or(EngineApiError::UnknownPayload)?
|
||||||
.ok_or(EngineApiError::UnknownPayload)
|
.map(|payload| (*payload).clone().into_v2_payload())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called to retrieve execution payload bodies by range.
|
/// Called to retrieve execution payload bodies by range.
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
use jsonrpsee_types::error::{INTERNAL_ERROR_CODE, INVALID_PARAMS_CODE};
|
use jsonrpsee_types::error::{INTERNAL_ERROR_CODE, INVALID_PARAMS_CODE};
|
||||||
use reth_beacon_consensus::{BeaconEngineError, BeaconForkChoiceUpdateError};
|
use reth_beacon_consensus::{BeaconEngineError, BeaconForkChoiceUpdateError};
|
||||||
|
use reth_payload_builder::error::PayloadBuilderError;
|
||||||
use reth_primitives::{H256, U256};
|
use reth_primitives::{H256, U256};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::sync::{mpsc, oneshot};
|
use tokio::sync::{mpsc, oneshot};
|
||||||
@ -75,6 +76,9 @@ pub enum EngineApiError {
|
|||||||
/// Failed to send message due ot closed channel
|
/// Failed to send message due ot closed channel
|
||||||
#[error("Closed channel")]
|
#[error("Closed channel")]
|
||||||
ChannelClosed,
|
ChannelClosed,
|
||||||
|
/// Fetching the payload failed
|
||||||
|
#[error(transparent)]
|
||||||
|
GetPayloadError(#[from] PayloadBuilderError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> From<mpsc::error::SendError<T>> for EngineApiError {
|
impl<T> From<mpsc::error::SendError<T>> for EngineApiError {
|
||||||
|
|||||||
Reference in New Issue
Block a user