Block v3 endpoint (#4629)
## Issue Addressed #4582 ## Proposed Changes Add a new v3 block fetching flow that can decide to return a Full OR Blinded payload ## Additional Info Co-authored-by: Michael Sproul <micsproul@gmail.com>
This commit is contained in:
parent
42da392edc
commit
07f53b18fc
@ -33,6 +33,17 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
state.build_committee_cache(RelativeEpoch::Previous, &self.spec)?;
|
state.build_committee_cache(RelativeEpoch::Previous, &self.spec)?;
|
||||||
state.build_committee_cache(RelativeEpoch::Current, &self.spec)?;
|
state.build_committee_cache(RelativeEpoch::Current, &self.spec)?;
|
||||||
|
|
||||||
|
self.compute_beacon_block_reward_with_cache(block, block_root, state)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should only be called after a committee cache has been built
|
||||||
|
// for both the previous and current epoch
|
||||||
|
fn compute_beacon_block_reward_with_cache<Payload: AbstractExecPayload<T::EthSpec>>(
|
||||||
|
&self,
|
||||||
|
block: BeaconBlockRef<'_, T::EthSpec, Payload>,
|
||||||
|
block_root: Hash256,
|
||||||
|
state: &BeaconState<T::EthSpec>,
|
||||||
|
) -> Result<StandardBlockReward, BeaconChainError> {
|
||||||
let proposer_index = block.proposer_index();
|
let proposer_index = block.proposer_index();
|
||||||
|
|
||||||
let sync_aggregate_reward =
|
let sync_aggregate_reward =
|
||||||
@ -178,7 +189,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
>(
|
>(
|
||||||
&self,
|
&self,
|
||||||
block: BeaconBlockRef<'_, T::EthSpec, Payload>,
|
block: BeaconBlockRef<'_, T::EthSpec, Payload>,
|
||||||
state: &mut BeaconState<T::EthSpec>,
|
state: &BeaconState<T::EthSpec>,
|
||||||
) -> Result<BeaconBlockSubRewardValue, BeaconChainError> {
|
) -> Result<BeaconBlockSubRewardValue, BeaconChainError> {
|
||||||
let total_active_balance = state.get_total_active_balance()?;
|
let total_active_balance = state.get_total_active_balance()?;
|
||||||
let base_reward_per_increment =
|
let base_reward_per_increment =
|
||||||
@ -191,6 +202,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
.safe_mul(WEIGHT_DENOMINATOR)?
|
.safe_mul(WEIGHT_DENOMINATOR)?
|
||||||
.safe_div(PROPOSER_WEIGHT)?;
|
.safe_div(PROPOSER_WEIGHT)?;
|
||||||
|
|
||||||
|
let mut current_epoch_participation = state.current_epoch_participation()?.clone();
|
||||||
|
let mut previous_epoch_participation = state.previous_epoch_participation()?.clone();
|
||||||
|
|
||||||
for attestation in block.body().attestations() {
|
for attestation in block.body().attestations() {
|
||||||
let data = &attestation.data;
|
let data = &attestation.data;
|
||||||
let inclusion_delay = state.slot().safe_sub(data.slot)?.as_u64();
|
let inclusion_delay = state.slot().safe_sub(data.slot)?.as_u64();
|
||||||
@ -203,13 +217,16 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
let attesting_indices = get_attesting_indices_from_state(state, attestation)?;
|
let attesting_indices = get_attesting_indices_from_state(state, attestation)?;
|
||||||
|
|
||||||
let mut proposer_reward_numerator = 0;
|
let mut proposer_reward_numerator = 0;
|
||||||
for index in attesting_indices {
|
for index in attesting_indices {
|
||||||
let index = index as usize;
|
let index = index as usize;
|
||||||
for (flag_index, &weight) in PARTICIPATION_FLAG_WEIGHTS.iter().enumerate() {
|
for (flag_index, &weight) in PARTICIPATION_FLAG_WEIGHTS.iter().enumerate() {
|
||||||
let epoch_participation =
|
let epoch_participation = if data.target.epoch == state.current_epoch() {
|
||||||
state.get_epoch_participation_mut(data.target.epoch)?;
|
&mut current_epoch_participation
|
||||||
|
} else {
|
||||||
|
&mut previous_epoch_participation
|
||||||
|
};
|
||||||
|
|
||||||
let validator_participation = epoch_participation
|
let validator_participation = epoch_participation
|
||||||
.get_mut(index)
|
.get_mut(index)
|
||||||
.ok_or(BeaconStateError::ParticipationOutOfBounds(index))?;
|
.ok_or(BeaconStateError::ParticipationOutOfBounds(index))?;
|
||||||
|
@ -72,8 +72,8 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use eth2::types::{EventKind, SseBlobSidecar, SseBlock, SseExtendedPayloadAttributes, SyncDuty};
|
use eth2::types::{EventKind, SseBlobSidecar, SseBlock, SseExtendedPayloadAttributes, SyncDuty};
|
||||||
use execution_layer::{
|
use execution_layer::{
|
||||||
BlockProposalContents, BuilderParams, ChainHealth, ExecutionLayer, FailedCondition,
|
BlockProposalContents, BlockProposalContentsType, BuilderParams, ChainHealth, ExecutionLayer,
|
||||||
PayloadAttributes, PayloadStatus,
|
FailedCondition, PayloadAttributes, PayloadStatus,
|
||||||
};
|
};
|
||||||
use fork_choice::{
|
use fork_choice::{
|
||||||
AttestationFromBlock, ExecutionStatus, ForkChoice, ForkchoiceUpdateParameters,
|
AttestationFromBlock, ExecutionStatus, ForkChoice, ForkchoiceUpdateParameters,
|
||||||
@ -120,6 +120,7 @@ use tokio_stream::Stream;
|
|||||||
use tree_hash::TreeHash;
|
use tree_hash::TreeHash;
|
||||||
use types::beacon_state::CloneConfig;
|
use types::beacon_state::CloneConfig;
|
||||||
use types::blob_sidecar::{BlobSidecarList, FixedBlobSidecarList};
|
use types::blob_sidecar::{BlobSidecarList, FixedBlobSidecarList};
|
||||||
|
use types::payload::BlockProductionVersion;
|
||||||
use types::sidecar::BlobItems;
|
use types::sidecar::BlobItems;
|
||||||
use types::*;
|
use types::*;
|
||||||
|
|
||||||
@ -320,8 +321,7 @@ pub trait BeaconChainTypes: Send + Sync + 'static {
|
|||||||
type EthSpec: types::EthSpec;
|
type EthSpec: types::EthSpec;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used internally to split block production into discrete functions.
|
struct PartialBeaconBlock<E: EthSpec> {
|
||||||
struct PartialBeaconBlock<E: EthSpec, Payload: AbstractExecPayload<E>> {
|
|
||||||
state: BeaconState<E>,
|
state: BeaconState<E>,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
proposer_index: u64,
|
proposer_index: u64,
|
||||||
@ -335,7 +335,7 @@ struct PartialBeaconBlock<E: EthSpec, Payload: AbstractExecPayload<E>> {
|
|||||||
deposits: Vec<Deposit>,
|
deposits: Vec<Deposit>,
|
||||||
voluntary_exits: Vec<SignedVoluntaryExit>,
|
voluntary_exits: Vec<SignedVoluntaryExit>,
|
||||||
sync_aggregate: Option<SyncAggregate<E>>,
|
sync_aggregate: Option<SyncAggregate<E>>,
|
||||||
prepare_payload_handle: Option<PreparePayloadHandle<E, Payload>>,
|
prepare_payload_handle: Option<PreparePayloadHandle<E>>,
|
||||||
bls_to_execution_changes: Vec<SignedBlsToExecutionChange>,
|
bls_to_execution_changes: Vec<SignedBlsToExecutionChange>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -484,11 +484,18 @@ pub struct BeaconChain<T: BeaconChainTypes> {
|
|||||||
pub kzg: Option<Arc<Kzg>>,
|
pub kzg: Option<Arc<Kzg>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type BeaconBlockAndState<T, Payload> = (
|
pub enum BeaconBlockResponseType<T: EthSpec> {
|
||||||
BeaconBlock<T, Payload>,
|
Full(BeaconBlockResponse<T, FullPayload<T>>),
|
||||||
BeaconState<T>,
|
Blinded(BeaconBlockResponse<T, BlindedPayload<T>>),
|
||||||
Option<SidecarList<T, <Payload as AbstractExecPayload<T>>::Sidecar>>,
|
}
|
||||||
);
|
|
||||||
|
pub struct BeaconBlockResponse<T: EthSpec, Payload: AbstractExecPayload<T>> {
|
||||||
|
pub block: BeaconBlock<T, Payload>,
|
||||||
|
pub state: BeaconState<T>,
|
||||||
|
pub maybe_side_car: Option<SidecarList<T, <Payload as AbstractExecPayload<T>>::Sidecar>>,
|
||||||
|
pub execution_payload_value: Option<Uint256>,
|
||||||
|
pub consensus_block_value: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
impl FinalizationAndCanonicity {
|
impl FinalizationAndCanonicity {
|
||||||
pub fn is_finalized(self) -> bool {
|
pub fn is_finalized(self) -> bool {
|
||||||
@ -3949,38 +3956,16 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Produce a new block at the given `slot`.
|
pub async fn produce_block_with_verification(
|
||||||
///
|
|
||||||
/// The produced block will not be inherently valid, it must be signed by a block producer.
|
|
||||||
/// Block signing is out of the scope of this function and should be done by a separate program.
|
|
||||||
pub async fn produce_block<Payload: AbstractExecPayload<T::EthSpec> + 'static>(
|
|
||||||
self: &Arc<Self>,
|
|
||||||
randao_reveal: Signature,
|
|
||||||
slot: Slot,
|
|
||||||
validator_graffiti: Option<Graffiti>,
|
|
||||||
) -> Result<BeaconBlockAndState<T::EthSpec, Payload>, BlockProductionError> {
|
|
||||||
self.produce_block_with_verification(
|
|
||||||
randao_reveal,
|
|
||||||
slot,
|
|
||||||
validator_graffiti,
|
|
||||||
ProduceBlockVerification::VerifyRandao,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Same as `produce_block` but allowing for configuration of RANDAO-verification.
|
|
||||||
pub async fn produce_block_with_verification<
|
|
||||||
Payload: AbstractExecPayload<T::EthSpec> + 'static,
|
|
||||||
>(
|
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
randao_reveal: Signature,
|
randao_reveal: Signature,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
validator_graffiti: Option<Graffiti>,
|
validator_graffiti: Option<Graffiti>,
|
||||||
verification: ProduceBlockVerification,
|
verification: ProduceBlockVerification,
|
||||||
) -> Result<BeaconBlockAndState<T::EthSpec, Payload>, BlockProductionError> {
|
block_production_version: BlockProductionVersion,
|
||||||
|
) -> Result<BeaconBlockResponseType<T::EthSpec>, BlockProductionError> {
|
||||||
metrics::inc_counter(&metrics::BLOCK_PRODUCTION_REQUESTS);
|
metrics::inc_counter(&metrics::BLOCK_PRODUCTION_REQUESTS);
|
||||||
let _complete_timer = metrics::start_timer(&metrics::BLOCK_PRODUCTION_TIMES);
|
let _complete_timer = metrics::start_timer(&metrics::BLOCK_PRODUCTION_TIMES);
|
||||||
|
|
||||||
// Part 1/2 (blocking)
|
// Part 1/2 (blocking)
|
||||||
//
|
//
|
||||||
// Load the parent state from disk.
|
// Load the parent state from disk.
|
||||||
@ -3998,13 +3983,14 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
// Part 2/2 (async, with some blocking components)
|
// Part 2/2 (async, with some blocking components)
|
||||||
//
|
//
|
||||||
// Produce the block upon the state
|
// Produce the block upon the state
|
||||||
self.produce_block_on_state::<Payload>(
|
self.produce_block_on_state(
|
||||||
state,
|
state,
|
||||||
state_root_opt,
|
state_root_opt,
|
||||||
slot,
|
slot,
|
||||||
randao_reveal,
|
randao_reveal,
|
||||||
validator_graffiti,
|
validator_graffiti,
|
||||||
verification,
|
verification,
|
||||||
|
block_production_version,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@ -4568,7 +4554,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
/// The provided `state_root_opt` should only ever be set to `Some` if the contained value is
|
/// The provided `state_root_opt` should only ever be set to `Some` if the contained value is
|
||||||
/// equal to the root of `state`. Providing this value will serve as an optimization to avoid
|
/// equal to the root of `state`. Providing this value will serve as an optimization to avoid
|
||||||
/// performing a tree hash in some scenarios.
|
/// performing a tree hash in some scenarios.
|
||||||
pub async fn produce_block_on_state<Payload: AbstractExecPayload<T::EthSpec> + 'static>(
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub async fn produce_block_on_state(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
state: BeaconState<T::EthSpec>,
|
state: BeaconState<T::EthSpec>,
|
||||||
state_root_opt: Option<Hash256>,
|
state_root_opt: Option<Hash256>,
|
||||||
@ -4576,7 +4563,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
randao_reveal: Signature,
|
randao_reveal: Signature,
|
||||||
validator_graffiti: Option<Graffiti>,
|
validator_graffiti: Option<Graffiti>,
|
||||||
verification: ProduceBlockVerification,
|
verification: ProduceBlockVerification,
|
||||||
) -> Result<BeaconBlockAndState<T::EthSpec, Payload>, BlockProductionError> {
|
block_production_version: BlockProductionVersion,
|
||||||
|
) -> Result<BeaconBlockResponseType<T::EthSpec>, BlockProductionError> {
|
||||||
// Part 1/3 (blocking)
|
// Part 1/3 (blocking)
|
||||||
//
|
//
|
||||||
// Perform the state advance and block-packing functions.
|
// Perform the state advance and block-packing functions.
|
||||||
@ -4591,6 +4579,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
produce_at_slot,
|
produce_at_slot,
|
||||||
randao_reveal,
|
randao_reveal,
|
||||||
validator_graffiti,
|
validator_graffiti,
|
||||||
|
block_production_version,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
"produce_partial_beacon_block",
|
"produce_partial_beacon_block",
|
||||||
@ -4598,12 +4587,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
.ok_or(BlockProductionError::ShuttingDown)?
|
.ok_or(BlockProductionError::ShuttingDown)?
|
||||||
.await
|
.await
|
||||||
.map_err(BlockProductionError::TokioJoin)??;
|
.map_err(BlockProductionError::TokioJoin)??;
|
||||||
|
|
||||||
// Part 2/3 (async)
|
// Part 2/3 (async)
|
||||||
//
|
//
|
||||||
// Wait for the execution layer to return an execution payload (if one is required).
|
// Wait for the execution layer to return an execution payload (if one is required).
|
||||||
let prepare_payload_handle = partial_beacon_block.prepare_payload_handle.take();
|
let prepare_payload_handle = partial_beacon_block.prepare_payload_handle.take();
|
||||||
let block_contents = if let Some(prepare_payload_handle) = prepare_payload_handle {
|
let block_contents_type_option =
|
||||||
|
if let Some(prepare_payload_handle) = prepare_payload_handle {
|
||||||
Some(
|
Some(
|
||||||
prepare_payload_handle
|
prepare_payload_handle
|
||||||
.await
|
.await
|
||||||
@ -4613,17 +4602,18 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
// Part 3/3 (blocking)
|
// Part 3/3 (blocking)
|
||||||
//
|
if let Some(block_contents_type) = block_contents_type_option {
|
||||||
// Perform the final steps of combining all the parts and computing the state root.
|
match block_contents_type {
|
||||||
|
BlockProposalContentsType::Full(block_contents) => {
|
||||||
let chain = self.clone();
|
let chain = self.clone();
|
||||||
self.task_executor
|
let beacon_block_response = self
|
||||||
|
.task_executor
|
||||||
.spawn_blocking_handle(
|
.spawn_blocking_handle(
|
||||||
move || {
|
move || {
|
||||||
chain.complete_partial_beacon_block(
|
chain.complete_partial_beacon_block(
|
||||||
partial_beacon_block,
|
partial_beacon_block,
|
||||||
block_contents,
|
Some(block_contents),
|
||||||
verification,
|
verification,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -4631,17 +4621,62 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
)
|
)
|
||||||
.ok_or(BlockProductionError::ShuttingDown)?
|
.ok_or(BlockProductionError::ShuttingDown)?
|
||||||
.await
|
.await
|
||||||
.map_err(BlockProductionError::TokioJoin)?
|
.map_err(BlockProductionError::TokioJoin)??;
|
||||||
|
|
||||||
|
Ok(BeaconBlockResponseType::Full(beacon_block_response))
|
||||||
|
}
|
||||||
|
BlockProposalContentsType::Blinded(block_contents) => {
|
||||||
|
let chain = self.clone();
|
||||||
|
let beacon_block_response = self
|
||||||
|
.task_executor
|
||||||
|
.spawn_blocking_handle(
|
||||||
|
move || {
|
||||||
|
chain.complete_partial_beacon_block(
|
||||||
|
partial_beacon_block,
|
||||||
|
Some(block_contents),
|
||||||
|
verification,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"complete_partial_beacon_block",
|
||||||
|
)
|
||||||
|
.ok_or(BlockProductionError::ShuttingDown)?
|
||||||
|
.await
|
||||||
|
.map_err(BlockProductionError::TokioJoin)??;
|
||||||
|
|
||||||
|
Ok(BeaconBlockResponseType::Blinded(beacon_block_response))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let chain = self.clone();
|
||||||
|
let beacon_block_response = self
|
||||||
|
.task_executor
|
||||||
|
.spawn_blocking_handle(
|
||||||
|
move || {
|
||||||
|
chain.complete_partial_beacon_block(
|
||||||
|
partial_beacon_block,
|
||||||
|
None,
|
||||||
|
verification,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"complete_partial_beacon_block",
|
||||||
|
)
|
||||||
|
.ok_or(BlockProductionError::ShuttingDown)?
|
||||||
|
.await
|
||||||
|
.map_err(BlockProductionError::TokioJoin)??;
|
||||||
|
|
||||||
|
Ok(BeaconBlockResponseType::Full(beacon_block_response))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn produce_partial_beacon_block<Payload: AbstractExecPayload<T::EthSpec> + 'static>(
|
fn produce_partial_beacon_block(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
mut state: BeaconState<T::EthSpec>,
|
mut state: BeaconState<T::EthSpec>,
|
||||||
state_root_opt: Option<Hash256>,
|
state_root_opt: Option<Hash256>,
|
||||||
produce_at_slot: Slot,
|
produce_at_slot: Slot,
|
||||||
randao_reveal: Signature,
|
randao_reveal: Signature,
|
||||||
validator_graffiti: Option<Graffiti>,
|
validator_graffiti: Option<Graffiti>,
|
||||||
) -> Result<PartialBeaconBlock<T::EthSpec, Payload>, BlockProductionError> {
|
block_production_version: BlockProductionVersion,
|
||||||
|
) -> Result<PartialBeaconBlock<T::EthSpec>, BlockProductionError> {
|
||||||
let eth1_chain = self
|
let eth1_chain = self
|
||||||
.eth1_chain
|
.eth1_chain
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@ -4701,6 +4736,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
parent_root,
|
parent_root,
|
||||||
proposer_index,
|
proposer_index,
|
||||||
builder_params,
|
builder_params,
|
||||||
|
block_production_version,
|
||||||
)?;
|
)?;
|
||||||
Some(prepare_payload_handle)
|
Some(prepare_payload_handle)
|
||||||
}
|
}
|
||||||
@ -4710,6 +4746,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
self.op_pool.get_slashings_and_exits(&state, &self.spec);
|
self.op_pool.get_slashings_and_exits(&state, &self.spec);
|
||||||
|
|
||||||
let eth1_data = eth1_chain.eth1_data_for_block_production(&state, &self.spec)?;
|
let eth1_data = eth1_chain.eth1_data_for_block_production(&state, &self.spec)?;
|
||||||
|
|
||||||
let deposits = eth1_chain.deposits_for_block_inclusion(&state, ð1_data, &self.spec)?;
|
let deposits = eth1_chain.deposits_for_block_inclusion(&state, ð1_data, &self.spec)?;
|
||||||
|
|
||||||
let bls_to_execution_changes = self
|
let bls_to_execution_changes = self
|
||||||
@ -4880,10 +4917,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
|
|
||||||
fn complete_partial_beacon_block<Payload: AbstractExecPayload<T::EthSpec>>(
|
fn complete_partial_beacon_block<Payload: AbstractExecPayload<T::EthSpec>>(
|
||||||
&self,
|
&self,
|
||||||
partial_beacon_block: PartialBeaconBlock<T::EthSpec, Payload>,
|
partial_beacon_block: PartialBeaconBlock<T::EthSpec>,
|
||||||
block_contents: Option<BlockProposalContents<T::EthSpec, Payload>>,
|
block_contents: Option<BlockProposalContents<T::EthSpec, Payload>>,
|
||||||
verification: ProduceBlockVerification,
|
verification: ProduceBlockVerification,
|
||||||
) -> Result<BeaconBlockAndState<T::EthSpec, Payload>, BlockProductionError> {
|
) -> Result<BeaconBlockResponse<T::EthSpec, Payload>, BlockProductionError> {
|
||||||
let PartialBeaconBlock {
|
let PartialBeaconBlock {
|
||||||
mut state,
|
mut state,
|
||||||
slot,
|
slot,
|
||||||
@ -4905,7 +4942,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
bls_to_execution_changes,
|
bls_to_execution_changes,
|
||||||
} = partial_beacon_block;
|
} = partial_beacon_block;
|
||||||
|
|
||||||
let (inner_block, blobs_opt, proofs_opt) = match &state {
|
let (inner_block, blobs_opt, proofs_opt, execution_payload_value) = match &state {
|
||||||
BeaconState::Base(_) => (
|
BeaconState::Base(_) => (
|
||||||
BeaconBlock::Base(BeaconBlockBase {
|
BeaconBlock::Base(BeaconBlockBase {
|
||||||
slot,
|
slot,
|
||||||
@ -4926,6 +4963,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
}),
|
}),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
Uint256::zero(),
|
||||||
),
|
),
|
||||||
BeaconState::Altair(_) => (
|
BeaconState::Altair(_) => (
|
||||||
BeaconBlock::Altair(BeaconBlockAltair {
|
BeaconBlock::Altair(BeaconBlockAltair {
|
||||||
@ -4949,11 +4987,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
}),
|
}),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
Uint256::zero(),
|
||||||
),
|
),
|
||||||
BeaconState::Merge(_) => {
|
BeaconState::Merge(_) => {
|
||||||
let (payload, _, _, _) = block_contents
|
let block_proposal_contents =
|
||||||
.ok_or(BlockProductionError::MissingExecutionPayload)?
|
block_contents.ok_or(BlockProductionError::MissingExecutionPayload)?;
|
||||||
.deconstruct();
|
let execution_payload_value = block_proposal_contents.block_value().to_owned();
|
||||||
(
|
(
|
||||||
BeaconBlock::Merge(BeaconBlockMerge {
|
BeaconBlock::Merge(BeaconBlockMerge {
|
||||||
slot,
|
slot,
|
||||||
@ -4971,19 +5010,22 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
voluntary_exits: voluntary_exits.into(),
|
voluntary_exits: voluntary_exits.into(),
|
||||||
sync_aggregate: sync_aggregate
|
sync_aggregate: sync_aggregate
|
||||||
.ok_or(BlockProductionError::MissingSyncAggregate)?,
|
.ok_or(BlockProductionError::MissingSyncAggregate)?,
|
||||||
execution_payload: payload
|
execution_payload: block_proposal_contents
|
||||||
|
.to_payload()
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| BlockProductionError::InvalidPayloadFork)?,
|
.map_err(|_| BlockProductionError::InvalidPayloadFork)?,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
execution_payload_value,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
BeaconState::Capella(_) => {
|
BeaconState::Capella(_) => {
|
||||||
let (payload, _, _, _) = block_contents
|
let block_proposal_contents =
|
||||||
.ok_or(BlockProductionError::MissingExecutionPayload)?
|
block_contents.ok_or(BlockProductionError::MissingExecutionPayload)?;
|
||||||
.deconstruct();
|
let execution_payload_value = block_proposal_contents.block_value().to_owned();
|
||||||
|
|
||||||
(
|
(
|
||||||
BeaconBlock::Capella(BeaconBlockCapella {
|
BeaconBlock::Capella(BeaconBlockCapella {
|
||||||
slot,
|
slot,
|
||||||
@ -5001,7 +5043,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
voluntary_exits: voluntary_exits.into(),
|
voluntary_exits: voluntary_exits.into(),
|
||||||
sync_aggregate: sync_aggregate
|
sync_aggregate: sync_aggregate
|
||||||
.ok_or(BlockProductionError::MissingSyncAggregate)?,
|
.ok_or(BlockProductionError::MissingSyncAggregate)?,
|
||||||
execution_payload: payload
|
execution_payload: block_proposal_contents
|
||||||
|
.to_payload()
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| BlockProductionError::InvalidPayloadFork)?,
|
.map_err(|_| BlockProductionError::InvalidPayloadFork)?,
|
||||||
bls_to_execution_changes: bls_to_execution_changes.into(),
|
bls_to_execution_changes: bls_to_execution_changes.into(),
|
||||||
@ -5009,12 +5052,15 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
}),
|
}),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
execution_payload_value,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
BeaconState::Deneb(_) => {
|
BeaconState::Deneb(_) => {
|
||||||
let (payload, kzg_commitments, blobs, proofs) = block_contents
|
let (payload, kzg_commitments, blobs, proofs, execution_payload_value) =
|
||||||
|
block_contents
|
||||||
.ok_or(BlockProductionError::MissingExecutionPayload)?
|
.ok_or(BlockProductionError::MissingExecutionPayload)?
|
||||||
.deconstruct();
|
.deconstruct();
|
||||||
|
|
||||||
(
|
(
|
||||||
BeaconBlock::Deneb(BeaconBlockDeneb {
|
BeaconBlock::Deneb(BeaconBlockDeneb {
|
||||||
slot,
|
slot,
|
||||||
@ -5042,6 +5088,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
}),
|
}),
|
||||||
blobs,
|
blobs,
|
||||||
proofs,
|
proofs,
|
||||||
|
execution_payload_value,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -5057,7 +5104,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
self.log,
|
self.log,
|
||||||
"Produced block on state";
|
"Produced block on state";
|
||||||
"block_size" => block_size,
|
"block_size" => block_size,
|
||||||
"slot" => block.slot(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
metrics::observe(&metrics::BLOCK_SIZE, block_size as f64);
|
metrics::observe(&metrics::BLOCK_SIZE, block_size as f64);
|
||||||
@ -5075,6 +5121,11 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
// Use a context without block root or proposer index so that both are checked.
|
// Use a context without block root or proposer index so that both are checked.
|
||||||
let mut ctxt = ConsensusContext::new(block.slot());
|
let mut ctxt = ConsensusContext::new(block.slot());
|
||||||
|
|
||||||
|
let consensus_block_value = self
|
||||||
|
.compute_beacon_block_reward(block.message(), Hash256::zero(), &mut state)
|
||||||
|
.map(|reward| reward.total)
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
per_block_processing(
|
per_block_processing(
|
||||||
&mut state,
|
&mut state,
|
||||||
&block,
|
&block,
|
||||||
@ -5154,7 +5205,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
"slot" => block.slot()
|
"slot" => block.slot()
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok((block, state, maybe_sidecar_list))
|
Ok(BeaconBlockResponse {
|
||||||
|
block,
|
||||||
|
state,
|
||||||
|
maybe_side_car: maybe_sidecar_list,
|
||||||
|
execution_payload_value: Some(execution_payload_value),
|
||||||
|
consensus_block_value: Some(consensus_block_value),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This method must be called whenever an execution engine indicates that a payload is
|
/// This method must be called whenever an execution engine indicates that a payload is
|
||||||
|
@ -13,7 +13,8 @@ use crate::{
|
|||||||
ExecutionPayloadError,
|
ExecutionPayloadError,
|
||||||
};
|
};
|
||||||
use execution_layer::{
|
use execution_layer::{
|
||||||
BlockProposalContents, BuilderParams, NewPayloadRequest, PayloadAttributes, PayloadStatus,
|
BlockProposalContents, BlockProposalContentsType, BuilderParams, NewPayloadRequest,
|
||||||
|
PayloadAttributes, PayloadStatus,
|
||||||
};
|
};
|
||||||
use fork_choice::{InvalidationOperation, PayloadVerificationStatus};
|
use fork_choice::{InvalidationOperation, PayloadVerificationStatus};
|
||||||
use proto_array::{Block as ProtoBlock, ExecutionStatus};
|
use proto_array::{Block as ProtoBlock, ExecutionStatus};
|
||||||
@ -26,11 +27,11 @@ use state_processing::per_block_processing::{
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use tree_hash::TreeHash;
|
use tree_hash::TreeHash;
|
||||||
|
use types::payload::BlockProductionVersion;
|
||||||
use types::*;
|
use types::*;
|
||||||
|
|
||||||
pub type PreparePayloadResult<E, Payload> =
|
pub type PreparePayloadResult<E> = Result<BlockProposalContentsType<E>, BlockProductionError>;
|
||||||
Result<BlockProposalContents<E, Payload>, BlockProductionError>;
|
pub type PreparePayloadHandle<E> = JoinHandle<Option<PreparePayloadResult<E>>>;
|
||||||
pub type PreparePayloadHandle<E, Payload> = JoinHandle<Option<PreparePayloadResult<E, Payload>>>;
|
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub enum AllowOptimisticImport {
|
pub enum AllowOptimisticImport {
|
||||||
@ -398,16 +399,14 @@ pub fn validate_execution_payload_for_gossip<T: BeaconChainTypes>(
|
|||||||
/// Equivalent to the `get_execution_payload` function in the Validator Guide:
|
/// Equivalent to the `get_execution_payload` function in the Validator Guide:
|
||||||
///
|
///
|
||||||
/// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/validator.md#block-proposal
|
/// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/validator.md#block-proposal
|
||||||
pub fn get_execution_payload<
|
pub fn get_execution_payload<T: BeaconChainTypes>(
|
||||||
T: BeaconChainTypes,
|
|
||||||
Payload: AbstractExecPayload<T::EthSpec> + 'static,
|
|
||||||
>(
|
|
||||||
chain: Arc<BeaconChain<T>>,
|
chain: Arc<BeaconChain<T>>,
|
||||||
state: &BeaconState<T::EthSpec>,
|
state: &BeaconState<T::EthSpec>,
|
||||||
parent_block_root: Hash256,
|
parent_block_root: Hash256,
|
||||||
proposer_index: u64,
|
proposer_index: u64,
|
||||||
builder_params: BuilderParams,
|
builder_params: BuilderParams,
|
||||||
) -> Result<PreparePayloadHandle<T::EthSpec, Payload>, BlockProductionError> {
|
block_production_version: BlockProductionVersion,
|
||||||
|
) -> Result<PreparePayloadHandle<T::EthSpec>, BlockProductionError> {
|
||||||
// Compute all required values from the `state` now to avoid needing to pass it into a spawned
|
// Compute all required values from the `state` now to avoid needing to pass it into a spawned
|
||||||
// task.
|
// task.
|
||||||
let spec = &chain.spec;
|
let spec = &chain.spec;
|
||||||
@ -440,7 +439,7 @@ pub fn get_execution_payload<
|
|||||||
.clone()
|
.clone()
|
||||||
.spawn_handle(
|
.spawn_handle(
|
||||||
async move {
|
async move {
|
||||||
prepare_execution_payload::<T, Payload>(
|
prepare_execution_payload::<T>(
|
||||||
&chain,
|
&chain,
|
||||||
is_merge_transition_complete,
|
is_merge_transition_complete,
|
||||||
timestamp,
|
timestamp,
|
||||||
@ -450,6 +449,7 @@ pub fn get_execution_payload<
|
|||||||
builder_params,
|
builder_params,
|
||||||
withdrawals,
|
withdrawals,
|
||||||
parent_beacon_block_root,
|
parent_beacon_block_root,
|
||||||
|
block_production_version,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
},
|
},
|
||||||
@ -475,7 +475,7 @@ pub fn get_execution_payload<
|
|||||||
///
|
///
|
||||||
/// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/validator.md#block-proposal
|
/// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/validator.md#block-proposal
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn prepare_execution_payload<T, Payload>(
|
pub async fn prepare_execution_payload<T>(
|
||||||
chain: &Arc<BeaconChain<T>>,
|
chain: &Arc<BeaconChain<T>>,
|
||||||
is_merge_transition_complete: bool,
|
is_merge_transition_complete: bool,
|
||||||
timestamp: u64,
|
timestamp: u64,
|
||||||
@ -485,10 +485,10 @@ pub async fn prepare_execution_payload<T, Payload>(
|
|||||||
builder_params: BuilderParams,
|
builder_params: BuilderParams,
|
||||||
withdrawals: Option<Vec<Withdrawal>>,
|
withdrawals: Option<Vec<Withdrawal>>,
|
||||||
parent_beacon_block_root: Option<Hash256>,
|
parent_beacon_block_root: Option<Hash256>,
|
||||||
) -> Result<BlockProposalContents<T::EthSpec, Payload>, BlockProductionError>
|
block_production_version: BlockProductionVersion,
|
||||||
|
) -> Result<BlockProposalContentsType<T::EthSpec>, BlockProductionError>
|
||||||
where
|
where
|
||||||
T: BeaconChainTypes,
|
T: BeaconChainTypes,
|
||||||
Payload: AbstractExecPayload<T::EthSpec>,
|
|
||||||
{
|
{
|
||||||
let current_epoch = builder_params.slot.epoch(T::EthSpec::slots_per_epoch());
|
let current_epoch = builder_params.slot.epoch(T::EthSpec::slots_per_epoch());
|
||||||
let spec = &chain.spec;
|
let spec = &chain.spec;
|
||||||
@ -506,7 +506,12 @@ where
|
|||||||
if is_terminal_block_hash_set && !is_activation_epoch_reached {
|
if is_terminal_block_hash_set && !is_activation_epoch_reached {
|
||||||
// Use the "empty" payload if there's a terminal block hash, but we haven't reached the
|
// Use the "empty" payload if there's a terminal block hash, but we haven't reached the
|
||||||
// terminal block epoch yet.
|
// terminal block epoch yet.
|
||||||
return BlockProposalContents::default_at_fork(fork).map_err(Into::into);
|
return Ok(BlockProposalContentsType::Full(
|
||||||
|
BlockProposalContents::Payload {
|
||||||
|
payload: FullPayload::default_at_fork(fork)?,
|
||||||
|
block_value: Uint256::zero(),
|
||||||
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let terminal_pow_block_hash = execution_layer
|
let terminal_pow_block_hash = execution_layer
|
||||||
@ -519,7 +524,12 @@ where
|
|||||||
} else {
|
} else {
|
||||||
// If the merge transition hasn't occurred yet and the EL hasn't found the terminal
|
// If the merge transition hasn't occurred yet and the EL hasn't found the terminal
|
||||||
// block, return an "empty" payload.
|
// block, return an "empty" payload.
|
||||||
return BlockProposalContents::default_at_fork(fork).map_err(Into::into);
|
return Ok(BlockProposalContentsType::Full(
|
||||||
|
BlockProposalContents::Payload {
|
||||||
|
payload: FullPayload::default_at_fork(fork)?,
|
||||||
|
block_value: Uint256::zero(),
|
||||||
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
latest_execution_payload_header_block_hash
|
latest_execution_payload_header_block_hash
|
||||||
@ -558,13 +568,14 @@ where
|
|||||||
//
|
//
|
||||||
// This future is not executed here, it's up to the caller to await it.
|
// This future is not executed here, it's up to the caller to await it.
|
||||||
let block_contents = execution_layer
|
let block_contents = execution_layer
|
||||||
.get_payload::<Payload>(
|
.get_payload(
|
||||||
parent_hash,
|
parent_hash,
|
||||||
&payload_attributes,
|
&payload_attributes,
|
||||||
forkchoice_update_params,
|
forkchoice_update_params,
|
||||||
builder_params,
|
builder_params,
|
||||||
fork,
|
fork,
|
||||||
&chain.spec,
|
&chain.spec,
|
||||||
|
block_production_version,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(BlockProductionError::GetPayloadFailed)?;
|
.map_err(BlockProductionError::GetPayloadFailed)?;
|
||||||
|
@ -56,10 +56,10 @@ pub mod validator_monitor;
|
|||||||
pub mod validator_pubkey_cache;
|
pub mod validator_pubkey_cache;
|
||||||
|
|
||||||
pub use self::beacon_chain::{
|
pub use self::beacon_chain::{
|
||||||
AttestationProcessingOutcome, AvailabilityProcessingStatus, BeaconChain, BeaconChainTypes,
|
AttestationProcessingOutcome, AvailabilityProcessingStatus, BeaconBlockResponse,
|
||||||
BeaconStore, ChainSegmentResult, ForkChoiceError, OverrideForkchoiceUpdate,
|
BeaconBlockResponseType, BeaconChain, BeaconChainTypes, BeaconStore, ChainSegmentResult,
|
||||||
ProduceBlockVerification, StateSkipConfig, WhenSlotSkipped,
|
ForkChoiceError, OverrideForkchoiceUpdate, ProduceBlockVerification, StateSkipConfig,
|
||||||
INVALID_FINALIZED_MERGE_TRANSITION_BLOCK_SHUTDOWN_REASON,
|
WhenSlotSkipped, INVALID_FINALIZED_MERGE_TRANSITION_BLOCK_SHUTDOWN_REASON,
|
||||||
INVALID_JUSTIFIED_PAYLOAD_SHUTDOWN_REASON,
|
INVALID_JUSTIFIED_PAYLOAD_SHUTDOWN_REASON,
|
||||||
};
|
};
|
||||||
pub use self::beacon_snapshot::BeaconSnapshot;
|
pub use self::beacon_snapshot::BeaconSnapshot;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::block_verification_types::{AsBlock, RpcBlock};
|
use crate::block_verification_types::{AsBlock, RpcBlock};
|
||||||
use crate::observed_operations::ObservationOutcome;
|
use crate::observed_operations::ObservationOutcome;
|
||||||
pub use crate::persisted_beacon_chain::PersistedBeaconChain;
|
pub use crate::persisted_beacon_chain::PersistedBeaconChain;
|
||||||
|
use crate::BeaconBlockResponseType;
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
beacon_chain::{BEACON_CHAIN_DB_KEY, ETH1_CACHE_DB_KEY, FORK_CHOICE_DB_KEY, OP_POOL_DB_KEY},
|
beacon_chain::{BEACON_CHAIN_DB_KEY, ETH1_CACHE_DB_KEY, FORK_CHOICE_DB_KEY, OP_POOL_DB_KEY},
|
||||||
migrate::MigratorConfig,
|
migrate::MigratorConfig,
|
||||||
@ -60,6 +61,7 @@ use store::{config::StoreConfig, HotColdDB, ItemStore, LevelDB, MemoryStore};
|
|||||||
use task_executor::TaskExecutor;
|
use task_executor::TaskExecutor;
|
||||||
use task_executor::{test_utils::TestRuntime, ShutdownReason};
|
use task_executor::{test_utils::TestRuntime, ShutdownReason};
|
||||||
use tree_hash::TreeHash;
|
use tree_hash::TreeHash;
|
||||||
|
use types::payload::BlockProductionVersion;
|
||||||
use types::sync_selection_proof::SyncSelectionProof;
|
use types::sync_selection_proof::SyncSelectionProof;
|
||||||
pub use types::test_utils::generate_deterministic_keypairs;
|
pub use types::test_utils::generate_deterministic_keypairs;
|
||||||
use types::test_utils::TestRandom;
|
use types::test_utils::TestRandom;
|
||||||
@ -878,7 +880,7 @@ where
|
|||||||
|
|
||||||
let randao_reveal = self.sign_randao_reveal(&state, proposer_index, slot);
|
let randao_reveal = self.sign_randao_reveal(&state, proposer_index, slot);
|
||||||
|
|
||||||
let (block, state, maybe_blob_sidecars) = self
|
let BeaconBlockResponseType::Full(block_response) = self
|
||||||
.chain
|
.chain
|
||||||
.produce_block_on_state(
|
.produce_block_on_state(
|
||||||
state,
|
state,
|
||||||
@ -887,14 +889,18 @@ where
|
|||||||
randao_reveal,
|
randao_reveal,
|
||||||
Some(graffiti),
|
Some(graffiti),
|
||||||
ProduceBlockVerification::VerifyRandao,
|
ProduceBlockVerification::VerifyRandao,
|
||||||
|
BlockProductionVersion::FullV2,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap()
|
||||||
|
else {
|
||||||
|
panic!("Should always be a full payload response");
|
||||||
|
};
|
||||||
|
|
||||||
let signed_block = block.sign(
|
let signed_block = block_response.block.sign(
|
||||||
&self.validator_keypairs[proposer_index].sk,
|
&self.validator_keypairs[proposer_index].sk,
|
||||||
&state.fork(),
|
&block_response.state.fork(),
|
||||||
state.genesis_validators_root(),
|
block_response.state.genesis_validators_root(),
|
||||||
&self.spec,
|
&self.spec,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -905,11 +911,13 @@ where
|
|||||||
| SignedBeaconBlock::Capella(_) => (signed_block, None),
|
| SignedBeaconBlock::Capella(_) => (signed_block, None),
|
||||||
SignedBeaconBlock::Deneb(_) => (
|
SignedBeaconBlock::Deneb(_) => (
|
||||||
signed_block,
|
signed_block,
|
||||||
maybe_blob_sidecars.map(|blobs| self.sign_blobs(blobs, &state, proposer_index)),
|
block_response
|
||||||
|
.maybe_side_car
|
||||||
|
.map(|blobs| self.sign_blobs(blobs, &block_response.state, proposer_index)),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
(block_contents, state)
|
(block_contents, block_response.state)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Useful for the `per_block_processing` tests. Creates a block, and returns the state after
|
/// Useful for the `per_block_processing` tests. Creates a block, and returns the state after
|
||||||
@ -938,7 +946,7 @@ where
|
|||||||
|
|
||||||
let pre_state = state.clone();
|
let pre_state = state.clone();
|
||||||
|
|
||||||
let (block, state, maybe_blob_sidecars) = self
|
let BeaconBlockResponseType::Full(block_response) = self
|
||||||
.chain
|
.chain
|
||||||
.produce_block_on_state(
|
.produce_block_on_state(
|
||||||
state,
|
state,
|
||||||
@ -947,14 +955,18 @@ where
|
|||||||
randao_reveal,
|
randao_reveal,
|
||||||
Some(graffiti),
|
Some(graffiti),
|
||||||
ProduceBlockVerification::VerifyRandao,
|
ProduceBlockVerification::VerifyRandao,
|
||||||
|
BlockProductionVersion::FullV2,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap()
|
||||||
|
else {
|
||||||
|
panic!("Should always be a full payload response");
|
||||||
|
};
|
||||||
|
|
||||||
let signed_block = block.sign(
|
let signed_block = block_response.block.sign(
|
||||||
&self.validator_keypairs[proposer_index].sk,
|
&self.validator_keypairs[proposer_index].sk,
|
||||||
&state.fork(),
|
&block_response.state.fork(),
|
||||||
state.genesis_validators_root(),
|
block_response.state.genesis_validators_root(),
|
||||||
&self.spec,
|
&self.spec,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -964,14 +976,14 @@ where
|
|||||||
| SignedBeaconBlock::Merge(_)
|
| SignedBeaconBlock::Merge(_)
|
||||||
| SignedBeaconBlock::Capella(_) => (signed_block, None),
|
| SignedBeaconBlock::Capella(_) => (signed_block, None),
|
||||||
SignedBeaconBlock::Deneb(_) => {
|
SignedBeaconBlock::Deneb(_) => {
|
||||||
if let Some(blobs) = maybe_blob_sidecars {
|
if let Some(blobs) = block_response.maybe_side_car {
|
||||||
let signed_blobs: SignedSidecarList<E, BlobSidecar<E>> = Vec::from(blobs)
|
let signed_blobs: SignedSidecarList<E, BlobSidecar<E>> = Vec::from(blobs)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|blob| {
|
.map(|blob| {
|
||||||
blob.sign(
|
blob.sign(
|
||||||
&self.validator_keypairs[proposer_index].sk,
|
&self.validator_keypairs[proposer_index].sk,
|
||||||
&state.fork(),
|
&block_response.state.fork(),
|
||||||
state.genesis_validators_root(),
|
block_response.state.genesis_validators_root(),
|
||||||
&self.spec,
|
&self.spec,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -990,7 +1002,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
(block_contents, pre_state)
|
(block_contents, pre_state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,6 +466,11 @@ impl<T: EthSpec> From<GetPayloadResponse<T>>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum GetPayloadResponseType<E: EthSpec> {
|
||||||
|
Full(GetPayloadResponse<E>),
|
||||||
|
Blinded(GetPayloadResponse<E>),
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: EthSpec> GetPayloadResponse<T> {
|
impl<T: EthSpec> GetPayloadResponse<T> {
|
||||||
pub fn execution_payload_ref(&self) -> ExecutionPayloadRef<T> {
|
pub fn execution_payload_ref(&self) -> ExecutionPayloadRef<T> {
|
||||||
self.to_ref().into()
|
self.to_ref().into()
|
||||||
|
@ -42,13 +42,13 @@ use tokio_stream::wrappers::WatchStream;
|
|||||||
use tree_hash::TreeHash;
|
use tree_hash::TreeHash;
|
||||||
use types::beacon_block_body::KzgCommitments;
|
use types::beacon_block_body::KzgCommitments;
|
||||||
use types::builder_bid::BuilderBid;
|
use types::builder_bid::BuilderBid;
|
||||||
|
use types::payload::BlockProductionVersion;
|
||||||
use types::sidecar::{BlobItems, Sidecar};
|
use types::sidecar::{BlobItems, Sidecar};
|
||||||
use types::KzgProofs;
|
use types::{AbstractExecPayload, ExecutionPayloadDeneb, KzgProofs};
|
||||||
use types::{
|
use types::{
|
||||||
AbstractExecPayload, BeaconStateError, BlindedPayload, BlockType, ChainSpec, Epoch,
|
BeaconStateError, BlindedPayload, ChainSpec, Epoch, ExecPayload, ExecutionPayloadCapella,
|
||||||
ExecPayload, ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadMerge,
|
ExecutionPayloadMerge, FullPayload, ProposerPreparationData, PublicKeyBytes, Signature, Slot,
|
||||||
};
|
};
|
||||||
use types::{ProposerPreparationData, PublicKeyBytes, Signature, Slot};
|
|
||||||
|
|
||||||
mod block_hash;
|
mod block_hash;
|
||||||
mod engine_api;
|
mod engine_api;
|
||||||
@ -87,9 +87,7 @@ pub enum ProvenancedPayload<P> {
|
|||||||
Builder(P),
|
Builder(P),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: EthSpec, Payload: AbstractExecPayload<E>> TryFrom<BuilderBid<E>>
|
impl<E: EthSpec> TryFrom<BuilderBid<E>> for ProvenancedPayload<BlockProposalContentsType<E>> {
|
||||||
for ProvenancedPayload<BlockProposalContents<E, Payload>>
|
|
||||||
{
|
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn try_from(value: BuilderBid<E>) -> Result<Self, Error> {
|
fn try_from(value: BuilderBid<E>) -> Result<Self, Error> {
|
||||||
@ -112,12 +110,16 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> TryFrom<BuilderBid<E>>
|
|||||||
.map_err(|_| Error::InvalidPayloadConversion)?,
|
.map_err(|_| Error::InvalidPayloadConversion)?,
|
||||||
block_value: builder_bid.value,
|
block_value: builder_bid.value,
|
||||||
kzg_commitments: builder_bid.blinded_blobs_bundle.commitments,
|
kzg_commitments: builder_bid.blinded_blobs_bundle.commitments,
|
||||||
blobs: BlobItems::try_from_blob_roots(builder_bid.blinded_blobs_bundle.blob_roots)
|
blobs: BlobItems::<E>::try_from_blob_roots(
|
||||||
|
builder_bid.blinded_blobs_bundle.blob_roots,
|
||||||
|
)
|
||||||
.map_err(Error::InvalidBlobConversion)?,
|
.map_err(Error::InvalidBlobConversion)?,
|
||||||
proofs: builder_bid.blinded_blobs_bundle.proofs,
|
proofs: builder_bid.blinded_blobs_bundle.proofs,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
Ok(ProvenancedPayload::Builder(block_proposal_contents))
|
Ok(ProvenancedPayload::Builder(
|
||||||
|
BlockProposalContentsType::Blinded(block_proposal_contents),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,6 +147,7 @@ pub enum Error {
|
|||||||
InvalidPayloadConversion,
|
InvalidPayloadConversion,
|
||||||
InvalidBlobConversion(String),
|
InvalidBlobConversion(String),
|
||||||
BeaconStateError(BeaconStateError),
|
BeaconStateError(BeaconStateError),
|
||||||
|
PayloadTypeMismatch,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<BeaconStateError> for Error {
|
impl From<BeaconStateError> for Error {
|
||||||
@ -159,6 +162,11 @@ impl From<ApiError> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum BlockProposalContentsType<E: EthSpec> {
|
||||||
|
Full(BlockProposalContents<E, FullPayload<E>>),
|
||||||
|
Blinded(BlockProposalContents<E, BlindedPayload<E>>),
|
||||||
|
}
|
||||||
|
|
||||||
pub enum BlockProposalContents<T: EthSpec, Payload: AbstractExecPayload<T>> {
|
pub enum BlockProposalContents<T: EthSpec, Payload: AbstractExecPayload<T>> {
|
||||||
Payload {
|
Payload {
|
||||||
payload: Payload,
|
payload: Payload,
|
||||||
@ -173,6 +181,22 @@ pub enum BlockProposalContents<T: EthSpec, Payload: AbstractExecPayload<T>> {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: EthSpec> From<BlockProposalContents<T, FullPayload<T>>>
|
||||||
|
for BlockProposalContents<T, BlindedPayload<T>>
|
||||||
|
{
|
||||||
|
fn from(item: BlockProposalContents<T, FullPayload<T>>) -> Self {
|
||||||
|
let block_value = item.block_value().to_owned();
|
||||||
|
|
||||||
|
let blinded_payload: BlockProposalContents<T, BlindedPayload<T>> =
|
||||||
|
BlockProposalContents::Payload {
|
||||||
|
payload: item.to_payload().execution_payload().into(),
|
||||||
|
block_value,
|
||||||
|
};
|
||||||
|
|
||||||
|
blinded_payload
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<E: EthSpec, Payload: AbstractExecPayload<E>> TryFrom<GetPayloadResponse<E>>
|
impl<E: EthSpec, Payload: AbstractExecPayload<E>> TryFrom<GetPayloadResponse<E>>
|
||||||
for BlockProposalContents<E, Payload>
|
for BlockProposalContents<E, Payload>
|
||||||
{
|
{
|
||||||
@ -197,6 +221,17 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> TryFrom<GetPayloadResponse<E>>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<E: EthSpec> TryFrom<GetPayloadResponseType<E>> for BlockProposalContentsType<E> {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(response_type: GetPayloadResponseType<E>) -> Result<Self, Error> {
|
||||||
|
match response_type {
|
||||||
|
GetPayloadResponseType::Full(response) => Ok(Self::Full(response.try_into()?)),
|
||||||
|
GetPayloadResponseType::Blinded(response) => Ok(Self::Blinded(response.try_into()?)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
impl<T: EthSpec, Payload: AbstractExecPayload<T>> BlockProposalContents<T, Payload> {
|
impl<T: EthSpec, Payload: AbstractExecPayload<T>> BlockProposalContents<T, Payload> {
|
||||||
pub fn deconstruct(
|
pub fn deconstruct(
|
||||||
@ -206,19 +241,26 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> BlockProposalContents<T, Paylo
|
|||||||
Option<KzgCommitments<T>>,
|
Option<KzgCommitments<T>>,
|
||||||
Option<<Payload::Sidecar as Sidecar<T>>::BlobItems>,
|
Option<<Payload::Sidecar as Sidecar<T>>::BlobItems>,
|
||||||
Option<KzgProofs<T>>,
|
Option<KzgProofs<T>>,
|
||||||
|
Uint256,
|
||||||
) {
|
) {
|
||||||
match self {
|
match self {
|
||||||
Self::Payload {
|
Self::Payload {
|
||||||
payload,
|
payload,
|
||||||
block_value: _,
|
block_value,
|
||||||
} => (payload, None, None, None),
|
} => (payload, None, None, None, block_value),
|
||||||
Self::PayloadAndBlobs {
|
Self::PayloadAndBlobs {
|
||||||
payload,
|
payload,
|
||||||
block_value: _,
|
block_value,
|
||||||
kzg_commitments,
|
kzg_commitments,
|
||||||
blobs,
|
blobs,
|
||||||
proofs,
|
proofs,
|
||||||
} => (payload, Some(kzg_commitments), Some(blobs), Some(proofs)),
|
} => (
|
||||||
|
payload,
|
||||||
|
Some(kzg_commitments),
|
||||||
|
Some(blobs),
|
||||||
|
Some(proofs),
|
||||||
|
block_value,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -795,7 +837,8 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
///
|
///
|
||||||
/// The result will be returned from the first node that returns successfully. No more nodes
|
/// The result will be returned from the first node that returns successfully. No more nodes
|
||||||
/// will be contacted.
|
/// will be contacted.
|
||||||
pub async fn get_payload<Payload: AbstractExecPayload<T>>(
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub async fn get_payload(
|
||||||
&self,
|
&self,
|
||||||
parent_hash: ExecutionBlockHash,
|
parent_hash: ExecutionBlockHash,
|
||||||
payload_attributes: &PayloadAttributes,
|
payload_attributes: &PayloadAttributes,
|
||||||
@ -803,14 +846,11 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
builder_params: BuilderParams,
|
builder_params: BuilderParams,
|
||||||
current_fork: ForkName,
|
current_fork: ForkName,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> Result<BlockProposalContents<T, Payload>, Error> {
|
block_production_version: BlockProductionVersion,
|
||||||
let payload_result = match Payload::block_type() {
|
) -> Result<BlockProposalContentsType<T>, Error> {
|
||||||
BlockType::Blinded => {
|
let payload_result_type = match block_production_version {
|
||||||
let _timer = metrics::start_timer_vec(
|
BlockProductionVersion::V3 => match self
|
||||||
&metrics::EXECUTION_LAYER_REQUEST_TIMES,
|
.determine_and_fetch_payload(
|
||||||
&[metrics::GET_BLINDED_PAYLOAD],
|
|
||||||
);
|
|
||||||
self.get_blinded_payload(
|
|
||||||
parent_hash,
|
parent_hash,
|
||||||
payload_attributes,
|
payload_attributes,
|
||||||
forkchoice_update_params,
|
forkchoice_update_params,
|
||||||
@ -819,27 +859,51 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
spec,
|
spec,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
{
|
||||||
|
Ok(payload) => payload,
|
||||||
|
Err(e) => {
|
||||||
|
metrics::inc_counter_vec(
|
||||||
|
&metrics::EXECUTION_LAYER_GET_PAYLOAD_OUTCOME,
|
||||||
|
&[metrics::FAILURE],
|
||||||
|
);
|
||||||
|
return Err(e);
|
||||||
}
|
}
|
||||||
BlockType::Full => {
|
},
|
||||||
|
BlockProductionVersion::BlindedV2 => {
|
||||||
let _timer = metrics::start_timer_vec(
|
let _timer = metrics::start_timer_vec(
|
||||||
&metrics::EXECUTION_LAYER_REQUEST_TIMES,
|
&metrics::EXECUTION_LAYER_REQUEST_TIMES,
|
||||||
&[metrics::GET_PAYLOAD],
|
&[metrics::GET_BLINDED_PAYLOAD],
|
||||||
);
|
);
|
||||||
self.get_full_payload(
|
self.determine_and_fetch_payload(
|
||||||
|
parent_hash,
|
||||||
|
payload_attributes,
|
||||||
|
forkchoice_update_params,
|
||||||
|
builder_params,
|
||||||
|
current_fork,
|
||||||
|
spec,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
BlockProductionVersion::FullV2 => self
|
||||||
|
.get_full_payload_with(
|
||||||
parent_hash,
|
parent_hash,
|
||||||
payload_attributes,
|
payload_attributes,
|
||||||
forkchoice_update_params,
|
forkchoice_update_params,
|
||||||
current_fork,
|
current_fork,
|
||||||
|
noop,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.and_then(GetPayloadResponse::try_into)
|
.and_then(GetPayloadResponseType::try_into)
|
||||||
.map(ProvenancedPayload::Local)
|
.map(ProvenancedPayload::Local)?,
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Track some metrics and return the result.
|
let block_proposal_content_type = match payload_result_type {
|
||||||
match payload_result {
|
ProvenancedPayload::Local(local_payload) => local_payload,
|
||||||
Ok(ProvenancedPayload::Local(block_proposal_contents)) => {
|
ProvenancedPayload::Builder(builder_payload) => builder_payload,
|
||||||
|
};
|
||||||
|
|
||||||
|
match block_proposal_content_type {
|
||||||
|
BlockProposalContentsType::Full(block_proposal_contents) => {
|
||||||
metrics::inc_counter_vec(
|
metrics::inc_counter_vec(
|
||||||
&metrics::EXECUTION_LAYER_GET_PAYLOAD_OUTCOME,
|
&metrics::EXECUTION_LAYER_GET_PAYLOAD_OUTCOME,
|
||||||
&[metrics::SUCCESS],
|
&[metrics::SUCCESS],
|
||||||
@ -848,9 +912,15 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
&metrics::EXECUTION_LAYER_GET_PAYLOAD_SOURCE,
|
&metrics::EXECUTION_LAYER_GET_PAYLOAD_SOURCE,
|
||||||
&[metrics::LOCAL],
|
&[metrics::LOCAL],
|
||||||
);
|
);
|
||||||
Ok(block_proposal_contents)
|
if matches!(block_production_version, BlockProductionVersion::BlindedV2) {
|
||||||
|
Ok(BlockProposalContentsType::Blinded(
|
||||||
|
block_proposal_contents.into(),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(BlockProposalContentsType::Full(block_proposal_contents))
|
||||||
}
|
}
|
||||||
Ok(ProvenancedPayload::Builder(block_proposal_contents)) => {
|
}
|
||||||
|
BlockProposalContentsType::Blinded(block_proposal_contents) => {
|
||||||
metrics::inc_counter_vec(
|
metrics::inc_counter_vec(
|
||||||
&metrics::EXECUTION_LAYER_GET_PAYLOAD_OUTCOME,
|
&metrics::EXECUTION_LAYER_GET_PAYLOAD_OUTCOME,
|
||||||
&[metrics::SUCCESS],
|
&[metrics::SUCCESS],
|
||||||
@ -859,19 +929,12 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
&metrics::EXECUTION_LAYER_GET_PAYLOAD_SOURCE,
|
&metrics::EXECUTION_LAYER_GET_PAYLOAD_SOURCE,
|
||||||
&[metrics::BUILDER],
|
&[metrics::BUILDER],
|
||||||
);
|
);
|
||||||
Ok(block_proposal_contents)
|
Ok(BlockProposalContentsType::Blinded(block_proposal_contents))
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
metrics::inc_counter_vec(
|
|
||||||
&metrics::EXECUTION_LAYER_GET_PAYLOAD_OUTCOME,
|
|
||||||
&[metrics::FAILURE],
|
|
||||||
);
|
|
||||||
Err(e)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_blinded_payload<Payload: AbstractExecPayload<T>>(
|
async fn determine_and_fetch_payload(
|
||||||
&self,
|
&self,
|
||||||
parent_hash: ExecutionBlockHash,
|
parent_hash: ExecutionBlockHash,
|
||||||
payload_attributes: &PayloadAttributes,
|
payload_attributes: &PayloadAttributes,
|
||||||
@ -879,11 +942,10 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
builder_params: BuilderParams,
|
builder_params: BuilderParams,
|
||||||
current_fork: ForkName,
|
current_fork: ForkName,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> Result<ProvenancedPayload<BlockProposalContents<T, Payload>>, Error> {
|
) -> Result<ProvenancedPayload<BlockProposalContentsType<T>>, Error> {
|
||||||
if let Some(builder) = self.builder() {
|
if let Some(builder) = self.builder() {
|
||||||
let slot = builder_params.slot;
|
let slot = builder_params.slot;
|
||||||
let pubkey = builder_params.pubkey;
|
let pubkey = builder_params.pubkey;
|
||||||
|
|
||||||
match builder_params.chain_health {
|
match builder_params.chain_health {
|
||||||
ChainHealth::Healthy => {
|
ChainHealth::Healthy => {
|
||||||
info!(
|
info!(
|
||||||
@ -895,7 +957,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Wait for the builder *and* local EL to produce a payload (or return an error).
|
// Wait for the builder *and* local EL to produce a payload (or return an error).
|
||||||
let ((relay_result, relay_duration), (local_result, local_duration)) = tokio::join!(
|
let ((relay_result, relay_duration), (local_result_type, local_duration)) = tokio::join!(
|
||||||
timed_future(metrics::GET_BLINDED_PAYLOAD_BUILDER, async {
|
timed_future(metrics::GET_BLINDED_PAYLOAD_BUILDER, async {
|
||||||
builder
|
builder
|
||||||
.get_builder_header::<T>(slot, parent_hash, &pubkey)
|
.get_builder_header::<T>(slot, parent_hash, &pubkey)
|
||||||
@ -912,6 +974,11 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let local_result = match local_result_type? {
|
||||||
|
GetPayloadResponseType::Full(payload) => Ok(payload),
|
||||||
|
GetPayloadResponseType::Blinded(_) => Err(Error::PayloadTypeMismatch),
|
||||||
|
};
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
self.log(),
|
self.log(),
|
||||||
"Requested blinded execution payload";
|
"Requested blinded execution payload";
|
||||||
@ -939,7 +1006,9 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
"local_block_hash" => ?local.block_hash(),
|
"local_block_hash" => ?local.block_hash(),
|
||||||
"parent_hash" => ?parent_hash,
|
"parent_hash" => ?parent_hash,
|
||||||
);
|
);
|
||||||
Ok(ProvenancedPayload::Local(local.try_into()?))
|
Ok(ProvenancedPayload::Local(BlockProposalContentsType::Full(
|
||||||
|
local.try_into()?,
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
(Ok(None), Ok(local)) => {
|
(Ok(None), Ok(local)) => {
|
||||||
info!(
|
info!(
|
||||||
@ -949,7 +1018,9 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
"local_block_hash" => ?local.block_hash(),
|
"local_block_hash" => ?local.block_hash(),
|
||||||
"parent_hash" => ?parent_hash,
|
"parent_hash" => ?parent_hash,
|
||||||
);
|
);
|
||||||
Ok(ProvenancedPayload::Local(local.try_into()?))
|
Ok(ProvenancedPayload::Local(BlockProposalContentsType::Full(
|
||||||
|
local.try_into()?,
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
(Ok(Some(relay)), Ok(local)) => {
|
(Ok(Some(relay)), Ok(local)) => {
|
||||||
let header = &relay.data.message.header();
|
let header = &relay.data.message.header();
|
||||||
@ -973,7 +1044,9 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
"local_block_value" => %local_value,
|
"local_block_value" => %local_value,
|
||||||
"relay_value" => %relay_value
|
"relay_value" => %relay_value
|
||||||
);
|
);
|
||||||
return Ok(ProvenancedPayload::Local(local.try_into()?));
|
return Ok(ProvenancedPayload::Local(
|
||||||
|
BlockProposalContentsType::Full(local.try_into()?),
|
||||||
|
));
|
||||||
} else if local.should_override_builder().unwrap_or(false) {
|
} else if local.should_override_builder().unwrap_or(false) {
|
||||||
let percentage_difference =
|
let percentage_difference =
|
||||||
percentage_difference_u256(local_value, *relay_value);
|
percentage_difference_u256(local_value, *relay_value);
|
||||||
@ -989,7 +1062,9 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
"local_block_value" => %local_value,
|
"local_block_value" => %local_value,
|
||||||
"relay_value" => %relay_value
|
"relay_value" => %relay_value
|
||||||
);
|
);
|
||||||
return Ok(ProvenancedPayload::Local(local.try_into()?));
|
return Ok(ProvenancedPayload::Local(
|
||||||
|
BlockProposalContentsType::Full(local.try_into()?),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
info!(
|
info!(
|
||||||
@ -1020,7 +1095,9 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
"relay_block_hash" => ?header.block_hash(),
|
"relay_block_hash" => ?header.block_hash(),
|
||||||
"parent_hash" => ?parent_hash,
|
"parent_hash" => ?parent_hash,
|
||||||
);
|
);
|
||||||
Ok(ProvenancedPayload::Local(local.try_into()?))
|
Ok(ProvenancedPayload::Local(BlockProposalContentsType::Full(
|
||||||
|
local.try_into()?,
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
Err(reason) => {
|
Err(reason) => {
|
||||||
metrics::inc_counter_vec(
|
metrics::inc_counter_vec(
|
||||||
@ -1035,7 +1112,9 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
"relay_block_hash" => ?header.block_hash(),
|
"relay_block_hash" => ?header.block_hash(),
|
||||||
"parent_hash" => ?parent_hash,
|
"parent_hash" => ?parent_hash,
|
||||||
);
|
);
|
||||||
Ok(ProvenancedPayload::Local(local.try_into()?))
|
Ok(ProvenancedPayload::Local(BlockProposalContentsType::Full(
|
||||||
|
local.try_into()?,
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1132,28 +1211,10 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
current_fork,
|
current_fork,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.and_then(GetPayloadResponse::try_into)
|
.and_then(GetPayloadResponseType::try_into)
|
||||||
.map(ProvenancedPayload::Local)
|
.map(ProvenancedPayload::Local)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a full payload without caching its result in the execution layer's payload cache.
|
|
||||||
async fn get_full_payload(
|
|
||||||
&self,
|
|
||||||
parent_hash: ExecutionBlockHash,
|
|
||||||
payload_attributes: &PayloadAttributes,
|
|
||||||
forkchoice_update_params: ForkchoiceUpdateParameters,
|
|
||||||
current_fork: ForkName,
|
|
||||||
) -> Result<GetPayloadResponse<T>, Error> {
|
|
||||||
self.get_full_payload_with(
|
|
||||||
parent_hash,
|
|
||||||
payload_attributes,
|
|
||||||
forkchoice_update_params,
|
|
||||||
current_fork,
|
|
||||||
noop,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a full payload and cache its result in the execution layer's payload cache.
|
/// Get a full payload and cache its result in the execution layer's payload cache.
|
||||||
async fn get_full_payload_caching(
|
async fn get_full_payload_caching(
|
||||||
&self,
|
&self,
|
||||||
@ -1161,7 +1222,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
payload_attributes: &PayloadAttributes,
|
payload_attributes: &PayloadAttributes,
|
||||||
forkchoice_update_params: ForkchoiceUpdateParameters,
|
forkchoice_update_params: ForkchoiceUpdateParameters,
|
||||||
current_fork: ForkName,
|
current_fork: ForkName,
|
||||||
) -> Result<GetPayloadResponse<T>, Error> {
|
) -> Result<GetPayloadResponseType<T>, Error> {
|
||||||
self.get_full_payload_with(
|
self.get_full_payload_with(
|
||||||
parent_hash,
|
parent_hash,
|
||||||
payload_attributes,
|
payload_attributes,
|
||||||
@ -1182,7 +1243,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
&ExecutionLayer<T>,
|
&ExecutionLayer<T>,
|
||||||
PayloadContentsRefTuple<T>,
|
PayloadContentsRefTuple<T>,
|
||||||
) -> Option<FullPayloadContents<T>>,
|
) -> Option<FullPayloadContents<T>>,
|
||||||
) -> Result<GetPayloadResponse<T>, Error> {
|
) -> Result<GetPayloadResponseType<T>, Error> {
|
||||||
self.engine()
|
self.engine()
|
||||||
.request(move |engine| async move {
|
.request(move |engine| async move {
|
||||||
let payload_id = if let Some(id) = engine
|
let payload_id = if let Some(id) = engine
|
||||||
@ -1244,6 +1305,10 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
"timestamp" => payload_attributes.timestamp(),
|
"timestamp" => payload_attributes.timestamp(),
|
||||||
"parent_hash" => ?parent_hash,
|
"parent_hash" => ?parent_hash,
|
||||||
);
|
);
|
||||||
|
let _timer = metrics::start_timer_vec(
|
||||||
|
&metrics::EXECUTION_LAYER_REQUEST_TIMES,
|
||||||
|
&[metrics::GET_PAYLOAD],
|
||||||
|
);
|
||||||
engine.api.get_payload::<T>(current_fork, payload_id).await
|
engine.api.get_payload::<T>(current_fork, payload_id).await
|
||||||
}.await?;
|
}.await?;
|
||||||
|
|
||||||
@ -1268,7 +1333,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(payload_response)
|
Ok(GetPayloadResponseType::Full(payload_response))
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.map_err(Box::new)
|
.map_err(Box::new)
|
||||||
|
@ -508,11 +508,7 @@ pub fn serve<E: EthSpec>(
|
|||||||
finalized_hash: Some(finalized_execution_hash),
|
finalized_hash: Some(finalized_execution_hash),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (payload, _block_value, maybe_blobs_bundle): (
|
let payload_response_type = builder
|
||||||
ExecutionPayload<E>,
|
|
||||||
Uint256,
|
|
||||||
Option<BlobsBundle<E>>,
|
|
||||||
) = builder
|
|
||||||
.el
|
.el
|
||||||
.get_full_payload_caching(
|
.get_full_payload_caching(
|
||||||
head_execution_hash,
|
head_execution_hash,
|
||||||
@ -521,10 +517,17 @@ pub fn serve<E: EthSpec>(
|
|||||||
fork,
|
fork,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| reject("couldn't get payload"))?
|
.map_err(|_| reject("couldn't get payload"))?;
|
||||||
.into();
|
|
||||||
|
|
||||||
let mut message = match fork {
|
let mut message = match payload_response_type {
|
||||||
|
crate::GetPayloadResponseType::Full(payload_response) => {
|
||||||
|
let (payload, _block_value, maybe_blobs_bundle): (
|
||||||
|
ExecutionPayload<E>,
|
||||||
|
Uint256,
|
||||||
|
Option<BlobsBundle<E>>,
|
||||||
|
) = payload_response.into();
|
||||||
|
|
||||||
|
match fork {
|
||||||
ForkName::Deneb => BuilderBid::Deneb(BuilderBidDeneb {
|
ForkName::Deneb => BuilderBid::Deneb(BuilderBidDeneb {
|
||||||
header: payload
|
header: payload
|
||||||
.as_deneb()
|
.as_deneb()
|
||||||
@ -552,7 +555,50 @@ pub fn serve<E: EthSpec>(
|
|||||||
value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI),
|
value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI),
|
||||||
pubkey: builder.builder_sk.public_key().compress(),
|
pubkey: builder.builder_sk.public_key().compress(),
|
||||||
}),
|
}),
|
||||||
ForkName::Base | ForkName::Altair => return Err(reject("invalid fork")),
|
ForkName::Base | ForkName::Altair => {
|
||||||
|
return Err(reject("invalid fork"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
crate::GetPayloadResponseType::Blinded(payload_response) => {
|
||||||
|
let (payload, _block_value, maybe_blobs_bundle): (
|
||||||
|
ExecutionPayload<E>,
|
||||||
|
Uint256,
|
||||||
|
Option<BlobsBundle<E>>,
|
||||||
|
) = payload_response.into();
|
||||||
|
match fork {
|
||||||
|
ForkName::Deneb => BuilderBid::Deneb(BuilderBidDeneb {
|
||||||
|
header: payload
|
||||||
|
.as_deneb()
|
||||||
|
.map_err(|_| reject("incorrect payload variant"))?
|
||||||
|
.into(),
|
||||||
|
blinded_blobs_bundle: maybe_blobs_bundle
|
||||||
|
.map(Into::into)
|
||||||
|
.unwrap_or_default(),
|
||||||
|
value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI),
|
||||||
|
pubkey: builder.builder_sk.public_key().compress(),
|
||||||
|
}),
|
||||||
|
ForkName::Capella => BuilderBid::Capella(BuilderBidCapella {
|
||||||
|
header: payload
|
||||||
|
.as_capella()
|
||||||
|
.map_err(|_| reject("incorrect payload variant"))?
|
||||||
|
.into(),
|
||||||
|
value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI),
|
||||||
|
pubkey: builder.builder_sk.public_key().compress(),
|
||||||
|
}),
|
||||||
|
ForkName::Merge => BuilderBid::Merge(BuilderBidMerge {
|
||||||
|
header: payload
|
||||||
|
.as_merge()
|
||||||
|
.map_err(|_| reject("incorrect payload variant"))?
|
||||||
|
.into(),
|
||||||
|
value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI),
|
||||||
|
pubkey: builder.builder_sk.public_key().compress(),
|
||||||
|
}),
|
||||||
|
ForkName::Base | ForkName::Altair => {
|
||||||
|
return Err(reject("invalid fork"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
message.set_gas_limit(cached_data.gas_limit);
|
message.set_gas_limit(cached_data.gas_limit);
|
||||||
|
@ -5,12 +5,12 @@ use crate::{
|
|||||||
},
|
},
|
||||||
Config, *,
|
Config, *,
|
||||||
};
|
};
|
||||||
|
use keccak_hash::H256;
|
||||||
use kzg::Kzg;
|
use kzg::Kzg;
|
||||||
use sensitive_url::SensitiveUrl;
|
use sensitive_url::SensitiveUrl;
|
||||||
use task_executor::TaskExecutor;
|
use task_executor::TaskExecutor;
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
use tree_hash::TreeHash;
|
use types::{Address, ChainSpec, Epoch, EthSpec, Hash256, MainnetEthSpec};
|
||||||
use types::{Address, ChainSpec, Epoch, EthSpec, FullPayload, Hash256, MainnetEthSpec};
|
|
||||||
|
|
||||||
pub struct MockExecutionLayer<T: EthSpec> {
|
pub struct MockExecutionLayer<T: EthSpec> {
|
||||||
pub server: MockServer<T>,
|
pub server: MockServer<T>,
|
||||||
@ -133,20 +133,25 @@ impl<T: EthSpec> MockExecutionLayer<T> {
|
|||||||
let suggested_fee_recipient = self.el.get_suggested_fee_recipient(validator_index).await;
|
let suggested_fee_recipient = self.el.get_suggested_fee_recipient(validator_index).await;
|
||||||
let payload_attributes =
|
let payload_attributes =
|
||||||
PayloadAttributes::new(timestamp, prev_randao, suggested_fee_recipient, None, None);
|
PayloadAttributes::new(timestamp, prev_randao, suggested_fee_recipient, None, None);
|
||||||
let payload: ExecutionPayload<T> = self
|
|
||||||
|
let block_proposal_content_type = self
|
||||||
.el
|
.el
|
||||||
.get_payload::<FullPayload<T>>(
|
.get_payload(
|
||||||
parent_hash,
|
parent_hash,
|
||||||
&payload_attributes,
|
&payload_attributes,
|
||||||
forkchoice_update_params,
|
forkchoice_update_params,
|
||||||
builder_params,
|
builder_params,
|
||||||
ForkName::Merge,
|
ForkName::Merge,
|
||||||
&self.spec,
|
&self.spec,
|
||||||
|
BlockProductionVersion::FullV2,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap();
|
||||||
.to_payload()
|
|
||||||
.into();
|
let payload: ExecutionPayload<T> = match block_proposal_content_type {
|
||||||
|
BlockProposalContentsType::Full(block) => block.to_payload().into(),
|
||||||
|
BlockProposalContentsType::Blinded(_) => panic!("Should always be a full payload"),
|
||||||
|
};
|
||||||
|
|
||||||
let block_hash = payload.block_hash();
|
let block_hash = payload.block_hash();
|
||||||
assert_eq!(payload.parent_hash(), parent_hash);
|
assert_eq!(payload.parent_hash(), parent_hash);
|
||||||
@ -167,20 +172,64 @@ impl<T: EthSpec> MockExecutionLayer<T> {
|
|||||||
let suggested_fee_recipient = self.el.get_suggested_fee_recipient(validator_index).await;
|
let suggested_fee_recipient = self.el.get_suggested_fee_recipient(validator_index).await;
|
||||||
let payload_attributes =
|
let payload_attributes =
|
||||||
PayloadAttributes::new(timestamp, prev_randao, suggested_fee_recipient, None, None);
|
PayloadAttributes::new(timestamp, prev_randao, suggested_fee_recipient, None, None);
|
||||||
let payload_header = self
|
|
||||||
|
let block_proposal_content_type = self
|
||||||
.el
|
.el
|
||||||
.get_payload::<BlindedPayload<T>>(
|
.get_payload(
|
||||||
parent_hash,
|
parent_hash,
|
||||||
&payload_attributes,
|
&payload_attributes,
|
||||||
forkchoice_update_params,
|
forkchoice_update_params,
|
||||||
builder_params,
|
builder_params,
|
||||||
ForkName::Merge,
|
ForkName::Merge,
|
||||||
&self.spec,
|
&self.spec,
|
||||||
|
BlockProductionVersion::BlindedV2,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap();
|
||||||
.to_payload();
|
|
||||||
|
|
||||||
|
match block_proposal_content_type {
|
||||||
|
BlockProposalContentsType::Full(block) => {
|
||||||
|
let payload_header = block.to_payload();
|
||||||
|
self.assert_valid_execution_payload_on_head(
|
||||||
|
payload,
|
||||||
|
payload_header,
|
||||||
|
block_hash,
|
||||||
|
parent_hash,
|
||||||
|
block_number,
|
||||||
|
timestamp,
|
||||||
|
prev_randao,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
BlockProposalContentsType::Blinded(block) => {
|
||||||
|
let payload_header = block.to_payload();
|
||||||
|
self.assert_valid_execution_payload_on_head(
|
||||||
|
payload,
|
||||||
|
payload_header,
|
||||||
|
block_hash,
|
||||||
|
parent_hash,
|
||||||
|
block_number,
|
||||||
|
timestamp,
|
||||||
|
prev_randao,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub async fn assert_valid_execution_payload_on_head<Payload: AbstractExecPayload<T>>(
|
||||||
|
&self,
|
||||||
|
payload: ExecutionPayload<T>,
|
||||||
|
payload_header: Payload,
|
||||||
|
block_hash: ExecutionBlockHash,
|
||||||
|
parent_hash: ExecutionBlockHash,
|
||||||
|
block_number: u64,
|
||||||
|
timestamp: u64,
|
||||||
|
prev_randao: H256,
|
||||||
|
) {
|
||||||
assert_eq!(payload_header.block_hash(), block_hash);
|
assert_eq!(payload_header.block_hash(), block_hash);
|
||||||
assert_eq!(payload_header.parent_hash(), parent_hash);
|
assert_eq!(payload_header.parent_hash(), parent_hash);
|
||||||
assert_eq!(payload_header.block_number(), block_number);
|
assert_eq!(payload_header.block_number(), block_number);
|
||||||
@ -224,8 +273,6 @@ impl<T: EthSpec> MockExecutionLayer<T> {
|
|||||||
assert_eq!(head_execution_block.block_number(), block_number);
|
assert_eq!(head_execution_block.block_number(), block_number);
|
||||||
assert_eq!(head_execution_block.block_hash(), block_hash);
|
assert_eq!(head_execution_block.block_hash(), block_hash);
|
||||||
assert_eq!(head_execution_block.parent_hash(), parent_hash);
|
assert_eq!(head_execution_block.parent_hash(), parent_hash);
|
||||||
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_to_block_prior_to_terminal_block(self) -> Self {
|
pub fn move_to_block_prior_to_terminal_block(self) -> Self {
|
||||||
|
@ -1,46 +1,15 @@
|
|||||||
use beacon_chain::BlockProductionError;
|
use beacon_chain::BlockProductionError;
|
||||||
use eth2::types::{BeaconBlockAndBlobSidecars, BlindedBeaconBlockAndBlobSidecars, BlockContents};
|
use eth2::types::{BeaconBlockAndBlobSidecars, BlindedBeaconBlockAndBlobSidecars, BlockContents};
|
||||||
use types::{
|
use types::{AbstractExecPayload, BeaconBlock, EthSpec, ForkName, SidecarList};
|
||||||
BeaconBlock, BlindedBlobSidecarList, BlindedPayload, BlobSidecarList, EthSpec, ForkName,
|
|
||||||
FullPayload,
|
|
||||||
};
|
|
||||||
|
|
||||||
type Error = warp::reject::Rejection;
|
type Error = warp::reject::Rejection;
|
||||||
type FullBlockContents<E> = BlockContents<E, FullPayload<E>>;
|
|
||||||
type BlindedBlockContents<E> = BlockContents<E, BlindedPayload<E>>;
|
|
||||||
|
|
||||||
pub fn build_block_contents<E: EthSpec>(
|
pub fn build_block_contents<E: EthSpec, Payload: AbstractExecPayload<E>>(
|
||||||
fork_name: ForkName,
|
fork_name: ForkName,
|
||||||
block: BeaconBlock<E, FullPayload<E>>,
|
block: BeaconBlock<E, Payload>,
|
||||||
maybe_blobs: Option<BlobSidecarList<E>>,
|
maybe_blobs: Option<SidecarList<E, <Payload as AbstractExecPayload<E>>::Sidecar>>,
|
||||||
) -> Result<FullBlockContents<E>, Error> {
|
) -> Result<BlockContents<E, Payload>, Error> {
|
||||||
match fork_name {
|
match Payload::block_type() {
|
||||||
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {
|
types::BlockType::Blinded => match fork_name {
|
||||||
Ok(BlockContents::Block(block))
|
|
||||||
}
|
|
||||||
ForkName::Deneb => {
|
|
||||||
if let Some(blob_sidecars) = maybe_blobs {
|
|
||||||
let block_and_blobs = BeaconBlockAndBlobSidecars {
|
|
||||||
block,
|
|
||||||
blob_sidecars,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(BlockContents::BlockAndBlobSidecars(block_and_blobs))
|
|
||||||
} else {
|
|
||||||
Err(warp_utils::reject::block_production_error(
|
|
||||||
BlockProductionError::MissingBlobs,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build_blinded_block_contents<E: EthSpec>(
|
|
||||||
fork_name: ForkName,
|
|
||||||
block: BeaconBlock<E, BlindedPayload<E>>,
|
|
||||||
maybe_blobs: Option<BlindedBlobSidecarList<E>>,
|
|
||||||
) -> Result<BlindedBlockContents<E>, Error> {
|
|
||||||
match fork_name {
|
|
||||||
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {
|
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {
|
||||||
Ok(BlockContents::Block(block))
|
Ok(BlockContents::Block(block))
|
||||||
}
|
}
|
||||||
@ -58,5 +27,25 @@ pub fn build_blinded_block_contents<E: EthSpec>(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
types::BlockType::Full => match fork_name {
|
||||||
|
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {
|
||||||
|
Ok(BlockContents::Block(block))
|
||||||
|
}
|
||||||
|
ForkName::Deneb => {
|
||||||
|
if let Some(blob_sidecars) = maybe_blobs {
|
||||||
|
let block_and_blobs = BeaconBlockAndBlobSidecars {
|
||||||
|
block,
|
||||||
|
blob_sidecars,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(BlockContents::BlockAndBlobSidecars(block_and_blobs))
|
||||||
|
} else {
|
||||||
|
Err(warp_utils::reject::block_production_error(
|
||||||
|
BlockProductionError::MissingBlobs,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ mod build_block_contents;
|
|||||||
mod builder_states;
|
mod builder_states;
|
||||||
mod database;
|
mod database;
|
||||||
mod metrics;
|
mod metrics;
|
||||||
|
mod produce_block;
|
||||||
mod proposer_duties;
|
mod proposer_duties;
|
||||||
mod publish_blocks;
|
mod publish_blocks;
|
||||||
mod standard_block_rewards;
|
mod standard_block_rewards;
|
||||||
@ -27,10 +28,11 @@ mod validator;
|
|||||||
mod validator_inclusion;
|
mod validator_inclusion;
|
||||||
mod version;
|
mod version;
|
||||||
|
|
||||||
|
use crate::produce_block::{produce_blinded_block_v2, produce_block_v2, produce_block_v3};
|
||||||
use beacon_chain::{
|
use beacon_chain::{
|
||||||
attestation_verification::VerifiedAttestation, observed_operations::ObservationOutcome,
|
attestation_verification::VerifiedAttestation, observed_operations::ObservationOutcome,
|
||||||
validator_monitor::timestamp_now, AttestationError as AttnError, BeaconChain, BeaconChainError,
|
validator_monitor::timestamp_now, AttestationError as AttnError, BeaconChain, BeaconChainError,
|
||||||
BeaconChainTypes, ProduceBlockVerification, WhenSlotSkipped,
|
BeaconChainTypes, WhenSlotSkipped,
|
||||||
};
|
};
|
||||||
use beacon_processor::BeaconProcessorSend;
|
use beacon_processor::BeaconProcessorSend;
|
||||||
pub use block_id::BlockId;
|
pub use block_id::BlockId;
|
||||||
@ -39,8 +41,7 @@ use bytes::Bytes;
|
|||||||
use directory::DEFAULT_ROOT_DIR;
|
use directory::DEFAULT_ROOT_DIR;
|
||||||
use eth2::types::{
|
use eth2::types::{
|
||||||
self as api_types, BroadcastValidation, EndpointVersion, ForkChoice, ForkChoiceNode,
|
self as api_types, BroadcastValidation, EndpointVersion, ForkChoice, ForkChoiceNode,
|
||||||
SignedBlindedBlockContents, SignedBlockContents, SkipRandaoVerification, ValidatorId,
|
SignedBlindedBlockContents, SignedBlockContents, ValidatorId, ValidatorStatus,
|
||||||
ValidatorStatus,
|
|
||||||
};
|
};
|
||||||
use lighthouse_network::{types::SyncState, EnrExt, NetworkGlobals, PeerId, PubsubMessage};
|
use lighthouse_network::{types::SyncState, EnrExt, NetworkGlobals, PeerId, PubsubMessage};
|
||||||
use lighthouse_version::version_with_platform;
|
use lighthouse_version::version_with_platform;
|
||||||
@ -75,7 +76,7 @@ use tokio_stream::{
|
|||||||
};
|
};
|
||||||
use types::{
|
use types::{
|
||||||
Attestation, AttestationData, AttestationShufflingId, AttesterSlashing, BeaconStateError,
|
Attestation, AttestationData, AttestationShufflingId, AttesterSlashing, BeaconStateError,
|
||||||
BlindedPayload, CommitteeCache, ConfigAndPreset, Epoch, EthSpec, ForkName, FullPayload,
|
BlindedPayload, CommitteeCache, ConfigAndPreset, Epoch, EthSpec, ForkName,
|
||||||
ProposerPreparationData, ProposerSlashing, RelativeEpoch, SignedAggregateAndProof,
|
ProposerPreparationData, ProposerSlashing, RelativeEpoch, SignedAggregateAndProof,
|
||||||
SignedBlsToExecutionChange, SignedContributionAndProof, SignedValidatorRegistrationData,
|
SignedBlsToExecutionChange, SignedContributionAndProof, SignedValidatorRegistrationData,
|
||||||
SignedVoluntaryExit, Slot, SyncCommitteeMessage, SyncContributionData,
|
SignedVoluntaryExit, Slot, SyncCommitteeMessage, SyncContributionData,
|
||||||
@ -83,7 +84,7 @@ use types::{
|
|||||||
use validator::pubkey_to_validator_index;
|
use validator::pubkey_to_validator_index;
|
||||||
use version::{
|
use version::{
|
||||||
add_consensus_version_header, execution_optimistic_finalized_fork_versioned_response,
|
add_consensus_version_header, execution_optimistic_finalized_fork_versioned_response,
|
||||||
fork_versioned_response, inconsistent_fork_rejection, unsupported_version_rejection, V1, V2,
|
inconsistent_fork_rejection, unsupported_version_rejection, V1, V2, V3,
|
||||||
};
|
};
|
||||||
use warp::http::StatusCode;
|
use warp::http::StatusCode;
|
||||||
use warp::sse::Event;
|
use warp::sse::Event;
|
||||||
@ -3052,17 +3053,17 @@ pub fn serve<T: BeaconChainTypes>(
|
|||||||
))
|
))
|
||||||
}))
|
}))
|
||||||
.and(warp::path::end())
|
.and(warp::path::end())
|
||||||
|
.and(warp::header::optional::<api_types::Accept>("accept"))
|
||||||
.and(not_while_syncing_filter.clone())
|
.and(not_while_syncing_filter.clone())
|
||||||
.and(warp::query::<api_types::ValidatorBlocksQuery>())
|
.and(warp::query::<api_types::ValidatorBlocksQuery>())
|
||||||
.and(warp::header::optional::<api_types::Accept>("accept"))
|
|
||||||
.and(task_spawner_filter.clone())
|
.and(task_spawner_filter.clone())
|
||||||
.and(chain_filter.clone())
|
.and(chain_filter.clone())
|
||||||
.and(log_filter.clone())
|
.and(log_filter.clone())
|
||||||
.then(
|
.then(
|
||||||
|endpoint_version: EndpointVersion,
|
|endpoint_version: EndpointVersion,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
query: api_types::ValidatorBlocksQuery,
|
|
||||||
accept_header: Option<api_types::Accept>,
|
accept_header: Option<api_types::Accept>,
|
||||||
|
query: api_types::ValidatorBlocksQuery,
|
||||||
task_spawner: TaskSpawner<T::EthSpec>,
|
task_spawner: TaskSpawner<T::EthSpec>,
|
||||||
chain: Arc<BeaconChain<T>>,
|
chain: Arc<BeaconChain<T>>,
|
||||||
log: Logger| {
|
log: Logger| {
|
||||||
@ -3073,60 +3074,10 @@ pub fn serve<T: BeaconChainTypes>(
|
|||||||
"slot" => slot
|
"slot" => slot
|
||||||
);
|
);
|
||||||
|
|
||||||
let randao_reveal = query.randao_reveal.decompress().map_err(|e| {
|
if endpoint_version == V3 {
|
||||||
warp_utils::reject::custom_bad_request(format!(
|
produce_block_v3(endpoint_version, accept_header, chain, slot, query).await
|
||||||
"randao reveal is not a valid BLS signature: {:?}",
|
|
||||||
e
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let randao_verification =
|
|
||||||
if query.skip_randao_verification == SkipRandaoVerification::Yes {
|
|
||||||
if !randao_reveal.is_infinity() {
|
|
||||||
return Err(warp_utils::reject::custom_bad_request(
|
|
||||||
"randao_reveal must be point-at-infinity if verification is skipped"
|
|
||||||
.into(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
ProduceBlockVerification::NoVerification
|
|
||||||
} else {
|
} else {
|
||||||
ProduceBlockVerification::VerifyRandao
|
produce_block_v2(endpoint_version, accept_header, chain, slot, query).await
|
||||||
};
|
|
||||||
|
|
||||||
let (block, _, maybe_blobs) = chain
|
|
||||||
.produce_block_with_verification::<FullPayload<T::EthSpec>>(
|
|
||||||
randao_reveal,
|
|
||||||
slot,
|
|
||||||
query.graffiti.map(Into::into),
|
|
||||||
randao_verification,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(warp_utils::reject::block_production_error)?;
|
|
||||||
let fork_name = block
|
|
||||||
.to_ref()
|
|
||||||
.fork_name(&chain.spec)
|
|
||||||
.map_err(inconsistent_fork_rejection)?;
|
|
||||||
|
|
||||||
let block_contents =
|
|
||||||
build_block_contents::build_block_contents(fork_name, block, maybe_blobs)?;
|
|
||||||
|
|
||||||
match accept_header {
|
|
||||||
Some(api_types::Accept::Ssz) => Response::builder()
|
|
||||||
.status(200)
|
|
||||||
.header("Content-Type", "application/octet-stream")
|
|
||||||
.body(block_contents.as_ssz_bytes().into())
|
|
||||||
.map(|res: Response<Bytes>| {
|
|
||||||
add_consensus_version_header(res, fork_name)
|
|
||||||
})
|
|
||||||
.map_err(|e| {
|
|
||||||
warp_utils::reject::custom_server_error(format!(
|
|
||||||
"failed to create response: {}",
|
|
||||||
e
|
|
||||||
))
|
|
||||||
}),
|
|
||||||
_ => fork_versioned_response(endpoint_version, fork_name, block_contents)
|
|
||||||
.map(|response| warp::reply::json(&response).into_response())
|
|
||||||
.map(|res| add_consensus_version_header(res, fork_name)),
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -3154,65 +3105,8 @@ pub fn serve<T: BeaconChainTypes>(
|
|||||||
task_spawner: TaskSpawner<T::EthSpec>,
|
task_spawner: TaskSpawner<T::EthSpec>,
|
||||||
chain: Arc<BeaconChain<T>>| {
|
chain: Arc<BeaconChain<T>>| {
|
||||||
task_spawner.spawn_async_with_rejection(Priority::P0, async move {
|
task_spawner.spawn_async_with_rejection(Priority::P0, async move {
|
||||||
let randao_reveal = query.randao_reveal.decompress().map_err(|e| {
|
produce_blinded_block_v2(EndpointVersion(2), accept_header, chain, slot, query)
|
||||||
warp_utils::reject::custom_bad_request(format!(
|
|
||||||
"randao reveal is not a valid BLS signature: {:?}",
|
|
||||||
e
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let randao_verification =
|
|
||||||
if query.skip_randao_verification == SkipRandaoVerification::Yes {
|
|
||||||
if !randao_reveal.is_infinity() {
|
|
||||||
return Err(warp_utils::reject::custom_bad_request(
|
|
||||||
"randao_reveal must be point-at-infinity if verification is skipped"
|
|
||||||
.into()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
ProduceBlockVerification::NoVerification
|
|
||||||
} else {
|
|
||||||
ProduceBlockVerification::VerifyRandao
|
|
||||||
};
|
|
||||||
|
|
||||||
let (block, _, maybe_blobs) = chain
|
|
||||||
.produce_block_with_verification::<BlindedPayload<T::EthSpec>>(
|
|
||||||
randao_reveal,
|
|
||||||
slot,
|
|
||||||
query.graffiti.map(Into::into),
|
|
||||||
randao_verification,
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
.map_err(warp_utils::reject::block_production_error)?;
|
|
||||||
let fork_name = block
|
|
||||||
.to_ref()
|
|
||||||
.fork_name(&chain.spec)
|
|
||||||
.map_err(inconsistent_fork_rejection)?;
|
|
||||||
|
|
||||||
let block_contents = build_block_contents::build_blinded_block_contents(
|
|
||||||
fork_name,
|
|
||||||
block,
|
|
||||||
maybe_blobs,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
match accept_header {
|
|
||||||
Some(api_types::Accept::Ssz) => Response::builder()
|
|
||||||
.status(200)
|
|
||||||
.header("Content-Type", "application/octet-stream")
|
|
||||||
.body(block_contents.as_ssz_bytes().into())
|
|
||||||
.map(|res: Response<Bytes>| {
|
|
||||||
add_consensus_version_header(res, fork_name)
|
|
||||||
})
|
|
||||||
.map_err(|e| {
|
|
||||||
warp_utils::reject::custom_server_error(format!(
|
|
||||||
"failed to create response: {}",
|
|
||||||
e
|
|
||||||
))
|
|
||||||
}),
|
|
||||||
// Pose as a V2 endpoint so we return the fork `version`.
|
|
||||||
_ => fork_versioned_response(V2, fork_name, block_contents)
|
|
||||||
.map(|response| warp::reply::json(&response).into_response())
|
|
||||||
.map(|res| add_consensus_version_header(res, fork_name)),
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -3740,7 +3634,6 @@ pub fn serve<T: BeaconChainTypes>(
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.ok_or(BeaconChainError::BuilderMissing)
|
.ok_or(BeaconChainError::BuilderMissing)
|
||||||
.map_err(warp_utils::reject::beacon_chain_error)?;
|
.map_err(warp_utils::reject::beacon_chain_error)?;
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.post_builder_validators(&filtered_registration_data)
|
.post_builder_validators(&filtered_registration_data)
|
||||||
.await
|
.await
|
||||||
|
231
beacon_node/http_api/src/produce_block.rs
Normal file
231
beacon_node/http_api/src/produce_block.rs
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
use bytes::Bytes;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use types::{payload::BlockProductionVersion, *};
|
||||||
|
|
||||||
|
use beacon_chain::{
|
||||||
|
BeaconBlockResponse, BeaconBlockResponseType, BeaconChain, BeaconChainTypes,
|
||||||
|
ProduceBlockVerification,
|
||||||
|
};
|
||||||
|
use eth2::types::{self as api_types, EndpointVersion, SkipRandaoVerification};
|
||||||
|
use ssz::Encode;
|
||||||
|
use warp::{
|
||||||
|
hyper::{Body, Response},
|
||||||
|
Reply,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
build_block_contents,
|
||||||
|
version::{
|
||||||
|
add_consensus_block_value_header, add_consensus_version_header,
|
||||||
|
add_execution_payload_blinded_header, add_execution_payload_value_header,
|
||||||
|
fork_versioned_response, inconsistent_fork_rejection,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn get_randao_verification(
|
||||||
|
query: &api_types::ValidatorBlocksQuery,
|
||||||
|
randao_reveal_infinity: bool,
|
||||||
|
) -> Result<ProduceBlockVerification, warp::Rejection> {
|
||||||
|
let randao_verification = if query.skip_randao_verification == SkipRandaoVerification::Yes {
|
||||||
|
if !randao_reveal_infinity {
|
||||||
|
return Err(warp_utils::reject::custom_bad_request(
|
||||||
|
"randao_reveal must be point-at-infinity if verification is skipped".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
ProduceBlockVerification::NoVerification
|
||||||
|
} else {
|
||||||
|
ProduceBlockVerification::VerifyRandao
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(randao_verification)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn produce_block_v3<T: BeaconChainTypes>(
|
||||||
|
endpoint_version: EndpointVersion,
|
||||||
|
accept_header: Option<api_types::Accept>,
|
||||||
|
chain: Arc<BeaconChain<T>>,
|
||||||
|
slot: Slot,
|
||||||
|
query: api_types::ValidatorBlocksQuery,
|
||||||
|
) -> Result<Response<Body>, warp::Rejection> {
|
||||||
|
let randao_reveal = query.randao_reveal.decompress().map_err(|e| {
|
||||||
|
warp_utils::reject::custom_bad_request(format!(
|
||||||
|
"randao reveal is not a valid BLS signature: {:?}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let randao_verification = get_randao_verification(&query, randao_reveal.is_infinity())?;
|
||||||
|
|
||||||
|
let block_response_type = chain
|
||||||
|
.produce_block_with_verification(
|
||||||
|
randao_reveal,
|
||||||
|
slot,
|
||||||
|
query.graffiti.map(Into::into),
|
||||||
|
randao_verification,
|
||||||
|
BlockProductionVersion::V3,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
warp_utils::reject::custom_bad_request(format!("failed to fetch a block: {:?}", e))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
match block_response_type {
|
||||||
|
BeaconBlockResponseType::Full(block_response) => {
|
||||||
|
build_response_v3(chain, block_response, endpoint_version, accept_header)
|
||||||
|
}
|
||||||
|
BeaconBlockResponseType::Blinded(block_response) => {
|
||||||
|
build_response_v3(chain, block_response, endpoint_version, accept_header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_response_v3<T: BeaconChainTypes, E: EthSpec, Payload: AbstractExecPayload<E>>(
|
||||||
|
chain: Arc<BeaconChain<T>>,
|
||||||
|
block_response: BeaconBlockResponse<E, Payload>,
|
||||||
|
endpoint_version: EndpointVersion,
|
||||||
|
accept_header: Option<api_types::Accept>,
|
||||||
|
) -> Result<Response<Body>, warp::Rejection> {
|
||||||
|
let fork_name = block_response
|
||||||
|
.block
|
||||||
|
.to_ref()
|
||||||
|
.fork_name(&chain.spec)
|
||||||
|
.map_err(inconsistent_fork_rejection)?;
|
||||||
|
|
||||||
|
let block_contents = build_block_contents::build_block_contents(
|
||||||
|
fork_name,
|
||||||
|
block_response.block,
|
||||||
|
block_response.maybe_side_car,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let execution_payload_blinded = Payload::block_type() == BlockType::Blinded;
|
||||||
|
|
||||||
|
match accept_header {
|
||||||
|
Some(api_types::Accept::Ssz) => Response::builder()
|
||||||
|
.status(200)
|
||||||
|
.header("Content-Type", "application/ssz")
|
||||||
|
.body(block_contents.as_ssz_bytes().into())
|
||||||
|
.map(|res: Response<Body>| add_consensus_version_header(res, fork_name))
|
||||||
|
.map(|res| add_execution_payload_blinded_header(res, execution_payload_blinded))
|
||||||
|
.map(|res: Response<Body>| {
|
||||||
|
add_execution_payload_value_header(res, block_response.execution_payload_value)
|
||||||
|
})
|
||||||
|
.map(|res| add_consensus_block_value_header(res, block_response.consensus_block_value))
|
||||||
|
.map_err(|e| -> warp::Rejection {
|
||||||
|
warp_utils::reject::custom_server_error(format!("failed to create response: {}", e))
|
||||||
|
}),
|
||||||
|
_ => fork_versioned_response(endpoint_version, fork_name, block_contents)
|
||||||
|
.map(|response| warp::reply::json(&response).into_response())
|
||||||
|
.map(|res| add_consensus_version_header(res, fork_name))
|
||||||
|
.map(|res| add_execution_payload_blinded_header(res, execution_payload_blinded))
|
||||||
|
.map(|res| {
|
||||||
|
add_execution_payload_value_header(res, block_response.execution_payload_value)
|
||||||
|
})
|
||||||
|
.map(|res| add_consensus_block_value_header(res, block_response.consensus_block_value)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn produce_blinded_block_v2<T: BeaconChainTypes>(
|
||||||
|
endpoint_version: EndpointVersion,
|
||||||
|
accept_header: Option<api_types::Accept>,
|
||||||
|
chain: Arc<BeaconChain<T>>,
|
||||||
|
slot: Slot,
|
||||||
|
query: api_types::ValidatorBlocksQuery,
|
||||||
|
) -> Result<Response<Body>, warp::Rejection> {
|
||||||
|
let randao_reveal = query.randao_reveal.decompress().map_err(|e| {
|
||||||
|
warp_utils::reject::custom_bad_request(format!(
|
||||||
|
"randao reveal is not a valid BLS signature: {:?}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let randao_verification = get_randao_verification(&query, randao_reveal.is_infinity())?;
|
||||||
|
let block_response_type = chain
|
||||||
|
.produce_block_with_verification(
|
||||||
|
randao_reveal,
|
||||||
|
slot,
|
||||||
|
query.graffiti.map(Into::into),
|
||||||
|
randao_verification,
|
||||||
|
BlockProductionVersion::BlindedV2,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(warp_utils::reject::block_production_error)?;
|
||||||
|
|
||||||
|
match block_response_type {
|
||||||
|
BeaconBlockResponseType::Full(block_response) => {
|
||||||
|
build_response_v2(chain, block_response, endpoint_version, accept_header)
|
||||||
|
}
|
||||||
|
BeaconBlockResponseType::Blinded(block_response) => {
|
||||||
|
build_response_v2(chain, block_response, endpoint_version, accept_header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn produce_block_v2<T: BeaconChainTypes>(
|
||||||
|
endpoint_version: EndpointVersion,
|
||||||
|
accept_header: Option<api_types::Accept>,
|
||||||
|
chain: Arc<BeaconChain<T>>,
|
||||||
|
slot: Slot,
|
||||||
|
query: api_types::ValidatorBlocksQuery,
|
||||||
|
) -> Result<Response<Body>, warp::Rejection> {
|
||||||
|
let randao_reveal = query.randao_reveal.decompress().map_err(|e| {
|
||||||
|
warp_utils::reject::custom_bad_request(format!(
|
||||||
|
"randao reveal is not a valid BLS signature: {:?}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let randao_verification = get_randao_verification(&query, randao_reveal.is_infinity())?;
|
||||||
|
|
||||||
|
let block_response_type = chain
|
||||||
|
.produce_block_with_verification(
|
||||||
|
randao_reveal,
|
||||||
|
slot,
|
||||||
|
query.graffiti.map(Into::into),
|
||||||
|
randao_verification,
|
||||||
|
BlockProductionVersion::FullV2,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(warp_utils::reject::block_production_error)?;
|
||||||
|
|
||||||
|
match block_response_type {
|
||||||
|
BeaconBlockResponseType::Full(block_response) => {
|
||||||
|
build_response_v2(chain, block_response, endpoint_version, accept_header)
|
||||||
|
}
|
||||||
|
BeaconBlockResponseType::Blinded(block_response) => {
|
||||||
|
build_response_v2(chain, block_response, endpoint_version, accept_header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_response_v2<T: BeaconChainTypes, E: EthSpec, Payload: AbstractExecPayload<E>>(
|
||||||
|
chain: Arc<BeaconChain<T>>,
|
||||||
|
block_response: BeaconBlockResponse<E, Payload>,
|
||||||
|
endpoint_version: EndpointVersion,
|
||||||
|
accept_header: Option<api_types::Accept>,
|
||||||
|
) -> Result<Response<Body>, warp::Rejection> {
|
||||||
|
let fork_name = block_response
|
||||||
|
.block
|
||||||
|
.to_ref()
|
||||||
|
.fork_name(&chain.spec)
|
||||||
|
.map_err(inconsistent_fork_rejection)?;
|
||||||
|
|
||||||
|
let block_contents = build_block_contents::build_block_contents(
|
||||||
|
fork_name,
|
||||||
|
block_response.block,
|
||||||
|
block_response.maybe_side_car,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
match accept_header {
|
||||||
|
Some(api_types::Accept::Ssz) => Response::builder()
|
||||||
|
.status(200)
|
||||||
|
.header("Content-Type", "application/octet-stream")
|
||||||
|
.body(block_contents.as_ssz_bytes().into())
|
||||||
|
.map(|res: Response<Bytes>| add_consensus_version_header(res, fork_name))
|
||||||
|
.map_err(|e| {
|
||||||
|
warp_utils::reject::custom_server_error(format!("failed to create response: {}", e))
|
||||||
|
}),
|
||||||
|
_ => fork_versioned_response(endpoint_version, fork_name, block_contents)
|
||||||
|
.map(|response| warp::reply::json(&response).into_response())
|
||||||
|
.map(|res| add_consensus_version_header(res, fork_name)),
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
use beacon_chain::{BeaconChain, BeaconChainError, BeaconChainTypes};
|
use beacon_chain::{BeaconChain, BeaconChainError, BeaconChainTypes};
|
||||||
use types::*;
|
use types::{BeaconState, PublicKeyBytes};
|
||||||
|
|
||||||
/// Uses the `chain.validator_pubkey_cache` to resolve a pubkey to a validator
|
/// Uses the `chain.validator_pubkey_cache` to resolve a pubkey to a validator
|
||||||
/// index and then ensures that the validator exists in the given `state`.
|
/// index and then ensures that the validator exists in the given `state`.
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
use crate::api_types::fork_versioned_response::ExecutionOptimisticFinalizedForkVersionedResponse;
|
use crate::api_types::fork_versioned_response::ExecutionOptimisticFinalizedForkVersionedResponse;
|
||||||
use crate::api_types::EndpointVersion;
|
use crate::api_types::EndpointVersion;
|
||||||
use eth2::CONSENSUS_VERSION_HEADER;
|
use eth2::{
|
||||||
|
CONSENSUS_BLOCK_VALUE_HEADER, CONSENSUS_VERSION_HEADER, EXECUTION_PAYLOAD_BLINDED_HEADER,
|
||||||
|
EXECUTION_PAYLOAD_VALUE_HEADER,
|
||||||
|
};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use types::{ForkName, ForkVersionedResponse, InconsistentFork};
|
use types::{ForkName, ForkVersionedResponse, InconsistentFork, Uint256};
|
||||||
use warp::reply::{self, Reply, Response};
|
use warp::reply::{self, Reply, Response};
|
||||||
|
|
||||||
pub const V1: EndpointVersion = EndpointVersion(1);
|
pub const V1: EndpointVersion = EndpointVersion(1);
|
||||||
pub const V2: EndpointVersion = EndpointVersion(2);
|
pub const V2: EndpointVersion = EndpointVersion(2);
|
||||||
|
pub const V3: EndpointVersion = EndpointVersion(3);
|
||||||
|
|
||||||
pub fn fork_versioned_response<T: Serialize>(
|
pub fn fork_versioned_response<T: Serialize>(
|
||||||
endpoint_version: EndpointVersion,
|
endpoint_version: EndpointVersion,
|
||||||
@ -15,7 +19,7 @@ pub fn fork_versioned_response<T: Serialize>(
|
|||||||
) -> Result<ForkVersionedResponse<T>, warp::reject::Rejection> {
|
) -> Result<ForkVersionedResponse<T>, warp::reject::Rejection> {
|
||||||
let fork_name = if endpoint_version == V1 {
|
let fork_name = if endpoint_version == V1 {
|
||||||
None
|
None
|
||||||
} else if endpoint_version == V2 {
|
} else if endpoint_version == V2 || endpoint_version == V3 {
|
||||||
Some(fork_name)
|
Some(fork_name)
|
||||||
} else {
|
} else {
|
||||||
return Err(unsupported_version_rejection(endpoint_version));
|
return Err(unsupported_version_rejection(endpoint_version));
|
||||||
@ -53,6 +57,45 @@ pub fn add_consensus_version_header<T: Reply>(reply: T, fork_name: ForkName) ->
|
|||||||
reply::with_header(reply, CONSENSUS_VERSION_HEADER, fork_name.to_string()).into_response()
|
reply::with_header(reply, CONSENSUS_VERSION_HEADER, fork_name.to_string()).into_response()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add the `Eth-Execution-Payload-Blinded` header to a response.
|
||||||
|
pub fn add_execution_payload_blinded_header<T: Reply>(
|
||||||
|
reply: T,
|
||||||
|
execution_payload_blinded: bool,
|
||||||
|
) -> Response {
|
||||||
|
reply::with_header(
|
||||||
|
reply,
|
||||||
|
EXECUTION_PAYLOAD_BLINDED_HEADER,
|
||||||
|
execution_payload_blinded.to_string(),
|
||||||
|
)
|
||||||
|
.into_response()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add the `Eth-Execution-Payload-Value` header to a response.
|
||||||
|
pub fn add_execution_payload_value_header<T: Reply>(
|
||||||
|
reply: T,
|
||||||
|
execution_payload_value: Option<Uint256>,
|
||||||
|
) -> Response {
|
||||||
|
reply::with_header(
|
||||||
|
reply,
|
||||||
|
EXECUTION_PAYLOAD_VALUE_HEADER,
|
||||||
|
execution_payload_value.unwrap_or_default().to_string(),
|
||||||
|
)
|
||||||
|
.into_response()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add the `Eth-Consensus-Block-Value` header to a response.
|
||||||
|
pub fn add_consensus_block_value_header<T: Reply>(
|
||||||
|
reply: T,
|
||||||
|
consensus_payload_value: Option<u64>,
|
||||||
|
) -> Response {
|
||||||
|
reply::with_header(
|
||||||
|
reply,
|
||||||
|
CONSENSUS_BLOCK_VALUE_HEADER,
|
||||||
|
consensus_payload_value.unwrap_or_default().to_string(),
|
||||||
|
)
|
||||||
|
.into_response()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn inconsistent_fork_rejection(error: InconsistentFork) -> warp::reject::Rejection {
|
pub fn inconsistent_fork_rejection(error: InconsistentFork) -> warp::reject::Rejection {
|
||||||
warp_utils::reject::custom_server_error(format!("wrong fork: {:?}", error))
|
warp_utils::reject::custom_server_error(format!("wrong fork: {:?}", error))
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,8 @@ use types::{
|
|||||||
MainnetEthSpec, MinimalEthSpec, ProposerPreparationData, Slot,
|
MainnetEthSpec, MinimalEthSpec, ProposerPreparationData, Slot,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use eth2::types::ForkVersionedBeaconBlockType::{Blinded, Full};
|
||||||
|
|
||||||
type E = MainnetEthSpec;
|
type E = MainnetEthSpec;
|
||||||
|
|
||||||
// Test that the deposit_contract endpoint returns the correct chain_id and address.
|
// Test that the deposit_contract endpoint returns the correct chain_id and address.
|
||||||
@ -617,13 +619,18 @@ pub async fn proposer_boost_re_org_test(
|
|||||||
let randao_reveal = harness
|
let randao_reveal = harness
|
||||||
.sign_randao_reveal(&state_b, proposer_index, slot_c)
|
.sign_randao_reveal(&state_b, proposer_index, slot_c)
|
||||||
.into();
|
.into();
|
||||||
let unsigned_block_contents_c = tester
|
let unsigned_block_type = tester
|
||||||
.client
|
.client
|
||||||
.get_validator_blocks(slot_c, &randao_reveal, None)
|
.get_validator_blocks_v3::<E>(slot_c, &randao_reveal, None)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap();
|
||||||
.data;
|
|
||||||
let (unsigned_block_c, block_c_blobs) = unsigned_block_contents_c.deconstruct();
|
let (unsigned_block_c, block_c_blobs) = match unsigned_block_type {
|
||||||
|
Full(unsigned_block_contents_c) => unsigned_block_contents_c.data.deconstruct(),
|
||||||
|
Blinded(_) => {
|
||||||
|
panic!("Should not be a blinded block");
|
||||||
|
}
|
||||||
|
};
|
||||||
let block_c = harness.sign_beacon_block(unsigned_block_c, &state_b);
|
let block_c = harness.sign_beacon_block(unsigned_block_c, &state_b);
|
||||||
|
|
||||||
if should_re_org {
|
if should_re_org {
|
||||||
|
@ -3544,7 +3544,6 @@ impl ApiTester {
|
|||||||
.cached_head()
|
.cached_head()
|
||||||
.head_random()
|
.head_random()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let (_, randao_reveal) = self.get_test_randao(slot, epoch).await;
|
let (_, randao_reveal) = self.get_test_randao(slot, epoch).await;
|
||||||
|
|
||||||
let payload: BlindedPayload<E> = self
|
let payload: BlindedPayload<E> = self
|
||||||
@ -4586,8 +4585,7 @@ impl ApiTester {
|
|||||||
assert_eq!(withdrawal_response.finalized, Some(false));
|
assert_eq!(withdrawal_response.finalized, Some(false));
|
||||||
assert_eq!(withdrawal_response.data, expected_withdrawals.to_vec());
|
assert_eq!(withdrawal_response.data, expected_withdrawals.to_vec());
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(_) => {
|
||||||
println!("{:?}", e);
|
|
||||||
panic!("query failed incorrectly");
|
panic!("query failed incorrectly");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,8 +38,12 @@ use store::fork_versioned_response::ExecutionOptimisticFinalizedForkVersionedRes
|
|||||||
|
|
||||||
pub const V1: EndpointVersion = EndpointVersion(1);
|
pub const V1: EndpointVersion = EndpointVersion(1);
|
||||||
pub const V2: EndpointVersion = EndpointVersion(2);
|
pub const V2: EndpointVersion = EndpointVersion(2);
|
||||||
|
pub const V3: EndpointVersion = EndpointVersion(3);
|
||||||
|
|
||||||
pub const CONSENSUS_VERSION_HEADER: &str = "Eth-Consensus-Version";
|
pub const CONSENSUS_VERSION_HEADER: &str = "Eth-Consensus-Version";
|
||||||
|
pub const EXECUTION_PAYLOAD_BLINDED_HEADER: &str = "Eth-Execution-Payload-Blinded";
|
||||||
|
pub const EXECUTION_PAYLOAD_VALUE_HEADER: &str = "Eth-Execution-Payload-Value";
|
||||||
|
pub const CONSENSUS_BLOCK_VALUE_HEADER: &str = "Eth-Consensus-Block-Value";
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@ -1628,6 +1632,26 @@ impl BeaconNodeHttpClient {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `GET v2/validator/blocks/{slot}`
|
||||||
|
pub async fn get_validator_blocks_modular<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
||||||
|
&self,
|
||||||
|
slot: Slot,
|
||||||
|
randao_reveal: &SignatureBytes,
|
||||||
|
graffiti: Option<&Graffiti>,
|
||||||
|
skip_randao_verification: SkipRandaoVerification,
|
||||||
|
) -> Result<ForkVersionedResponse<BlockContents<T, Payload>>, Error> {
|
||||||
|
let path = self
|
||||||
|
.get_validator_blocks_path::<T, Payload>(
|
||||||
|
slot,
|
||||||
|
randao_reveal,
|
||||||
|
graffiti,
|
||||||
|
skip_randao_verification,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
self.get(path).await
|
||||||
|
}
|
||||||
|
|
||||||
/// returns `GET v2/validator/blocks/{slot}` URL path
|
/// returns `GET v2/validator/blocks/{slot}` URL path
|
||||||
pub async fn get_validator_blocks_path<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
pub async fn get_validator_blocks_path<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
||||||
&self,
|
&self,
|
||||||
@ -1660,24 +1684,70 @@ impl BeaconNodeHttpClient {
|
|||||||
Ok(path)
|
Ok(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `GET v2/validator/blocks/{slot}`
|
/// `GET v3/validator/blocks/{slot}`
|
||||||
pub async fn get_validator_blocks_modular<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
pub async fn get_validator_blocks_v3<T: EthSpec>(
|
||||||
|
&self,
|
||||||
|
slot: Slot,
|
||||||
|
randao_reveal: &SignatureBytes,
|
||||||
|
graffiti: Option<&Graffiti>,
|
||||||
|
) -> Result<ForkVersionedBeaconBlockType<T>, Error> {
|
||||||
|
self.get_validator_blocks_v3_modular(
|
||||||
|
slot,
|
||||||
|
randao_reveal,
|
||||||
|
graffiti,
|
||||||
|
SkipRandaoVerification::No,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `GET v3/validator/blocks/{slot}`
|
||||||
|
pub async fn get_validator_blocks_v3_modular<T: EthSpec>(
|
||||||
&self,
|
&self,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
randao_reveal: &SignatureBytes,
|
randao_reveal: &SignatureBytes,
|
||||||
graffiti: Option<&Graffiti>,
|
graffiti: Option<&Graffiti>,
|
||||||
skip_randao_verification: SkipRandaoVerification,
|
skip_randao_verification: SkipRandaoVerification,
|
||||||
) -> Result<ForkVersionedResponse<BlockContents<T, Payload>>, Error> {
|
) -> Result<ForkVersionedBeaconBlockType<T>, Error> {
|
||||||
let path = self
|
let mut path = self.eth_path(V3)?;
|
||||||
.get_validator_blocks_path::<T, Payload>(
|
|
||||||
slot,
|
|
||||||
randao_reveal,
|
|
||||||
graffiti,
|
|
||||||
skip_randao_verification,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
self.get(path).await
|
path.path_segments_mut()
|
||||||
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||||
|
.push("validator")
|
||||||
|
.push("blocks")
|
||||||
|
.push(&slot.to_string());
|
||||||
|
|
||||||
|
path.query_pairs_mut()
|
||||||
|
.append_pair("randao_reveal", &randao_reveal.to_string());
|
||||||
|
|
||||||
|
if let Some(graffiti) = graffiti {
|
||||||
|
path.query_pairs_mut()
|
||||||
|
.append_pair("graffiti", &graffiti.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
if skip_randao_verification == SkipRandaoVerification::Yes {
|
||||||
|
path.query_pairs_mut()
|
||||||
|
.append_pair("skip_randao_verification", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = self.get_response(path, |b| b).await?;
|
||||||
|
|
||||||
|
let is_blinded_payload = response
|
||||||
|
.headers()
|
||||||
|
.get(EXECUTION_PAYLOAD_BLINDED_HEADER)
|
||||||
|
.map(|value| value.to_str().unwrap_or_default().to_lowercase() == "true")
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
if is_blinded_payload {
|
||||||
|
let blinded_payload = response
|
||||||
|
.json::<ForkVersionedResponse<BlockContents<T, BlindedPayload<T>>>>()
|
||||||
|
.await?;
|
||||||
|
Ok(ForkVersionedBeaconBlockType::Blinded(blinded_payload))
|
||||||
|
} else {
|
||||||
|
let full_payload = response
|
||||||
|
.json::<ForkVersionedResponse<BlockContents<T, FullPayload<T>>>>()
|
||||||
|
.await?;
|
||||||
|
Ok(ForkVersionedBeaconBlockType::Full(full_payload))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `GET v2/validator/blocks/{slot}` in ssz format
|
/// `GET v2/validator/blocks/{slot}` in ssz format
|
||||||
|
@ -1384,6 +1384,11 @@ pub mod serde_status_code {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum ForkVersionedBeaconBlockType<T: EthSpec> {
|
||||||
|
Full(ForkVersionedResponse<BlockContents<T, FullPayload<T>>>),
|
||||||
|
Blinded(ForkVersionedResponse<BlockContents<T, BlindedPayload<T>>>),
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -979,3 +979,10 @@ impl<T: EthSpec> From<BlindedPayload<T>> for ExecutionPayloadHeader<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The block production flow version to be used.
|
||||||
|
pub enum BlockProductionVersion {
|
||||||
|
V3,
|
||||||
|
BlindedV2,
|
||||||
|
FullV2,
|
||||||
|
}
|
||||||
|
@ -4,7 +4,8 @@ use crate::execution_engine::{
|
|||||||
use crate::transactions::transactions;
|
use crate::transactions::transactions;
|
||||||
use ethers_providers::Middleware;
|
use ethers_providers::Middleware;
|
||||||
use execution_layer::{
|
use execution_layer::{
|
||||||
BuilderParams, ChainHealth, ExecutionLayer, PayloadAttributes, PayloadStatus,
|
BlockProposalContentsType, BuilderParams, ChainHealth, ExecutionLayer, PayloadAttributes,
|
||||||
|
PayloadStatus,
|
||||||
};
|
};
|
||||||
use fork_choice::ForkchoiceUpdateParameters;
|
use fork_choice::ForkchoiceUpdateParameters;
|
||||||
use reqwest::{header::CONTENT_TYPE, Client};
|
use reqwest::{header::CONTENT_TYPE, Client};
|
||||||
@ -14,9 +15,10 @@ use std::sync::Arc;
|
|||||||
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
|
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
|
||||||
use task_executor::TaskExecutor;
|
use task_executor::TaskExecutor;
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
use types::payload::BlockProductionVersion;
|
||||||
use types::{
|
use types::{
|
||||||
Address, ChainSpec, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadHeader,
|
Address, ChainSpec, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadHeader,
|
||||||
ForkName, FullPayload, Hash256, MainnetEthSpec, PublicKeyBytes, Slot, Uint256,
|
ForkName, Hash256, MainnetEthSpec, PublicKeyBytes, Slot, Uint256,
|
||||||
};
|
};
|
||||||
const EXECUTION_ENGINE_START_TIMEOUT: Duration = Duration::from_secs(60);
|
const EXECUTION_ENGINE_START_TIMEOUT: Duration = Duration::from_secs(60);
|
||||||
|
|
||||||
@ -322,21 +324,26 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
|||||||
Some(vec![]),
|
Some(vec![]),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
let valid_payload = self
|
let block_proposal_content_type = self
|
||||||
.ee_a
|
.ee_a
|
||||||
.execution_layer
|
.execution_layer
|
||||||
.get_payload::<FullPayload<MainnetEthSpec>>(
|
.get_payload(
|
||||||
parent_hash,
|
parent_hash,
|
||||||
&payload_attributes,
|
&payload_attributes,
|
||||||
forkchoice_update_params,
|
forkchoice_update_params,
|
||||||
builder_params,
|
builder_params,
|
||||||
TEST_FORK,
|
TEST_FORK,
|
||||||
&self.spec,
|
&self.spec,
|
||||||
|
BlockProductionVersion::FullV2,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap();
|
||||||
.to_payload()
|
|
||||||
.execution_payload();
|
let valid_payload = match block_proposal_content_type {
|
||||||
|
BlockProposalContentsType::Full(block) => block.to_payload().execution_payload(),
|
||||||
|
BlockProposalContentsType::Blinded(_) => panic!("Should always be a full payload"),
|
||||||
|
};
|
||||||
|
|
||||||
assert_eq!(valid_payload.transactions().len(), pending_txs.len());
|
assert_eq!(valid_payload.transactions().len(), pending_txs.len());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -468,21 +475,25 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
|||||||
Some(vec![]),
|
Some(vec![]),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
let second_payload = self
|
let block_proposal_content_type = self
|
||||||
.ee_a
|
.ee_a
|
||||||
.execution_layer
|
.execution_layer
|
||||||
.get_payload::<FullPayload<MainnetEthSpec>>(
|
.get_payload(
|
||||||
parent_hash,
|
parent_hash,
|
||||||
&payload_attributes,
|
&payload_attributes,
|
||||||
forkchoice_update_params,
|
forkchoice_update_params,
|
||||||
builder_params,
|
builder_params,
|
||||||
TEST_FORK,
|
TEST_FORK,
|
||||||
&self.spec,
|
&self.spec,
|
||||||
|
BlockProductionVersion::FullV2,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap();
|
||||||
.to_payload()
|
|
||||||
.execution_payload();
|
let second_payload = match block_proposal_content_type {
|
||||||
|
BlockProposalContentsType::Full(block) => block.to_payload().execution_payload(),
|
||||||
|
BlockProposalContentsType::Blinded(_) => panic!("Should always be a full payload"),
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Execution Engine A:
|
* Execution Engine A:
|
||||||
|
Loading…
Reference in New Issue
Block a user