Merge remote-tracking branch 'origin/unstable' into tree-states

This commit is contained in:
Michael Sproul
2022-10-19 13:21:47 +11:00
152 changed files with 3788 additions and 3067 deletions

View File

@@ -796,7 +796,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
if let Some(request_root) = request_root_opt {
if let Ok(prev_root) = state.get_block_root(prev_slot) {
return Ok(Some((*prev_root != request_root).then(|| request_root)));
return Ok(Some((*prev_root != request_root).then_some(request_root)));
}
}
@@ -818,7 +818,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
slot: curr_slot,
});
}
Ok((curr_root != prev_root).then(|| curr_root))
Ok((curr_root != prev_root).then_some(curr_root))
} else {
Ok(None)
}
@@ -1962,60 +1962,75 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
target_epoch: Epoch,
state: &BeaconState<T::EthSpec>,
) -> bool {
let slots_per_epoch = T::EthSpec::slots_per_epoch();
let shuffling_lookahead = 1 + self.spec.min_seed_lookahead.as_u64();
// Shuffling can't have changed if we're in the first few epochs
if state.current_epoch() < shuffling_lookahead {
return true;
}
// Otherwise the shuffling is determined by the block at the end of the target epoch
// minus the shuffling lookahead (usually 2). We call this the "pivot".
let pivot_slot =
if target_epoch == state.previous_epoch() || target_epoch == state.current_epoch() {
(target_epoch - shuffling_lookahead).end_slot(slots_per_epoch)
} else {
return false;
};
let state_pivot_block_root = match state.get_block_root(pivot_slot) {
Ok(root) => *root,
Err(e) => {
warn!(
&self.log,
"Missing pivot block root for attestation";
"slot" => pivot_slot,
"error" => ?e,
);
return false;
}
};
// Use fork choice's view of the block DAG to quickly evaluate whether the attestation's
// pivot block is the same as the current state's pivot block. If it is, then the
// attestation's shuffling is the same as the current state's.
// To account for skipped slots, find the first block at *or before* the pivot slot.
let fork_choice_lock = self.canonical_head.fork_choice_read_lock();
let pivot_block_root = fork_choice_lock
.proto_array()
.core_proto_array()
.iter_block_roots(block_root)
.find(|(_, slot)| *slot <= pivot_slot)
.map(|(block_root, _)| block_root);
drop(fork_choice_lock);
match pivot_block_root {
Some(root) => root == state_pivot_block_root,
None => {
self.shuffling_is_compatible_result(block_root, target_epoch, state)
.unwrap_or_else(|e| {
debug!(
&self.log,
"Discarding attestation because of missing ancestor";
"pivot_slot" => pivot_slot.as_u64(),
self.log,
"Skipping attestation with incompatible shuffling";
"block_root" => ?block_root,
"target_epoch" => target_epoch,
"reason" => ?e,
);
false
})
}
fn shuffling_is_compatible_result(
&self,
block_root: &Hash256,
target_epoch: Epoch,
state: &BeaconState<T::EthSpec>,
) -> Result<bool, Error> {
// Compute the shuffling ID for the head state in the `target_epoch`.
let relative_epoch = RelativeEpoch::from_epoch(state.current_epoch(), target_epoch)
.map_err(|e| Error::BeaconStateError(e.into()))?;
let head_shuffling_id =
AttestationShufflingId::new(self.genesis_block_root, state, relative_epoch)?;
// Load the block's shuffling ID from fork choice. We use the variant of `get_block` that
// checks descent from the finalized block, so there's one case where we'll spuriously
// return `false`: where an attestation for the previous epoch nominates the pivot block
// which is the parent block of the finalized block. Such attestations are not useful, so
// this doesn't matter.
let fork_choice_lock = self.canonical_head.fork_choice_read_lock();
let block = fork_choice_lock
.get_block(block_root)
.ok_or(Error::AttestationHeadNotInForkChoice(*block_root))?;
drop(fork_choice_lock);
let block_shuffling_id = if target_epoch == block.current_epoch_shuffling_id.shuffling_epoch
{
block.current_epoch_shuffling_id
} else if target_epoch == block.next_epoch_shuffling_id.shuffling_epoch {
block.next_epoch_shuffling_id
} else if target_epoch > block.next_epoch_shuffling_id.shuffling_epoch {
AttestationShufflingId {
shuffling_epoch: target_epoch,
shuffling_decision_block: *block_root,
}
} else {
debug!(
self.log,
"Skipping attestation with incompatible shuffling";
"block_root" => ?block_root,
"target_epoch" => target_epoch,
"reason" => "target epoch less than block epoch"
);
return Ok(false);
};
if head_shuffling_id == block_shuffling_id {
Ok(true)
} else {
debug!(
self.log,
"Skipping attestation with incompatible shuffling";
"block_root" => ?block_root,
"target_epoch" => target_epoch,
"head_shuffling_id" => ?head_shuffling_id,
"block_shuffling_id" => ?block_shuffling_id,
);
Ok(false)
}
}
@@ -2191,7 +2206,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}
}
match check_block_relevancy(&block, Some(block_root), self) {
match check_block_relevancy(&block, block_root, self) {
// If the block is relevant, add it to the filtered chain segment.
Ok(_) => filtered_chain_segment.push((block_root, block)),
// If the block is already known, simply ignore this block.
@@ -2315,7 +2330,11 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// Import the blocks into the chain.
for signature_verified_block in signature_verified_blocks {
match self
.process_block(signature_verified_block, count_unrealized)
.process_block(
signature_verified_block.block_root(),
signature_verified_block,
count_unrealized,
)
.await
{
Ok(_) => imported_blocks += 1,
@@ -2358,7 +2377,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
Ok(verified) => {
debug!(
chain.log,
"Successfully processed gossip block";
"Successfully verified gossip block";
"graffiti" => graffiti_string,
"slot" => slot,
"root" => ?verified.block_root(),
@@ -2400,6 +2419,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
/// verification.
pub async fn process_block<B: IntoExecutionPendingBlock<T>>(
self: &Arc<Self>,
block_root: Hash256,
unverified_block: B,
count_unrealized: CountUnrealized,
) -> Result<Hash256, BlockError<T::EthSpec>> {
@@ -2415,7 +2435,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// A small closure to group the verification and import errors.
let chain = self.clone();
let import_block = async move {
let execution_pending = unverified_block.into_execution_pending_block(&chain)?;
let execution_pending =
unverified_block.into_execution_pending_block(block_root, &chain)?;
chain
.import_execution_pending_block(execution_pending, count_unrealized)
.await
@@ -2842,7 +2863,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.pubkeys
.iter()
.zip(sync_aggregate.sync_committee_bits.iter())
.filter_map(|(pubkey, bit)| bit.then(|| pubkey))
.filter_map(|(pubkey, bit)| bit.then_some(pubkey))
.collect::<Vec<_>>();
validator_monitor.register_sync_aggregate_in_block(
@@ -3414,7 +3435,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}
let slot = state.slot();
let proposer_index = state.get_beacon_proposer_index(state.slot(), &self.spec)? as u64;
let sync_aggregate = if matches!(&state, BeaconState::Base(_)) {
None
@@ -3561,7 +3581,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
ProduceBlockVerification::VerifyRandao => BlockSignatureStrategy::VerifyRandao,
ProduceBlockVerification::NoVerification => BlockSignatureStrategy::NoVerification,
};
let mut ctxt = ConsensusContext::new(block.slot()).set_proposer_index(proposer_index);
// Use a context without block root or proposer index so that both are checked.
let mut ctxt = ConsensusContext::new(block.slot());
per_block_processing(
&mut state,
&block,
@@ -4376,7 +4397,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
///
/// If the committee for `(head_block_root, shuffling_epoch)` isn't found in the
/// `shuffling_cache`, we will read a state from disk and then update the `shuffling_cache`.
pub(crate) fn with_committee_cache<F, R>(
pub fn with_committee_cache<F, R>(
&self,
head_block_root: Hash256,
shuffling_epoch: Epoch,

View File

@@ -528,7 +528,7 @@ pub fn signature_verify_chain_segment<T: BeaconChainTypes>(
}
let (first_root, first_block) = chain_segment.remove(0);
let (mut parent, first_block) = load_parent(first_block, chain)?;
let (mut parent, first_block) = load_parent(first_root, first_block, chain)?;
let slot = first_block.slot();
chain_segment.insert(0, (first_root, first_block));
@@ -548,7 +548,7 @@ pub fn signature_verify_chain_segment<T: BeaconChainTypes>(
let mut signature_verifier = get_signature_verifier::<T>(&state, &pubkey_cache, &chain.spec);
for (block_root, block) in &chain_segment {
signature_verifier.include_all_signatures(block, Some(*block_root), true)?;
signature_verifier.include_all_signatures(block, Some(*block_root), None)?;
}
if signature_verifier.verify().is_err() {
@@ -560,8 +560,7 @@ pub fn signature_verify_chain_segment<T: BeaconChainTypes>(
let mut signature_verified_blocks = chain_segment
.into_iter()
.map(|(block_root, block)| {
// It's safe to include the proposer index here as the signature check also checks that
// the block is signed by the correct proposer (see `block_proposal_signature_set`).
// Proposer index has already been verified above during signature verification.
let consensus_context = ConsensusContext::new(block.slot())
.set_current_block_root(block_root)
.set_proposer_index(block.message().proposer_index());
@@ -631,9 +630,10 @@ pub struct ExecutionPendingBlock<T: BeaconChainTypes> {
pub trait IntoExecutionPendingBlock<T: BeaconChainTypes>: Sized {
fn into_execution_pending_block(
self,
block_root: Hash256,
chain: &Arc<BeaconChain<T>>,
) -> Result<ExecutionPendingBlock<T>, BlockError<T::EthSpec>> {
self.into_execution_pending_block_slashable(chain)
self.into_execution_pending_block_slashable(block_root, chain)
.map(|execution_pending| {
// Supply valid block to slasher.
if let Some(slasher) = chain.slasher.as_ref() {
@@ -647,6 +647,7 @@ pub trait IntoExecutionPendingBlock<T: BeaconChainTypes>: Sized {
/// Convert the block to fully-verified form while producing data to aid checking slashability.
fn into_execution_pending_block_slashable(
self,
block_root: Hash256,
chain: &Arc<BeaconChain<T>>,
) -> Result<ExecutionPendingBlock<T>, BlockSlashInfo<BlockError<T::EthSpec>>>;
@@ -790,7 +791,7 @@ impl<T: BeaconChainTypes> GossipVerifiedBlock<T> {
} else {
// The proposer index was *not* cached and we must load the parent in order to determine
// the proposer index.
let (mut parent, block) = load_parent(block, chain)?;
let (mut parent, block) = load_parent(block_root, block, chain)?;
debug!(
chain.log,
@@ -892,11 +893,12 @@ impl<T: BeaconChainTypes> IntoExecutionPendingBlock<T> for GossipVerifiedBlock<T
/// Completes verification of the wrapped `block`.
fn into_execution_pending_block_slashable(
self,
block_root: Hash256,
chain: &Arc<BeaconChain<T>>,
) -> Result<ExecutionPendingBlock<T>, BlockSlashInfo<BlockError<T::EthSpec>>> {
let execution_pending =
SignatureVerifiedBlock::from_gossip_verified_block_check_slashable(self, chain)?;
execution_pending.into_execution_pending_block_slashable(chain)
execution_pending.into_execution_pending_block_slashable(block_root, chain)
}
fn block(&self) -> &SignedBeaconBlock<T::EthSpec> {
@@ -922,7 +924,7 @@ impl<T: BeaconChainTypes> SignatureVerifiedBlock<T> {
// Check the anchor slot before loading the parent, to avoid spurious lookups.
check_block_against_anchor_slot(block.message(), chain)?;
let (mut parent, block) = load_parent(block, chain)?;
let (mut parent, block) = load_parent(block_root, block, chain)?;
// Reject any block that exceeds our limit on skipped slots.
check_block_skip_slots(chain, parent.beacon_block.slot(), block.message())?;
@@ -939,7 +941,7 @@ impl<T: BeaconChainTypes> SignatureVerifiedBlock<T> {
let mut signature_verifier =
get_signature_verifier::<T>(&state, &pubkey_cache, &chain.spec);
signature_verifier.include_all_signatures(&block, Some(block_root), true)?;
signature_verifier.include_all_signatures(&block, Some(block_root), None)?;
if signature_verifier.verify().is_ok() {
Ok(Self {
@@ -974,7 +976,7 @@ impl<T: BeaconChainTypes> SignatureVerifiedBlock<T> {
let (mut parent, block) = if let Some(parent) = from.parent {
(parent, from.block)
} else {
load_parent(from.block, chain)?
load_parent(from.block_root, from.block, chain)?
};
let state = cheap_state_advance_to_obtain_committees(
@@ -989,7 +991,11 @@ impl<T: BeaconChainTypes> SignatureVerifiedBlock<T> {
let mut signature_verifier =
get_signature_verifier::<T>(&state, &pubkey_cache, &chain.spec);
signature_verifier.include_all_signatures_except_proposal(&block)?;
// Gossip verification has already checked the proposer index. Use it to check the RANDAO
// signature.
let verified_proposer_index = Some(block.message().proposer_index());
signature_verifier
.include_all_signatures_except_proposal(&block, verified_proposer_index)?;
if signature_verifier.verify().is_ok() {
Ok(Self {
@@ -1012,25 +1018,30 @@ impl<T: BeaconChainTypes> SignatureVerifiedBlock<T> {
Self::from_gossip_verified_block(from, chain)
.map_err(|e| BlockSlashInfo::from_early_error(header, e))
}
pub fn block_root(&self) -> Hash256 {
self.block_root
}
}
impl<T: BeaconChainTypes> IntoExecutionPendingBlock<T> for SignatureVerifiedBlock<T> {
/// Completes verification of the wrapped `block`.
fn into_execution_pending_block_slashable(
self,
block_root: Hash256,
chain: &Arc<BeaconChain<T>>,
) -> Result<ExecutionPendingBlock<T>, BlockSlashInfo<BlockError<T::EthSpec>>> {
let header = self.block.signed_block_header();
let (parent, block) = if let Some(parent) = self.parent {
(parent, self.block)
} else {
load_parent(self.block, chain)
load_parent(self.block_root, self.block, chain)
.map_err(|e| BlockSlashInfo::SignatureValid(header.clone(), e))?
};
ExecutionPendingBlock::from_signature_verified_components(
block,
self.block_root,
block_root,
parent,
self.consensus_context,
chain,
@@ -1048,14 +1059,15 @@ impl<T: BeaconChainTypes> IntoExecutionPendingBlock<T> for Arc<SignedBeaconBlock
/// and then using that implementation of `IntoExecutionPendingBlock` to complete verification.
fn into_execution_pending_block_slashable(
self,
block_root: Hash256,
chain: &Arc<BeaconChain<T>>,
) -> Result<ExecutionPendingBlock<T>, BlockSlashInfo<BlockError<T::EthSpec>>> {
// Perform an early check to prevent wasting time on irrelevant blocks.
let block_root = check_block_relevancy(&self, None, chain)
let block_root = check_block_relevancy(&self, block_root, chain)
.map_err(|e| BlockSlashInfo::SignatureNotChecked(self.signed_block_header(), e))?;
SignatureVerifiedBlock::check_slashable(self, block_root, chain)?
.into_execution_pending_block_slashable(chain)
.into_execution_pending_block_slashable(block_root, chain)
}
fn block(&self) -> &SignedBeaconBlock<T::EthSpec> {
@@ -1111,7 +1123,7 @@ impl<T: BeaconChainTypes> ExecutionPendingBlock<T> {
* Perform cursory checks to see if the block is even worth processing.
*/
check_block_relevancy(&block, Some(block_root), chain)?;
check_block_relevancy(&block, block_root, chain)?;
/*
* Advance the given `parent.beacon_state` to the slot of the given `block`.
@@ -1504,7 +1516,7 @@ pub fn check_block_is_finalized_descendant<T: BeaconChainTypes>(
/// experienced whilst attempting to verify.
pub fn check_block_relevancy<T: BeaconChainTypes>(
signed_block: &SignedBeaconBlock<T::EthSpec>,
block_root: Option<Hash256>,
block_root: Hash256,
chain: &BeaconChain<T>,
) -> Result<Hash256, BlockError<T::EthSpec>> {
let block = signed_block.message();
@@ -1528,8 +1540,6 @@ pub fn check_block_relevancy<T: BeaconChainTypes>(
return Err(BlockError::BlockSlotLimitReached);
}
let block_root = block_root.unwrap_or_else(|| get_block_root(signed_block));
// Do not process a block from a finalized slot.
check_block_against_finalized_slot(block, block_root, chain)?;
@@ -1583,6 +1593,7 @@ fn verify_parent_block_is_known<T: BeaconChainTypes>(
/// whilst attempting the operation.
#[allow(clippy::type_complexity)]
fn load_parent<T: BeaconChainTypes>(
block_root: Hash256,
block: Arc<SignedBeaconBlock<T::EthSpec>>,
chain: &BeaconChain<T>,
) -> Result<

View File

@@ -911,7 +911,7 @@ where
.ok_or("dummy_eth1_backend requires a log")?;
let backend =
CachingEth1Backend::new(Eth1Config::default(), log.clone(), self.spec.clone());
CachingEth1Backend::new(Eth1Config::default(), log.clone(), self.spec.clone())?;
self.eth1_chain = Some(Eth1Chain::new_dummy(backend));

View File

@@ -639,11 +639,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}
};
// Regardless of where we got the state from, attempt to build the committee
// caches.
new_snapshot
.beacon_state
.build_all_committee_caches(&self.spec)?;
// Regardless of where we got the state from, attempt to build all the
// caches except the tree hash cache.
new_snapshot.beacon_state.build_all_caches(&self.spec)?;
let new_cached_head = CachedHead {
snapshot: Arc::new(new_snapshot),

View File

@@ -431,12 +431,13 @@ impl<T: EthSpec> CachingEth1Backend<T> {
/// Instantiates `self` with empty caches.
///
/// Does not connect to the eth1 node or start any tasks to keep the cache updated.
pub fn new(config: Eth1Config, log: Logger, spec: ChainSpec) -> Self {
Self {
core: HttpService::new(config, log.clone(), spec),
pub fn new(config: Eth1Config, log: Logger, spec: ChainSpec) -> Result<Self, String> {
Ok(Self {
core: HttpService::new(config, log.clone(), spec)
.map_err(|e| format!("Failed to create eth1 http service: {:?}", e))?,
log,
_phantom: PhantomData,
}
})
}
/// Starts the routine which connects to the external eth1 node and updates the caches.
@@ -730,11 +731,9 @@ mod test {
};
let log = null_logger().unwrap();
Eth1Chain::new(CachingEth1Backend::new(
eth1_config,
log,
MainnetEthSpec::default_spec(),
))
Eth1Chain::new(
CachingEth1Backend::new(eth1_config, log, MainnetEthSpec::default_spec()).unwrap(),
)
}
fn get_deposit_log(i: u64, spec: &ChainSpec) -> DepositLog {

View File

@@ -53,7 +53,9 @@ pub use self::errors::{BeaconChainError, BlockProductionError};
pub use self::historical_blocks::HistoricalBlockError;
pub use attestation_verification::Error as AttestationError;
pub use beacon_fork_choice_store::{BeaconForkChoiceStore, Error as ForkChoiceStoreError};
pub use block_verification::{BlockError, ExecutionPayloadError, GossipVerifiedBlock};
pub use block_verification::{
get_block_root, BlockError, ExecutionPayloadError, GossipVerifiedBlock,
};
pub use canonical_head::{CachedHead, CanonicalHead, CanonicalHeadRwLock};
pub use eth1_chain::{Eth1Chain, Eth1ChainBackend};
pub use events::ServerSentEventHandler;

View File

@@ -212,7 +212,7 @@ fn map_relevant_epochs_to_roots<T: BeaconChainTypes>(
let root = iter
.find_map(|next| match next {
Ok((root, slot)) => (slot == start_slot).then(|| Ok(root)),
Ok((root, slot)) => (slot == start_slot).then_some(Ok(root)),
Err(e) => Some(Err(format!("{:?}", e))),
})
.transpose()?
@@ -286,7 +286,7 @@ fn find_finalized_descendant_heads(
.filter_map(|(index, node)| {
(!nodes_referenced_as_parents.contains(&index)
&& fork_choice.is_descendant(finalized_root, node.root))
.then(|| HeadInfo {
.then_some(HeadInfo {
index,
root: node.root,
slot: node.slot,
@@ -306,7 +306,7 @@ fn update_store_justified_checkpoint(
.filter_map(|node| {
(node.finalized_checkpoint
== Some(persisted_fork_choice.fork_choice_store.finalized_checkpoint))
.then(|| node.justified_checkpoint)
.then_some(node.justified_checkpoint)
.flatten()
})
.max_by_key(|justified_checkpoint| justified_checkpoint.epoch)

View File

@@ -343,7 +343,7 @@ impl<T: BeaconChainTypes> VerifiedSyncContribution<T> {
let participant_pubkeys = sync_subcommittee_pubkeys
.into_iter()
.zip(contribution.aggregation_bits.iter())
.filter_map(|(pubkey, bit)| bit.then(|| pubkey))
.filter_map(|(pubkey, bit)| bit.then_some(pubkey))
.collect::<Vec<_>>();
// Ensure that all signatures are valid.

View File

@@ -1123,7 +1123,7 @@ where
selection_proof
.is_aggregator::<E>()
.expect("should determine aggregator")
.then(|| validator_index)
.then_some(validator_index)
})?;
let default = SyncCommitteeContribution::from_message(
@@ -1446,12 +1446,13 @@ where
pub async fn process_block(
&self,
slot: Slot,
block_root: Hash256,
block: SignedBeaconBlock<E>,
) -> Result<SignedBeaconBlockHash, BlockError<E>> {
self.set_current_slot(slot);
let block_hash: SignedBeaconBlockHash = self
.chain
.process_block(Arc::new(block), CountUnrealized::True)
.process_block(block_root, Arc::new(block), CountUnrealized::True)
.await?
.into();
self.chain.recompute_head_at_current_slot().await;
@@ -1464,7 +1465,11 @@ where
) -> Result<SignedBeaconBlockHash, BlockError<E>> {
let block_hash: SignedBeaconBlockHash = self
.chain
.process_block(Arc::new(block), CountUnrealized::True)
.process_block(
block.canonical_root(),
Arc::new(block),
CountUnrealized::True,
)
.await?
.into();
self.chain.recompute_head_at_current_slot().await;
@@ -1529,7 +1534,9 @@ where
) -> Result<(SignedBeaconBlockHash, SignedBeaconBlock<E>, BeaconState<E>), BlockError<E>> {
self.set_current_slot(slot);
let (block, new_state) = self.make_block(state, slot).await;
let block_hash = self.process_block(slot, block.clone()).await?;
let block_hash = self
.process_block(slot, block.canonical_root(), block.clone())
.await?;
Ok((block_hash, block, new_state))
}