mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-20 13:24:44 +00:00
Refactor block_processing
- Add the rayon library for parallelization - Move from std::sync::Rwlock to rayon::RwLock - Add `state` field to BeaconChain - Fix major bug in attestation validator where justified slot was incorrectly looked up.
This commit is contained in:
@@ -19,15 +19,9 @@ where
|
||||
&self,
|
||||
free_attestation: FreeAttestation,
|
||||
) -> Result<ProcessOutcome, Error> {
|
||||
let present_slot = self
|
||||
.present_slot()
|
||||
.ok_or_else(|| Error::PresentSlotUnknown)?;
|
||||
let state = self.state(present_slot)?;
|
||||
|
||||
self.attestation_aggregator
|
||||
.write()
|
||||
.expect("Aggregator unlock failed.")
|
||||
.process_free_attestation(&state, &free_attestation, &self.spec)
|
||||
.process_free_attestation(&self.state.read(), &free_attestation, &self.spec)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,30 +13,27 @@ where
|
||||
T: ClientDB,
|
||||
U: SlotClock,
|
||||
{
|
||||
pub fn produce_attestation_data(
|
||||
&self,
|
||||
slot: u64,
|
||||
shard: u64,
|
||||
) -> Result<AttestationData, Error> {
|
||||
let present_slot = self
|
||||
.present_slot()
|
||||
.ok_or_else(|| Error::PresentSlotUnknown)?;
|
||||
let state = self.state(present_slot).map_err(|_| Error::StateError)?;
|
||||
|
||||
pub fn produce_attestation_data(&self, shard: u64) -> Result<AttestationData, Error> {
|
||||
let justified_slot = self.justified_slot();
|
||||
|
||||
let justified_block_root = *state
|
||||
let justified_block_root = self
|
||||
.state
|
||||
.read()
|
||||
.get_block_root(justified_slot, &self.spec)
|
||||
.ok_or_else(|| Error::SlotTooOld)?;
|
||||
.ok_or_else(|| Error::SlotTooOld)?
|
||||
.clone();
|
||||
|
||||
let head_slot = self.head().beacon_block.slot;
|
||||
let previous_epoch_start_slot = head_slot - (head_slot % self.spec.epoch_length);
|
||||
let epoch_boundary_root = *state
|
||||
.get_block_root(previous_epoch_start_slot, &self.spec)
|
||||
.ok_or_else(|| Error::SlotTooOld)?;
|
||||
let epoch_boundary_root = self
|
||||
.state
|
||||
.read()
|
||||
.get_block_root(
|
||||
self.state.read().current_epoch_start_slot(&self.spec),
|
||||
&self.spec,
|
||||
)
|
||||
.ok_or_else(|| Error::SlotTooOld)?
|
||||
.clone();
|
||||
|
||||
Ok(AttestationData {
|
||||
slot,
|
||||
slot: self.state.read().slot,
|
||||
shard,
|
||||
beacon_block_root: self.head().beacon_block_root.clone(),
|
||||
epoch_boundary_root,
|
||||
|
||||
@@ -28,18 +28,12 @@ where
|
||||
U: SlotClock,
|
||||
{
|
||||
pub fn insert_latest_attestation_target(&self, validator_index: u64, block_root: Hash256) {
|
||||
let mut targets = self
|
||||
.latest_attestation_targets
|
||||
.write()
|
||||
.expect("CRITICAL: CanonicalHead poisioned.");
|
||||
let mut targets = self.latest_attestation_targets.write();
|
||||
targets.insert(validator_index, block_root);
|
||||
}
|
||||
|
||||
pub fn get_latest_attestation_target(&self, validator_index: u64) -> Option<Hash256> {
|
||||
let targets = self
|
||||
.latest_attestation_targets
|
||||
.read()
|
||||
.expect("CRITICAL: CanonicalHead poisioned.");
|
||||
let targets = self.latest_attestation_targets.read();
|
||||
|
||||
match targets.get(validator_index) {
|
||||
Some(hash) => Some(hash.clone()),
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use super::state_transition::Error as TransitionError;
|
||||
use super::{BeaconChain, ClientDB, DBError, SlotClock};
|
||||
use log::debug;
|
||||
use slot_clock::{SystemTimeSlotClockError, TestingSlotClockError};
|
||||
use ssz::{ssz_encode, Encodable};
|
||||
use types::{
|
||||
beacon_state::{BlockProcessingError, SlotProcessingError},
|
||||
readers::{BeaconBlockReader, BeaconStateReader},
|
||||
Hash256,
|
||||
};
|
||||
@@ -16,7 +16,6 @@ pub enum ValidBlock {
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum InvalidBlock {
|
||||
FutureSlot,
|
||||
StateTransitionFailed(TransitionError),
|
||||
StateRootMismatch,
|
||||
}
|
||||
|
||||
@@ -29,31 +28,16 @@ pub enum Outcome {
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
DBError(String),
|
||||
SlotClockError(SystemTimeSlotClockError),
|
||||
|
||||
NotImplemented,
|
||||
PresentSlotIsNone,
|
||||
UnableToDecodeBlock,
|
||||
PresentSlotIsNone,
|
||||
SlotClockError(SystemTimeSlotClockError),
|
||||
MissingParentState(Hash256),
|
||||
InvalidParentState(Hash256),
|
||||
MissingBeaconBlock(Hash256),
|
||||
InvalidBeaconBlock(Hash256),
|
||||
MissingParentBlock(Hash256),
|
||||
NoBlockProducer,
|
||||
StateSlotMismatch,
|
||||
BadBlockSignature,
|
||||
BadRandaoSignature,
|
||||
MaxProposerSlashingsExceeded,
|
||||
BadProposerSlashing,
|
||||
MaxAttestationsExceeded,
|
||||
BadAttestation,
|
||||
NoBlockRoot,
|
||||
MaxDepositsExceeded,
|
||||
MaxExitsExceeded,
|
||||
BadExit,
|
||||
BadCustodyReseeds,
|
||||
BadCustodyChallenges,
|
||||
BadCustodyResponses,
|
||||
SlotProcessingError(SlotProcessingError),
|
||||
PerBlockProcessingError(BlockProcessingError),
|
||||
}
|
||||
|
||||
impl<T, U> BeaconChain<T, U>
|
||||
@@ -99,14 +83,13 @@ where
|
||||
.into_beacon_state()
|
||||
.ok_or(Error::InvalidParentState(parent_state_root))?;
|
||||
|
||||
let state = match self.state_transition(parent_state, &block) {
|
||||
Ok(state) => state,
|
||||
Err(error) => {
|
||||
return Ok(Outcome::InvalidBlock(InvalidBlock::StateTransitionFailed(
|
||||
error,
|
||||
)));
|
||||
}
|
||||
};
|
||||
let mut state = parent_state;
|
||||
|
||||
for _ in state.slot..present_slot {
|
||||
state.per_slot_processing(parent_block_root.clone(), &self.spec)?;
|
||||
}
|
||||
|
||||
state.per_block_processing(&block, &self.spec)?;
|
||||
|
||||
let state_root = state.canonical_root();
|
||||
|
||||
@@ -131,6 +114,7 @@ where
|
||||
state.clone(),
|
||||
state_root.clone(),
|
||||
);
|
||||
*self.state.write() = state.clone();
|
||||
}
|
||||
|
||||
// The block was sucessfully processed.
|
||||
@@ -144,6 +128,18 @@ impl From<DBError> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SlotProcessingError> for Error {
|
||||
fn from(e: SlotProcessingError) -> Error {
|
||||
Error::SlotProcessingError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BlockProcessingError> for Error {
|
||||
fn from(e: BlockProcessingError) -> Error {
|
||||
Error::PerBlockProcessingError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TestingSlotClockError> for Error {
|
||||
fn from(_: TestingSlotClockError) -> Error {
|
||||
unreachable!(); // Testing clock never throws an error.
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use super::state_transition::Error as TransitionError;
|
||||
use super::{BeaconChain, ClientDB, DBError, SlotClock};
|
||||
use bls::Signature;
|
||||
use log::debug;
|
||||
use slot_clock::TestingSlotClockError;
|
||||
use slot_clock::{SystemTimeSlotClockError, TestingSlotClockError};
|
||||
use types::{
|
||||
beacon_state::SlotProcessingError,
|
||||
beacon_state::{BlockProcessingError, SlotProcessingError},
|
||||
readers::{BeaconBlockReader, BeaconStateReader},
|
||||
BeaconBlock, BeaconBlockBody, BeaconState, Eth1Data, Hash256,
|
||||
};
|
||||
@@ -12,9 +11,10 @@ use types::{
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
DBError(String),
|
||||
StateTransitionError(TransitionError),
|
||||
PresentSlotIsNone,
|
||||
SlotProcessingError(SlotProcessingError),
|
||||
PerBlockProcessingError(BlockProcessingError),
|
||||
SlotClockError(SystemTimeSlotClockError),
|
||||
}
|
||||
|
||||
impl<T, U> BeaconChain<T, U>
|
||||
@@ -29,43 +29,31 @@ where
|
||||
where
|
||||
Error: From<<U>::Error>,
|
||||
{
|
||||
// TODO: allow producing a block from a previous (or future?) slot.
|
||||
let present_slot = self
|
||||
.slot_clock
|
||||
.present_slot()
|
||||
.map_err(|e| e.into())?
|
||||
.ok_or(Error::PresentSlotIsNone)?;
|
||||
debug!("Starting block production...");
|
||||
|
||||
debug!("Producing block for slot {}...", present_slot);
|
||||
let mut state = self.state.read().clone();
|
||||
|
||||
let parent_root = self.head().beacon_block_root;
|
||||
let parent_block_reader = self
|
||||
.block_store
|
||||
.get_reader(&parent_root)?
|
||||
.ok_or_else(|| Error::DBError("Block not found.".to_string()))?;
|
||||
let parent_state = self
|
||||
.state_store
|
||||
.get_reader(&parent_block_reader.state_root())?
|
||||
.ok_or_else(|| Error::DBError("State not found.".to_string()))?
|
||||
.into_beacon_state()
|
||||
.ok_or_else(|| Error::DBError("State invalid.".to_string()))?;
|
||||
debug!("Finding attesatations for new block...");
|
||||
|
||||
debug!("Finding attesatations for block...");
|
||||
let attestations = self
|
||||
.attestation_aggregator
|
||||
.read()
|
||||
.get_attestations_for_state(&state, &self.spec);
|
||||
|
||||
let attestations = {
|
||||
let mut next_state = parent_state.clone();
|
||||
next_state.per_slot_processing(Hash256::zero(), &self.spec)?;
|
||||
self.attestation_aggregator
|
||||
.read()
|
||||
.unwrap()
|
||||
.get_attestations_for_state(&next_state, &self.spec)
|
||||
};
|
||||
debug!(
|
||||
"Inserting {} attestation(s) into new block.",
|
||||
attestations.len()
|
||||
);
|
||||
|
||||
debug!("Found {} attestation(s).", attestations.len());
|
||||
let parent_root = state
|
||||
.get_block_root(state.slot.saturating_sub(1), &self.spec)
|
||||
// TODO: fix unwrap
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
let mut block = BeaconBlock {
|
||||
slot: present_slot,
|
||||
parent_root: parent_root.clone(),
|
||||
slot: state.slot,
|
||||
parent_root,
|
||||
state_root: Hash256::zero(), // Updated after the state is calculated.
|
||||
randao_reveal: randao_reveal,
|
||||
eth1_data: Eth1Data {
|
||||
@@ -86,8 +74,8 @@ where
|
||||
},
|
||||
};
|
||||
|
||||
let state =
|
||||
self.state_transition_without_verifying_block_signature(parent_state, &block)?;
|
||||
state.per_block_processing_without_verifying_block_signature(&block, &self.spec)?;
|
||||
|
||||
let state_root = state.canonical_root();
|
||||
|
||||
block.state_root = state_root;
|
||||
@@ -104,9 +92,15 @@ impl From<DBError> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TransitionError> for Error {
|
||||
fn from(e: TransitionError) -> Error {
|
||||
Error::StateTransitionError(e)
|
||||
impl From<SlotProcessingError> for Error {
|
||||
fn from(e: SlotProcessingError) -> Error {
|
||||
Error::SlotProcessingError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BlockProcessingError> for Error {
|
||||
fn from(e: BlockProcessingError) -> Error {
|
||||
Error::PerBlockProcessingError(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,8 +110,8 @@ impl From<TestingSlotClockError> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SlotProcessingError> for Error {
|
||||
fn from(e: SlotProcessingError) -> Error {
|
||||
Error::SlotProcessingError(e)
|
||||
impl From<SystemTimeSlotClockError> for Error {
|
||||
fn from(e: SystemTimeSlotClockError) -> Error {
|
||||
Error::SlotClockError(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::{BeaconChain, CheckPoint, ClientDB, SlotClock};
|
||||
use std::sync::RwLockReadGuard;
|
||||
use parking_lot::RwLockReadGuard;
|
||||
use types::{beacon_state::SlotProcessingError, BeaconBlock, BeaconState, Hash256};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@@ -20,10 +20,7 @@ where
|
||||
new_beacon_state: BeaconState,
|
||||
new_beacon_state_root: Hash256,
|
||||
) {
|
||||
let mut head = self
|
||||
.canonical_head
|
||||
.write()
|
||||
.expect("CRITICAL: CanonicalHead poisioned.");
|
||||
let mut head = self.canonical_head.write();
|
||||
head.update(
|
||||
new_beacon_block,
|
||||
new_beacon_block_root,
|
||||
@@ -33,34 +30,18 @@ where
|
||||
}
|
||||
|
||||
pub fn head(&self) -> RwLockReadGuard<CheckPoint> {
|
||||
self.canonical_head
|
||||
.read()
|
||||
.expect("CRITICAL: CanonicalHead poisioned.")
|
||||
self.canonical_head.read()
|
||||
}
|
||||
|
||||
pub fn state(&self, slot: u64) -> Result<BeaconState, Error> {
|
||||
let mut state = self
|
||||
.canonical_head
|
||||
.read()
|
||||
.expect("CRITICAL: CanonicalHead poisioned.")
|
||||
.beacon_state
|
||||
.clone();
|
||||
let previous_block_root = self
|
||||
.canonical_head
|
||||
.read()
|
||||
.expect("CRITICAL: CanonicalHead poisioned.")
|
||||
.beacon_block_root
|
||||
.clone();
|
||||
|
||||
match slot.checked_sub(state.slot) {
|
||||
None => Err(Error::PastSlot),
|
||||
Some(distance) => {
|
||||
for _ in 0..distance {
|
||||
state.per_slot_processing(previous_block_root.clone(), &self.spec)?
|
||||
}
|
||||
Ok(state)
|
||||
}
|
||||
pub fn advance_state(&self, slot: u64) -> Result<(), SlotProcessingError> {
|
||||
let state_slot = self.state.read().slot;
|
||||
let head_block_root = self.head().beacon_block_root;
|
||||
for _ in state_slot..slot {
|
||||
self.state
|
||||
.write()
|
||||
.per_slot_processing(head_block_root.clone(), &self.spec)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::{BeaconChain, CheckPoint, ClientDB, SlotClock};
|
||||
use std::sync::RwLockReadGuard;
|
||||
use parking_lot::RwLockReadGuard;
|
||||
use types::{BeaconBlock, BeaconState, Hash256};
|
||||
|
||||
impl<T, U> BeaconChain<T, U>
|
||||
@@ -14,10 +14,7 @@ where
|
||||
new_beacon_state: BeaconState,
|
||||
new_beacon_state_root: Hash256,
|
||||
) {
|
||||
let mut finalized_head = self
|
||||
.finalized_head
|
||||
.write()
|
||||
.expect("CRITICAL: finalized_head poisioned.");
|
||||
let mut finalized_head = self.finalized_head.write();
|
||||
finalized_head.update(
|
||||
new_beacon_block,
|
||||
new_beacon_block_root,
|
||||
@@ -27,8 +24,6 @@ where
|
||||
}
|
||||
|
||||
pub fn finalized_head(&self) -> RwLockReadGuard<CheckPoint> {
|
||||
self.finalized_head
|
||||
.read()
|
||||
.expect("CRITICAL: finalized_head poisioned.")
|
||||
self.finalized_head.read()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,10 +28,7 @@ where
|
||||
}
|
||||
|
||||
pub fn proposer_slots(&self, validator_index: usize) -> Option<u64> {
|
||||
let slot = self.present_slot()?;
|
||||
let state = self.state(slot).ok()?;
|
||||
|
||||
if let Some(validator) = state.validator_registry.get(validator_index) {
|
||||
if let Some(validator) = self.state.read().validator_registry.get(validator_index) {
|
||||
Some(validator.proposer_slots)
|
||||
} else {
|
||||
None
|
||||
@@ -46,35 +43,22 @@ where
|
||||
}
|
||||
|
||||
pub fn block_proposer(&self, slot: u64) -> Result<usize, CommitteesError> {
|
||||
// TODO: fix unwrap
|
||||
let present_slot = self.present_slot().unwrap();
|
||||
// TODO: fix unwrap
|
||||
let state = self.state(present_slot).unwrap();
|
||||
let index = state.get_beacon_proposer_index(slot, &self.spec)?;
|
||||
let index = self
|
||||
.state
|
||||
.read()
|
||||
.get_beacon_proposer_index(slot, &self.spec)?;
|
||||
|
||||
Ok(index)
|
||||
}
|
||||
|
||||
pub fn justified_slot(&self) -> u64 {
|
||||
// TODO: fix unwrap
|
||||
let present_slot = self.present_slot().unwrap();
|
||||
// TODO: fix unwrap
|
||||
let state = self.state(present_slot).unwrap();
|
||||
state.justified_slot
|
||||
/*
|
||||
self.justified_head
|
||||
.read()
|
||||
.expect("Justified head poisoned")
|
||||
.beacon_block
|
||||
.slot
|
||||
*/
|
||||
self.state.read().justified_slot
|
||||
}
|
||||
|
||||
pub fn validator_attestion_slot_and_shard(&self, validator_index: usize) -> Option<(u64, u64)> {
|
||||
let present_slot = self.present_slot()?;
|
||||
let state = self.state(present_slot).ok()?;
|
||||
|
||||
let (slot, shard, _committee) = state
|
||||
let (slot, shard, _committee) = self
|
||||
.state
|
||||
.read()
|
||||
.attestation_slot_and_shard_for_validator(validator_index, &self.spec)
|
||||
.ok()?;
|
||||
Some((slot, shard))
|
||||
|
||||
@@ -10,7 +10,7 @@ pub mod dump;
|
||||
mod finalized_head;
|
||||
mod info;
|
||||
mod lmd_ghost;
|
||||
mod state_transition;
|
||||
// mod state_transition;
|
||||
|
||||
use self::attestation_targets::AttestationTargets;
|
||||
use self::block_graph::BlockGraph;
|
||||
@@ -20,9 +20,10 @@ use db::{
|
||||
ClientDB, DBError,
|
||||
};
|
||||
use genesis::{genesis_beacon_block, genesis_beacon_state, GenesisError};
|
||||
use parking_lot::RwLock;
|
||||
use slot_clock::SlotClock;
|
||||
use ssz::ssz_encode;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::sync::Arc;
|
||||
use types::{BeaconBlock, BeaconState, ChainSpec, Hash256};
|
||||
|
||||
pub use self::block_processing::Outcome as BlockProcessingOutcome;
|
||||
@@ -79,6 +80,7 @@ pub struct BeaconChain<T: ClientDB + Sized, U: SlotClock> {
|
||||
canonical_head: RwLock<CheckPoint>,
|
||||
finalized_head: RwLock<CheckPoint>,
|
||||
justified_head: RwLock<CheckPoint>,
|
||||
pub state: RwLock<BeaconState>,
|
||||
pub latest_attestation_targets: RwLock<AttestationTargets>,
|
||||
pub spec: ChainSpec,
|
||||
}
|
||||
@@ -137,6 +139,7 @@ where
|
||||
slot_clock,
|
||||
block_graph,
|
||||
attestation_aggregator,
|
||||
state: RwLock::new(genesis_state.clone()),
|
||||
justified_head,
|
||||
finalized_head,
|
||||
canonical_head,
|
||||
|
||||
@@ -1,366 +0,0 @@
|
||||
use super::{BeaconChain, ClientDB, DBError, SlotClock};
|
||||
use bls::{PublicKey, Signature};
|
||||
use hashing::hash;
|
||||
use log::debug;
|
||||
use slot_clock::{SystemTimeSlotClockError, TestingSlotClockError};
|
||||
use ssz::{ssz_encode, TreeHash};
|
||||
use types::{
|
||||
beacon_state::{AttestationValidationError, CommitteesError, SlotProcessingError},
|
||||
readers::BeaconBlockReader,
|
||||
BeaconBlock, BeaconState, Exit, Fork, Hash256, PendingAttestation,
|
||||
};
|
||||
|
||||
// TODO: define elsehwere.
|
||||
const DOMAIN_PROPOSAL: u64 = 2;
|
||||
const DOMAIN_EXIT: u64 = 3;
|
||||
const DOMAIN_RANDAO: u64 = 4;
|
||||
|
||||
macro_rules! ensure {
|
||||
($condition: expr, $result: expr) => {
|
||||
if !$condition {
|
||||
return Err($result);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
DBError(String),
|
||||
StateAlreadyTransitioned,
|
||||
NotImplemented,
|
||||
PresentSlotIsNone,
|
||||
UnableToDecodeBlock,
|
||||
MissingParentState(Hash256),
|
||||
InvalidParentState(Hash256),
|
||||
MissingBeaconBlock(Hash256),
|
||||
InvalidBeaconBlock(Hash256),
|
||||
MissingParentBlock(Hash256),
|
||||
NoBlockProducer,
|
||||
StateSlotMismatch,
|
||||
BadBlockSignature,
|
||||
BadRandaoSignature,
|
||||
MaxProposerSlashingsExceeded,
|
||||
BadProposerSlashing,
|
||||
MaxAttestationsExceeded,
|
||||
InvalidAttestation(AttestationValidationError),
|
||||
NoBlockRoot,
|
||||
MaxDepositsExceeded,
|
||||
MaxExitsExceeded,
|
||||
BadExit,
|
||||
BadCustodyReseeds,
|
||||
BadCustodyChallenges,
|
||||
BadCustodyResponses,
|
||||
SlotClockError(SystemTimeSlotClockError),
|
||||
CommitteesError(CommitteesError),
|
||||
SlotProcessingError(SlotProcessingError),
|
||||
}
|
||||
|
||||
impl<T, U> BeaconChain<T, U>
|
||||
where
|
||||
T: ClientDB,
|
||||
U: SlotClock,
|
||||
{
|
||||
pub fn state_transition(
|
||||
&self,
|
||||
state: BeaconState,
|
||||
block: &BeaconBlock,
|
||||
) -> Result<BeaconState, Error> {
|
||||
self.internal_state_transition(state, block, true)
|
||||
}
|
||||
|
||||
pub fn state_transition_without_verifying_block_signature(
|
||||
&self,
|
||||
state: BeaconState,
|
||||
block: &BeaconBlock,
|
||||
) -> Result<BeaconState, Error> {
|
||||
self.internal_state_transition(state, block, false)
|
||||
}
|
||||
|
||||
fn internal_state_transition(
|
||||
&self,
|
||||
mut state: BeaconState,
|
||||
block: &BeaconBlock,
|
||||
verify_block_signature: bool,
|
||||
) -> Result<BeaconState, Error> {
|
||||
ensure!(state.slot < block.slot, Error::StateAlreadyTransitioned);
|
||||
|
||||
debug!(
|
||||
"Starting state transition from slot {} to {}...",
|
||||
state.slot, block.slot
|
||||
);
|
||||
|
||||
for _ in state.slot..block.slot {
|
||||
state.per_slot_processing(block.parent_root.clone(), &self.spec)?;
|
||||
}
|
||||
|
||||
/*
|
||||
* Slot
|
||||
*/
|
||||
|
||||
ensure!(block.slot() == state.slot, Error::StateSlotMismatch);
|
||||
|
||||
/*
|
||||
* Proposer Signature
|
||||
*/
|
||||
|
||||
let block_proposer_index = state
|
||||
.get_beacon_proposer_index(block.slot, &self.spec)
|
||||
.map_err(|_| Error::NoBlockProducer)?;
|
||||
let block_proposer = &state.validator_registry[block_proposer_index];
|
||||
|
||||
if verify_block_signature {
|
||||
ensure!(
|
||||
bls_verify(
|
||||
&block_proposer.pubkey,
|
||||
&block.proposal_root(&self.spec)[..],
|
||||
&block.signature,
|
||||
get_domain(&state.fork_data, state.slot, DOMAIN_PROPOSAL)
|
||||
),
|
||||
Error::BadBlockSignature
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* RANDAO
|
||||
*/
|
||||
|
||||
ensure!(
|
||||
bls_verify(
|
||||
&block_proposer.pubkey,
|
||||
&ssz_encode(&block_proposer.proposer_slots),
|
||||
&block.randao_reveal,
|
||||
get_domain(&state.fork_data, state.slot, DOMAIN_RANDAO)
|
||||
),
|
||||
Error::BadRandaoSignature
|
||||
);
|
||||
|
||||
// TODO: check this is correct.
|
||||
let new_mix = {
|
||||
let mut mix = state.latest_randao_mixes
|
||||
[(state.slot % self.spec.latest_randao_mixes_length) as usize]
|
||||
.to_vec();
|
||||
mix.append(&mut ssz_encode(&block.randao_reveal));
|
||||
Hash256::from(&hash(&mix)[..])
|
||||
};
|
||||
|
||||
state.latest_randao_mixes[(state.slot % self.spec.latest_randao_mixes_length) as usize] =
|
||||
new_mix;
|
||||
|
||||
/*
|
||||
* Eth1 data
|
||||
*/
|
||||
|
||||
// TODO: Eth1 data stuff.
|
||||
|
||||
/*
|
||||
* OPERATIONS
|
||||
*/
|
||||
|
||||
/*
|
||||
* Proposer slashings
|
||||
*/
|
||||
|
||||
ensure!(
|
||||
block.body.proposer_slashings.len() as u64 <= self.spec.max_proposer_slashings,
|
||||
Error::MaxProposerSlashingsExceeded
|
||||
);
|
||||
for proposer_slashing in &block.body.proposer_slashings {
|
||||
let proposer = state
|
||||
.validator_registry
|
||||
.get(proposer_slashing.proposer_index as usize)
|
||||
.ok_or(Error::BadProposerSlashing)?;
|
||||
ensure!(
|
||||
proposer_slashing.proposal_data_1.slot == proposer_slashing.proposal_data_2.slot,
|
||||
Error::BadProposerSlashing
|
||||
);
|
||||
ensure!(
|
||||
proposer_slashing.proposal_data_1.shard == proposer_slashing.proposal_data_2.shard,
|
||||
Error::BadProposerSlashing
|
||||
);
|
||||
ensure!(
|
||||
proposer_slashing.proposal_data_1.block_root
|
||||
!= proposer_slashing.proposal_data_2.block_root,
|
||||
Error::BadProposerSlashing
|
||||
);
|
||||
ensure!(
|
||||
proposer.penalized_slot > state.slot,
|
||||
Error::BadProposerSlashing
|
||||
);
|
||||
ensure!(
|
||||
bls_verify(
|
||||
&proposer.pubkey,
|
||||
&proposer_slashing.proposal_data_1.hash_tree_root(),
|
||||
&proposer_slashing.proposal_signature_1,
|
||||
get_domain(
|
||||
&state.fork_data,
|
||||
proposer_slashing.proposal_data_1.slot,
|
||||
DOMAIN_PROPOSAL
|
||||
)
|
||||
),
|
||||
Error::BadProposerSlashing
|
||||
);
|
||||
ensure!(
|
||||
bls_verify(
|
||||
&proposer.pubkey,
|
||||
&proposer_slashing.proposal_data_2.hash_tree_root(),
|
||||
&proposer_slashing.proposal_signature_2,
|
||||
get_domain(
|
||||
&state.fork_data,
|
||||
proposer_slashing.proposal_data_2.slot,
|
||||
DOMAIN_PROPOSAL
|
||||
)
|
||||
),
|
||||
Error::BadProposerSlashing
|
||||
);
|
||||
penalize_validator(&state, proposer_slashing.proposer_index as usize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attestations
|
||||
*/
|
||||
ensure!(
|
||||
block.body.attestations.len() as u64 <= self.spec.max_attestations,
|
||||
Error::MaxAttestationsExceeded
|
||||
);
|
||||
|
||||
for attestation in &block.body.attestations {
|
||||
state.validate_attestation(attestation, &self.spec)?;
|
||||
|
||||
let pending_attestation = PendingAttestation {
|
||||
data: attestation.data.clone(),
|
||||
aggregation_bitfield: attestation.aggregation_bitfield.clone(),
|
||||
custody_bitfield: attestation.custody_bitfield.clone(),
|
||||
slot_included: state.slot,
|
||||
};
|
||||
state.latest_attestations.push(pending_attestation);
|
||||
}
|
||||
|
||||
debug!(
|
||||
"{} attestations verified & processed.",
|
||||
block.body.attestations.len()
|
||||
);
|
||||
|
||||
/*
|
||||
* Deposits
|
||||
*/
|
||||
ensure!(
|
||||
block.body.deposits.len() as u64 <= self.spec.max_deposits,
|
||||
Error::MaxDepositsExceeded
|
||||
);
|
||||
|
||||
// TODO: process deposits.
|
||||
|
||||
/*
|
||||
* Exits
|
||||
*/
|
||||
|
||||
ensure!(
|
||||
block.body.exits.len() as u64 <= self.spec.max_exits,
|
||||
Error::MaxExitsExceeded
|
||||
);
|
||||
|
||||
for exit in &block.body.exits {
|
||||
let validator = state
|
||||
.validator_registry
|
||||
.get(exit.validator_index as usize)
|
||||
.ok_or(Error::BadExit)?;
|
||||
ensure!(
|
||||
validator.exit_slot > state.slot + self.spec.entry_exit_delay,
|
||||
Error::BadExit
|
||||
);
|
||||
ensure!(state.slot >= exit.slot, Error::BadExit);
|
||||
let exit_message = {
|
||||
let exit_struct = Exit {
|
||||
slot: exit.slot,
|
||||
validator_index: exit.validator_index,
|
||||
signature: self.spec.empty_signature.clone(),
|
||||
};
|
||||
exit_struct.hash_tree_root()
|
||||
};
|
||||
ensure!(
|
||||
bls_verify(
|
||||
&validator.pubkey,
|
||||
&exit_message,
|
||||
&exit.signature,
|
||||
get_domain(&state.fork_data, exit.slot, DOMAIN_EXIT)
|
||||
),
|
||||
Error::BadProposerSlashing
|
||||
);
|
||||
initiate_validator_exit(&state, exit.validator_index);
|
||||
}
|
||||
|
||||
/*
|
||||
* Custody
|
||||
*/
|
||||
ensure!(
|
||||
block.body.custody_reseeds.is_empty(),
|
||||
Error::BadCustodyReseeds
|
||||
);
|
||||
ensure!(
|
||||
block.body.custody_challenges.is_empty(),
|
||||
Error::BadCustodyChallenges
|
||||
);
|
||||
ensure!(
|
||||
block.body.custody_responses.is_empty(),
|
||||
Error::BadCustodyResponses
|
||||
);
|
||||
|
||||
debug!("State transition complete.");
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
}
|
||||
|
||||
fn initiate_validator_exit(_state: &BeaconState, _index: u32) {
|
||||
// TODO: stubbed out.
|
||||
}
|
||||
|
||||
fn penalize_validator(_state: &BeaconState, _proposer_index: usize) {
|
||||
// TODO: stubbed out.
|
||||
}
|
||||
|
||||
fn get_domain(_fork: &Fork, _slot: u64, _domain_type: u64) -> u64 {
|
||||
// TODO: stubbed out.
|
||||
0
|
||||
}
|
||||
|
||||
fn bls_verify(pubkey: &PublicKey, message: &[u8], signature: &Signature, _domain: u64) -> bool {
|
||||
// TODO: add domain
|
||||
signature.verify(message, pubkey)
|
||||
}
|
||||
|
||||
impl From<DBError> for Error {
|
||||
fn from(e: DBError) -> Error {
|
||||
Error::DBError(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TestingSlotClockError> for Error {
|
||||
fn from(_: TestingSlotClockError) -> Error {
|
||||
unreachable!(); // Testing clock never throws an error.
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SystemTimeSlotClockError> for Error {
|
||||
fn from(e: SystemTimeSlotClockError) -> Error {
|
||||
Error::SlotClockError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AttestationValidationError> for Error {
|
||||
fn from(e: AttestationValidationError) -> Error {
|
||||
Error::InvalidAttestation(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CommitteesError> for Error {
|
||||
fn from(e: CommitteesError) -> Error {
|
||||
Error::CommitteesError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SlotProcessingError> for Error {
|
||||
fn from(e: SlotProcessingError) -> Error {
|
||||
Error::SlotProcessingError(e)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user