mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-16 19:32:55 +00:00
Add broadcast validation routes to Beacon Node HTTP API (#4316)
## Issue Addressed - #4293 - #4264 ## Proposed Changes *Changes largely follow those suggested in the main issue*. - Add new routes to HTTP API - `post_beacon_blocks_v2` - `post_blinded_beacon_blocks_v2` - Add new routes to `BeaconNodeHttpClient` - `post_beacon_blocks_v2` - `post_blinded_beacon_blocks_v2` - Define new Eth2 common types - `BroadcastValidation`, enum representing the level of validation to apply to blocks prior to broadcast - `BroadcastValidationQuery`, the corresponding HTTP query string type for the above type - ~~Define `_checked` variants of both `publish_block` and `publish_blinded_block` that enforce a validation level at a type level~~ - Add interactive tests to the `bn_http_api_tests` test target covering each validation level (to their own test module, `broadcast_validation_tests`) - `beacon/blocks` - `broadcast_validation=gossip` - Invalid (400) - Full Pass (200) - Partial Pass (202) - `broadcast_validation=consensus` - Invalid (400) - Only gossip (400) - Only consensus pass (i.e., equivocates) (200) - Full pass (200) - `broadcast_validation=consensus_and_equivocation` - Invalid (400) - Invalid due to early equivocation (400) - Only gossip (400) - Only consensus (400) - Pass (200) - `beacon/blinded_blocks` - `broadcast_validation=gossip` - Invalid (400) - Full Pass (200) - Partial Pass (202) - `broadcast_validation=consensus` - Invalid (400) - Only gossip (400) - ~~Only consensus pass (i.e., equivocates) (200)~~ - Full pass (200) - `broadcast_validation=consensus_and_equivocation` - Invalid (400) - Invalid due to early equivocation (400) - Only gossip (400) - Only consensus (400) - Pass (200) - Add a new trait, `IntoGossipVerifiedBlock`, which allows type-level guarantees to be made as to gossip validity - Modify the structure of the `ObservedBlockProducers` cache from a `(slot, validator_index)` mapping to a `((slot, validator_index), block_root)` mapping - Modify `ObservedBlockProducers::proposer_has_been_observed` to return a `SeenBlock` rather than a boolean on success - Punish gossip peer (low) for submitting equivocating blocks - Rename `BlockError::SlashablePublish` to `BlockError::SlashableProposal` ## Additional Info This PR contains changes that directly modify how blocks are verified within the client. For more context, consult [comments in-thread](https://github.com/sigp/lighthouse/pull/4316#discussion_r1234724202). Co-authored-by: Michael Sproul <michael@sigmaprime.io>
This commit is contained in:
@@ -322,6 +322,26 @@ impl BeaconNodeHttpClient {
|
||||
ok_or_error(response).await
|
||||
}
|
||||
|
||||
/// Generic POST function supporting arbitrary responses and timeouts.
|
||||
async fn post_generic_with_consensus_version<T: Serialize, U: IntoUrl>(
|
||||
&self,
|
||||
url: U,
|
||||
body: &T,
|
||||
timeout: Option<Duration>,
|
||||
fork: ForkName,
|
||||
) -> Result<Response, Error> {
|
||||
let mut builder = self.client.post(url);
|
||||
if let Some(timeout) = timeout {
|
||||
builder = builder.timeout(timeout);
|
||||
}
|
||||
let response = builder
|
||||
.header(CONSENSUS_VERSION_HEADER, fork.to_string())
|
||||
.json(body)
|
||||
.send()
|
||||
.await?;
|
||||
ok_or_error(response).await
|
||||
}
|
||||
|
||||
/// `GET beacon/genesis`
|
||||
///
|
||||
/// ## Errors
|
||||
@@ -654,6 +674,76 @@ impl BeaconNodeHttpClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn post_beacon_blocks_v2_path(
|
||||
&self,
|
||||
validation_level: Option<BroadcastValidation>,
|
||||
) -> Result<Url, Error> {
|
||||
let mut path = self.eth_path(V2)?;
|
||||
path.path_segments_mut()
|
||||
.map_err(|_| Error::InvalidUrl(self.server.clone()))?
|
||||
.extend(&["beacon", "blocks"]);
|
||||
|
||||
path.set_query(
|
||||
validation_level
|
||||
.map(|v| format!("broadcast_validation={}", v))
|
||||
.as_deref(),
|
||||
);
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
pub fn post_beacon_blinded_blocks_v2_path(
|
||||
&self,
|
||||
validation_level: Option<BroadcastValidation>,
|
||||
) -> Result<Url, Error> {
|
||||
let mut path = self.eth_path(V2)?;
|
||||
path.path_segments_mut()
|
||||
.map_err(|_| Error::InvalidUrl(self.server.clone()))?
|
||||
.extend(&["beacon", "blinded_blocks"]);
|
||||
|
||||
path.set_query(
|
||||
validation_level
|
||||
.map(|v| format!("broadcast_validation={}", v))
|
||||
.as_deref(),
|
||||
);
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
/// `POST v2/beacon/blocks`
|
||||
pub async fn post_beacon_blocks_v2<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
||||
&self,
|
||||
block: &SignedBeaconBlock<T, Payload>,
|
||||
validation_level: Option<BroadcastValidation>,
|
||||
) -> Result<(), Error> {
|
||||
self.post_generic_with_consensus_version(
|
||||
self.post_beacon_blocks_v2_path(validation_level)?,
|
||||
block,
|
||||
Some(self.timeouts.proposal),
|
||||
block.message().body().fork_name(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `POST v2/beacon/blinded_blocks`
|
||||
pub async fn post_beacon_blinded_blocks_v2<T: EthSpec>(
|
||||
&self,
|
||||
block: &SignedBlindedBeaconBlock<T>,
|
||||
validation_level: Option<BroadcastValidation>,
|
||||
) -> Result<(), Error> {
|
||||
self.post_generic_with_consensus_version(
|
||||
self.post_beacon_blinded_blocks_v2_path(validation_level)?,
|
||||
block,
|
||||
Some(self.timeouts.proposal),
|
||||
block.message().body().fork_name(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Path for `v2/beacon/blocks`
|
||||
pub fn get_beacon_blocks_path(&self, block_id: BlockId) -> Result<Url, Error> {
|
||||
let mut path = self.eth_path(V2)?;
|
||||
|
||||
@@ -6,7 +6,7 @@ use lighthouse_network::{ConnectionDirection, Enr, Multiaddr, PeerConnectionStat
|
||||
use mediatype::{names, MediaType, MediaTypeList};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::fmt::{self, Display};
|
||||
use std::str::{from_utf8, FromStr};
|
||||
use std::time::Duration;
|
||||
pub use types::*;
|
||||
@@ -1260,6 +1260,50 @@ pub struct ForkChoiceNode {
|
||||
pub execution_block_hash: Option<Hash256>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum BroadcastValidation {
|
||||
Gossip,
|
||||
Consensus,
|
||||
ConsensusAndEquivocation,
|
||||
}
|
||||
|
||||
impl Default for BroadcastValidation {
|
||||
fn default() -> Self {
|
||||
Self::Gossip
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for BroadcastValidation {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Gossip => write!(f, "gossip"),
|
||||
Self::Consensus => write!(f, "consensus"),
|
||||
Self::ConsensusAndEquivocation => write!(f, "consensus_and_equivocation"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for BroadcastValidation {
|
||||
type Err = &'static str;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"gossip" => Ok(Self::Gossip),
|
||||
"consensus" => Ok(Self::Consensus),
|
||||
"consensus_and_equivocation" => Ok(Self::ConsensusAndEquivocation),
|
||||
_ => Err("Invalid broadcast validation level"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct BroadcastValidationQuery {
|
||||
#[serde(default)]
|
||||
pub broadcast_validation: BroadcastValidation,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user