Merge pull request #293 from michaelsproul/operation-pool

Implement Operation Pool
This commit is contained in:
Paul Hauner
2019-04-02 13:53:22 +11:00
committed by GitHub
26 changed files with 1451 additions and 403 deletions

View File

@@ -1,4 +1,3 @@
use self::verify_proposer_slashing::verify_proposer_slashing;
use crate::common::slash_validator;
use errors::{BlockInvalid as Invalid, BlockProcessingError as Error, IntoWithIndex};
use rayon::prelude::*;
@@ -6,13 +5,20 @@ use ssz::{SignedRoot, TreeHash};
use types::*;
pub use self::verify_attester_slashing::{
gather_attester_slashing_indices, verify_attester_slashing,
gather_attester_slashing_indices, gather_attester_slashing_indices_modular,
verify_attester_slashing,
};
pub use self::verify_proposer_slashing::verify_proposer_slashing;
pub use validate_attestation::{
validate_attestation, validate_attestation_time_independent_only,
validate_attestation_without_signature,
};
pub use validate_attestation::{validate_attestation, validate_attestation_without_signature};
pub use verify_deposit::{get_existing_validator_index, verify_deposit, verify_deposit_index};
pub use verify_exit::verify_exit;
pub use verify_exit::{verify_exit, verify_exit_time_independent_only};
pub use verify_slashable_attestation::verify_slashable_attestation;
pub use verify_transfer::{execute_transfer, verify_transfer};
pub use verify_transfer::{
execute_transfer, verify_transfer, verify_transfer_time_independent_only,
};
pub mod errors;
mod validate_attestation;
@@ -316,13 +322,7 @@ pub fn process_attestations(
// Update the state in series.
for attestation in attestations {
let pending_attestation = PendingAttestation {
data: attestation.data.clone(),
aggregation_bitfield: attestation.aggregation_bitfield.clone(),
custody_bitfield: attestation.custody_bitfield.clone(),
inclusion_slot: state.slot,
};
let pending_attestation = PendingAttestation::from_attestation(attestation, state.slot);
let attestation_epoch = attestation.data.slot.epoch(spec.slots_per_epoch);
if attestation_epoch == state.current_epoch(spec) {

View File

@@ -390,6 +390,11 @@ pub enum TransferInvalid {
///
/// (state_slot, transfer_slot)
StateSlotMismatch(Slot, Slot),
/// The `transfer.slot` is in the past relative to the state slot.
///
///
/// (state_slot, transfer_slot)
TransferSlotInPast(Slot, Slot),
/// The `transfer.from` validator has been activated and is not withdrawable.
///
/// (from_validator)

View File

@@ -14,7 +14,16 @@ pub fn validate_attestation(
attestation: &Attestation,
spec: &ChainSpec,
) -> Result<(), Error> {
validate_attestation_signature_optional(state, attestation, spec, true)
validate_attestation_parametric(state, attestation, spec, true, false)
}
/// Like `validate_attestation` but doesn't run checks which may become true in future states.
pub fn validate_attestation_time_independent_only(
state: &BeaconState,
attestation: &Attestation,
spec: &ChainSpec,
) -> Result<(), Error> {
validate_attestation_parametric(state, attestation, spec, true, true)
}
/// Indicates if an `Attestation` is valid to be included in a block in the current epoch of the
@@ -28,7 +37,7 @@ pub fn validate_attestation_without_signature(
attestation: &Attestation,
spec: &ChainSpec,
) -> Result<(), Error> {
validate_attestation_signature_optional(state, attestation, spec, false)
validate_attestation_parametric(state, attestation, spec, false, false)
}
/// Indicates if an `Attestation` is valid to be included in a block in the current epoch of the
@@ -36,15 +45,13 @@ pub fn validate_attestation_without_signature(
///
///
/// Spec v0.5.0
fn validate_attestation_signature_optional(
fn validate_attestation_parametric(
state: &BeaconState,
attestation: &Attestation,
spec: &ChainSpec,
verify_signature: bool,
time_independent_only: bool,
) -> Result<(), Error> {
let state_epoch = state.slot.epoch(spec.slots_per_epoch);
let attestation_epoch = attestation.data.slot.epoch(spec.slots_per_epoch);
// Can't submit pre-historic attestations.
verify!(
attestation.data.slot >= spec.genesis_slot,
@@ -65,7 +72,8 @@ fn validate_attestation_signature_optional(
// Can't submit attestation too quickly.
verify!(
attestation.data.slot + spec.min_attestation_inclusion_delay <= state.slot,
time_independent_only
|| attestation.data.slot + spec.min_attestation_inclusion_delay <= state.slot,
Invalid::IncludedTooEarly {
state: state.slot,
delay: spec.min_attestation_inclusion_delay,
@@ -74,40 +82,8 @@ fn validate_attestation_signature_optional(
);
// Verify the justified epoch and root is correct.
if attestation_epoch >= state_epoch {
verify!(
attestation.data.source_epoch == state.current_justified_epoch,
Invalid::WrongJustifiedEpoch {
state: state.current_justified_epoch,
attestation: attestation.data.source_epoch,
is_current: true,
}
);
verify!(
attestation.data.source_root == state.current_justified_root,
Invalid::WrongJustifiedRoot {
state: state.current_justified_root,
attestation: attestation.data.source_root,
is_current: true,
}
);
} else {
verify!(
attestation.data.source_epoch == state.previous_justified_epoch,
Invalid::WrongJustifiedEpoch {
state: state.previous_justified_epoch,
attestation: attestation.data.source_epoch,
is_current: false,
}
);
verify!(
attestation.data.source_root == state.previous_justified_root,
Invalid::WrongJustifiedRoot {
state: state.previous_justified_root,
attestation: attestation.data.source_root,
is_current: true,
}
);
if !time_independent_only {
verify_justified_epoch_and_root(attestation, state, spec)?;
}
// Check that the crosslink data is valid.
@@ -188,6 +164,56 @@ fn validate_attestation_signature_optional(
Ok(())
}
/// Verify that the `source_epoch` and `source_root` of an `Attestation` correctly
/// match the current (or previous) justified epoch and root from the state.
///
/// Spec v0.5.0
fn verify_justified_epoch_and_root(
attestation: &Attestation,
state: &BeaconState,
spec: &ChainSpec,
) -> Result<(), Error> {
let state_epoch = state.slot.epoch(spec.slots_per_epoch);
let attestation_epoch = attestation.data.slot.epoch(spec.slots_per_epoch);
if attestation_epoch >= state_epoch {
verify!(
attestation.data.source_epoch == state.current_justified_epoch,
Invalid::WrongJustifiedEpoch {
state: state.current_justified_epoch,
attestation: attestation.data.source_epoch,
is_current: true,
}
);
verify!(
attestation.data.source_root == state.current_justified_root,
Invalid::WrongJustifiedRoot {
state: state.current_justified_root,
attestation: attestation.data.source_root,
is_current: true,
}
);
} else {
verify!(
attestation.data.source_epoch == state.previous_justified_epoch,
Invalid::WrongJustifiedEpoch {
state: state.previous_justified_epoch,
attestation: attestation.data.source_epoch,
is_current: false,
}
);
verify!(
attestation.data.source_root == state.previous_justified_root,
Invalid::WrongJustifiedRoot {
state: state.previous_justified_root,
attestation: attestation.data.source_root,
is_current: true,
}
);
}
Ok(())
}
/// Verifies an aggregate signature for some given `AttestationData`, returning `true` if the
/// `aggregate_signature` is valid.
///

View File

@@ -47,6 +47,25 @@ pub fn gather_attester_slashing_indices(
attester_slashing: &AttesterSlashing,
spec: &ChainSpec,
) -> Result<Vec<u64>, Error> {
gather_attester_slashing_indices_modular(
state,
attester_slashing,
|_, validator| validator.slashed,
spec,
)
}
/// Same as `gather_attester_slashing_indices` but allows the caller to specify the criteria
/// for determining whether a given validator should be considered slashed.
pub fn gather_attester_slashing_indices_modular<F>(
state: &BeaconState,
attester_slashing: &AttesterSlashing,
is_slashed: F,
spec: &ChainSpec,
) -> Result<Vec<u64>, Error>
where
F: Fn(u64, &Validator) -> bool,
{
let slashable_attestation_1 = &attester_slashing.slashable_attestation_1;
let slashable_attestation_2 = &attester_slashing.slashable_attestation_2;
@@ -57,7 +76,7 @@ pub fn gather_attester_slashing_indices(
.get(*i as usize)
.ok_or_else(|| Error::Invalid(Invalid::UnknownValidator(*i)))?;
if slashable_attestation_2.validator_indices.contains(&i) & !validator.slashed {
if slashable_attestation_2.validator_indices.contains(&i) & !is_slashed(*i, validator) {
// TODO: verify that we should reject any slashable attestation which includes a
// withdrawn validator. PH has asked the question on gitter, awaiting response.
verify!(

View File

@@ -12,6 +12,25 @@ pub fn verify_exit(
state: &BeaconState,
exit: &VoluntaryExit,
spec: &ChainSpec,
) -> Result<(), Error> {
verify_exit_parametric(state, exit, spec, false)
}
/// Like `verify_exit` but doesn't run checks which may become true in future states.
pub fn verify_exit_time_independent_only(
state: &BeaconState,
exit: &VoluntaryExit,
spec: &ChainSpec,
) -> Result<(), Error> {
verify_exit_parametric(state, exit, spec, true)
}
/// Parametric version of `verify_exit` that skips some checks if `time_independent_only` is true.
fn verify_exit_parametric(
state: &BeaconState,
exit: &VoluntaryExit,
spec: &ChainSpec,
time_independent_only: bool,
) -> Result<(), Error> {
let validator = state
.validator_registry
@@ -32,7 +51,7 @@ pub fn verify_exit(
// Exits must specify an epoch when they become valid; they are not valid before then.
verify!(
state.current_epoch(spec) >= exit.epoch,
time_independent_only || state.current_epoch(spec) >= exit.epoch,
Invalid::FutureEpoch {
state: state.current_epoch(spec),
exit: exit.epoch

View File

@@ -15,6 +15,25 @@ pub fn verify_transfer(
state: &BeaconState,
transfer: &Transfer,
spec: &ChainSpec,
) -> Result<(), Error> {
verify_transfer_parametric(state, transfer, spec, false)
}
/// Like `verify_transfer` but doesn't run checks which may become true in future states.
pub fn verify_transfer_time_independent_only(
state: &BeaconState,
transfer: &Transfer,
spec: &ChainSpec,
) -> Result<(), Error> {
verify_transfer_parametric(state, transfer, spec, true)
}
/// Parametric version of `verify_transfer` that allows some checks to be skipped.
fn verify_transfer_parametric(
state: &BeaconState,
transfer: &Transfer,
spec: &ChainSpec,
time_independent_only: bool,
) -> Result<(), Error> {
let sender_balance = *state
.validator_balances
@@ -27,17 +46,18 @@ pub fn verify_transfer(
.ok_or_else(|| Error::Invalid(Invalid::FeeOverflow(transfer.amount, transfer.fee)))?;
verify!(
sender_balance >= transfer.amount,
time_independent_only || sender_balance >= transfer.amount,
Invalid::FromBalanceInsufficient(transfer.amount, sender_balance)
);
verify!(
sender_balance >= transfer.fee,
time_independent_only || sender_balance >= transfer.fee,
Invalid::FromBalanceInsufficient(transfer.fee, sender_balance)
);
verify!(
(sender_balance == total_amount)
time_independent_only
|| (sender_balance == total_amount)
|| (sender_balance >= (total_amount + spec.min_deposit_amount)),
Invalid::InvalidResultingFromBalance(
sender_balance - total_amount,
@@ -45,10 +65,17 @@ pub fn verify_transfer(
)
);
verify!(
state.slot == transfer.slot,
Invalid::StateSlotMismatch(state.slot, transfer.slot)
);
if time_independent_only {
verify!(
state.slot <= transfer.slot,
Invalid::TransferSlotInPast(state.slot, transfer.slot)
);
} else {
verify!(
state.slot == transfer.slot,
Invalid::StateSlotMismatch(state.slot, transfer.slot)
);
}
let sender_validator = state
.validator_registry
@@ -57,7 +84,8 @@ pub fn verify_transfer(
let epoch = state.slot.epoch(spec.slots_per_epoch);
verify!(
sender_validator.is_withdrawable_at(epoch)
time_independent_only
|| sender_validator.is_withdrawable_at(epoch)
|| sender_validator.activation_epoch == spec.far_future_epoch,
Invalid::FromValidatorIneligableForTransfer(transfer.sender)
);