Update block processing to v0.5.0

This commit is contained in:
Paul Hauner 2019-03-17 12:25:37 +11:00
parent 7f4af20212
commit 6bd2055a0a
No known key found for this signature in database
GPG Key ID: D362883A9218FCC6
13 changed files with 413 additions and 336 deletions

View File

@ -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],

View File

@ -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,
}

View File

@ -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 the justified epoch and root is correct.
if attestation_epoch >= state_epoch {
verify!(
state.slot < attestation.data.slot + spec.slots_per_epoch,
Invalid::IncludedTooLate(state.slot, attestation.data.slot)
attestation.data.source_epoch == state.current_justified_epoch,
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!(
attestation.data.justified_epoch == state.justified_epoch,
Invalid::WrongJustifiedEpoch(
attestation.data.justified_epoch,
state.justified_epoch,
false
)
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(),
}));
}
};

View File

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

View File

@ -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,

View File

@ -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();

View File

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

View File

@ -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(|| {

View File

@ -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)

View File

@ -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(())
}

View File

@ -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,

View File

@ -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)]

View File

@ -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,
}
}
}