[SQUASHED] Optionally skip RANDAO verif during block production (#3116)

This commit is contained in:
Michael Sproul
2022-03-25 16:30:24 +11:00
parent c5212d0f98
commit eb0324aa6b
7 changed files with 223 additions and 18 deletions

View File

@@ -142,6 +142,12 @@ pub enum ChainSegmentResult<T: EthSpec> {
},
}
/// Configure the signature verification of produced blocks.
pub enum ProduceBlockVerification {
VerifyRandao,
NoVerification,
}
/// The accepted clock drift for nodes gossiping blocks and attestations. See:
///
/// https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/p2p-interface.md#configuration
@@ -2847,6 +2853,22 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
randao_reveal: Signature,
slot: Slot,
validator_graffiti: Option<Graffiti>,
) -> Result<BeaconBlockAndState<T::EthSpec>, BlockProductionError> {
self.produce_block_with_verification(
randao_reveal,
slot,
validator_graffiti,
ProduceBlockVerification::VerifyRandao,
)
}
/// Same as `produce_block` but allowing for configuration of RANDAO-verification.
pub fn produce_block_with_verification(
&self,
randao_reveal: Signature,
slot: Slot,
validator_graffiti: Option<Graffiti>,
verification: ProduceBlockVerification,
) -> Result<BeaconBlockAndState<T::EthSpec>, BlockProductionError> {
metrics::inc_counter(&metrics::BLOCK_PRODUCTION_REQUESTS);
let _complete_timer = metrics::start_timer(&metrics::BLOCK_PRODUCTION_TIMES);
@@ -2887,6 +2909,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
slot,
randao_reveal,
validator_graffiti,
verification,
)
}
@@ -2909,6 +2932,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
produce_at_slot: Slot,
randao_reveal: Signature,
validator_graffiti: Option<Graffiti>,
verification: ProduceBlockVerification,
) -> Result<BeaconBlockAndState<T::EthSpec>, BlockProductionError> {
let eth1_chain = self
.eth1_chain
@@ -3100,11 +3124,15 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}
let process_timer = metrics::start_timer(&metrics::BLOCK_PRODUCTION_PROCESS_TIMES);
let signature_strategy = match verification {
ProduceBlockVerification::VerifyRandao => BlockSignatureStrategy::VerifyRandao,
ProduceBlockVerification::NoVerification => BlockSignatureStrategy::NoVerification,
};
let mut ctxt = ConsensusContext::new(block.slot()).set_proposer_index(proposer_index);
per_block_processing(
&mut state,
&block,
BlockSignatureStrategy::VerifyRandao,
signature_strategy,
VerifyBlockRoot::True,
&mut ctxt,
&self.spec,

View File

@@ -40,8 +40,8 @@ mod validator_pubkey_cache;
pub use self::beacon_chain::{
AttestationProcessingOutcome, BeaconChain, BeaconChainTypes, BeaconStore, ChainSegmentResult,
ForkChoiceError, HeadInfo, HeadSafetyStatus, StateSkipConfig, WhenSlotSkipped,
INVALID_JUSTIFIED_PAYLOAD_SHUTDOWN_REASON, MAXIMUM_GOSSIP_CLOCK_DISPARITY,
ForkChoiceError, HeadInfo, HeadSafetyStatus, ProduceBlockVerification, StateSkipConfig,
WhenSlotSkipped, INVALID_JUSTIFIED_PAYLOAD_SHUTDOWN_REASON, MAXIMUM_GOSSIP_CLOCK_DISPARITY,
};
pub use self::beacon_snapshot::BeaconSnapshot;
pub use self::chain_config::ChainConfig;

View File

@@ -2,7 +2,7 @@ pub use crate::persisted_beacon_chain::PersistedBeaconChain;
pub use crate::{
beacon_chain::{BEACON_CHAIN_DB_KEY, ETH1_CACHE_DB_KEY, FORK_CHOICE_DB_KEY, OP_POOL_DB_KEY},
migrate::MigratorConfig,
BeaconChainError,
BeaconChainError, ProduceBlockVerification,
};
use crate::{
builder::{BeaconChainBuilder, Witness},
@@ -605,7 +605,14 @@ where
let (block, state) = self
.chain
.produce_block_on_state(state, None, slot, randao_reveal, Some(graffiti))
.produce_block_on_state(
state,
None,
slot,
randao_reveal,
Some(graffiti),
ProduceBlockVerification::VerifyRandao,
)
.unwrap();
let signed_block = block.sign(
@@ -659,7 +666,14 @@ where
let (block, state) = self
.chain
.produce_block_on_state(state, None, slot, randao_reveal, Some(graffiti))
.produce_block_on_state(
state,
None,
slot,
randao_reveal,
Some(graffiti),
ProduceBlockVerification::VerifyRandao,
)
.unwrap();
let signed_block = block.sign(

View File

@@ -23,7 +23,7 @@ use beacon_chain::{
observed_operations::ObservationOutcome,
validator_monitor::{get_block_delay_ms, timestamp_now},
AttestationError as AttnError, BeaconChain, BeaconChainError, BeaconChainTypes,
HeadSafetyStatus, WhenSlotSkipped,
HeadSafetyStatus, ProduceBlockVerification, WhenSlotSkipped,
};
use block_id::BlockId;
use eth2::types::{self as api_types, EndpointVersion, ValidatorId};
@@ -46,7 +46,7 @@ use tokio::sync::mpsc::UnboundedSender;
use tokio_stream::{wrappers::BroadcastStream, StreamExt};
use types::{
Attestation, AttesterSlashing, BeaconStateError, CommitteeCache, ConfigAndPreset, Epoch,
EthSpec, ForkName, ProposerPreparationData, ProposerSlashing, RelativeEpoch,
EthSpec, ForkName, ProposerPreparationData, ProposerSlashing, RelativeEpoch, Signature,
SignedAggregateAndProof, SignedBeaconBlock, SignedContributionAndProof, SignedVoluntaryExit,
Slot, SyncCommitteeMessage, SyncContributionData,
};
@@ -1872,15 +1872,39 @@ pub fn serve<T: BeaconChainTypes>(
query: api_types::ValidatorBlocksQuery,
chain: Arc<BeaconChain<T>>| {
blocking_json_task(move || {
let randao_reveal = (&query.randao_reveal).try_into().map_err(|e| {
warp_utils::reject::custom_bad_request(format!(
"randao reveal is not valid BLS signature: {:?}",
e
))
})?;
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_verification = if query.verify_randao {
ProduceBlockVerification::VerifyRandao
} else {
ProduceBlockVerification::NoVerification
};
let (block, _) = chain
.produce_block(randao_reveal, slot, query.graffiti.map(Into::into))
.produce_block_with_verification(
randao_reveal,
slot,
query.graffiti.map(Into::into),
randao_verification,
)
.map_err(warp_utils::reject::block_production_error)?;
let fork_name = block
.to_ref()

View File

@@ -1919,6 +1919,104 @@ impl ApiTester {
self
}
pub async fn test_block_production_no_verify_randao(self) -> Self {
for _ in 0..E::slots_per_epoch() {
let slot = self.chain.slot().unwrap();
let block = self
.client
.get_validator_blocks_with_verify_randao::<E>(slot, None, None, Some(false))
.await
.unwrap()
.data;
assert_eq!(block.slot(), slot);
self.chain.slot_clock.set_slot(slot.as_u64() + 1);
}
self
}
pub async fn test_block_production_verify_randao_invalid(self) -> Self {
let fork = self.chain.head_info().unwrap().fork;
let genesis_validators_root = self.chain.genesis_validators_root;
for _ in 0..E::slots_per_epoch() {
let slot = self.chain.slot().unwrap();
let epoch = self.chain.epoch().unwrap();
let proposer_pubkey_bytes = self
.client
.get_validator_duties_proposer(epoch)
.await
.unwrap()
.data
.into_iter()
.find(|duty| duty.slot == slot)
.map(|duty| duty.pubkey)
.unwrap();
let proposer_pubkey = (&proposer_pubkey_bytes).try_into().unwrap();
let sk = self
.validator_keypairs
.iter()
.find(|kp| kp.pk == proposer_pubkey)
.map(|kp| kp.sk.clone())
.unwrap();
let bad_randao_reveal = {
let domain = self.chain.spec.get_domain(
epoch,
Domain::Randao,
&fork,
genesis_validators_root,
);
let message = (epoch + 1).signing_root(domain);
sk.sign(message).into()
};
// Check failure with no `verify_randao` passed.
self.client
.get_validator_blocks::<E>(slot, &bad_randao_reveal, None)
.await
.unwrap_err();
// Check failure with `verify_randao=true`.
self.client
.get_validator_blocks_with_verify_randao::<E>(
slot,
Some(&bad_randao_reveal),
None,
Some(true),
)
.await
.unwrap_err();
// Check failure with no randao reveal provided.
self.client
.get_validator_blocks_with_verify_randao::<E>(slot, None, None, None)
.await
.unwrap_err();
// Check success with `verify_randao=false`.
let block = self
.client
.get_validator_blocks_with_verify_randao::<E>(
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);
}
self
}
pub async fn test_get_validator_attestation_data(self) -> Self {
let mut state = self.chain.head_beacon_state().unwrap();
let slot = state.slot();
@@ -2770,6 +2868,22 @@ async fn block_production_with_skip_slots() {
.await;
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn block_production_no_verify_randao() {
ApiTester::new()
.await
.test_block_production_no_verify_randao()
.await;
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn block_production_verify_randao_invalid() {
ApiTester::new()
.await
.test_block_production_verify_randao_invalid()
.await;
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn get_validator_attestation_data() {
ApiTester::new()

View File

@@ -1155,6 +1155,18 @@ impl BeaconNodeHttpClient {
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
) -> Result<ForkVersionedResponse<BeaconBlock<T>>, Error> {
self.get_validator_blocks_with_verify_randao(slot, Some(randao_reveal), graffiti, None)
.await
}
/// `GET v2/validator/blocks/{slot}`
pub async fn get_validator_blocks_with_verify_randao<T: EthSpec>(
&self,
slot: Slot,
randao_reveal: Option<&SignatureBytes>,
graffiti: Option<&Graffiti>,
verify_randao: Option<bool>,
) -> Result<ForkVersionedResponse<BeaconBlock<T>>, Error> {
let mut path = self.eth_path(V2)?;
@@ -1164,14 +1176,21 @@ impl BeaconNodeHttpClient {
.push("blocks")
.push(&slot.to_string());
path.query_pairs_mut()
.append_pair("randao_reveal", &randao_reveal.to_string());
if let Some(randao_reveal) = randao_reveal {
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 {
path.query_pairs_mut()
.append_pair("verify_randao", &verify_randao.to_string());
}
self.get(path).await
}

View File

@@ -627,8 +627,14 @@ pub struct ProposerData {
#[derive(Clone, Serialize, Deserialize)]
pub struct ValidatorBlocksQuery {
pub randao_reveal: SignatureBytes,
pub randao_reveal: Option<SignatureBytes>,
pub graffiti: Option<Graffiti>,
#[serde(default = "default_verify_randao")]
pub verify_randao: bool,
}
fn default_verify_randao() -> bool {
true
}
#[derive(Clone, Serialize, Deserialize)]