diff --git a/beacon_node/http_api/src/block_rewards.rs b/beacon_node/http_api/src/block_rewards.rs index 3b81b894d..05886a4d0 100644 --- a/beacon_node/http_api/src/block_rewards.rs +++ b/beacon_node/http_api/src/block_rewards.rs @@ -4,7 +4,7 @@ use lru::LruCache; use slog::{debug, warn, Logger}; use state_processing::BlockReplayer; use std::sync::Arc; -use types::BeaconBlock; +use types::BlindedBeaconBlock; use warp_utils::reject::{ beacon_chain_error, beacon_state_error, custom_bad_request, custom_server_error, }; @@ -96,7 +96,7 @@ pub fn get_block_rewards( /// Compute block rewards for blocks passed in as input. pub fn compute_block_rewards( - blocks: Vec>, + blocks: Vec>, chain: Arc>, log: Logger, ) -> Result, warp::Rejection> { diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index a21b67417..5c2660b30 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -25,7 +25,9 @@ use beacon_chain::{ BeaconChainTypes, ProduceBlockVerification, WhenSlotSkipped, }; pub use block_id::BlockId; -use eth2::types::{self as api_types, EndpointVersion, ValidatorId, ValidatorStatus}; +use eth2::types::{ + self as api_types, EndpointVersion, SkipRandaoVerification, ValidatorId, ValidatorStatus, +}; use lighthouse_network::{types::SyncState, EnrExt, NetworkGlobals, PeerId, PubsubMessage}; use lighthouse_version::version_with_platform; use network::{NetworkMessage, NetworkSenders, ValidatorSubscriptionMessage}; @@ -35,7 +37,6 @@ use slot_clock::SlotClock; use ssz::Encode; pub use state_id::StateId; use std::borrow::Cow; -use std::convert::TryInto; use std::future::Future; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::path::PathBuf; @@ -46,7 +47,7 @@ use tokio_stream::{wrappers::BroadcastStream, StreamExt}; use types::{ Attestation, AttestationData, AttesterSlashing, BeaconStateError, BlindedPayload, CommitteeCache, ConfigAndPreset, Epoch, EthSpec, ForkName, FullPayload, - ProposerPreparationData, ProposerSlashing, RelativeEpoch, Signature, SignedAggregateAndProof, + ProposerPreparationData, ProposerSlashing, RelativeEpoch, SignedAggregateAndProof, SignedBeaconBlock, SignedBlindedBeaconBlock, SignedContributionAndProof, SignedValidatorRegistrationData, SignedVoluntaryExit, Slot, SyncCommitteeMessage, SyncContributionData, @@ -2002,31 +2003,25 @@ pub fn serve( slot: Slot, query: api_types::ValidatorBlocksQuery, chain: Arc>| async move { - let randao_reveal = query.randao_reveal.as_ref().map_or_else( - || { - if query.verify_randao { - Err(warp_utils::reject::custom_bad_request( - "randao_reveal is mandatory unless verify_randao=false".into(), - )) - } else { - Ok(Signature::empty()) - } - }, - |sig_bytes| { - sig_bytes.try_into().map_err(|e| { - warp_utils::reject::custom_bad_request(format!( - "randao reveal is not a valid BLS signature: {:?}", - e - )) - }) - }, - )?; + 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 = if query.verify_randao { - ProduceBlockVerification::VerifyRandao - } else { - ProduceBlockVerification::NoVerification - }; + 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, _) = chain .produce_block_with_verification::>( @@ -2064,31 +2059,25 @@ pub fn serve( |slot: Slot, query: api_types::ValidatorBlocksQuery, chain: Arc>| async move { - let randao_reveal = query.randao_reveal.as_ref().map_or_else( - || { - if query.verify_randao { - Err(warp_utils::reject::custom_bad_request( - "randao_reveal is mandatory unless verify_randao=false".into(), - )) - } else { - Ok(Signature::empty()) - } - }, - |sig_bytes| { - sig_bytes.try_into().map_err(|e| { - warp_utils::reject::custom_bad_request(format!( - "randao reveal is not a valid BLS signature: {:?}", - e - )) - }) - }, - )?; + 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 = if query.verify_randao { - ProduceBlockVerification::VerifyRandao - } else { - ProduceBlockVerification::NoVerification - }; + 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, _) = chain .produce_block_with_verification::>( @@ -2103,6 +2092,7 @@ pub fn serve( .to_ref() .fork_name(&chain.spec) .map_err(inconsistent_fork_rejection)?; + // Pose as a V2 endpoint so we return the fork `version`. fork_versioned_response(V2, fork_name, block) .map(|response| warp::reply::json(&response)) diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index ca240e64d..ff664d6ff 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -1939,11 +1939,11 @@ impl ApiTester { let block = self .client - .get_validator_blocks_with_verify_randao::>( + .get_validator_blocks_modular::>( slot, + &Signature::infinity().unwrap().into(), None, - None, - Some(false), + SkipRandaoVerification::Yes, ) .await .unwrap() @@ -1993,45 +1993,23 @@ impl ApiTester { sk.sign(message).into() }; - // Check failure with no `verify_randao` passed. + // Check failure with no `skip_randao_verification` passed. self.client .get_validator_blocks::>(slot, &bad_randao_reveal, None) .await .unwrap_err(); - // Check failure with `verify_randao=true`. + // Check failure with `skip_randao_verification` (requires infinity sig). self.client - .get_validator_blocks_with_verify_randao::>( + .get_validator_blocks_modular::>( slot, - Some(&bad_randao_reveal), + &bad_randao_reveal, None, - Some(true), + SkipRandaoVerification::Yes, ) .await .unwrap_err(); - // Check failure with no randao reveal provided. - self.client - .get_validator_blocks_with_verify_randao::>( - slot, None, None, None, - ) - .await - .unwrap_err(); - - // Check success with `verify_randao=false`. - let block = self - .client - .get_validator_blocks_with_verify_randao::>( - slot, - Some(&bad_randao_reveal), - None, - Some(false), - ) - .await - .unwrap() - .data; - - assert_eq!(block.slot(), slot); self.chain.slot_clock.set_slot(slot.as_u64() + 1); } @@ -2106,11 +2084,11 @@ impl ApiTester { let block = self .client - .get_validator_blinded_blocks_with_verify_randao::( + .get_validator_blinded_blocks_modular::( slot, + &Signature::infinity().unwrap().into(), None, - None, - Some(false), + SkipRandaoVerification::Yes, ) .await .unwrap() @@ -2162,45 +2140,23 @@ impl ApiTester { sk.sign(message).into() }; - // Check failure with no `verify_randao` passed. + // Check failure with full randao verification enabled. self.client .get_validator_blinded_blocks::(slot, &bad_randao_reveal, None) .await .unwrap_err(); - // Check failure with `verify_randao=true`. + // Check failure with `skip_randao_verification` (requires infinity sig). self.client - .get_validator_blinded_blocks_with_verify_randao::( + .get_validator_blinded_blocks_modular::( slot, - Some(&bad_randao_reveal), + &bad_randao_reveal, None, - Some(true), + SkipRandaoVerification::Yes, ) .await .unwrap_err(); - // Check failure with no randao reveal provided. - self.client - .get_validator_blinded_blocks_with_verify_randao::( - slot, None, None, None, - ) - .await - .unwrap_err(); - - // Check success with `verify_randao=false`. - let block = self - .client - .get_validator_blinded_blocks_with_verify_randao::( - slot, - Some(&bad_randao_reveal), - None, - Some(false), - ) - .await - .unwrap() - .data; - - assert_eq!(block.slot(), slot); self.chain.slot_clock.set_slot(slot.as_u64() + 1); } diff --git a/common/eth2/src/lib.rs b/common/eth2/src/lib.rs index f096aca97..104ca9ccd 100644 --- a/common/eth2/src/lib.rs +++ b/common/eth2/src/lib.rs @@ -1233,17 +1233,17 @@ impl BeaconNodeHttpClient { randao_reveal: &SignatureBytes, graffiti: Option<&Graffiti>, ) -> Result>, Error> { - self.get_validator_blocks_with_verify_randao(slot, Some(randao_reveal), graffiti, None) + self.get_validator_blocks_modular(slot, randao_reveal, graffiti, SkipRandaoVerification::No) .await } /// `GET v2/validator/blocks/{slot}` - pub async fn get_validator_blocks_with_verify_randao>( + pub async fn get_validator_blocks_modular>( &self, slot: Slot, - randao_reveal: Option<&SignatureBytes>, + randao_reveal: &SignatureBytes, graffiti: Option<&Graffiti>, - verify_randao: Option, + skip_randao_verification: SkipRandaoVerification, ) -> Result>, Error> { let mut path = self.eth_path(V2)?; @@ -1253,19 +1253,17 @@ impl BeaconNodeHttpClient { .push("blocks") .push(&slot.to_string()); - if let Some(randao_reveal) = randao_reveal { - path.query_pairs_mut() - .append_pair("randao_reveal", &randao_reveal.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 let Some(verify_randao) = verify_randao { + if skip_randao_verification == SkipRandaoVerification::Yes { path.query_pairs_mut() - .append_pair("verify_randao", &verify_randao.to_string()); + .append_pair("skip_randao_verification", ""); } self.get(path).await @@ -1278,25 +1276,22 @@ impl BeaconNodeHttpClient { randao_reveal: &SignatureBytes, graffiti: Option<&Graffiti>, ) -> Result>, Error> { - self.get_validator_blinded_blocks_with_verify_randao( + self.get_validator_blinded_blocks_modular( slot, - Some(randao_reveal), + randao_reveal, graffiti, - None, + SkipRandaoVerification::No, ) .await } /// `GET v1/validator/blinded_blocks/{slot}` - pub async fn get_validator_blinded_blocks_with_verify_randao< - T: EthSpec, - Payload: ExecPayload, - >( + pub async fn get_validator_blinded_blocks_modular>( &self, slot: Slot, - randao_reveal: Option<&SignatureBytes>, + randao_reveal: &SignatureBytes, graffiti: Option<&Graffiti>, - verify_randao: Option, + skip_randao_verification: SkipRandaoVerification, ) -> Result>, Error> { let mut path = self.eth_path(V1)?; @@ -1306,19 +1301,17 @@ impl BeaconNodeHttpClient { .push("blinded_blocks") .push(&slot.to_string()); - if let Some(randao_reveal) = randao_reveal { - path.query_pairs_mut() - .append_pair("randao_reveal", &randao_reveal.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 let Some(verify_randao) = verify_randao { + if skip_randao_verification == SkipRandaoVerification::Yes { path.query_pairs_mut() - .append_pair("verify_randao", &verify_randao.to_string()); + .append_key_only("skip_randao_verification"); } self.get(path).await diff --git a/common/eth2/src/types.rs b/common/eth2/src/types.rs index 0f8ec5123..e65735800 100644 --- a/common/eth2/src/types.rs +++ b/common/eth2/src/types.rs @@ -658,16 +658,34 @@ pub struct ProposerData { pub slot: Slot, } -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Deserialize)] pub struct ValidatorBlocksQuery { - pub randao_reveal: Option, + pub randao_reveal: SignatureBytes, pub graffiti: Option, - #[serde(default = "default_verify_randao")] - pub verify_randao: bool, + pub skip_randao_verification: SkipRandaoVerification, } -fn default_verify_randao() -> bool { - true +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Deserialize)] +#[serde(try_from = "Option")] +pub enum SkipRandaoVerification { + Yes, + #[default] + No, +} + +/// Parse a `skip_randao_verification` query parameter. +impl TryFrom> for SkipRandaoVerification { + type Error = String; + + fn try_from(opt: Option) -> Result { + match opt.as_deref() { + None => Ok(SkipRandaoVerification::No), + Some("") => Ok(SkipRandaoVerification::Yes), + Some(s) => Err(format!( + "skip_randao_verification does not take a value, got: {s}" + )), + } + } } #[derive(Clone, Serialize, Deserialize)] diff --git a/consensus/types/src/beacon_block.rs b/consensus/types/src/beacon_block.rs index da8566dcb..0ec1f9a37 100644 --- a/consensus/types/src/beacon_block.rs +++ b/consensus/types/src/beacon_block.rs @@ -66,6 +66,8 @@ pub struct BeaconBlock = FullPayload> { pub body: BeaconBlockBodyMerge, } +pub type BlindedBeaconBlock = BeaconBlock>; + impl> SignedRoot for BeaconBlock {} impl<'a, T: EthSpec, Payload: ExecPayload> SignedRoot for BeaconBlockRef<'a, T, Payload> {} diff --git a/consensus/types/src/lib.rs b/consensus/types/src/lib.rs index f05012c0b..32300173e 100644 --- a/consensus/types/src/lib.rs +++ b/consensus/types/src/lib.rs @@ -99,7 +99,7 @@ pub use crate::attestation_duty::AttestationDuty; pub use crate::attester_slashing::AttesterSlashing; pub use crate::beacon_block::{ BeaconBlock, BeaconBlockAltair, BeaconBlockBase, BeaconBlockMerge, BeaconBlockRef, - BeaconBlockRefMut, + BeaconBlockRefMut, BlindedBeaconBlock, }; pub use crate::beacon_block_body::{ BeaconBlockBody, BeaconBlockBodyAltair, BeaconBlockBodyBase, BeaconBlockBodyMerge, diff --git a/crypto/bls/src/generic_signature.rs b/crypto/bls/src/generic_signature.rs index 10ef75fc6..01e5ed1d4 100644 --- a/crypto/bls/src/generic_signature.rs +++ b/crypto/bls/src/generic_signature.rs @@ -80,6 +80,18 @@ where self.point.is_none() } + /// Initialize self to the point-at-infinity. + /// + /// In general `AggregateSignature::infinity` should be used in favour of this function. + pub fn infinity() -> Result { + Self::deserialize(&INFINITY_SIGNATURE) + } + + /// Returns `true` if `self` is equal to the point at infinity. + pub fn is_infinity(&self) -> bool { + self.is_infinity + } + /// Returns a reference to the underlying BLS point. pub(crate) fn point(&self) -> Option<&Sig> { self.point.as_ref()