mirror of
https://gitlab.com/pulsechaincom/lighthouse-pulse.git
synced 2025-01-01 00:41:20 +00:00
Update block processing to v0.5.0
This commit is contained in:
parent
7f4af20212
commit
6bd2055a0a
@ -1,17 +1,14 @@
|
||||
use self::verify_proposer_slashing::verify_proposer_slashing;
|
||||
use errors::{BlockInvalid as Invalid, BlockProcessingError as Error, IntoWithIndex};
|
||||
use hashing::hash;
|
||||
use rayon::prelude::*;
|
||||
use ssz::{ssz_encode, SignedRoot, TreeHash};
|
||||
use ssz::{SignedRoot, TreeHash};
|
||||
use types::*;
|
||||
|
||||
pub use self::verify_attester_slashing::{
|
||||
gather_attester_slashing_indices, verify_attester_slashing,
|
||||
};
|
||||
pub use validate_attestation::{validate_attestation, validate_attestation_without_signature};
|
||||
pub use verify_deposit::{
|
||||
build_public_key_hashmap, get_existing_validator_index, verify_deposit, verify_deposit_index,
|
||||
};
|
||||
pub use verify_deposit::{get_existing_validator_index, verify_deposit, verify_deposit_index};
|
||||
pub use verify_exit::verify_exit;
|
||||
pub use verify_slashable_attestation::verify_slashable_attestation;
|
||||
pub use verify_transfer::{execute_transfer, verify_transfer};
|
||||
@ -72,8 +69,7 @@ fn per_block_processing_signature_optional(
|
||||
should_verify_block_signature: bool,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
// Verify that `block.slot == state.slot`.
|
||||
verify!(block.slot == state.slot, Invalid::StateSlotMismatch);
|
||||
process_block_header(state, block, spec)?;
|
||||
|
||||
// Ensure the current and previous epoch cache is built.
|
||||
state.build_epoch_cache(RelativeEpoch::Current, spec)?;
|
||||
@ -83,7 +79,7 @@ fn per_block_processing_signature_optional(
|
||||
verify_block_signature(&state, &block, &spec)?;
|
||||
}
|
||||
process_randao(&mut state, &block, &spec)?;
|
||||
process_eth1_data(&mut state, &block.eth1_data)?;
|
||||
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)?;
|
||||
@ -94,33 +90,47 @@ fn per_block_processing_signature_optional(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
pub fn verify_block_signature(
|
||||
state: &BeaconState,
|
||||
block: &BeaconBlock,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let block_proposer =
|
||||
&state.validator_registry[state.get_beacon_proposer_index(block.slot, spec)?];
|
||||
let block_proposer = &state.validator_registry
|
||||
[state.get_beacon_proposer_index(block.slot, RelativeEpoch::Current, spec)?];
|
||||
|
||||
let proposal = Proposal {
|
||||
slot: block.slot,
|
||||
shard: spec.beacon_chain_shard_number,
|
||||
block_root: Hash256::from_slice(&block.signed_root()[..]),
|
||||
signature: block.signature.clone(),
|
||||
};
|
||||
let domain = spec.get_domain(
|
||||
block.slot.epoch(spec.slots_per_epoch),
|
||||
Domain::Proposal,
|
||||
Domain::BeaconBlock,
|
||||
&state.fork,
|
||||
);
|
||||
|
||||
verify!(
|
||||
proposal
|
||||
block
|
||||
.signature
|
||||
.verify(&proposal.signed_root()[..], domain, &block_proposer.pubkey),
|
||||
.verify(&block.signed_root()[..], domain, &block_proposer.pubkey),
|
||||
Invalid::BadSignature
|
||||
);
|
||||
|
||||
@ -130,21 +140,18 @@ pub fn verify_block_signature(
|
||||
/// Verifies the `randao_reveal` against the block's proposer pubkey and updates
|
||||
/// `state.latest_randao_mixes`.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
pub fn process_randao(
|
||||
state: &mut BeaconState,
|
||||
block: &BeaconBlock,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
// Let `proposer = state.validator_registry[get_beacon_proposer_index(state, state.slot)]`.
|
||||
let block_proposer =
|
||||
&state.validator_registry[state.get_beacon_proposer_index(block.slot, spec)?];
|
||||
let block_proposer = &state.validator_registry
|
||||
[state.get_beacon_proposer_index(block.slot, RelativeEpoch::Current, spec)?];
|
||||
|
||||
// Verify that `bls_verify(pubkey=proposer.pubkey,
|
||||
// message_hash=hash_tree_root(get_current_epoch(state)), signature=block.randao_reveal,
|
||||
// domain=get_domain(state.fork, get_current_epoch(state), DOMAIN_RANDAO))`.
|
||||
// Verify the RANDAO is a valid signature of the proposer.
|
||||
verify!(
|
||||
block.randao_reveal.verify(
|
||||
block.body.randao_reveal.verify(
|
||||
&state.current_epoch(spec).hash_tree_root()[..],
|
||||
spec.get_domain(
|
||||
block.slot.epoch(spec.slots_per_epoch),
|
||||
@ -156,21 +163,23 @@ pub fn process_randao(
|
||||
Invalid::BadRandaoSignature
|
||||
);
|
||||
|
||||
// Update the state's RANDAO mix with the one revealed in the block.
|
||||
update_randao(state, &block.randao_reveal, spec)?;
|
||||
// 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.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
pub fn process_eth1_data(state: &mut BeaconState, eth1_data: &Eth1Data) -> Result<(), Error> {
|
||||
// Either increment the eth1_data vote count, or add a new eth1_data.
|
||||
// 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);
|
||||
|
||||
// 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 {
|
||||
@ -183,46 +192,12 @@ pub fn process_eth1_data(state: &mut BeaconState, eth1_data: &Eth1Data) -> Resul
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Updates the present randao mix.
|
||||
///
|
||||
/// Set `state.latest_randao_mixes[get_current_epoch(state) % LATEST_RANDAO_MIXES_LENGTH] =
|
||||
/// xor(get_randao_mix(state, get_current_epoch(state)), hash(block.randao_reveal))`.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
pub fn update_randao(
|
||||
state: &mut BeaconState,
|
||||
reveal: &Signature,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), BeaconStateError> {
|
||||
let hashed_reveal = {
|
||||
let encoded_signature = ssz_encode(reveal);
|
||||
Hash256::from_slice(&hash(&encoded_signature[..])[..])
|
||||
};
|
||||
|
||||
let current_epoch = state.slot.epoch(spec.slots_per_epoch);
|
||||
|
||||
let current_mix = state
|
||||
.get_randao_mix(current_epoch, spec)
|
||||
.ok_or_else(|| BeaconStateError::InsufficientRandaoMixes)?;
|
||||
|
||||
let new_mix = *current_mix ^ hashed_reveal;
|
||||
|
||||
let index = current_epoch.as_usize() % spec.latest_randao_mixes_length;
|
||||
|
||||
if index < state.latest_randao_mixes.len() {
|
||||
state.latest_randao_mixes[index] = new_mix;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(BeaconStateError::InsufficientRandaoMixes)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
pub fn process_proposer_slashings(
|
||||
state: &mut BeaconState,
|
||||
proposer_slashings: &[ProposerSlashing],
|
||||
@ -242,6 +217,7 @@ pub fn process_proposer_slashings(
|
||||
.map_err(|e| e.into_with_index(i))
|
||||
})?;
|
||||
|
||||
// Update the state.
|
||||
for proposer_slashing in proposer_slashings {
|
||||
state.slash_validator(proposer_slashing.proposer_index as usize, spec)?;
|
||||
}
|
||||
@ -254,7 +230,7 @@ pub fn process_proposer_slashings(
|
||||
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
|
||||
/// an `Err` describing the invalid object or cause of failure.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
pub fn process_attester_slashings(
|
||||
state: &mut BeaconState,
|
||||
attester_slashings: &[AttesterSlashing],
|
||||
@ -296,7 +272,7 @@ pub fn process_attester_slashings(
|
||||
)
|
||||
.map_err(|e| e.into_with_index(i))?;
|
||||
|
||||
let slashable_indices = gather_attester_slashing_indices(&state, &attester_slashing)
|
||||
let slashable_indices = gather_attester_slashing_indices(&state, &attester_slashing, spec)
|
||||
.map_err(|e| e.into_with_index(i))?;
|
||||
|
||||
for i in slashable_indices {
|
||||
@ -312,7 +288,7 @@ pub fn process_attester_slashings(
|
||||
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
|
||||
/// an `Err` describing the invalid object or cause of failure.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
pub fn process_attestations(
|
||||
state: &mut BeaconState,
|
||||
attestations: &[Attestation],
|
||||
@ -342,7 +318,14 @@ pub fn process_attestations(
|
||||
custody_bitfield: attestation.custody_bitfield.clone(),
|
||||
inclusion_slot: state.slot,
|
||||
};
|
||||
state.latest_attestations.push(pending_attestation);
|
||||
|
||||
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(())
|
||||
@ -353,7 +336,7 @@ pub fn process_attestations(
|
||||
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
|
||||
/// an `Err` describing the invalid object or cause of failure.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
pub fn process_deposits(
|
||||
state: &mut BeaconState,
|
||||
deposits: &[Deposit],
|
||||
@ -423,7 +406,7 @@ pub fn process_deposits(
|
||||
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
|
||||
/// an `Err` describing the invalid object or cause of failure.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
pub fn process_exits(
|
||||
state: &mut BeaconState,
|
||||
voluntary_exits: &[VoluntaryExit],
|
||||
@ -455,7 +438,7 @@ pub fn process_exits(
|
||||
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
|
||||
/// an `Err` describing the invalid object or cause of failure.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
pub fn process_transfers(
|
||||
state: &mut BeaconState,
|
||||
transfers: &[Transfer],
|
||||
|
@ -67,6 +67,7 @@ impl_from_beacon_state_error!(BlockProcessingError);
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum BlockInvalid {
|
||||
StateSlotMismatch,
|
||||
ParentBlockRootMismatch,
|
||||
BadSignature,
|
||||
BadRandaoSignature,
|
||||
MaxAttestationsExceeded,
|
||||
@ -112,45 +113,53 @@ pub enum AttestationValidationError {
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum AttestationInvalid {
|
||||
/// Attestation references a pre-genesis slot.
|
||||
///
|
||||
/// (genesis_slot, attestation_slot)
|
||||
PreGenesis(Slot, Slot),
|
||||
PreGenesis { genesis: Slot, attestation: Slot },
|
||||
/// Attestation included before the inclusion delay.
|
||||
///
|
||||
/// (state_slot, inclusion_delay, attestation_slot)
|
||||
IncludedTooEarly(Slot, u64, Slot),
|
||||
IncludedTooEarly {
|
||||
state: Slot,
|
||||
delay: u64,
|
||||
attestation: Slot,
|
||||
},
|
||||
/// Attestation slot is too far in the past to be included in a block.
|
||||
///
|
||||
/// (state_slot, attestation_slot)
|
||||
IncludedTooLate(Slot, Slot),
|
||||
IncludedTooLate { state: Slot, attestation: Slot },
|
||||
/// Attestation justified epoch does not match the states current or previous justified epoch.
|
||||
///
|
||||
/// (attestation_justified_epoch, state_epoch, used_previous_epoch)
|
||||
WrongJustifiedEpoch(Epoch, Epoch, bool),
|
||||
/// `is_current` is `true` if the attestation was compared to the
|
||||
/// `state.current_justified_epoch`, `false` if compared to `state.previous_justified_epoch`.
|
||||
WrongJustifiedEpoch {
|
||||
state: Epoch,
|
||||
attestation: Epoch,
|
||||
is_current: bool,
|
||||
},
|
||||
/// Attestation justified epoch root does not match root known to the state.
|
||||
///
|
||||
/// (state_justified_root, attestation_justified_root)
|
||||
WrongJustifiedRoot(Hash256, Hash256),
|
||||
/// `is_current` is `true` if the attestation was compared to the
|
||||
/// `state.current_justified_epoch`, `false` if compared to `state.previous_justified_epoch`.
|
||||
WrongJustifiedRoot {
|
||||
state: Hash256,
|
||||
attestation: Hash256,
|
||||
is_current: bool,
|
||||
},
|
||||
/// Attestation crosslink root does not match the state crosslink root for the attestations
|
||||
/// slot.
|
||||
BadLatestCrosslinkRoot,
|
||||
BadPreviousCrosslink,
|
||||
/// The custody bitfield has some bits set `true`. This is not allowed in phase 0.
|
||||
CustodyBitfieldHasSetBits,
|
||||
/// There are no set bits on the attestation -- an attestation must be signed by at least one
|
||||
/// validator.
|
||||
AggregationBitfieldIsEmpty,
|
||||
/// The custody bitfield length is not the smallest possible size to represent the committee.
|
||||
///
|
||||
/// (committee_len, bitfield_len)
|
||||
BadCustodyBitfieldLength(usize, usize),
|
||||
BadCustodyBitfieldLength {
|
||||
committee_len: usize,
|
||||
bitfield_len: usize,
|
||||
},
|
||||
/// The aggregation bitfield length is not the smallest possible size to represent the committee.
|
||||
///
|
||||
/// (committee_len, bitfield_len)
|
||||
BadAggregationBitfieldLength(usize, usize),
|
||||
/// There was no known committee for the given shard in the given slot.
|
||||
///
|
||||
/// (attestation_data_shard, attestation_data_slot)
|
||||
NoCommitteeForShard(u64, Slot),
|
||||
BadAggregationBitfieldLength {
|
||||
committee_len: usize,
|
||||
bitfield_len: usize,
|
||||
},
|
||||
/// There was no known committee in this `epoch` for the given shard and slot.
|
||||
NoCommitteeForShard { shard: u64, slot: Slot },
|
||||
/// The validator index was unknown.
|
||||
UnknownValidator(u64),
|
||||
/// The attestation signature verification failed.
|
||||
@ -188,6 +197,8 @@ pub enum AttesterSlashingInvalid {
|
||||
SlashableAttestation2Invalid(SlashableAttestationInvalid),
|
||||
/// The validator index is unknown. One cannot slash one who does not exist.
|
||||
UnknownValidator(u64),
|
||||
/// The specified validator has already been withdrawn.
|
||||
ValidatorAlreadyWithdrawn(u64),
|
||||
/// There were no indices able to be slashed.
|
||||
NoSlashableIndices,
|
||||
}
|
||||
@ -264,16 +275,12 @@ pub enum ProposerSlashingInvalid {
|
||||
///
|
||||
/// (proposal_1_slot, proposal_2_slot)
|
||||
ProposalSlotMismatch(Slot, Slot),
|
||||
/// The two proposal have different shards.
|
||||
///
|
||||
/// (proposal_1_shard, proposal_2_shard)
|
||||
ProposalShardMismatch(u64, u64),
|
||||
/// The two proposal have different block roots.
|
||||
///
|
||||
/// (proposal_1_root, proposal_2_root)
|
||||
ProposalBlockRootMismatch(Hash256, Hash256),
|
||||
/// The proposals are identical and therefore not slashable.
|
||||
ProposalsIdentical,
|
||||
/// The specified proposer has already been slashed.
|
||||
ProposerAlreadySlashed,
|
||||
/// The specified proposer has already been withdrawn.
|
||||
ProposerAlreadyWithdrawn(u64),
|
||||
/// The first proposal signature was invalid.
|
||||
BadProposal1Signature,
|
||||
/// The second proposal signature was invalid.
|
||||
@ -302,9 +309,7 @@ pub enum DepositValidationError {
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum DepositInvalid {
|
||||
/// The deposit index does not match the state index.
|
||||
///
|
||||
/// (state_index, deposit_index)
|
||||
BadIndex(u64, u64),
|
||||
BadIndex { state: u64, deposit: u64 },
|
||||
/// The proof-of-possession does not match the given pubkey.
|
||||
BadProofOfPossession,
|
||||
/// The withdrawal credentials for the depositing validator did not match the withdrawal
|
||||
@ -334,11 +339,14 @@ pub enum ExitValidationError {
|
||||
pub enum ExitInvalid {
|
||||
/// The specified validator is not in the state's validator registry.
|
||||
ValidatorUnknown(u64),
|
||||
AlreadyExited,
|
||||
/// The specified validator has a non-maximum exit epoch.
|
||||
AlreadyExited(u64),
|
||||
/// The specified validator has already initiated exit.
|
||||
AlreadyInitiatedExited(u64),
|
||||
/// The exit is for a future epoch.
|
||||
///
|
||||
/// (state_epoch, exit_epoch)
|
||||
FutureEpoch(Epoch, Epoch),
|
||||
FutureEpoch { state: Epoch, exit: Epoch },
|
||||
/// The validator has not been active for long enough.
|
||||
TooYoungToLeave { lifespan: Epoch, expected: u64 },
|
||||
/// The exit signature was not signed by the validator.
|
||||
BadSignature,
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use types::*;
|
||||
///
|
||||
/// Returns `Ok(())` if the `Attestation` is valid, otherwise indicates the reason for invalidity.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
pub fn validate_attestation(
|
||||
state: &BeaconState,
|
||||
attestation: &Attestation,
|
||||
@ -22,7 +22,7 @@ pub fn validate_attestation(
|
||||
///
|
||||
/// Returns `Ok(())` if the `Attestation` is valid, otherwise indicates the reason for invalidity.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
pub fn validate_attestation_without_signature(
|
||||
state: &BeaconState,
|
||||
attestation: &Attestation,
|
||||
@ -35,74 +35,83 @@ pub fn validate_attestation_without_signature(
|
||||
/// given state, optionally validating the aggregate signature.
|
||||
///
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
fn validate_attestation_signature_optional(
|
||||
state: &BeaconState,
|
||||
attestation: &Attestation,
|
||||
spec: &ChainSpec,
|
||||
verify_signature: bool,
|
||||
) -> Result<(), Error> {
|
||||
// Verify that `attestation.data.slot >= GENESIS_SLOT`.
|
||||
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,
|
||||
Invalid::PreGenesis(spec.genesis_slot, attestation.data.slot)
|
||||
Invalid::PreGenesis {
|
||||
genesis: spec.genesis_slot,
|
||||
attestation: attestation.data.slot
|
||||
}
|
||||
);
|
||||
|
||||
// Verify that `attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot`.
|
||||
// Can't submit attestations too far in history.
|
||||
verify!(
|
||||
state.slot <= attestation.data.slot + spec.slots_per_epoch,
|
||||
Invalid::IncludedTooLate {
|
||||
state: spec.genesis_slot,
|
||||
attestation: attestation.data.slot
|
||||
}
|
||||
);
|
||||
|
||||
// Can't submit attestation too quickly.
|
||||
verify!(
|
||||
attestation.data.slot + spec.min_attestation_inclusion_delay <= state.slot,
|
||||
Invalid::IncludedTooEarly(
|
||||
state.slot,
|
||||
spec.min_attestation_inclusion_delay,
|
||||
attestation.data.slot
|
||||
)
|
||||
Invalid::IncludedTooEarly {
|
||||
state: state.slot,
|
||||
delay: spec.min_attestation_inclusion_delay,
|
||||
attestation: attestation.data.slot
|
||||
}
|
||||
);
|
||||
|
||||
// Verify that `state.slot < attestation.data.slot + SLOTS_PER_EPOCH`.
|
||||
verify!(
|
||||
state.slot < attestation.data.slot + spec.slots_per_epoch,
|
||||
Invalid::IncludedTooLate(state.slot, attestation.data.slot)
|
||||
);
|
||||
|
||||
// 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) {
|
||||
// Verify the justified epoch and root is correct.
|
||||
if attestation_epoch >= state_epoch {
|
||||
verify!(
|
||||
attestation.data.justified_epoch == state.justified_epoch,
|
||||
Invalid::WrongJustifiedEpoch(
|
||||
attestation.data.justified_epoch,
|
||||
state.justified_epoch,
|
||||
false
|
||||
)
|
||||
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.justified_epoch == state.previous_justified_epoch,
|
||||
Invalid::WrongJustifiedEpoch(
|
||||
attestation.data.justified_epoch,
|
||||
state.previous_justified_epoch,
|
||||
true
|
||||
)
|
||||
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,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Verify that `attestation.data.justified_block_root` is equal to `get_block_root(state,
|
||||
// get_epoch_start_slot(attestation.data.justified_epoch))`.
|
||||
let justified_block_root = *state
|
||||
.get_block_root(
|
||||
attestation
|
||||
.data
|
||||
.justified_epoch
|
||||
.start_slot(spec.slots_per_epoch),
|
||||
&spec,
|
||||
)
|
||||
.ok_or(BeaconStateError::InsufficientBlockRoots)?;
|
||||
verify!(
|
||||
attestation.data.justified_block_root == justified_block_root,
|
||||
Invalid::WrongJustifiedRoot(justified_block_root, attestation.data.justified_block_root)
|
||||
);
|
||||
|
||||
// Check that the crosslink data is valid.
|
||||
//
|
||||
// Verify that either:
|
||||
//
|
||||
// (i)`state.latest_crosslinks[attestation.data.shard] == attestation.data.latest_crosslink`,
|
||||
@ -115,46 +124,59 @@ fn validate_attestation_signature_optional(
|
||||
epoch: attestation.data.slot.epoch(spec.slots_per_epoch),
|
||||
};
|
||||
verify!(
|
||||
(attestation.data.latest_crosslink
|
||||
(attestation.data.previous_crosslink
|
||||
== state.latest_crosslinks[attestation.data.shard as usize])
|
||||
| (state.latest_crosslinks[attestation.data.shard as usize] == potential_crosslink),
|
||||
Invalid::BadLatestCrosslinkRoot
|
||||
Invalid::BadPreviousCrosslink
|
||||
);
|
||||
|
||||
// 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(
|
||||
attestation.data.shard,
|
||||
attestation.data.slot,
|
||||
))
|
||||
})?;
|
||||
|
||||
// Custody bitfield is all zeros (phase 0 requirement).
|
||||
verify!(
|
||||
attestation.custody_bitfield.num_set_bits() == 0,
|
||||
Invalid::CustodyBitfieldHasSetBits
|
||||
);
|
||||
// Custody bitfield length is correct.
|
||||
verify!(
|
||||
verify_bitfield_length(&attestation.custody_bitfield, committee.len()),
|
||||
Invalid::BadCustodyBitfieldLength(committee.len(), attestation.custody_bitfield.len())
|
||||
);
|
||||
// Aggregation bitfield isn't empty.
|
||||
// Attestation must be non-empty!
|
||||
verify!(
|
||||
attestation.aggregation_bitfield.num_set_bits() != 0,
|
||||
Invalid::AggregationBitfieldIsEmpty
|
||||
);
|
||||
// Custody bitfield must be empty (be be removed in phase 1)
|
||||
verify!(
|
||||
attestation.custody_bitfield.num_set_bits() == 0,
|
||||
Invalid::CustodyBitfieldHasSetBits
|
||||
);
|
||||
|
||||
// Get the committee for the specific shard that this attestation is for.
|
||||
let crosslink_committee = state
|
||||
.get_crosslink_committees_at_slot(
|
||||
attestation.data.slot,
|
||||
RelativeEpoch::NextWithoutRegistryChange,
|
||||
spec,
|
||||
)?
|
||||
.iter()
|
||||
.find(|c| c.shard == attestation.data.shard)
|
||||
.ok_or_else(|| {
|
||||
Error::Invalid(Invalid::NoCommitteeForShard {
|
||||
shard: attestation.data.shard,
|
||||
slot: attestation.data.slot,
|
||||
})
|
||||
})?;
|
||||
let committee = &crosslink_committee.committee;
|
||||
|
||||
// Custody bitfield length is correct.
|
||||
//
|
||||
// This is not directly in the spec, but it is inferred.
|
||||
verify!(
|
||||
verify_bitfield_length(&attestation.custody_bitfield, committee.len()),
|
||||
Invalid::BadCustodyBitfieldLength {
|
||||
committee_len: committee.len(),
|
||||
bitfield_len: attestation.custody_bitfield.len()
|
||||
}
|
||||
);
|
||||
// Aggregation bitfield length is correct.
|
||||
//
|
||||
// This is not directly in the spec, but it is inferred.
|
||||
verify!(
|
||||
verify_bitfield_length(&attestation.aggregation_bitfield, committee.len()),
|
||||
Invalid::BadAggregationBitfieldLength(
|
||||
committee.len(),
|
||||
attestation.aggregation_bitfield.len()
|
||||
)
|
||||
Invalid::BadAggregationBitfieldLength {
|
||||
committee_len: committee.len(),
|
||||
bitfield_len: attestation.custody_bitfield.len()
|
||||
}
|
||||
);
|
||||
|
||||
if verify_signature {
|
||||
@ -171,7 +193,7 @@ fn validate_attestation_signature_optional(
|
||||
)?;
|
||||
}
|
||||
|
||||
// [TO BE REMOVED IN PHASE 1] Verify that `attestation.data.crosslink_data_root == ZERO_HASH`.
|
||||
// Crosslink data root is zero (to be removed in phase 1).
|
||||
verify!(
|
||||
attestation.data.crosslink_data_root == spec.zero_hash,
|
||||
Invalid::ShardBlockRootNotZero
|
||||
@ -188,7 +210,7 @@ fn validate_attestation_signature_optional(
|
||||
/// - `custody_bitfield` does not have a bit for each index of `committee`.
|
||||
/// - A `validator_index` in `committee` is not in `state.validator_registry`.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
fn verify_attestation_signature(
|
||||
state: &BeaconState,
|
||||
committee: &[usize],
|
||||
@ -204,10 +226,10 @@ fn verify_attestation_signature(
|
||||
|
||||
for (i, v) in committee.iter().enumerate() {
|
||||
let validator_signed = aggregation_bitfield.get(i).map_err(|_| {
|
||||
Error::Invalid(Invalid::BadAggregationBitfieldLength(
|
||||
committee.len(),
|
||||
aggregation_bitfield.len(),
|
||||
))
|
||||
Error::Invalid(Invalid::BadAggregationBitfieldLength {
|
||||
committee_len: committee.len(),
|
||||
bitfield_len: aggregation_bitfield.len(),
|
||||
})
|
||||
})?;
|
||||
|
||||
if validator_signed {
|
||||
@ -215,10 +237,10 @@ fn verify_attestation_signature(
|
||||
Ok(bit) => bit,
|
||||
// Invalidate signature if custody_bitfield.len() < committee
|
||||
Err(_) => {
|
||||
return Err(Error::Invalid(Invalid::BadCustodyBitfieldLength(
|
||||
committee.len(),
|
||||
custody_bitfield.len(),
|
||||
)));
|
||||
return Err(Error::Invalid(Invalid::BadCustodyBitfieldLength {
|
||||
committee_len: committee.len(),
|
||||
bitfield_len: aggregation_bitfield.len(),
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -7,7 +7,7 @@ use types::*;
|
||||
///
|
||||
/// Returns `Ok(())` if the `AttesterSlashing` is valid, otherwise indicates the reason for invalidity.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
pub fn verify_attester_slashing(
|
||||
state: &BeaconState,
|
||||
attester_slashing: &AttesterSlashing,
|
||||
@ -41,15 +41,16 @@ pub fn verify_attester_slashing(
|
||||
///
|
||||
/// Returns Ok(indices) if `indices.len() > 0`.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
pub fn gather_attester_slashing_indices(
|
||||
state: &BeaconState,
|
||||
attester_slashing: &AttesterSlashing,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Vec<u64>, Error> {
|
||||
let slashable_attestation_1 = &attester_slashing.slashable_attestation_1;
|
||||
let slashable_attestation_2 = &attester_slashing.slashable_attestation_2;
|
||||
|
||||
let mut slashable_indices = vec![];
|
||||
let mut slashable_indices = Vec::with_capacity(spec.max_indices_per_slashable_vote);
|
||||
for i in &slashable_attestation_1.validator_indices {
|
||||
let validator = state
|
||||
.validator_registry
|
||||
@ -57,11 +58,20 @@ pub fn gather_attester_slashing_indices(
|
||||
.ok_or_else(|| Error::Invalid(Invalid::UnknownValidator(*i)))?;
|
||||
|
||||
if slashable_attestation_2.validator_indices.contains(&i) & !validator.slashed {
|
||||
// TODO: verify that we should reject any slashable attestation which includes a
|
||||
// withdrawn validator. PH has asked the question on gitter, awaiting response.
|
||||
verify!(
|
||||
validator.withdrawable_epoch > state.slot.epoch(spec.slots_per_epoch),
|
||||
Invalid::ValidatorAlreadyWithdrawn(*i)
|
||||
);
|
||||
|
||||
slashable_indices.push(*i);
|
||||
}
|
||||
}
|
||||
|
||||
verify!(!slashable_indices.is_empty(), Invalid::NoSlashableIndices);
|
||||
|
||||
slashable_indices.shrink_to_fit();
|
||||
|
||||
Ok(slashable_indices)
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ pub type PublicKeyValidatorIndexHashmap = HashMap<PublicKey, u64>;
|
||||
///
|
||||
/// Note: this function is incomplete.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
pub fn verify_deposit(
|
||||
state: &BeaconState,
|
||||
deposit: &Deposit,
|
||||
@ -49,26 +49,25 @@ pub fn verify_deposit(
|
||||
|
||||
/// Verify that the `Deposit` index is correct.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
pub fn verify_deposit_index(state: &BeaconState, deposit: &Deposit) -> Result<(), Error> {
|
||||
verify!(
|
||||
deposit.index == state.deposit_index,
|
||||
Invalid::BadIndex(state.deposit_index, deposit.index)
|
||||
Invalid::BadIndex {
|
||||
state: state.deposit_index,
|
||||
deposit: deposit.index
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn build_public_key_hashmap(state: &BeaconState) -> PublicKeyValidatorIndexHashmap {
|
||||
let mut hashmap = HashMap::with_capacity(state.validator_registry.len());
|
||||
|
||||
for (i, validator) in state.validator_registry.iter().enumerate() {
|
||||
hashmap.insert(validator.pubkey.clone(), i as u64);
|
||||
}
|
||||
|
||||
hashmap
|
||||
}
|
||||
|
||||
/// Returns a `Some(validator index)` if a pubkey already exists in the `validator_registry`,
|
||||
/// otherwise returns `None`.
|
||||
///
|
||||
/// ## Errors
|
||||
///
|
||||
/// Errors if the state's `pubkey_cache` is not current.
|
||||
pub fn get_existing_validator_index(
|
||||
state: &BeaconState,
|
||||
deposit: &Deposit,
|
||||
@ -94,12 +93,12 @@ pub fn get_existing_validator_index(
|
||||
|
||||
/// Verify that a deposit is included in the state's eth1 deposit root.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
fn verify_deposit_merkle_proof(state: &BeaconState, deposit: &Deposit, spec: &ChainSpec) -> bool {
|
||||
let leaf = hash(&get_serialized_deposit_data(deposit));
|
||||
verify_merkle_proof(
|
||||
Hash256::from_slice(&leaf),
|
||||
&deposit.branch,
|
||||
&deposit.proof,
|
||||
spec.deposit_contract_tree_depth as usize,
|
||||
deposit.index as usize,
|
||||
state.latest_eth1_data.deposit_root,
|
||||
@ -108,7 +107,7 @@ fn verify_deposit_merkle_proof(state: &BeaconState, deposit: &Deposit, spec: &Ch
|
||||
|
||||
/// Helper struct for easily getting the serialized data generated by the deposit contract.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
#[derive(Encode)]
|
||||
struct SerializedDepositData {
|
||||
amount: u64,
|
||||
@ -119,7 +118,7 @@ struct SerializedDepositData {
|
||||
/// Return the serialized data generated by the deposit contract that is used to generate the
|
||||
/// merkle proof.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
fn get_serialized_deposit_data(deposit: &Deposit) -> Vec<u8> {
|
||||
let serialized_deposit_data = SerializedDepositData {
|
||||
amount: deposit.deposit_data.amount,
|
||||
|
@ -7,7 +7,7 @@ use types::*;
|
||||
///
|
||||
/// Returns `Ok(())` if the `Exit` is valid, otherwise indicates the reason for invalidity.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
pub fn verify_exit(
|
||||
state: &BeaconState,
|
||||
exit: &VoluntaryExit,
|
||||
@ -18,15 +18,35 @@ pub fn verify_exit(
|
||||
.get(exit.validator_index as usize)
|
||||
.ok_or_else(|| Error::Invalid(Invalid::ValidatorUnknown(exit.validator_index)))?;
|
||||
|
||||
// Verify that the validator has not yet exited.
|
||||
verify!(
|
||||
validator.exit_epoch
|
||||
> state.get_delayed_activation_exit_epoch(state.current_epoch(spec), spec),
|
||||
Invalid::AlreadyExited
|
||||
validator.exit_epoch == spec.far_future_epoch,
|
||||
Invalid::AlreadyExited(exit.validator_index)
|
||||
);
|
||||
|
||||
// Verify that the validator has not yet initiated.
|
||||
verify!(
|
||||
!validator.initiated_exit,
|
||||
Invalid::AlreadyInitiatedExited(exit.validator_index)
|
||||
);
|
||||
|
||||
// Exits must specify an epoch when they become valid; they are not valid before then.
|
||||
verify!(
|
||||
state.current_epoch(spec) >= exit.epoch,
|
||||
Invalid::FutureEpoch(state.current_epoch(spec), exit.epoch)
|
||||
Invalid::FutureEpoch {
|
||||
state: state.current_epoch(spec),
|
||||
exit: exit.epoch
|
||||
}
|
||||
);
|
||||
|
||||
// Must have been in the validator set long enough.
|
||||
let lifespan = state.slot.epoch(spec.slots_per_epoch) - validator.activation_epoch;
|
||||
verify!(
|
||||
lifespan >= spec.persistent_committee_period,
|
||||
Invalid::TooYoungToLeave {
|
||||
lifespan,
|
||||
expected: spec.persistent_committee_period,
|
||||
}
|
||||
);
|
||||
|
||||
let message = exit.signed_root();
|
||||
|
@ -7,7 +7,7 @@ use types::*;
|
||||
///
|
||||
/// Returns `Ok(())` if the `ProposerSlashing` is valid, otherwise indicates the reason for invalidity.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
pub fn verify_proposer_slashing(
|
||||
proposer_slashing: &ProposerSlashing,
|
||||
state: &BeaconState,
|
||||
@ -21,34 +21,28 @@ pub fn verify_proposer_slashing(
|
||||
})?;
|
||||
|
||||
verify!(
|
||||
proposer_slashing.proposal_1.slot == proposer_slashing.proposal_2.slot,
|
||||
proposer_slashing.header_1.slot == proposer_slashing.header_2.slot,
|
||||
Invalid::ProposalSlotMismatch(
|
||||
proposer_slashing.proposal_1.slot,
|
||||
proposer_slashing.proposal_2.slot
|
||||
proposer_slashing.header_1.slot,
|
||||
proposer_slashing.header_2.slot
|
||||
)
|
||||
);
|
||||
|
||||
verify!(
|
||||
proposer_slashing.proposal_1.shard == proposer_slashing.proposal_2.shard,
|
||||
Invalid::ProposalShardMismatch(
|
||||
proposer_slashing.proposal_1.shard,
|
||||
proposer_slashing.proposal_2.shard
|
||||
)
|
||||
);
|
||||
|
||||
verify!(
|
||||
proposer_slashing.proposal_1.block_root != proposer_slashing.proposal_2.block_root,
|
||||
Invalid::ProposalBlockRootMismatch(
|
||||
proposer_slashing.proposal_1.block_root,
|
||||
proposer_slashing.proposal_2.block_root
|
||||
)
|
||||
proposer_slashing.header_1 != proposer_slashing.header_2,
|
||||
Invalid::ProposalsIdentical
|
||||
);
|
||||
|
||||
verify!(!proposer.slashed, Invalid::ProposerAlreadySlashed);
|
||||
|
||||
verify!(
|
||||
verify_proposal_signature(
|
||||
&proposer_slashing.proposal_1,
|
||||
proposer.withdrawable_epoch > state.slot.epoch(spec.slots_per_epoch),
|
||||
Invalid::ProposerAlreadyWithdrawn(proposer_slashing.proposer_index)
|
||||
);
|
||||
|
||||
verify!(
|
||||
verify_header_signature(
|
||||
&proposer_slashing.header_1,
|
||||
&proposer.pubkey,
|
||||
&state.fork,
|
||||
spec
|
||||
@ -56,8 +50,8 @@ pub fn verify_proposer_slashing(
|
||||
Invalid::BadProposal1Signature
|
||||
);
|
||||
verify!(
|
||||
verify_proposal_signature(
|
||||
&proposer_slashing.proposal_2,
|
||||
verify_header_signature(
|
||||
&proposer_slashing.header_2,
|
||||
&proposer.pubkey,
|
||||
&state.fork,
|
||||
spec
|
||||
@ -71,17 +65,19 @@ pub fn verify_proposer_slashing(
|
||||
/// Verifies the signature of a proposal.
|
||||
///
|
||||
/// Returns `true` if the signature is valid.
|
||||
fn verify_proposal_signature(
|
||||
proposal: &Proposal,
|
||||
///
|
||||
/// Spec v0.5.0
|
||||
fn verify_header_signature(
|
||||
header: &BeaconBlockHeader,
|
||||
pubkey: &PublicKey,
|
||||
fork: &Fork,
|
||||
spec: &ChainSpec,
|
||||
) -> bool {
|
||||
let message = proposal.signed_root();
|
||||
let message = header.signed_root();
|
||||
let domain = spec.get_domain(
|
||||
proposal.slot.epoch(spec.slots_per_epoch),
|
||||
Domain::Proposal,
|
||||
header.slot.epoch(spec.slots_per_epoch),
|
||||
Domain::BeaconBlock,
|
||||
fork,
|
||||
);
|
||||
proposal.signature.verify(&message[..], domain, pubkey)
|
||||
header.signature.verify(&message[..], domain, pubkey)
|
||||
}
|
||||
|
@ -10,16 +10,16 @@ use types::*;
|
||||
///
|
||||
/// Note: this function is incomplete.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
pub fn verify_transfer(
|
||||
state: &BeaconState,
|
||||
transfer: &Transfer,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let from_balance = *state
|
||||
let sender_balance = *state
|
||||
.validator_balances
|
||||
.get(transfer.from as usize)
|
||||
.ok_or_else(|| Error::Invalid(Invalid::FromValidatorUnknown(transfer.from)))?;
|
||||
.get(transfer.sender as usize)
|
||||
.ok_or_else(|| Error::Invalid(Invalid::FromValidatorUnknown(transfer.sender)))?;
|
||||
|
||||
let total_amount = transfer
|
||||
.amount
|
||||
@ -27,19 +27,22 @@ pub fn verify_transfer(
|
||||
.ok_or_else(|| Error::Invalid(Invalid::FeeOverflow(transfer.amount, transfer.fee)))?;
|
||||
|
||||
verify!(
|
||||
from_balance >= transfer.amount,
|
||||
Invalid::FromBalanceInsufficient(transfer.amount, from_balance)
|
||||
sender_balance >= transfer.amount,
|
||||
Invalid::FromBalanceInsufficient(transfer.amount, sender_balance)
|
||||
);
|
||||
|
||||
verify!(
|
||||
from_balance >= transfer.fee,
|
||||
Invalid::FromBalanceInsufficient(transfer.fee, from_balance)
|
||||
sender_balance >= transfer.fee,
|
||||
Invalid::FromBalanceInsufficient(transfer.fee, sender_balance)
|
||||
);
|
||||
|
||||
verify!(
|
||||
(from_balance == total_amount)
|
||||
|| (from_balance >= (total_amount + spec.min_deposit_amount)),
|
||||
Invalid::InvalidResultingFromBalance(from_balance - total_amount, spec.min_deposit_amount)
|
||||
(sender_balance == total_amount)
|
||||
|| (sender_balance >= (total_amount + spec.min_deposit_amount)),
|
||||
Invalid::InvalidResultingFromBalance(
|
||||
sender_balance - total_amount,
|
||||
spec.min_deposit_amount
|
||||
)
|
||||
);
|
||||
|
||||
verify!(
|
||||
@ -47,25 +50,25 @@ pub fn verify_transfer(
|
||||
Invalid::StateSlotMismatch(state.slot, transfer.slot)
|
||||
);
|
||||
|
||||
let from_validator = state
|
||||
let sender_validator = state
|
||||
.validator_registry
|
||||
.get(transfer.from as usize)
|
||||
.ok_or_else(|| Error::Invalid(Invalid::FromValidatorUnknown(transfer.from)))?;
|
||||
.get(transfer.sender as usize)
|
||||
.ok_or_else(|| Error::Invalid(Invalid::FromValidatorUnknown(transfer.sender)))?;
|
||||
let epoch = state.slot.epoch(spec.slots_per_epoch);
|
||||
|
||||
verify!(
|
||||
from_validator.is_withdrawable_at(epoch)
|
||||
|| from_validator.activation_epoch == spec.far_future_epoch,
|
||||
Invalid::FromValidatorIneligableForTransfer(transfer.from)
|
||||
sender_validator.is_withdrawable_at(epoch)
|
||||
|| sender_validator.activation_epoch == spec.far_future_epoch,
|
||||
Invalid::FromValidatorIneligableForTransfer(transfer.sender)
|
||||
);
|
||||
|
||||
let transfer_withdrawal_credentials = Hash256::from_slice(
|
||||
&get_withdrawal_credentials(&transfer.pubkey, spec.bls_withdrawal_prefix_byte)[..],
|
||||
);
|
||||
verify!(
|
||||
from_validator.withdrawal_credentials == transfer_withdrawal_credentials,
|
||||
sender_validator.withdrawal_credentials == transfer_withdrawal_credentials,
|
||||
Invalid::WithdrawalCredentialsMismatch(
|
||||
from_validator.withdrawal_credentials,
|
||||
sender_validator.withdrawal_credentials,
|
||||
transfer_withdrawal_credentials
|
||||
)
|
||||
);
|
||||
@ -97,16 +100,17 @@ pub fn execute_transfer(
|
||||
transfer: &Transfer,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let from_balance = *state
|
||||
let sender_balance = *state
|
||||
.validator_balances
|
||||
.get(transfer.from as usize)
|
||||
.ok_or_else(|| Error::Invalid(Invalid::FromValidatorUnknown(transfer.from)))?;
|
||||
let to_balance = *state
|
||||
.get(transfer.sender as usize)
|
||||
.ok_or_else(|| Error::Invalid(Invalid::FromValidatorUnknown(transfer.sender)))?;
|
||||
let recipient_balance = *state
|
||||
.validator_balances
|
||||
.get(transfer.to as usize)
|
||||
.ok_or_else(|| Error::Invalid(Invalid::ToValidatorUnknown(transfer.to)))?;
|
||||
.get(transfer.recipient as usize)
|
||||
.ok_or_else(|| Error::Invalid(Invalid::ToValidatorUnknown(transfer.recipient)))?;
|
||||
|
||||
let proposer_index = state.get_beacon_proposer_index(state.slot, spec)?;
|
||||
let proposer_index =
|
||||
state.get_beacon_proposer_index(state.slot, RelativeEpoch::Current, spec)?;
|
||||
let proposer_balance = state.validator_balances[proposer_index];
|
||||
|
||||
let total_amount = transfer
|
||||
@ -114,14 +118,22 @@ pub fn execute_transfer(
|
||||
.checked_add(transfer.fee)
|
||||
.ok_or_else(|| Error::Invalid(Invalid::FeeOverflow(transfer.amount, transfer.fee)))?;
|
||||
|
||||
state.validator_balances[transfer.from as usize] =
|
||||
from_balance.checked_sub(total_amount).ok_or_else(|| {
|
||||
Error::Invalid(Invalid::FromBalanceInsufficient(total_amount, from_balance))
|
||||
state.validator_balances[transfer.sender as usize] =
|
||||
sender_balance.checked_sub(total_amount).ok_or_else(|| {
|
||||
Error::Invalid(Invalid::FromBalanceInsufficient(
|
||||
total_amount,
|
||||
sender_balance,
|
||||
))
|
||||
})?;
|
||||
|
||||
state.validator_balances[transfer.to as usize] = to_balance
|
||||
state.validator_balances[transfer.recipient as usize] = recipient_balance
|
||||
.checked_add(transfer.amount)
|
||||
.ok_or_else(|| Error::Invalid(Invalid::ToBalanceOverflow(to_balance, transfer.amount)))?;
|
||||
.ok_or_else(|| {
|
||||
Error::Invalid(Invalid::ToBalanceOverflow(
|
||||
recipient_balance,
|
||||
transfer.amount,
|
||||
))
|
||||
})?;
|
||||
|
||||
state.validator_balances[proposer_index] =
|
||||
proposer_balance.checked_add(transfer.fee).ok_or_else(|| {
|
||||
|
@ -9,7 +9,7 @@ pub enum Error {
|
||||
|
||||
/// Advances a state forward by one slot, performing per-epoch processing if required.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
pub fn per_slot_processing(
|
||||
state: &mut BeaconState,
|
||||
previous_block_root: Hash256,
|
||||
@ -22,29 +22,9 @@ pub fn per_slot_processing(
|
||||
|
||||
state.slot += 1;
|
||||
|
||||
update_block_roots(state, previous_block_root, spec);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Updates the state's block roots as per-slot processing is performed.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
pub fn update_block_roots(state: &mut BeaconState, previous_block_root: Hash256, spec: &ChainSpec) {
|
||||
state.latest_block_roots[(state.slot.as_usize() - 1) % spec.latest_block_roots_length] =
|
||||
previous_block_root;
|
||||
|
||||
if state.slot.as_usize() % spec.latest_block_roots_length == 0 {
|
||||
let root = merkle_root(&state.latest_block_roots[..]);
|
||||
state.batched_block_roots.push(root);
|
||||
}
|
||||
}
|
||||
|
||||
fn merkle_root(_input: &[Hash256]) -> Hash256 {
|
||||
// TODO: implement correctly.
|
||||
Hash256::zero()
|
||||
}
|
||||
|
||||
impl From<BeaconStateError> for Error {
|
||||
fn from(e: BeaconStateError) -> Error {
|
||||
Error::BeaconStateError(e)
|
||||
|
@ -2,11 +2,11 @@ use self::epoch_cache::EpochCache;
|
||||
use crate::test_utils::TestRandom;
|
||||
use crate::{validator_registry::get_active_validator_indices, *};
|
||||
use int_to_bytes::int_to_bytes32;
|
||||
use log::{debug, trace};
|
||||
use log::trace;
|
||||
use pubkey_cache::PubkeyCache;
|
||||
use rand::RngCore;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz::{hash, SignedRoot, TreeHash};
|
||||
use ssz::{hash, ssz_encode, SignedRoot, TreeHash};
|
||||
use ssz_derive::{Decode, Encode, TreeHash};
|
||||
use std::collections::HashMap;
|
||||
use test_random_derive::TestRandom;
|
||||
@ -31,12 +31,14 @@ pub enum Error {
|
||||
UnableToShuffle,
|
||||
UnknownValidator,
|
||||
InvalidBitfield,
|
||||
ValidatorIsWithdrawable,
|
||||
InsufficientRandaoMixes,
|
||||
InsufficientValidators,
|
||||
InsufficientBlockRoots,
|
||||
InsufficientIndexRoots,
|
||||
InsufficientAttestations,
|
||||
InsufficientCommittees,
|
||||
InsufficientSlashedBalances,
|
||||
EpochCacheUninitialized(RelativeEpoch),
|
||||
PubkeyCacheInconsistent,
|
||||
PubkeyCacheIncomplete {
|
||||
@ -377,10 +379,37 @@ impl BeaconState {
|
||||
}
|
||||
}
|
||||
|
||||
/// XOR-assigns the existing `epoch` randao mix with the hash of the `signature`.
|
||||
///
|
||||
/// # Errors:
|
||||
///
|
||||
/// See `Self::get_randao_mix`.
|
||||
///
|
||||
/// Spec v0.5.0
|
||||
pub fn update_randao_mix(
|
||||
&mut self,
|
||||
epoch: Epoch,
|
||||
signature: &Signature,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let i = epoch.as_usize() % spec.latest_randao_mixes_length;
|
||||
|
||||
let signature_hash = Hash256::from_slice(&hash(&ssz_encode(signature)));
|
||||
|
||||
self.latest_randao_mixes[i] = *self.get_randao_mix(epoch, spec)? ^ signature_hash;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return the randao mix at a recent ``epoch``.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
pub fn get_randao_mix(&self, epoch: Epoch, spec: &ChainSpec) -> Option<&Hash256> {
|
||||
/// # Errors:
|
||||
/// - `InsufficientRandaoMixes` if `self.latest_randao_mixes` is shorter than
|
||||
/// `spec.latest_randao_mixes_length`.
|
||||
/// - `EpochOutOfBounds` if the state no longer stores randao mixes for the given `epoch`.
|
||||
///
|
||||
/// Spec v0.5.0
|
||||
pub fn get_randao_mix(&self, epoch: Epoch, spec: &ChainSpec) -> Result<&Hash256, Error> {
|
||||
let current_epoch = self.current_epoch(spec);
|
||||
|
||||
if (current_epoch - (spec.latest_randao_mixes_length as u64) < epoch)
|
||||
@ -388,8 +417,9 @@ impl BeaconState {
|
||||
{
|
||||
self.latest_randao_mixes
|
||||
.get(epoch.as_usize() % spec.latest_randao_mixes_length)
|
||||
.ok_or_else(|| Error::InsufficientRandaoMixes)
|
||||
} else {
|
||||
None
|
||||
Err(Error::EpochOutOfBounds)
|
||||
}
|
||||
}
|
||||
|
||||
@ -418,8 +448,7 @@ impl BeaconState {
|
||||
/// Spec v0.4.0
|
||||
pub fn generate_seed(&self, epoch: Epoch, spec: &ChainSpec) -> Result<Hash256, Error> {
|
||||
let mut input = self
|
||||
.get_randao_mix(epoch - spec.min_seed_lookahead, spec)
|
||||
.ok_or_else(|| Error::InsufficientRandaoMixes)?
|
||||
.get_randao_mix(epoch - spec.min_seed_lookahead, spec)?
|
||||
.as_bytes()
|
||||
.to_vec();
|
||||
|
||||
@ -601,7 +630,7 @@ impl BeaconState {
|
||||
|
||||
/// Initiate an exit for the validator of the given `index`.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
pub fn initiate_validator_exit(&mut self, validator_index: usize) {
|
||||
self.validator_registry[validator_index].initiated_exit = true;
|
||||
}
|
||||
@ -622,7 +651,7 @@ impl BeaconState {
|
||||
|
||||
/// Slash the validator with index ``index``.
|
||||
///
|
||||
/// Spec v0.4.0
|
||||
/// Spec v0.5.0
|
||||
pub fn slash_validator(
|
||||
&mut self,
|
||||
validator_index: usize,
|
||||
@ -634,26 +663,27 @@ impl BeaconState {
|
||||
.validator_registry
|
||||
.get(validator_index)
|
||||
.ok_or_else(|| Error::UnknownValidator)?;
|
||||
let effective_balance = self.get_effective_balance(validator_index, spec)?;
|
||||
|
||||
// A validator that is withdrawn cannot be slashed.
|
||||
//
|
||||
// This constraint will be lifted in Phase 0.
|
||||
if self.slot
|
||||
>= validator
|
||||
.withdrawable_epoch
|
||||
.start_slot(spec.slots_per_epoch)
|
||||
{
|
||||
return Err(Error::SlotOutOfBounds);
|
||||
return Err(Error::ValidatorIsWithdrawable);
|
||||
}
|
||||
|
||||
self.exit_validator(validator_index, spec);
|
||||
|
||||
let effective_balance = self.get_effective_balance(validator_index, spec)?;
|
||||
|
||||
self.latest_slashed_balances[current_epoch.as_usize() % spec.latest_slashed_exit_length] +=
|
||||
effective_balance;
|
||||
self.increment_current_epoch_slashed_balances(effective_balance, spec)?;
|
||||
|
||||
let whistleblower_index =
|
||||
self.get_beacon_proposer_index(self.slot, RelativeEpoch::Current, spec)?;
|
||||
let whistleblower_reward = effective_balance / spec.whistleblower_reward_quotient;
|
||||
|
||||
let whistleblower_reward = effective_balance;
|
||||
safe_add_assign!(
|
||||
self.validator_balances[whistleblower_index as usize],
|
||||
whistleblower_reward
|
||||
@ -662,14 +692,31 @@ impl BeaconState {
|
||||
self.validator_balances[validator_index],
|
||||
whistleblower_reward
|
||||
);
|
||||
|
||||
self.validator_registry[validator_index].slashed = true;
|
||||
|
||||
self.validator_registry[validator_index].withdrawable_epoch =
|
||||
current_epoch + Epoch::from(spec.latest_slashed_exit_length);
|
||||
|
||||
debug!(
|
||||
"Whistleblower {} penalized validator {}.",
|
||||
whistleblower_index, validator_index
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Increment `self.latest_slashed_balances` with a slashing from the current epoch.
|
||||
///
|
||||
/// Spec v0.5.0.
|
||||
fn increment_current_epoch_slashed_balances(
|
||||
&mut self,
|
||||
increment: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let current_epoch = self.current_epoch(spec);
|
||||
|
||||
let slashed_balances_index = current_epoch.as_usize() % spec.latest_slashed_exit_length;
|
||||
if slashed_balances_index >= self.latest_slashed_balances.len() {
|
||||
return Err(Error::InsufficientSlashedBalances);
|
||||
}
|
||||
|
||||
self.latest_slashed_balances[slashed_balances_index] += increment;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ pub struct ChainSpec {
|
||||
pub shard_count: u64,
|
||||
pub target_committee_size: u64,
|
||||
pub max_balance_churn_quotient: u64,
|
||||
pub max_indices_per_slashable_vote: u64,
|
||||
pub max_indices_per_slashable_vote: usize,
|
||||
pub max_exit_dequeues_per_epoch: u64,
|
||||
pub shuffle_round_count: u8,
|
||||
|
||||
|
@ -11,8 +11,8 @@ use test_random_derive::TestRandom;
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)]
|
||||
pub struct ProposerSlashing {
|
||||
pub proposer_index: u64,
|
||||
pub proposal_1: BeaconBlockHeader,
|
||||
pub proposal_2: BeaconBlockHeader,
|
||||
pub header_1: BeaconBlockHeader,
|
||||
pub header_2: BeaconBlockHeader,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -25,7 +25,7 @@ impl TestingProposerSlashingBuilder {
|
||||
let hash_1 = Hash256::from([1; 32]);
|
||||
let hash_2 = Hash256::from([2; 32]);
|
||||
|
||||
let mut proposal_1 = BeaconBlockHeader {
|
||||
let mut header_1 = BeaconBlockHeader {
|
||||
slot,
|
||||
previous_block_root: hash_1,
|
||||
state_root: hash_1,
|
||||
@ -33,27 +33,27 @@ impl TestingProposerSlashingBuilder {
|
||||
signature: Signature::empty_signature(),
|
||||
};
|
||||
|
||||
let mut proposal_2 = BeaconBlockHeader {
|
||||
let mut header_2 = BeaconBlockHeader {
|
||||
previous_block_root: hash_2,
|
||||
..proposal_1.clone()
|
||||
..header_1.clone()
|
||||
};
|
||||
|
||||
proposal_1.signature = {
|
||||
let message = proposal_1.signed_root();
|
||||
header_1.signature = {
|
||||
let message = header_1.signed_root();
|
||||
let epoch = slot.epoch(spec.slots_per_epoch);
|
||||
signer(proposer_index, &message[..], epoch, Domain::BeaconBlock)
|
||||
};
|
||||
|
||||
proposal_2.signature = {
|
||||
let message = proposal_2.signed_root();
|
||||
header_2.signature = {
|
||||
let message = header_2.signed_root();
|
||||
let epoch = slot.epoch(spec.slots_per_epoch);
|
||||
signer(proposer_index, &message[..], epoch, Domain::BeaconBlock)
|
||||
};
|
||||
|
||||
ProposerSlashing {
|
||||
proposer_index,
|
||||
proposal_1,
|
||||
proposal_2,
|
||||
header_1,
|
||||
header_2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user