mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: add EIP-4788 parent_beacon_block_root to Header (#4299)
This commit is contained in:
@ -277,6 +277,7 @@ impl StorageInner {
|
||||
blob_gas_used: None,
|
||||
excess_blob_gas: None,
|
||||
extra_data: Default::default(),
|
||||
parent_beacon_block_root: None,
|
||||
};
|
||||
|
||||
header.transactions_root = if transactions.is_empty() {
|
||||
|
||||
@ -5,6 +5,7 @@ use crate::{
|
||||
BeaconForkChoiceUpdateError, BeaconOnNewPayloadError,
|
||||
};
|
||||
use futures::TryFutureExt;
|
||||
use reth_primitives::H256;
|
||||
use reth_rpc_types::engine::{
|
||||
ExecutionPayload, ForkchoiceState, ForkchoiceUpdated, PayloadAttributes, PayloadStatus,
|
||||
};
|
||||
@ -34,9 +35,14 @@ impl BeaconConsensusEngineHandle {
|
||||
pub async fn new_payload(
|
||||
&self,
|
||||
payload: ExecutionPayload,
|
||||
parent_beacon_block_root: Option<H256>,
|
||||
) -> Result<PayloadStatus, BeaconOnNewPayloadError> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let _ = self.to_engine.send(BeaconEngineMessage::NewPayload { payload, tx });
|
||||
let _ = self.to_engine.send(BeaconEngineMessage::NewPayload {
|
||||
payload,
|
||||
parent_beacon_block_root,
|
||||
tx,
|
||||
});
|
||||
rx.await.map_err(|_| BeaconOnNewPayloadError::EngineUnavailable)?
|
||||
}
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ use crate::{
|
||||
use futures::{future::Either, FutureExt};
|
||||
use reth_interfaces::consensus::ForkchoiceState;
|
||||
use reth_payload_builder::error::PayloadBuilderError;
|
||||
use reth_primitives::H256;
|
||||
use reth_rpc_types::engine::{
|
||||
ExecutionPayload, ForkChoiceUpdateResult, ForkchoiceUpdateError, ForkchoiceUpdated,
|
||||
PayloadAttributes, PayloadId, PayloadStatus, PayloadStatusEnum,
|
||||
@ -146,6 +147,8 @@ pub enum BeaconEngineMessage {
|
||||
NewPayload {
|
||||
/// The execution payload received by Engine API.
|
||||
payload: ExecutionPayload,
|
||||
/// The parent beacon block root, if any.
|
||||
parent_beacon_block_root: Option<H256>,
|
||||
/// The sender for returning payload status result.
|
||||
tx: oneshot::Sender<Result<PayloadStatus, BeaconOnNewPayloadError>>,
|
||||
},
|
||||
|
||||
@ -1049,12 +1049,13 @@ where
|
||||
///
|
||||
/// This returns a [`PayloadStatus`] that represents the outcome of a processed new payload and
|
||||
/// returns an error if an internal error occurred.
|
||||
#[instrument(level = "trace", skip(self, payload), fields(block_hash= ?payload.block_hash, block_number = %payload.block_number.as_u64(), is_pipeline_idle = %self.sync.is_pipeline_idle()), target = "consensus::engine")]
|
||||
#[instrument(level = "trace", skip(self, payload, parent_beacon_block_root), fields(block_hash= ?payload.block_hash, block_number = %payload.block_number.as_u64(), is_pipeline_idle = %self.sync.is_pipeline_idle()), target = "consensus::engine")]
|
||||
fn on_new_payload(
|
||||
&mut self,
|
||||
payload: ExecutionPayload,
|
||||
parent_beacon_block_root: Option<H256>,
|
||||
) -> Result<PayloadStatus, BeaconOnNewPayloadError> {
|
||||
let block = match self.ensure_well_formed_payload(payload) {
|
||||
let block = match self.ensure_well_formed_payload(payload, parent_beacon_block_root) {
|
||||
Ok(block) => block,
|
||||
Err(status) => return Ok(status),
|
||||
};
|
||||
@ -1118,9 +1119,10 @@ where
|
||||
fn ensure_well_formed_payload(
|
||||
&self,
|
||||
payload: ExecutionPayload,
|
||||
parent_beacon_block_root: Option<H256>,
|
||||
) -> Result<SealedBlock, PayloadStatus> {
|
||||
let parent_hash = payload.parent_hash;
|
||||
let block = match SealedBlock::try_from(payload) {
|
||||
let block = match payload.try_into_sealed_block(parent_beacon_block_root) {
|
||||
Ok(block) => block,
|
||||
Err(error) => {
|
||||
error!(target: "consensus::engine", ?error, "Invalid payload");
|
||||
@ -1725,9 +1727,9 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
BeaconEngineMessage::NewPayload { payload, tx } => {
|
||||
BeaconEngineMessage::NewPayload { payload, parent_beacon_block_root, tx } => {
|
||||
this.metrics.new_payload_messages.increment(1);
|
||||
let res = this.on_new_payload(payload);
|
||||
let res = this.on_new_payload(payload, parent_beacon_block_root);
|
||||
let _ = tx.send(res);
|
||||
}
|
||||
BeaconEngineMessage::TransitionConfigurationExchanged => {
|
||||
@ -1865,7 +1867,7 @@ mod tests {
|
||||
assert_matches!(rx.try_recv(), Err(TryRecvError::Empty));
|
||||
|
||||
// consensus engine is still idle because no FCUs were received
|
||||
let _ = env.send_new_payload(SealedBlock::default().into()).await;
|
||||
let _ = env.send_new_payload(SealedBlock::default().into(), None).await;
|
||||
assert_matches!(rx.try_recv(), Err(TryRecvError::Empty));
|
||||
|
||||
// consensus engine is still idle because pruning is running
|
||||
@ -2279,14 +2281,16 @@ mod tests {
|
||||
let mut engine_rx = spawn_consensus_engine(consensus_engine);
|
||||
|
||||
// Send new payload
|
||||
let res =
|
||||
env.send_new_payload(random_block(&mut rng, 0, None, None, Some(0)).into()).await;
|
||||
let res = env
|
||||
.send_new_payload(random_block(&mut rng, 0, None, None, Some(0)).into(), None)
|
||||
.await;
|
||||
// Invalid, because this is a genesis block
|
||||
assert_matches!(res, Ok(result) => assert_matches!(result.status, PayloadStatusEnum::Invalid { .. }));
|
||||
|
||||
// Send new payload
|
||||
let res =
|
||||
env.send_new_payload(random_block(&mut rng, 1, None, None, Some(0)).into()).await;
|
||||
let res = env
|
||||
.send_new_payload(random_block(&mut rng, 1, None, None, Some(0)).into(), None)
|
||||
.await;
|
||||
let expected_result = PayloadStatus::from_status(PayloadStatusEnum::Syncing);
|
||||
assert_matches!(res, Ok(result) => assert_eq!(result, expected_result));
|
||||
|
||||
@ -2336,7 +2340,7 @@ mod tests {
|
||||
|
||||
// Send new payload
|
||||
let result =
|
||||
env.send_new_payload_retry_on_syncing(block2.clone().into()).await.unwrap();
|
||||
env.send_new_payload_retry_on_syncing(block2.clone().into(), None).await.unwrap();
|
||||
let expected_result = PayloadStatus::from_status(PayloadStatusEnum::Valid)
|
||||
.with_latest_valid_hash(block2.hash);
|
||||
assert_eq!(result, expected_result);
|
||||
@ -2434,7 +2438,7 @@ mod tests {
|
||||
|
||||
// Send new payload
|
||||
let block = random_block(&mut rng, 2, Some(H256::random()), None, Some(0));
|
||||
let res = env.send_new_payload(block.into()).await;
|
||||
let res = env.send_new_payload(block.into(), None).await;
|
||||
let expected_result = PayloadStatus::from_status(PayloadStatusEnum::Syncing);
|
||||
assert_matches!(res, Ok(result) => assert_eq!(result, expected_result));
|
||||
|
||||
@ -2497,7 +2501,7 @@ mod tests {
|
||||
|
||||
// Send new payload
|
||||
let result =
|
||||
env.send_new_payload_retry_on_syncing(block2.clone().into()).await.unwrap();
|
||||
env.send_new_payload_retry_on_syncing(block2.clone().into(), None).await.unwrap();
|
||||
|
||||
let expected_result = PayloadStatus::from_status(PayloadStatusEnum::Invalid {
|
||||
validation_error: BlockValidationError::BlockPreMerge { hash: block2.hash }
|
||||
|
||||
@ -69,8 +69,9 @@ impl<DB> TestEnv<DB> {
|
||||
pub async fn send_new_payload(
|
||||
&self,
|
||||
payload: ExecutionPayload,
|
||||
parent_beacon_block_root: Option<H256>,
|
||||
) -> Result<PayloadStatus, BeaconOnNewPayloadError> {
|
||||
self.engine_handle.new_payload(payload).await
|
||||
self.engine_handle.new_payload(payload, parent_beacon_block_root).await
|
||||
}
|
||||
|
||||
/// Sends the `ExecutionPayload` message to the consensus engine and retries if the engine
|
||||
@ -78,9 +79,10 @@ impl<DB> TestEnv<DB> {
|
||||
pub async fn send_new_payload_retry_on_syncing(
|
||||
&self,
|
||||
payload: ExecutionPayload,
|
||||
parent_beacon_block_root: Option<H256>,
|
||||
) -> Result<PayloadStatus, BeaconOnNewPayloadError> {
|
||||
loop {
|
||||
let result = self.send_new_payload(payload.clone()).await?;
|
||||
let result = self.send_new_payload(payload.clone(), parent_beacon_block_root).await?;
|
||||
if !result.is_syncing() {
|
||||
return Ok(result)
|
||||
}
|
||||
|
||||
@ -50,6 +50,8 @@ pub fn validate_header_standalone(
|
||||
return Err(ConsensusError::BlobGasUsedUnexpected)
|
||||
} else if header.excess_blob_gas.is_some() {
|
||||
return Err(ConsensusError::ExcessBlobGasUnexpected)
|
||||
} else if header.parent_beacon_block_root.is_some() {
|
||||
return Err(ConsensusError::ParentBeaconBlockRootUnexpected)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -451,6 +453,7 @@ pub fn validate_4844_header_with_parent(
|
||||
///
|
||||
/// * `blob_gas_used` exists as a header field
|
||||
/// * `excess_blob_gas` exists as a header field
|
||||
/// * `parent_beacon_block_root` exists as a header field
|
||||
/// * `blob_gas_used` is less than or equal to `MAX_DATA_GAS_PER_BLOCK`
|
||||
/// * `blob_gas_used` is a multiple of `DATA_GAS_PER_BLOB`
|
||||
pub fn validate_4844_header_standalone(header: &SealedHeader) -> Result<(), ConsensusError> {
|
||||
@ -460,6 +463,10 @@ pub fn validate_4844_header_standalone(header: &SealedHeader) -> Result<(), Cons
|
||||
return Err(ConsensusError::ExcessBlobGasMissing)
|
||||
}
|
||||
|
||||
if header.parent_beacon_block_root.is_none() {
|
||||
return Err(ConsensusError::ParentBeaconBlockRootMissing)
|
||||
}
|
||||
|
||||
if blob_gas_used > MAX_DATA_GAS_PER_BLOCK {
|
||||
return Err(ConsensusError::BlobGasUsedExceedsMaxBlobGasPerBlock {
|
||||
blob_gas_used,
|
||||
@ -633,6 +640,7 @@ mod tests {
|
||||
withdrawals_root: None,
|
||||
blob_gas_used: None,
|
||||
excess_blob_gas: None,
|
||||
parent_beacon_block_root: None,
|
||||
};
|
||||
// size: 0x9b5
|
||||
|
||||
|
||||
Reference in New Issue
Block a user