mirror of
https://gitlab.com/pulsechaincom/lighthouse-pulse.git
synced 2025-01-01 00:41:20 +00:00
Start new Error structure in state_processing
This commit is contained in:
parent
0be8e57fd3
commit
a15ed0acd3
@ -5,7 +5,11 @@ use int_to_bytes::int_to_bytes32;
|
|||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
use ssz::{ssz_encode, TreeHash};
|
use ssz::{ssz_encode, TreeHash};
|
||||||
use types::*;
|
use types::*;
|
||||||
|
use validate_attestation::validate_attestations;
|
||||||
|
|
||||||
|
pub use validate_attestation::{validate_attestation, validate_attestation_without_signature};
|
||||||
|
|
||||||
|
mod validate_attestation;
|
||||||
mod verify_slashable_attestation;
|
mod verify_slashable_attestation;
|
||||||
|
|
||||||
const PHASE_0_CUSTODY_BIT: bool = false;
|
const PHASE_0_CUSTODY_BIT: bool = false;
|
||||||
@ -41,7 +45,6 @@ pub enum Error {
|
|||||||
BeaconStateError(BeaconStateError),
|
BeaconStateError(BeaconStateError),
|
||||||
SlotProcessingError(SlotProcessingError),
|
SlotProcessingError(SlotProcessingError),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum AttestationValidationError {
|
pub enum AttestationValidationError {
|
||||||
IncludedTooEarly,
|
IncludedTooEarly,
|
||||||
@ -221,19 +224,10 @@ fn per_block_processing_signature_optional(
|
|||||||
verify_slashable_attestation(&mut state, &attester_slashing, spec)?;
|
verify_slashable_attestation(&mut state, &attester_slashing, spec)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
validate_attestations(&mut state, &block, spec);
|
||||||
* Attestations
|
|
||||||
*/
|
|
||||||
ensure!(
|
|
||||||
block.body.attestations.len() as u64 <= spec.max_attestations,
|
|
||||||
Error::MaxAttestationsExceeded
|
|
||||||
);
|
|
||||||
|
|
||||||
debug!("Verifying {} attestations.", block.body.attestations.len());
|
|
||||||
|
|
||||||
|
// Convert each attestation into a `PendingAttestation` and insert into the state.
|
||||||
for attestation in &block.body.attestations {
|
for attestation in &block.body.attestations {
|
||||||
validate_attestation(&state, attestation, spec)?;
|
|
||||||
|
|
||||||
let pending_attestation = PendingAttestation {
|
let pending_attestation = PendingAttestation {
|
||||||
data: attestation.data.clone(),
|
data: attestation.data.clone(),
|
||||||
aggregation_bitfield: attestation.aggregation_bitfield.clone(),
|
aggregation_bitfield: attestation.aggregation_bitfield.clone(),
|
||||||
@ -293,7 +287,7 @@ fn per_block_processing_signature_optional(
|
|||||||
);
|
);
|
||||||
ensure!(state.current_epoch(spec) >= exit.epoch, Error::BadExit);
|
ensure!(state.current_epoch(spec) >= exit.epoch, Error::BadExit);
|
||||||
let exit_message = {
|
let exit_message = {
|
||||||
let exit_struct = Exit {
|
let exit_struct = VoluntaryExit {
|
||||||
epoch: exit.epoch,
|
epoch: exit.epoch,
|
||||||
validator_index: exit.validator_index,
|
validator_index: exit.validator_index,
|
||||||
signature: spec.empty_signature.clone(),
|
signature: spec.empty_signature.clone(),
|
||||||
@ -317,110 +311,6 @@ fn per_block_processing_signature_optional(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate_attestation(
|
|
||||||
state: &BeaconState,
|
|
||||||
attestation: &Attestation,
|
|
||||||
spec: &ChainSpec,
|
|
||||||
) -> Result<(), AttestationValidationError> {
|
|
||||||
validate_attestation_signature_optional(state, attestation, spec, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn validate_attestation_without_signature(
|
|
||||||
state: &BeaconState,
|
|
||||||
attestation: &Attestation,
|
|
||||||
spec: &ChainSpec,
|
|
||||||
) -> Result<(), AttestationValidationError> {
|
|
||||||
validate_attestation_signature_optional(state, attestation, spec, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate_attestation_signature_optional(
|
|
||||||
state: &BeaconState,
|
|
||||||
attestation: &Attestation,
|
|
||||||
spec: &ChainSpec,
|
|
||||||
verify_signature: bool,
|
|
||||||
) -> Result<(), AttestationValidationError> {
|
|
||||||
trace!(
|
|
||||||
"validate_attestation_signature_optional: attestation epoch: {}",
|
|
||||||
attestation.data.slot.epoch(spec.slots_per_epoch)
|
|
||||||
);
|
|
||||||
ensure!(
|
|
||||||
attestation.data.slot + spec.min_attestation_inclusion_delay <= state.slot,
|
|
||||||
AttestationValidationError::IncludedTooEarly
|
|
||||||
);
|
|
||||||
ensure!(
|
|
||||||
attestation.data.slot + spec.slots_per_epoch >= state.slot,
|
|
||||||
AttestationValidationError::IncludedTooLate
|
|
||||||
);
|
|
||||||
if attestation.data.slot >= state.current_epoch_start_slot(spec) {
|
|
||||||
ensure!(
|
|
||||||
attestation.data.justified_epoch == state.justified_epoch,
|
|
||||||
AttestationValidationError::WrongJustifiedSlot
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
ensure!(
|
|
||||||
attestation.data.justified_epoch == state.previous_justified_epoch,
|
|
||||||
AttestationValidationError::WrongJustifiedSlot
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ensure!(
|
|
||||||
attestation.data.justified_block_root
|
|
||||||
== *state
|
|
||||||
.get_block_root(
|
|
||||||
attestation
|
|
||||||
.data
|
|
||||||
.justified_epoch
|
|
||||||
.start_slot(spec.slots_per_epoch),
|
|
||||||
&spec
|
|
||||||
)
|
|
||||||
.ok_or(AttestationValidationError::NoBlockRoot)?,
|
|
||||||
AttestationValidationError::WrongJustifiedRoot
|
|
||||||
);
|
|
||||||
let potential_crosslink = Crosslink {
|
|
||||||
shard_block_root: attestation.data.shard_block_root,
|
|
||||||
epoch: attestation.data.slot.epoch(spec.slots_per_epoch),
|
|
||||||
};
|
|
||||||
ensure!(
|
|
||||||
(attestation.data.latest_crosslink
|
|
||||||
== state.latest_crosslinks[attestation.data.shard as usize])
|
|
||||||
| (attestation.data.latest_crosslink == potential_crosslink),
|
|
||||||
AttestationValidationError::BadLatestCrosslinkRoot
|
|
||||||
);
|
|
||||||
if verify_signature {
|
|
||||||
let participants = state.get_attestation_participants(
|
|
||||||
&attestation.data,
|
|
||||||
&attestation.aggregation_bitfield,
|
|
||||||
spec,
|
|
||||||
)?;
|
|
||||||
trace!(
|
|
||||||
"slot: {}, shard: {}, participants: {:?}",
|
|
||||||
attestation.data.slot,
|
|
||||||
attestation.data.shard,
|
|
||||||
participants
|
|
||||||
);
|
|
||||||
let mut group_public_key = AggregatePublicKey::new();
|
|
||||||
for participant in participants {
|
|
||||||
group_public_key.add(&state.validator_registry[participant as usize].pubkey)
|
|
||||||
}
|
|
||||||
ensure!(
|
|
||||||
attestation.verify_signature(
|
|
||||||
&group_public_key,
|
|
||||||
PHASE_0_CUSTODY_BIT,
|
|
||||||
get_domain(
|
|
||||||
&state.fork,
|
|
||||||
attestation.data.slot.epoch(spec.slots_per_epoch),
|
|
||||||
spec.domain_attestation,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
AttestationValidationError::BadSignature
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ensure!(
|
|
||||||
attestation.data.shard_block_root == spec.zero_hash,
|
|
||||||
AttestationValidationError::ShardBlockRootNotZero
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_domain(fork: &Fork, epoch: Epoch, domain_type: u64) -> u64 {
|
fn get_domain(fork: &Fork, epoch: Epoch, domain_type: u64) -> u64 {
|
||||||
fork.get_domain(epoch, domain_type)
|
fork.get_domain(epoch, domain_type)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,238 @@
|
|||||||
|
use crate::errors::{AttestationInvalid as Invalid, AttestationValidationError as Error};
|
||||||
|
use ssz::TreeHash;
|
||||||
|
use types::beacon_state::helpers::*;
|
||||||
|
use types::*;
|
||||||
|
|
||||||
|
/// Validate the attestations in some block, converting each into a `PendingAttestation` which is
|
||||||
|
/// then added to `state.latest_attestations`.
|
||||||
|
///
|
||||||
|
/// Spec v0.4.0
|
||||||
|
pub fn validate_attestations(
|
||||||
|
state: &BeaconState,
|
||||||
|
block: &BeaconBlock,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
ensure!(
|
||||||
|
block.body.attestations.len() as u64 <= spec.max_attestations,
|
||||||
|
MaxAttestationsExceeded
|
||||||
|
);
|
||||||
|
|
||||||
|
for attestation in &block.body.attestations {
|
||||||
|
validate_attestation(&state, attestation, spec)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validate an attestation, checking the aggregate signature.
|
||||||
|
///
|
||||||
|
/// Spec v0.4.0
|
||||||
|
pub fn validate_attestation(
|
||||||
|
state: &BeaconState,
|
||||||
|
attestation: &Attestation,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
validate_attestation_signature_optional(state, attestation, spec, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validate an attestation, without checking the aggregate signature.
|
||||||
|
///
|
||||||
|
/// Spec v0.4.0
|
||||||
|
pub fn validate_attestation_without_signature(
|
||||||
|
state: &BeaconState,
|
||||||
|
attestation: &Attestation,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
validate_attestation_signature_optional(state, attestation, spec, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validate an attestation, optionally checking the aggregate signature.
|
||||||
|
///
|
||||||
|
/// Spec v0.2.0
|
||||||
|
fn validate_attestation_signature_optional(
|
||||||
|
state: &BeaconState,
|
||||||
|
attestation: &Attestation,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
verify_signature: bool,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
// Verify that `attestation.data.slot >= GENESIS_SLOT`.
|
||||||
|
ensure!(attestation.data.slot >= spec.genesis_slot, PreGenesis);
|
||||||
|
|
||||||
|
// Verify that `attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot`.
|
||||||
|
ensure!(
|
||||||
|
attestation.data.slot + spec.min_attestation_inclusion_delay <= state.slot,
|
||||||
|
IncludedTooEarly
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify that `state.slot < attestation.data.slot + SLOTS_PER_EPOCH`.
|
||||||
|
ensure!(
|
||||||
|
state.slot < attestation.data.slot + spec.slots_per_epoch,
|
||||||
|
IncludedTooLate
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify that `attestation.data.justified_epoch` is equal to `state.justified_epoch` if
|
||||||
|
// `slot_to_epoch(attestation.data.slot + 1) >= get_current_epoch(state) else
|
||||||
|
// state.previous_justified_epoch`.
|
||||||
|
if (attestation.data.slot + 1).epoch(spec.slots_per_epoch) >= state.current_epoch(spec) {
|
||||||
|
ensure!(
|
||||||
|
attestation.data.justified_epoch == state.justified_epoch,
|
||||||
|
WrongJustifiedSlot
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
ensure!(
|
||||||
|
attestation.data.justified_epoch == state.previous_justified_epoch,
|
||||||
|
WrongJustifiedSlot
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that `attestation.data.justified_block_root` is equal to `get_block_root(state,
|
||||||
|
// get_epoch_start_slot(attestation.data.justified_epoch))`.
|
||||||
|
ensure!(
|
||||||
|
attestation.data.justified_block_root
|
||||||
|
== *state
|
||||||
|
.get_block_root(
|
||||||
|
attestation
|
||||||
|
.data
|
||||||
|
.justified_epoch
|
||||||
|
.start_slot(spec.slots_per_epoch),
|
||||||
|
&spec
|
||||||
|
)
|
||||||
|
.ok_or(BeaconStateError::InsufficientBlockRoots)?,
|
||||||
|
WrongJustifiedRoot
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify that either:
|
||||||
|
//
|
||||||
|
// (i)`state.latest_crosslinks[attestation.data.shard] == attestation.data.latest_crosslink`,
|
||||||
|
//
|
||||||
|
// (ii) `state.latest_crosslinks[attestation.data.shard] ==
|
||||||
|
// Crosslink(crosslink_data_root=attestation.data.crosslink_data_root,
|
||||||
|
// epoch=slot_to_epoch(attestation.data.slot))`.
|
||||||
|
let potential_crosslink = Crosslink {
|
||||||
|
crosslink_data_root: attestation.data.crosslink_data_root,
|
||||||
|
epoch: attestation.data.slot.epoch(spec.slots_per_epoch),
|
||||||
|
};
|
||||||
|
ensure!(
|
||||||
|
(attestation.data.latest_crosslink
|
||||||
|
== state.latest_crosslinks[attestation.data.shard as usize])
|
||||||
|
| (state.latest_crosslinks[attestation.data.shard as usize] == potential_crosslink),
|
||||||
|
BadLatestCrosslinkRoot
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get the committee for this attestation
|
||||||
|
let (committee, _shard) = state
|
||||||
|
.get_crosslink_committees_at_slot(attestation.data.slot, spec)?
|
||||||
|
.iter()
|
||||||
|
.find(|(committee, shard)| *shard == attestation.data.shard)
|
||||||
|
.ok_or_else(|| Error::Invalid(Invalid::NoCommitteeForShard))?;
|
||||||
|
|
||||||
|
// Custody bitfield is all zeros (phase 0 requirement).
|
||||||
|
ensure!(
|
||||||
|
attestation.custody_bitfield.num_set_bits() == 0,
|
||||||
|
CustodyBitfieldHasSetBits
|
||||||
|
);
|
||||||
|
// Custody bitfield length is correct.
|
||||||
|
ensure!(
|
||||||
|
verify_bitfield_length(&attestation.aggregation_bitfield, committee.len()),
|
||||||
|
BadCustodyBitfieldLength
|
||||||
|
);
|
||||||
|
// Aggregation bitfield isn't empty.
|
||||||
|
ensure!(
|
||||||
|
attestation.aggregation_bitfield.num_set_bits() != 0,
|
||||||
|
AggregationBitfieldIsEmpty
|
||||||
|
);
|
||||||
|
// Aggregation bitfield length is correct.
|
||||||
|
ensure!(
|
||||||
|
verify_bitfield_length(&attestation.aggregation_bitfield, committee.len()),
|
||||||
|
BadAggregationBitfieldLength
|
||||||
|
);
|
||||||
|
|
||||||
|
if verify_signature {
|
||||||
|
ensure!(
|
||||||
|
verify_attestation_signature(
|
||||||
|
state,
|
||||||
|
committee,
|
||||||
|
&attestation.custody_bitfield,
|
||||||
|
&attestation.data,
|
||||||
|
&attestation.aggregate_signature,
|
||||||
|
spec
|
||||||
|
),
|
||||||
|
BadSignature
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// [TO BE REMOVED IN PHASE 1] Verify that `attestation.data.crosslink_data_root == ZERO_HASH`.
|
||||||
|
ensure!(
|
||||||
|
attestation.data.crosslink_data_root == spec.zero_hash,
|
||||||
|
ShardBlockRootNotZero
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verifies an aggregate signature for some given `AttestationData`, returning `true` if the
|
||||||
|
/// `aggregate_signature` is valid.
|
||||||
|
///
|
||||||
|
/// Returns `false` if:
|
||||||
|
/// - `aggregate_signature` was not signed correctly.
|
||||||
|
/// - `custody_bitfield` does not have a bit for each index of `committee`.
|
||||||
|
/// - A `validator_index` in `committee` is not in `state.validator_registry`.
|
||||||
|
fn verify_attestation_signature(
|
||||||
|
state: &BeaconState,
|
||||||
|
committee: &[usize],
|
||||||
|
custody_bitfield: &Bitfield,
|
||||||
|
attestation_data: &AttestationData,
|
||||||
|
aggregate_signature: &AggregateSignature,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> bool {
|
||||||
|
let mut aggregate_pubs = vec![AggregatePublicKey::new(); 2];
|
||||||
|
let mut message_exists = vec![false; 2];
|
||||||
|
|
||||||
|
for (i, v) in committee.iter().enumerate() {
|
||||||
|
let custody_bit = match custody_bitfield.get(i) {
|
||||||
|
Ok(bit) => bit,
|
||||||
|
// Invalidate signature if custody_bitfield.len() < committee
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
message_exists[custody_bit as usize] = true;
|
||||||
|
|
||||||
|
match state.validator_registry.get(*v as usize) {
|
||||||
|
Some(validator) => {
|
||||||
|
aggregate_pubs[custody_bit as usize].add(&validator.pubkey);
|
||||||
|
}
|
||||||
|
// Invalidate signature if validator index is unknown.
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message when custody bitfield is `false`
|
||||||
|
let message_0 = AttestationDataAndCustodyBit {
|
||||||
|
data: attestation_data.clone(),
|
||||||
|
custody_bit: false,
|
||||||
|
}
|
||||||
|
.hash_tree_root();
|
||||||
|
|
||||||
|
// Message when custody bitfield is `true`
|
||||||
|
let message_1 = AttestationDataAndCustodyBit {
|
||||||
|
data: attestation_data.clone(),
|
||||||
|
custody_bit: true,
|
||||||
|
}
|
||||||
|
.hash_tree_root();
|
||||||
|
|
||||||
|
let mut messages = vec![];
|
||||||
|
let mut keys = vec![];
|
||||||
|
|
||||||
|
// If any validator signed a message with a `false` custody bit.
|
||||||
|
if message_exists[0] {
|
||||||
|
messages.push(&message_0[..]);
|
||||||
|
keys.push(&aggregate_pubs[0]);
|
||||||
|
}
|
||||||
|
// If any validator signed a message with a `true` custody bit.
|
||||||
|
if message_exists[1] {
|
||||||
|
messages.push(&message_1[..]);
|
||||||
|
keys.push(&aggregate_pubs[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
aggregate_signature.verify_multiple(&messages[..], spec.domain_attestation, &keys[..])
|
||||||
|
}
|
30
eth2/state_processing/src/errors.rs
Normal file
30
eth2/state_processing/src/errors.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
use types::BeaconStateError;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum AttestationValidationError {
|
||||||
|
Invalid(AttestationInvalid),
|
||||||
|
ProcessingError(BeaconStateError),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum AttestationInvalid {
|
||||||
|
PreGenesis,
|
||||||
|
IncludedTooEarly,
|
||||||
|
IncludedTooLate,
|
||||||
|
WrongJustifiedSlot,
|
||||||
|
WrongJustifiedRoot,
|
||||||
|
BadLatestCrosslinkRoot,
|
||||||
|
CustodyBitfieldHasSetBits,
|
||||||
|
AggregationBitfieldIsEmpty,
|
||||||
|
BadAggregationBitfieldLength,
|
||||||
|
BadCustodyBitfieldLength,
|
||||||
|
NoCommitteeForShard,
|
||||||
|
BadSignature,
|
||||||
|
ShardBlockRootNotZero,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BeaconStateError> for AttestationValidationError {
|
||||||
|
fn from(e: BeaconStateError) -> AttestationValidationError {
|
||||||
|
AttestationValidationError::ProcessingError(e)
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,13 @@
|
|||||||
|
#[macro_use]
|
||||||
|
mod macros;
|
||||||
mod block_processable;
|
mod block_processable;
|
||||||
mod epoch_processable;
|
// mod epoch_processable;
|
||||||
mod slot_processable;
|
mod errors;
|
||||||
|
// mod slot_processable;
|
||||||
|
|
||||||
pub use block_processable::{
|
pub use block_processable::{
|
||||||
validate_attestation, validate_attestation_without_signature, BlockProcessable,
|
validate_attestation, validate_attestation_without_signature, BlockProcessable,
|
||||||
Error as BlockProcessingError,
|
Error as BlockProcessingError,
|
||||||
};
|
};
|
||||||
pub use epoch_processable::{EpochProcessable, Error as EpochProcessingError};
|
// pub use epoch_processable::{EpochProcessable, Error as EpochProcessingError};
|
||||||
pub use slot_processable::{Error as SlotProcessingError, SlotProcessable};
|
// pub use slot_processable::{Error as SlotProcessingError, SlotProcessable};
|
||||||
|
8
eth2/state_processing/src/macros.rs
Normal file
8
eth2/state_processing/src/macros.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#[macro_use]
|
||||||
|
macro_rules! ensure {
|
||||||
|
($condition: expr, $result: ident) => {
|
||||||
|
if !$condition {
|
||||||
|
return Err(Error::Invalid(Invalid::$result));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user