mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-15 10:52:43 +00:00
Merge branch 'remove-into-gossip-verified-block' of https://github.com/realbigsean/lighthouse into merge-unstable-deneb-june-6th
This commit is contained in:
@@ -49,7 +49,7 @@ pub fn get_block_rewards<T: BeaconChainTypes>(
|
||||
.map_err(beacon_chain_error)?;
|
||||
|
||||
state
|
||||
.build_all_caches(&chain.spec)
|
||||
.build_caches(&chain.spec)
|
||||
.map_err(beacon_state_error)?;
|
||||
|
||||
let mut reward_cache = Default::default();
|
||||
|
||||
@@ -32,7 +32,7 @@ use beacon_chain::{
|
||||
pub use block_id::BlockId;
|
||||
use directory::DEFAULT_ROOT_DIR;
|
||||
use eth2::types::{
|
||||
self as api_types, EndpointVersion, ForkChoice, ForkChoiceNode, SignedBlockContents,
|
||||
self as api_types, BroadcastValidation, EndpointVersion, ForkChoice, ForkChoiceNode, SignedBlockContents,
|
||||
SkipRandaoVerification, ValidatorId, ValidatorStatus,
|
||||
};
|
||||
use lighthouse_network::{types::SyncState, EnrExt, NetworkGlobals, PeerId, PubsubMessage};
|
||||
@@ -41,7 +41,9 @@ use logging::SSELoggingComponents;
|
||||
use network::{NetworkMessage, NetworkSenders, ValidatorSubscriptionMessage};
|
||||
use operation_pool::ReceivedPreCapella;
|
||||
use parking_lot::RwLock;
|
||||
use publish_blocks::ProvenancedBlock;
|
||||
pub use publish_blocks::{
|
||||
publish_blinded_block, publish_block, reconstruct_block, ProvenancedBlock,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use slog::{crit, debug, error, info, warn, Logger};
|
||||
use slot_clock::SlotClock;
|
||||
@@ -325,6 +327,7 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
};
|
||||
|
||||
let eth_v1 = single_version(V1);
|
||||
let eth_v2 = single_version(V2);
|
||||
|
||||
// Create a `warp` filter that provides access to the network globals.
|
||||
let inner_network_globals = ctx.network_globals.clone();
|
||||
@@ -1223,16 +1226,59 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
log: Logger| async move {
|
||||
publish_blocks::publish_block(
|
||||
None,
|
||||
ProvenancedBlock::Local(block_contents),
|
||||
ProvenancedBlock::local(block_contents),
|
||||
chain,
|
||||
&network_tx,
|
||||
log,
|
||||
BroadcastValidation::default(),
|
||||
)
|
||||
.await
|
||||
.map(|()| warp::reply().into_response())
|
||||
},
|
||||
);
|
||||
|
||||
let post_beacon_blocks_v2 = eth_v2
|
||||
.and(warp::path("beacon"))
|
||||
.and(warp::path("blocks"))
|
||||
.and(warp::query::<api_types::BroadcastValidationQuery>())
|
||||
.and(warp::path::end())
|
||||
.and(warp::body::json())
|
||||
.and(chain_filter.clone())
|
||||
.and(network_tx_filter.clone())
|
||||
.and(log_filter.clone())
|
||||
.then(
|
||||
|validation_level: api_types::BroadcastValidationQuery,
|
||||
block_contents: SignedBlockContents<T::EthSpec>,
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
log: Logger| async move {
|
||||
match publish_blocks::publish_block(
|
||||
None,
|
||||
ProvenancedBlock::local(block_contents),
|
||||
chain,
|
||||
&network_tx,
|
||||
log,
|
||||
validation_level.broadcast_validation,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(()) => warp::reply().into_response(),
|
||||
Err(e) => match warp_utils::reject::handle_rejection(e).await {
|
||||
Ok(reply) => reply.into_response(),
|
||||
Err(_) => warp::reply::with_status(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
eth2::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
)
|
||||
.into_response(),
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/*
|
||||
* beacon/blocks
|
||||
*/
|
||||
|
||||
// POST beacon/blinded_blocks
|
||||
let post_beacon_blinded_blocks = eth_v1
|
||||
.and(warp::path("beacon"))
|
||||
@@ -1247,9 +1293,52 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
log: Logger| async move {
|
||||
publish_blocks::publish_blinded_block(block, chain, &network_tx, log)
|
||||
.await
|
||||
.map(|()| warp::reply().into_response())
|
||||
publish_blocks::publish_blinded_block(
|
||||
block,
|
||||
chain,
|
||||
&network_tx,
|
||||
log,
|
||||
BroadcastValidation::default(),
|
||||
)
|
||||
.await
|
||||
.map(|()| warp::reply().into_response())
|
||||
},
|
||||
);
|
||||
|
||||
let post_beacon_blinded_blocks_v2 = eth_v2
|
||||
.and(warp::path("beacon"))
|
||||
.and(warp::path("blinded_blocks"))
|
||||
.and(warp::query::<api_types::BroadcastValidationQuery>())
|
||||
.and(warp::path::end())
|
||||
.and(warp::body::json())
|
||||
.and(chain_filter.clone())
|
||||
.and(network_tx_filter.clone())
|
||||
.and(log_filter.clone())
|
||||
.then(
|
||||
|validation_level: api_types::BroadcastValidationQuery,
|
||||
block_contents: SignedBlockContents<T::EthSpec, BlindedPayload<_>>,
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
log: Logger| async move {
|
||||
match publish_blocks::publish_blinded_block(
|
||||
block_contents,
|
||||
chain,
|
||||
&network_tx,
|
||||
log,
|
||||
validation_level.broadcast_validation,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(()) => warp::reply().into_response(),
|
||||
Err(e) => match warp_utils::reject::handle_rejection(e).await {
|
||||
Ok(reply) => reply.into_response(),
|
||||
Err(_) => warp::reply::with_status(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
eth2::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
)
|
||||
.into_response(),
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -2369,24 +2458,41 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.and(warp::path("health"))
|
||||
.and(warp::path::end())
|
||||
.and(network_globals.clone())
|
||||
.and_then(|network_globals: Arc<NetworkGlobals<T::EthSpec>>| {
|
||||
blocking_response_task(move || match *network_globals.sync_state.read() {
|
||||
SyncState::SyncingFinalized { .. }
|
||||
| SyncState::SyncingHead { .. }
|
||||
| SyncState::SyncTransition
|
||||
| SyncState::BackFillSyncing { .. } => Ok(warp::reply::with_status(
|
||||
warp::reply(),
|
||||
warp::http::StatusCode::PARTIAL_CONTENT,
|
||||
)),
|
||||
SyncState::Synced => Ok(warp::reply::with_status(
|
||||
warp::reply(),
|
||||
warp::http::StatusCode::OK,
|
||||
)),
|
||||
SyncState::Stalled => Err(warp_utils::reject::not_synced(
|
||||
"sync stalled, beacon chain may not yet be initialized.".to_string(),
|
||||
)),
|
||||
})
|
||||
});
|
||||
.and(chain_filter.clone())
|
||||
.and_then(
|
||||
|network_globals: Arc<NetworkGlobals<T::EthSpec>>, chain: Arc<BeaconChain<T>>| {
|
||||
async move {
|
||||
let el_offline = if let Some(el) = &chain.execution_layer {
|
||||
el.is_offline_or_erroring().await
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
blocking_response_task(move || {
|
||||
let is_optimistic = chain
|
||||
.is_optimistic_or_invalid_head()
|
||||
.map_err(warp_utils::reject::beacon_chain_error)?;
|
||||
|
||||
let is_syncing = !network_globals.sync_state.read().is_synced();
|
||||
|
||||
if el_offline {
|
||||
Err(warp_utils::reject::not_synced("execution layer is offline".to_string()))
|
||||
} else if is_syncing || is_optimistic {
|
||||
Ok(warp::reply::with_status(
|
||||
warp::reply(),
|
||||
warp::http::StatusCode::PARTIAL_CONTENT,
|
||||
))
|
||||
} else {
|
||||
Ok(warp::reply::with_status(
|
||||
warp::reply(),
|
||||
warp::http::StatusCode::OK,
|
||||
))
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// GET node/peers/{peer_id}
|
||||
let get_node_peers_by_id = eth_v1
|
||||
@@ -3866,6 +3972,8 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
warp::post().and(
|
||||
post_beacon_blocks
|
||||
.uor(post_beacon_blinded_blocks)
|
||||
.uor(post_beacon_blocks_v2)
|
||||
.uor(post_beacon_blinded_blocks_v2)
|
||||
.uor(post_beacon_pool_attestations)
|
||||
.uor(post_beacon_pool_attester_slashings)
|
||||
.uor(post_beacon_pool_proposer_slashings)
|
||||
|
||||
@@ -2,9 +2,12 @@ use crate::metrics;
|
||||
|
||||
use beacon_chain::blob_verification::{AsBlock, BlockWrapper};
|
||||
use beacon_chain::validator_monitor::{get_block_delay_ms, timestamp_now};
|
||||
use beacon_chain::{AvailabilityProcessingStatus, NotifyExecutionLayer};
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes, BlockError};
|
||||
use beacon_chain::{
|
||||
BeaconChain, BeaconChainError, BeaconChainTypes, BlockError, GossipVerifiedBlock,
|
||||
NotifyExecutionLayer, AvailabilityProcessingStatus
|
||||
};
|
||||
use eth2::types::SignedBlockContents;
|
||||
use eth2::types::BroadcastValidation;
|
||||
use execution_layer::ProvenancedPayload;
|
||||
use lighthouse_network::PubsubMessage;
|
||||
use network::NetworkMessage;
|
||||
@@ -29,6 +32,16 @@ pub enum ProvenancedBlock<T: EthSpec> {
|
||||
Builder(SignedBlockContents<T, FullPayload<T>>),
|
||||
}
|
||||
|
||||
impl<T: EthSpec> ProvenancedBlock<T> {
|
||||
pub fn local(block: Arc<SignedBeaconBlock<T>>) -> Self {
|
||||
Self::Local(block)
|
||||
}
|
||||
|
||||
pub fn builder(block: Arc<SignedBeaconBlock<T>>) -> Self {
|
||||
Self::Builder(block)
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles a request from the HTTP API for full blocks.
|
||||
pub async fn publish_block<T: BeaconChainTypes>(
|
||||
block_root: Option<Hash256>,
|
||||
@@ -36,6 +49,7 @@ pub async fn publish_block<T: BeaconChainTypes>(
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
network_tx: &UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
log: Logger,
|
||||
validation_level: BroadcastValidation,
|
||||
) -> Result<(), Rejection> {
|
||||
let seen_timestamp = timestamp_now();
|
||||
let (block, maybe_blobs, is_locally_built_block) = match provenanced_block {
|
||||
@@ -48,17 +62,24 @@ pub async fn publish_block<T: BeaconChainTypes>(
|
||||
(Arc::new(block), maybe_blobs, false)
|
||||
}
|
||||
};
|
||||
let delay = get_block_delay_ms(seen_timestamp, block.message(), &chain.slot_clock);
|
||||
|
||||
//FIXME(sean) have to move this to prior to publishing because it's included in the blobs sidecar message.
|
||||
let beacon_block = block.clone();
|
||||
let delay = get_block_delay_ms(seen_timestamp, beacon_block.message(), &chain.slot_clock);
|
||||
//FIXME(sean) have to move this to prior to publishing because it's included in the blobs sidecar message.
|
||||
//this may skew metrics
|
||||
let block_root = block_root.unwrap_or_else(|| block.canonical_root());
|
||||
debug!(
|
||||
log,
|
||||
"Signed block published to HTTP API";
|
||||
"slot" => block.slot()
|
||||
);
|
||||
debug!(log, "Signed block received in HTTP API"; "slot" => beacon_block.slot());
|
||||
|
||||
/* actually publish a block */
|
||||
let publish_block = move |block: Arc<SignedBeaconBlock<T::EthSpec>>,
|
||||
sender,
|
||||
log,
|
||||
seen_timestamp| {
|
||||
let publish_timestamp = timestamp_now();
|
||||
let publish_delay = publish_timestamp
|
||||
.checked_sub(seen_timestamp)
|
||||
.unwrap_or_else(|| Duration::from_secs(0));
|
||||
|
||||
info!(log, "Signed block published to network via HTTP API"; "slot" => block.slot(), "publish_delay" => ?publish_delay);
|
||||
// Send the block, regardless of whether or not it is valid. The API
|
||||
// specification is very clear that this is the desired behaviour.
|
||||
let wrapped_block: BlockWrapper<T::EthSpec> = match block.as_ref() {
|
||||
@@ -88,13 +109,70 @@ pub async fn publish_block<T: BeaconChainTypes>(
|
||||
}
|
||||
}
|
||||
};
|
||||
// Determine the delay after the start of the slot, register it with metrics.
|
||||
let message = PubsubMessage::BeaconBlock(block);
|
||||
crate::publish_pubsub_message(&sender, message)
|
||||
.map_err(|_| BeaconChainError::UnableToPublish.into())
|
||||
};
|
||||
|
||||
/* if we can form a `GossipVerifiedBlock`, we've passed our basic gossip checks */
|
||||
let gossip_verified_block = GossipVerifiedBlock::new(block, &chain).map_err(|e| {
|
||||
warn!(log, "Not publishing block, not gossip verified"; "slot" => beacon_block.slot(), "error" => ?e);
|
||||
warp_utils::reject::custom_bad_request(e.to_string())
|
||||
})?;
|
||||
|
||||
let block_root = block_root.unwrap_or(gossip_verified_block.block_root);
|
||||
|
||||
if let BroadcastValidation::Gossip = validation_level {
|
||||
publish_block(
|
||||
beacon_block.clone(),
|
||||
network_tx.clone(),
|
||||
log.clone(),
|
||||
seen_timestamp,
|
||||
)
|
||||
.map_err(|_| warp_utils::reject::custom_server_error("unable to publish".into()))?;
|
||||
}
|
||||
|
||||
/* only publish if gossip- and consensus-valid and equivocation-free */
|
||||
let chain_clone = chain.clone();
|
||||
let block_clone = beacon_block.clone();
|
||||
let log_clone = log.clone();
|
||||
let sender_clone = network_tx.clone();
|
||||
|
||||
let publish_fn = move || match validation_level {
|
||||
BroadcastValidation::Gossip => Ok(()),
|
||||
BroadcastValidation::Consensus => {
|
||||
publish_block(block_clone, sender_clone, log_clone, seen_timestamp)
|
||||
}
|
||||
BroadcastValidation::ConsensusAndEquivocation => {
|
||||
if chain_clone
|
||||
.observed_block_producers
|
||||
.read()
|
||||
.proposer_has_been_observed(block_clone.message(), block_root)
|
||||
.map_err(|e| BlockError::BeaconChainError(e.into()))?
|
||||
.is_slashable()
|
||||
{
|
||||
warn!(
|
||||
log_clone,
|
||||
"Not publishing equivocating block";
|
||||
"slot" => block_clone.slot()
|
||||
);
|
||||
Err(BlockError::Slashable)
|
||||
} else {
|
||||
publish_block(block_clone, sender_clone, log_clone, seen_timestamp)
|
||||
}
|
||||
}
|
||||
};
|
||||
let block_clone = wrapped_block.block_cloned();
|
||||
let slot = block_clone.message().slot();
|
||||
let proposer_index = block_clone.message().proposer_index();
|
||||
|
||||
match chain
|
||||
.process_block(block_root, wrapped_block, NotifyExecutionLayer::Yes)
|
||||
.process_block(
|
||||
block_root,
|
||||
gossip_verified_block,
|
||||
NotifyExecutionLayer::Yes,
|
||||
publish_fn,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(AvailabilityProcessingStatus::Imported(root)) => {
|
||||
@@ -144,35 +222,32 @@ pub async fn publish_block<T: BeaconChainTypes>(
|
||||
);
|
||||
Err(warp_utils::reject::broadcast_without_import(msg))
|
||||
}
|
||||
Err(BlockError::BlockIsAlreadyKnown) => {
|
||||
info!(
|
||||
log,
|
||||
"Block from HTTP API already known";
|
||||
"block" => ?block_root,
|
||||
"slot" => slot,
|
||||
);
|
||||
Ok(())
|
||||
Err(BlockError::BeaconChainError(BeaconChainError::UnableToPublish)) => {
|
||||
Err(warp_utils::reject::custom_server_error(
|
||||
"unable to publish to network channel".to_string(),
|
||||
))
|
||||
}
|
||||
Err(BlockError::RepeatProposal { proposer, slot }) => {
|
||||
warn!(
|
||||
log,
|
||||
"Block ignored due to repeat proposal";
|
||||
"msg" => "this can happen when a VC uses fallback BNs. \
|
||||
whilst this is not necessarily an error, it can indicate issues with a BN \
|
||||
or between the VC and BN.",
|
||||
"slot" => slot,
|
||||
"proposer" => proposer,
|
||||
);
|
||||
Err(BlockError::Slashable) => Err(warp_utils::reject::custom_bad_request(
|
||||
"proposal for this slot and proposer has already been seen".to_string(),
|
||||
)),
|
||||
Err(BlockError::BlockIsAlreadyKnown) => {
|
||||
info!(log, "Block from HTTP API already known"; "block" => ?block_root);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
let msg = format!("{:?}", e);
|
||||
error!(
|
||||
log,
|
||||
"Invalid block provided to HTTP API";
|
||||
"reason" => &msg
|
||||
);
|
||||
Err(warp_utils::reject::broadcast_without_import(msg))
|
||||
if let BroadcastValidation::Gossip = validation_level {
|
||||
Err(warp_utils::reject::broadcast_without_import(format!("{e}")))
|
||||
} else {
|
||||
let msg = format!("{:?}", e);
|
||||
error!(
|
||||
log,
|
||||
"Invalid block provided to HTTP API";
|
||||
"reason" => &msg
|
||||
);
|
||||
Err(warp_utils::reject::custom_bad_request(format!(
|
||||
"Invalid block: {e}"
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,21 +259,31 @@ pub async fn publish_blinded_block<T: BeaconChainTypes>(
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
network_tx: &UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
log: Logger,
|
||||
validation_level: BroadcastValidation,
|
||||
) -> Result<(), Rejection> {
|
||||
let block_root = block.canonical_root();
|
||||
let full_block = reconstruct_block(chain.clone(), block_root, block, log.clone()).await?;
|
||||
publish_block::<T>(Some(block_root), full_block, chain, network_tx, log).await
|
||||
let full_block: ProvenancedBlock<T> =
|
||||
reconstruct_block(chain.clone(), block_root, block, log.clone()).await?;
|
||||
publish_block::<T>(
|
||||
Some(block_root),
|
||||
full_block,
|
||||
chain,
|
||||
network_tx,
|
||||
log,
|
||||
validation_level,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Deconstruct the given blinded block, and construct a full block. This attempts to use the
|
||||
/// execution layer's payload cache, and if that misses, attempts a blind block proposal to retrieve
|
||||
/// the full payload.
|
||||
async fn reconstruct_block<T: BeaconChainTypes>(
|
||||
pub async fn reconstruct_block<T: BeaconChainTypes>(
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
block_root: Hash256,
|
||||
block: SignedBeaconBlock<T::EthSpec, BlindedPayload<T::EthSpec>>,
|
||||
log: Logger,
|
||||
) -> Result<ProvenancedBlock<T::EthSpec>, Rejection> {
|
||||
) -> Result<ProvenancedBlock<T>, Rejection> {
|
||||
let full_payload_opt = if let Ok(payload_header) = block.message().body().execution_payload() {
|
||||
let el = chain.execution_layer.as_ref().ok_or_else(|| {
|
||||
warp_utils::reject::custom_server_error("Missing execution layer".to_string())
|
||||
@@ -264,15 +349,15 @@ async fn reconstruct_block<T: BeaconChainTypes>(
|
||||
None => block
|
||||
.try_into_full_block(None)
|
||||
.map(SignedBlockContents::Block)
|
||||
.map(ProvenancedBlock::Local),
|
||||
.map(ProvenancedBlock::local),
|
||||
Some(ProvenancedPayload::Local(full_payload)) => block
|
||||
.try_into_full_block(Some(full_payload))
|
||||
.map(SignedBlockContents::Block)
|
||||
.map(ProvenancedBlock::Local),
|
||||
.map(ProvenancedBlock::local),
|
||||
Some(ProvenancedPayload::Builder(full_payload)) => block
|
||||
.try_into_full_block(Some(full_payload))
|
||||
.map(SignedBlockContents::Block)
|
||||
.map(ProvenancedBlock::Builder),
|
||||
.map(ProvenancedBlock::builder),
|
||||
}
|
||||
.ok_or_else(|| {
|
||||
warp_utils::reject::custom_server_error("Unable to add payload to block".to_string())
|
||||
|
||||
Reference in New Issue
Block a user