Fix bug with fork choice, tidy
This commit is contained in:
parent
284166c7f8
commit
76bb671084
@ -524,7 +524,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
|
|
||||||
// If it turns out that the attestation was made using the head state, then there
|
// If it turns out that the attestation was made using the head state, then there
|
||||||
// is no need to load a state from the database to process the attestation.
|
// is no need to load a state from the database to process the attestation.
|
||||||
if state.current_epoch() == attestation_head_block.epoch()
|
//
|
||||||
|
// Note: use the epoch of the target because it indicates which epoch the
|
||||||
|
// attestation was created in. You cannot use the epoch of the head block, because
|
||||||
|
// the block doesn't necessarily need to be in the same epoch as the attestation
|
||||||
|
// (e.g., if there are skip slots between the epoch the block was created in and
|
||||||
|
// the epoch for the attestation).
|
||||||
|
if state.current_epoch() == attestation.data.target.epoch
|
||||||
&& (state
|
&& (state
|
||||||
.get_block_root(attestation_head_block.slot)
|
.get_block_root(attestation_head_block.slot)
|
||||||
.map(|root| *root == attestation.data.beacon_block_root)
|
.map(|root| *root == attestation.data.beacon_block_root)
|
||||||
@ -546,7 +552,11 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
if let Some(outcome) = optional_outcome {
|
if let Some(outcome) = optional_outcome {
|
||||||
outcome
|
outcome
|
||||||
} else {
|
} else {
|
||||||
// The state required to verify this attestation must be loaded from the database.
|
// Use the `data.beacon_block_root` to load the state from the latest non-skipped
|
||||||
|
// slot preceding the attestations creation.
|
||||||
|
//
|
||||||
|
// This state is guaranteed to be in the same chain as the attestation, but it's
|
||||||
|
// not guaranteed to be from the same slot or epoch as the attestation.
|
||||||
let mut state: BeaconState<T::EthSpec> = self
|
let mut state: BeaconState<T::EthSpec> = self
|
||||||
.store
|
.store
|
||||||
.get(&attestation_head_block.state_root)?
|
.get(&attestation_head_block.state_root)?
|
||||||
@ -554,7 +564,21 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
|
|
||||||
// Ensure the state loaded from the database matches the state of the attestation
|
// Ensure the state loaded from the database matches the state of the attestation
|
||||||
// head block.
|
// head block.
|
||||||
for _ in state.slot.as_u64()..attestation_head_block.slot.as_u64() {
|
//
|
||||||
|
// The state needs to be advanced from the current slot through to the epoch in
|
||||||
|
// which the attestation was created in. It would be an error to try and use
|
||||||
|
// `state.get_attestation_data_slot(..)` because the state matching the
|
||||||
|
// `data.beacon_block_root` isn't necessarily in a nearby epoch to the attestation
|
||||||
|
// (e.g., if there were lots of skip slots since the head of the chain and the
|
||||||
|
// epoch creation epoch).
|
||||||
|
for _ in state.slot.as_u64()
|
||||||
|
..attestation
|
||||||
|
.data
|
||||||
|
.target
|
||||||
|
.epoch
|
||||||
|
.start_slot(T::EthSpec::slots_per_epoch())
|
||||||
|
.as_u64()
|
||||||
|
{
|
||||||
per_slot_processing(&mut state, &self.spec)?;
|
per_slot_processing(&mut state, &self.spec)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -639,36 +663,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
timer.observe_duration();
|
timer.observe_duration();
|
||||||
|
|
||||||
result
|
result
|
||||||
|
|
||||||
/*
|
|
||||||
if self
|
|
||||||
.fork_choice
|
|
||||||
.should_process_attestation(state, &attestation)?
|
|
||||||
{
|
|
||||||
// TODO: check validation.
|
|
||||||
let indexed_attestation = common::get_indexed_attestation(state, &attestation)?;
|
|
||||||
per_block_processing::is_valid_indexed_attestation(
|
|
||||||
state,
|
|
||||||
&indexed_attestation,
|
|
||||||
&self.spec,
|
|
||||||
)?;
|
|
||||||
self.fork_choice.process_attestation(&state, &attestation)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = self
|
|
||||||
.op_pool
|
|
||||||
.insert_attestation(attestation, state, &self.spec);
|
|
||||||
|
|
||||||
timer.observe_duration();
|
|
||||||
|
|
||||||
if result.is_ok() {
|
|
||||||
self.metrics.attestation_processing_successes.inc();
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
.map(|_| AttestationProcessingOutcome::Processed)
|
|
||||||
.map_err(|e| Error::AttestationValidationError(e))
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Accept some deposit and queue it for inclusion in an appropriate block.
|
/// Accept some deposit and queue it for inclusion in an appropriate block.
|
||||||
@ -735,7 +729,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
return Ok(BlockProcessingOutcome::GenesisBlock);
|
return Ok(BlockProcessingOutcome::GenesisBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
let block_root = block.block_header().canonical_root();
|
let block_root = block.canonical_root();
|
||||||
|
|
||||||
if block_root == self.genesis_block_root {
|
if block_root == self.genesis_block_root {
|
||||||
return Ok(BlockProcessingOutcome::GenesisBlock);
|
return Ok(BlockProcessingOutcome::GenesisBlock);
|
||||||
@ -781,6 +775,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
per_slot_processing(&mut state, &self.spec)?;
|
per_slot_processing(&mut state, &self.spec)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.build_committee_cache(RelativeEpoch::Previous, &self.spec)?;
|
||||||
state.build_committee_cache(RelativeEpoch::Current, &self.spec)?;
|
state.build_committee_cache(RelativeEpoch::Current, &self.spec)?;
|
||||||
|
|
||||||
// Apply the received block to its parent state (which has been transitioned into this
|
// Apply the received block to its parent state (which has been transitioned into this
|
||||||
|
@ -19,6 +19,7 @@ pub enum Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct ForkChoice<T: BeaconChainTypes> {
|
pub struct ForkChoice<T: BeaconChainTypes> {
|
||||||
|
store: Arc<T::Store>,
|
||||||
backend: T::LmdGhost,
|
backend: T::LmdGhost,
|
||||||
/// Used for resolving the `0x00..00` alias back to genesis.
|
/// Used for resolving the `0x00..00` alias back to genesis.
|
||||||
///
|
///
|
||||||
@ -38,6 +39,7 @@ impl<T: BeaconChainTypes> ForkChoice<T> {
|
|||||||
genesis_block_root: Hash256,
|
genesis_block_root: Hash256,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
store: store.clone(),
|
||||||
backend: T::LmdGhost::new(store, genesis_block, genesis_block_root),
|
backend: T::LmdGhost::new(store, genesis_block, genesis_block_root),
|
||||||
genesis_block_root,
|
genesis_block_root,
|
||||||
}
|
}
|
||||||
@ -117,9 +119,19 @@ impl<T: BeaconChainTypes> ForkChoice<T> {
|
|||||||
//
|
//
|
||||||
// https://github.com/ethereum/eth2.0-specs/blob/v0.7.0/specs/core/0_fork-choice.md
|
// https://github.com/ethereum/eth2.0-specs/blob/v0.7.0/specs/core/0_fork-choice.md
|
||||||
for attestation in &block.body.attestations {
|
for attestation in &block.body.attestations {
|
||||||
self.process_attestation(state, attestation, block)?;
|
let block = self
|
||||||
|
.store
|
||||||
|
.get::<BeaconBlock<T::EthSpec>>(&attestation.data.beacon_block_root)?
|
||||||
|
.ok_or_else(|| Error::MissingBlock(attestation.data.beacon_block_root))?;
|
||||||
|
|
||||||
|
self.process_attestation(state, attestation, &block)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This does not apply a vote to the block, it just makes fork choice aware of the block so
|
||||||
|
// it can still be identified as the head even if it doesn't have any votes.
|
||||||
|
//
|
||||||
|
// A case where a block without any votes can be the head is where it is the only child of
|
||||||
|
// a block that has the majority of votes applied to it.
|
||||||
self.backend.process_block(block, block_root)?;
|
self.backend.process_block(block, block_root)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -349,14 +349,12 @@ where
|
|||||||
agg_sig
|
agg_sig
|
||||||
};
|
};
|
||||||
|
|
||||||
let attestation = Attestation {
|
vec.push(Attestation {
|
||||||
aggregation_bits,
|
aggregation_bits,
|
||||||
data,
|
data,
|
||||||
custody_bits,
|
custody_bits,
|
||||||
signature,
|
signature,
|
||||||
};
|
})
|
||||||
|
|
||||||
vec.push(attestation)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -342,27 +342,29 @@ fn free_attestations_added_to_fork_choice_some_none() {
|
|||||||
let state = &harness.chain.head().beacon_state;
|
let state = &harness.chain.head().beacon_state;
|
||||||
let fork_choice = &harness.chain.fork_choice;
|
let fork_choice = &harness.chain.fork_choice;
|
||||||
|
|
||||||
let validators: Vec<usize> = (0..VALIDATOR_COUNT).collect();
|
let validator_slots: Vec<(usize, Slot)> = (0..VALIDATOR_COUNT)
|
||||||
let slots: Vec<Slot> = validators
|
.into_iter()
|
||||||
.iter()
|
.map(|validator_index| {
|
||||||
.map(|&v| {
|
let slot = state
|
||||||
state
|
.get_attestation_duties(validator_index, RelativeEpoch::Current)
|
||||||
.get_attestation_duties(v, RelativeEpoch::Current)
|
|
||||||
.expect("should get attester duties")
|
.expect("should get attester duties")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.slot
|
.slot;
|
||||||
|
|
||||||
|
(validator_index, slot)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let validator_slots: Vec<(&usize, Slot)> = validators.iter().zip(slots).collect();
|
|
||||||
|
|
||||||
for (validator, slot) in validator_slots.clone() {
|
for (validator, slot) in validator_slots.clone() {
|
||||||
let latest_message = fork_choice.latest_message(*validator);
|
let latest_message = fork_choice.latest_message(validator);
|
||||||
|
|
||||||
if slot <= num_blocks_produced && slot != 0 {
|
if slot <= num_blocks_produced && slot != 0 {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
latest_message.unwrap().1,
|
latest_message.unwrap().1,
|
||||||
slot,
|
slot,
|
||||||
"Latest message slot should be equal to attester duty."
|
"Latest message slot for {} should be equal to slot {}.",
|
||||||
|
validator,
|
||||||
|
slot
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
assert!(
|
assert!(
|
||||||
|
@ -136,6 +136,9 @@ pub enum AttestationInvalid {
|
|||||||
delay: u64,
|
delay: u64,
|
||||||
attestation: Slot,
|
attestation: Slot,
|
||||||
},
|
},
|
||||||
|
/// The attestation is attesting to a state that is later than itself. (Viz., attesting to the
|
||||||
|
/// future).
|
||||||
|
AttestsToFutureState { state: Slot, attestation: Slot },
|
||||||
/// Attestation slot is too far in the past to be included in a block.
|
/// Attestation slot is too far in the past to be included in a block.
|
||||||
IncludedTooLate { state: Slot, attestation: Slot },
|
IncludedTooLate { state: Slot, attestation: Slot },
|
||||||
/// Attestation target epoch does not match the current or previous epoch.
|
/// Attestation target epoch does not match the current or previous epoch.
|
||||||
|
@ -62,6 +62,17 @@ pub fn verify_attestation_for_state<T: EthSpec>(
|
|||||||
Invalid::BadShard
|
Invalid::BadShard
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let attestation_slot = state.get_attestation_data_slot(&data)?;
|
||||||
|
|
||||||
|
// An attestation cannot attest to a state that is later than itself.
|
||||||
|
verify!(
|
||||||
|
attestation_slot <= state.slot,
|
||||||
|
Invalid::AttestsToFutureState {
|
||||||
|
state: state.slot,
|
||||||
|
attestation: attestation_slot
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Verify the Casper FFG vote and crosslink data.
|
// Verify the Casper FFG vote and crosslink data.
|
||||||
let parent_crosslink = verify_casper_ffg_vote(attestation, state)?;
|
let parent_crosslink = verify_casper_ffg_vote(attestation, state)?;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user