lighthouse/eth2/state_processing/src/per_block_processing.rs

465 lines
15 KiB
Rust
Raw Normal View History

use self::verify_proposer_slashing::verify_proposer_slashing;
2019-03-06 06:14:54 +00:00
use errors::{BlockInvalid as Invalid, BlockProcessingError as Error, IntoWithIndex};
2019-03-09 21:33:17 +00:00
use rayon::prelude::*;
2019-03-17 01:25:37 +00:00
use ssz::{SignedRoot, TreeHash};
use types::*;
2019-03-09 21:33:17 +00:00
pub use self::verify_attester_slashing::{
gather_attester_slashing_indices, verify_attester_slashing,
};
pub use validate_attestation::{validate_attestation, validate_attestation_without_signature};
2019-03-17 01:25:37 +00:00
pub use verify_deposit::{get_existing_validator_index, verify_deposit, verify_deposit_index};
pub use verify_exit::verify_exit;
2019-03-09 21:55:45 +00:00
pub use verify_slashable_attestation::verify_slashable_attestation;
pub use verify_transfer::{execute_transfer, verify_transfer};
2019-03-06 06:14:54 +00:00
pub mod errors;
mod validate_attestation;
mod verify_attester_slashing;
mod verify_deposit;
mod verify_exit;
mod verify_proposer_slashing;
mod verify_slashable_attestation;
mod verify_transfer;
// Set to `true` to check the merkle proof that a deposit is in the eth1 deposit root.
//
// Presently disabled to make testing easier.
const VERIFY_DEPOSIT_MERKLE_PROOFS: bool = false;
2019-03-06 05:24:56 +00:00
/// Updates the state for a new block, whilst validating that the block is valid.
///
/// Returns `Ok(())` if the block is valid and the state was successfully updated. Otherwise
/// returns an error describing why the block was invalid or how the function failed to execute.
///
/// Spec v0.4.0
pub fn per_block_processing(
state: &mut BeaconState,
block: &BeaconBlock,
spec: &ChainSpec,
) -> Result<(), Error> {
per_block_processing_signature_optional(state, block, true, spec)
}
2019-03-06 05:24:56 +00:00
/// Updates the state for a new block, whilst validating that the block is valid, without actually
/// checking the block proposer signature.
///
/// Returns `Ok(())` if the block is valid and the state was successfully updated. Otherwise
/// returns an error describing why the block was invalid or how the function failed to execute.
///
/// Spec v0.4.0
pub fn per_block_processing_without_verifying_block_signature(
state: &mut BeaconState,
block: &BeaconBlock,
spec: &ChainSpec,
) -> Result<(), Error> {
per_block_processing_signature_optional(state, block, false, spec)
}
2019-03-06 05:24:56 +00:00
/// Updates the state for a new block, whilst validating that the block is valid, optionally
/// checking the block proposer signature.
///
/// Returns `Ok(())` if the block is valid and the state was successfully updated. Otherwise
/// returns an error describing why the block was invalid or how the function failed to execute.
///
/// Spec v0.4.0
fn per_block_processing_signature_optional(
mut state: &mut BeaconState,
block: &BeaconBlock,
should_verify_block_signature: bool,
spec: &ChainSpec,
) -> Result<(), Error> {
2019-03-17 01:25:37 +00:00
process_block_header(state, block, spec)?;
// Ensure the current and previous epoch cache is built.
state.build_epoch_cache(RelativeEpoch::Current, spec)?;
state.build_epoch_cache(RelativeEpoch::Previous, spec)?;
if should_verify_block_signature {
verify_block_signature(&state, &block, &spec)?;
}
process_randao(&mut state, &block, &spec)?;
2019-03-17 01:25:37 +00:00
process_eth1_data(&mut state, &block.body.eth1_data)?;
process_proposer_slashings(&mut state, &block.body.proposer_slashings, spec)?;
process_attester_slashings(&mut state, &block.body.attester_slashings, spec)?;
process_attestations(&mut state, &block.body.attestations, spec)?;
process_deposits(&mut state, &block.body.deposits, spec)?;
process_exits(&mut state, &block.body.voluntary_exits, spec)?;
process_transfers(&mut state, &block.body.transfers, spec)?;
Ok(())
}
2019-03-17 01:25:37 +00:00
/// Processes the block header.
///
/// Spec v0.5.0
pub fn process_block_header(
state: &BeaconState,
block: &BeaconBlock,
spec: &ChainSpec,
) -> Result<(), Error> {
verify!(block.slot == state.slot, Invalid::StateSlotMismatch);
verify!(
block.previous_block_root.as_bytes() == &state.latest_block_header.hash_tree_root()[..],
Invalid::ParentBlockRootMismatch
);
state.latest_block_header = block.into_temporary_header(spec);
Ok(())
}
/// Verifies the signature of a block.
///
2019-03-17 01:25:37 +00:00
/// Spec v0.5.0
pub fn verify_block_signature(
state: &BeaconState,
block: &BeaconBlock,
spec: &ChainSpec,
) -> Result<(), Error> {
2019-03-17 01:25:37 +00:00
let block_proposer = &state.validator_registry
[state.get_beacon_proposer_index(block.slot, RelativeEpoch::Current, spec)?];
let domain = spec.get_domain(
block.slot.epoch(spec.slots_per_epoch),
2019-03-17 01:25:37 +00:00
Domain::BeaconBlock,
&state.fork,
);
verify!(
2019-03-17 01:25:37 +00:00
block
.signature
2019-03-17 01:25:37 +00:00
.verify(&block.signed_root()[..], domain, &block_proposer.pubkey),
Invalid::BadSignature
);
Ok(())
}
/// Verifies the `randao_reveal` against the block's proposer pubkey and updates
/// `state.latest_randao_mixes`.
///
2019-03-17 01:25:37 +00:00
/// Spec v0.5.0
pub fn process_randao(
state: &mut BeaconState,
block: &BeaconBlock,
spec: &ChainSpec,
) -> Result<(), Error> {
2019-03-17 01:25:37 +00:00
let block_proposer = &state.validator_registry
[state.get_beacon_proposer_index(block.slot, RelativeEpoch::Current, spec)?];
2019-03-17 01:25:37 +00:00
// Verify the RANDAO is a valid signature of the proposer.
verify!(
2019-03-17 01:25:37 +00:00
block.body.randao_reveal.verify(
&state.current_epoch(spec).hash_tree_root()[..],
spec.get_domain(
block.slot.epoch(spec.slots_per_epoch),
Domain::Randao,
&state.fork
),
&block_proposer.pubkey
),
Invalid::BadRandaoSignature
);
2019-03-17 01:25:37 +00:00
// Update the current epoch RANDAO mix.
state.update_randao_mix(state.current_epoch(spec), &block.body.randao_reveal, spec)?;
Ok(())
}
/// Update the `state.eth1_data_votes` based upon the `eth1_data` provided.
///
2019-03-17 01:25:37 +00:00
/// Spec v0.5.0
pub fn process_eth1_data(state: &mut BeaconState, eth1_data: &Eth1Data) -> Result<(), Error> {
2019-03-17 01:25:37 +00:00
// Attempt to find a `Eth1DataVote` with matching `Eth1Data`.
let matching_eth1_vote_index = state
.eth1_data_votes
.iter()
.position(|vote| vote.eth1_data == *eth1_data);
2019-03-17 01:25:37 +00:00
// If a vote exists, increment it's `vote_count`. Otherwise, create a new `Eth1DataVote`.
if let Some(index) = matching_eth1_vote_index {
state.eth1_data_votes[index].vote_count += 1;
} else {
state.eth1_data_votes.push(Eth1DataVote {
eth1_data: eth1_data.clone(),
vote_count: 1,
});
}
Ok(())
}
/// Validates each `ProposerSlashing` and updates the state, short-circuiting on an invalid object.
///
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
/// an `Err` describing the invalid object or cause of failure.
///
2019-03-17 01:25:37 +00:00
/// Spec v0.5.0
pub fn process_proposer_slashings(
state: &mut BeaconState,
proposer_slashings: &[ProposerSlashing],
spec: &ChainSpec,
) -> Result<(), Error> {
verify!(
proposer_slashings.len() as u64 <= spec.max_proposer_slashings,
Invalid::MaxProposerSlashingsExceeded
);
2019-03-09 21:33:17 +00:00
// Verify proposer slashings in parallel.
proposer_slashings
.par_iter()
.enumerate()
.try_for_each(|(i, proposer_slashing)| {
verify_proposer_slashing(proposer_slashing, &state, spec)
.map_err(|e| e.into_with_index(i))
})?;
2019-03-17 01:25:37 +00:00
// Update the state.
2019-03-09 21:33:17 +00:00
for proposer_slashing in proposer_slashings {
state.slash_validator(proposer_slashing.proposer_index as usize, spec)?;
}
Ok(())
}
/// Validates each `AttesterSlsashing` and updates the state, short-circuiting on an invalid object.
///
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
/// an `Err` describing the invalid object or cause of failure.
///
2019-03-17 01:25:37 +00:00
/// Spec v0.5.0
pub fn process_attester_slashings(
state: &mut BeaconState,
attester_slashings: &[AttesterSlashing],
spec: &ChainSpec,
) -> Result<(), Error> {
verify!(
attester_slashings.len() as u64 <= spec.max_attester_slashings,
Invalid::MaxAttesterSlashingsExceed
);
2019-03-09 21:33:17 +00:00
2019-03-09 21:55:45 +00:00
// Verify the `SlashableAttestation`s in parallel (these are the resource-consuming objects, not
// the `AttesterSlashing`s themselves).
let mut slashable_attestations: Vec<&SlashableAttestation> =
Vec::with_capacity(attester_slashings.len() * 2);
for attester_slashing in attester_slashings {
slashable_attestations.push(&attester_slashing.slashable_attestation_1);
slashable_attestations.push(&attester_slashing.slashable_attestation_2);
}
// Verify slashable attestations in parallel.
slashable_attestations
2019-03-09 21:33:17 +00:00
.par_iter()
.enumerate()
2019-03-09 21:55:45 +00:00
.try_for_each(|(i, slashable_attestation)| {
verify_slashable_attestation(&state, slashable_attestation, spec)
2019-03-09 21:33:17 +00:00
.map_err(|e| e.into_with_index(i))
})?;
2019-03-09 21:55:45 +00:00
let all_slashable_attestations_have_been_checked = true;
2019-03-09 21:33:17 +00:00
2019-03-09 21:55:45 +00:00
// Gather the slashable indices and preform the final verification and update the state in series.
for (i, attester_slashing) in attester_slashings.iter().enumerate() {
2019-03-09 21:55:45 +00:00
let should_verify_slashable_attestations = !all_slashable_attestations_have_been_checked;
verify_attester_slashing(
&state,
&attester_slashing,
should_verify_slashable_attestations,
spec,
)
.map_err(|e| e.into_with_index(i))?;
2019-03-17 01:25:37 +00:00
let slashable_indices = gather_attester_slashing_indices(&state, &attester_slashing, spec)
.map_err(|e| e.into_with_index(i))?;
2019-03-09 21:55:45 +00:00
for i in slashable_indices {
state.slash_validator(i as usize, spec)?;
}
}
Ok(())
}
/// Validates each `Attestation` and updates the state, short-circuiting on an invalid object.
///
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
/// an `Err` describing the invalid object or cause of failure.
///
2019-03-17 01:25:37 +00:00
/// Spec v0.5.0
pub fn process_attestations(
state: &mut BeaconState,
attestations: &[Attestation],
spec: &ChainSpec,
) -> Result<(), Error> {
verify!(
attestations.len() as u64 <= spec.max_attestations,
Invalid::MaxAttestationsExceeded
);
2019-03-09 21:33:17 +00:00
// Ensure the previous epoch cache exists.
state.build_epoch_cache(RelativeEpoch::Previous, spec)?;
// Verify attestations in parallel.
attestations
.par_iter()
.enumerate()
.try_for_each(|(i, attestation)| {
validate_attestation(state, attestation, spec).map_err(|e| e.into_with_index(i))
})?;
2019-03-09 21:33:17 +00:00
// 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,
};
2019-03-17 01:25:37 +00:00
let attestation_epoch = attestation.data.slot.epoch(spec.slots_per_epoch);
if attestation_epoch == state.current_epoch(spec) {
state.current_epoch_attestations.push(pending_attestation)
} else if attestation_epoch == state.previous_epoch(spec) {
state.previous_epoch_attestations.push(pending_attestation)
}
}
Ok(())
}
/// Validates each `Deposit` and updates the state, short-circuiting on an invalid object.
///
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
/// an `Err` describing the invalid object or cause of failure.
///
2019-03-17 01:25:37 +00:00
/// Spec v0.5.0
pub fn process_deposits(
state: &mut BeaconState,
deposits: &[Deposit],
spec: &ChainSpec,
) -> Result<(), Error> {
verify!(
deposits.len() as u64 <= spec.max_deposits,
Invalid::MaxDepositsExceeded
);
2019-03-09 21:33:17 +00:00
// Verify deposits in parallel.
deposits
.par_iter()
.enumerate()
.try_for_each(|(i, deposit)| {
verify_deposit(state, deposit, VERIFY_DEPOSIT_MERKLE_PROOFS, spec)
.map_err(|e| e.into_with_index(i))
})?;
// Check `state.deposit_index` and update the state in series.
for (i, deposit) in deposits.iter().enumerate() {
verify_deposit_index(state, deposit).map_err(|e| e.into_with_index(i))?;
// Ensure the state's pubkey cache is fully up-to-date, it will be used to check to see if the
// depositing validator already exists in the registry.
state.update_pubkey_cache()?;
2019-03-09 21:33:17 +00:00
// Get an `Option<u64>` where `u64` is the validator index if this deposit public key
// already exists in the beacon_state.
//
// This function also verifies the withdrawal credentials.
let validator_index =
get_existing_validator_index(state, deposit).map_err(|e| e.into_with_index(i))?;
2019-03-09 21:33:17 +00:00
let deposit_data = &deposit.deposit_data;
let deposit_input = &deposit.deposit_data.deposit_input;
if let Some(index) = validator_index {
// Update the existing validator balance.
safe_add_assign!(
state.validator_balances[index as usize],
deposit_data.amount
);
} else {
// Create a new validator.
let validator = Validator {
pubkey: deposit_input.pubkey.clone(),
withdrawal_credentials: deposit_input.withdrawal_credentials.clone(),
activation_epoch: spec.far_future_epoch,
exit_epoch: spec.far_future_epoch,
withdrawable_epoch: spec.far_future_epoch,
initiated_exit: false,
slashed: false,
};
state.validator_registry.push(validator);
state.validator_balances.push(deposit_data.amount);
}
state.deposit_index += 1;
}
Ok(())
}
/// Validates each `Exit` and updates the state, short-circuiting on an invalid object.
///
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
/// an `Err` describing the invalid object or cause of failure.
///
2019-03-17 01:25:37 +00:00
/// Spec v0.5.0
pub fn process_exits(
state: &mut BeaconState,
voluntary_exits: &[VoluntaryExit],
spec: &ChainSpec,
) -> Result<(), Error> {
verify!(
voluntary_exits.len() as u64 <= spec.max_voluntary_exits,
Invalid::MaxExitsExceeded
);
2019-03-09 21:33:17 +00:00
// Verify exits in parallel.
voluntary_exits
.par_iter()
.enumerate()
.try_for_each(|(i, exit)| {
verify_exit(&state, exit, spec).map_err(|e| e.into_with_index(i))
})?;
// Update the state in series.
for exit in voluntary_exits {
state.initiate_validator_exit(exit.validator_index as usize);
}
Ok(())
}
/// Validates each `Transfer` and updates the state, short-circuiting on an invalid object.
///
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
/// an `Err` describing the invalid object or cause of failure.
///
2019-03-17 01:25:37 +00:00
/// Spec v0.5.0
pub fn process_transfers(
state: &mut BeaconState,
transfers: &[Transfer],
spec: &ChainSpec,
) -> Result<(), Error> {
verify!(
transfers.len() as u64 <= spec.max_transfers,
Invalid::MaxTransfersExceed
);
2019-03-09 21:36:49 +00:00
transfers
.par_iter()
.enumerate()
.try_for_each(|(i, transfer)| {
verify_transfer(&state, transfer, spec).map_err(|e| e.into_with_index(i))
})?;
for (i, transfer) in transfers.iter().enumerate() {
execute_transfer(state, transfer, spec).map_err(|e| e.into_with_index(i))?;
}
Ok(())
}