Start new Error structure in state_processing

This commit is contained in:
Paul Hauner 2019-03-06 10:22:19 +11:00
parent 0be8e57fd3
commit a15ed0acd3
No known key found for this signature in database
GPG Key ID: D362883A9218FCC6
5 changed files with 290 additions and 121 deletions

View File

@ -5,7 +5,11 @@ use int_to_bytes::int_to_bytes32;
use log::{debug, trace};
use ssz::{ssz_encode, TreeHash};
use types::*;
use validate_attestation::validate_attestations;
pub use validate_attestation::{validate_attestation, validate_attestation_without_signature};
mod validate_attestation;
mod verify_slashable_attestation;
const PHASE_0_CUSTODY_BIT: bool = false;
@ -41,7 +45,6 @@ pub enum Error {
BeaconStateError(BeaconStateError),
SlotProcessingError(SlotProcessingError),
}
#[derive(Debug, PartialEq)]
pub enum AttestationValidationError {
IncludedTooEarly,
@ -221,19 +224,10 @@ fn per_block_processing_signature_optional(
verify_slashable_attestation(&mut state, &attester_slashing, spec)?;
}
/*
* Attestations
*/
ensure!(
block.body.attestations.len() as u64 <= spec.max_attestations,
Error::MaxAttestationsExceeded
);
debug!("Verifying {} attestations.", block.body.attestations.len());
validate_attestations(&mut state, &block, spec);
// Convert each attestation into a `PendingAttestation` and insert into the state.
for attestation in &block.body.attestations {
validate_attestation(&state, attestation, spec)?;
let pending_attestation = PendingAttestation {
data: attestation.data.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);
let exit_message = {
let exit_struct = Exit {
let exit_struct = VoluntaryExit {
epoch: exit.epoch,
validator_index: exit.validator_index,
signature: spec.empty_signature.clone(),
@ -317,110 +311,6 @@ fn per_block_processing_signature_optional(
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 {
fork.get_domain(epoch, domain_type)
}

View File

@ -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[..])
}

View 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)
}
}

View File

@ -1,10 +1,13 @@
#[macro_use]
mod macros;
mod block_processable;
mod epoch_processable;
mod slot_processable;
// mod epoch_processable;
mod errors;
// mod slot_processable;
pub use block_processable::{
validate_attestation, validate_attestation_without_signature, BlockProcessable,
Error as BlockProcessingError,
};
pub use epoch_processable::{EpochProcessable, Error as EpochProcessingError};
pub use slot_processable::{Error as SlotProcessingError, SlotProcessable};
// pub use epoch_processable::{EpochProcessable, Error as EpochProcessingError};
// pub use slot_processable::{Error as SlotProcessingError, SlotProcessable};

View File

@ -0,0 +1,8 @@
#[macro_use]
macro_rules! ensure {
($condition: expr, $result: ident) => {
if !$condition {
return Err(Error::Invalid(Invalid::$result));
}
};
}