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