mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-20 05:14:35 +00:00
Implement very raw state transition logic
This commit is contained in:
136
beacon_node/beacon_chain/src/block_processing.rs
Normal file
136
beacon_node/beacon_chain/src/block_processing.rs
Normal file
@@ -0,0 +1,136 @@
|
||||
use super::state_transition::Error as TransitionError;
|
||||
use super::{BeaconChain, ClientDB, DBError, SlotClock};
|
||||
use slot_clock::{SystemTimeSlotClockError, TestingSlotClockError};
|
||||
use ssz::{ssz_encode, Encodable};
|
||||
use types::{
|
||||
readers::{BeaconBlockReader, BeaconStateReader},
|
||||
Hash256,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Outcome {
|
||||
FutureSlot,
|
||||
Processed,
|
||||
NewCanonicalBlock,
|
||||
NewReorgBlock,
|
||||
NewForkBlock,
|
||||
StateTransitionFailed(TransitionError),
|
||||
StateRootMismatch,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
DBError(String),
|
||||
SlotClockError(SystemTimeSlotClockError),
|
||||
|
||||
NotImplemented,
|
||||
PresentSlotIsNone,
|
||||
UnableToDecodeBlock,
|
||||
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,
|
||||
}
|
||||
|
||||
impl<T, U> BeaconChain<T, U>
|
||||
where
|
||||
T: ClientDB,
|
||||
U: SlotClock,
|
||||
Error: From<<U as SlotClock>::Error>,
|
||||
{
|
||||
pub fn process_block<V>(&mut self, block: V) -> Result<Outcome, Error>
|
||||
where
|
||||
V: BeaconBlockReader + Encodable + Sized,
|
||||
{
|
||||
let block = block
|
||||
.into_beacon_block()
|
||||
.ok_or(Error::UnableToDecodeBlock)?;
|
||||
let block_root = block.canonical_root();
|
||||
|
||||
let present_slot = self
|
||||
.slot_clock
|
||||
.present_slot()?
|
||||
.ok_or(Error::PresentSlotIsNone)?;
|
||||
|
||||
// Block from future slots (i.e., greater than the present slot) should not be processed.
|
||||
if block.slot() > present_slot {
|
||||
return Ok(Outcome::FutureSlot);
|
||||
}
|
||||
|
||||
let parent_block_root = block.parent_root();
|
||||
|
||||
let parent_block = self
|
||||
.block_store
|
||||
.get_reader(&parent_block_root)?
|
||||
.ok_or(Error::MissingParentBlock(parent_block_root))?;
|
||||
|
||||
let parent_state_root = parent_block.parent_root();
|
||||
let parent_state = self
|
||||
.state_store
|
||||
.get_reader(&parent_state_root)?
|
||||
.ok_or(Error::MissingParentState(parent_state_root))?
|
||||
.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::StateTransitionFailed(error)),
|
||||
};
|
||||
|
||||
let state_root = state.canonical_root();
|
||||
|
||||
if block.state_root != state_root {
|
||||
return Ok(Outcome::StateRootMismatch);
|
||||
}
|
||||
|
||||
// Store the block and state.
|
||||
self.block_store.put(&block_root, &ssz_encode(&block)[..])?;
|
||||
self.state_store.put(&state_root, &ssz_encode(&state)[..])?;
|
||||
|
||||
// Update leaf blocks so the implementation can track the chain heads.
|
||||
if self.leaf_blocks.contains(&block.parent_root()) {
|
||||
self.leaf_blocks.remove(&block.parent_root());
|
||||
}
|
||||
if self.canonical_leaf_block == block.parent_root() {
|
||||
self.canonical_leaf_block = block_root;
|
||||
}
|
||||
self.leaf_blocks.insert(block_root);
|
||||
|
||||
// The block was sucessfully processed.
|
||||
Ok(Outcome::Processed)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
89
beacon_node/beacon_chain/src/block_production.rs
Normal file
89
beacon_node/beacon_chain/src/block_production.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
use super::state_transition::Error as TransitionError;
|
||||
use super::{BeaconChain, ClientDB, DBError, SlotClock};
|
||||
use bls::Signature;
|
||||
use slot_clock::TestingSlotClockError;
|
||||
use types::{
|
||||
readers::{BeaconBlockReader, BeaconStateReader},
|
||||
BeaconBlock, BeaconBlockBody, BeaconState, Hash256,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
DBError(String),
|
||||
StateTransitionError(TransitionError),
|
||||
PresentSlotIsNone,
|
||||
}
|
||||
|
||||
impl<T, U> BeaconChain<T, U>
|
||||
where
|
||||
T: ClientDB,
|
||||
U: SlotClock,
|
||||
Error: From<<U as SlotClock>::Error>,
|
||||
{
|
||||
pub fn produce_block(
|
||||
&mut self,
|
||||
randao_reveal: Signature,
|
||||
) -> Result<(BeaconBlock, BeaconState), Error> {
|
||||
let present_slot = self
|
||||
.slot_clock
|
||||
.present_slot()?
|
||||
.ok_or(Error::PresentSlotIsNone)?;
|
||||
|
||||
let parent_root = self.canonical_leaf_block;
|
||||
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()))?;
|
||||
|
||||
let mut block = BeaconBlock {
|
||||
slot: present_slot,
|
||||
parent_root,
|
||||
state_root: Hash256::zero(), // Updated after the state is calculated.
|
||||
randao_reveal: randao_reveal,
|
||||
candidate_pow_receipt_root: Hash256::zero(), // TODO: replace w/ eth1 data.
|
||||
signature: self.spec.empty_signature.clone(), // To be completed by a validator.
|
||||
body: BeaconBlockBody {
|
||||
proposer_slashings: vec![],
|
||||
casper_slashings: vec![],
|
||||
attestations: vec![],
|
||||
custody_reseeds: vec![],
|
||||
custody_challenges: vec![],
|
||||
custody_responses: vec![],
|
||||
deposits: vec![],
|
||||
exits: vec![],
|
||||
},
|
||||
};
|
||||
|
||||
let state = self.state_transition(parent_state, &block)?;
|
||||
|
||||
let state_root = state.canonical_root();
|
||||
|
||||
block.state_root = state_root;
|
||||
|
||||
Ok((block, state))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DBError> for Error {
|
||||
fn from(e: DBError) -> Error {
|
||||
Error::DBError(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TransitionError> for Error {
|
||||
fn from(e: TransitionError) -> Error {
|
||||
Error::StateTransitionError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TestingSlotClockError> for Error {
|
||||
fn from(_: TestingSlotClockError) -> Error {
|
||||
unreachable!(); // Testing clock never throws an error.
|
||||
}
|
||||
}
|
||||
100
beacon_node/beacon_chain/src/lib.rs
Normal file
100
beacon_node/beacon_chain/src/lib.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
mod block_processing;
|
||||
mod block_production;
|
||||
mod lmd_ghost;
|
||||
mod state_transition;
|
||||
|
||||
use db::{
|
||||
stores::{BeaconBlockStore, BeaconStateStore},
|
||||
ClientDB, DBError,
|
||||
};
|
||||
use genesis::{genesis_beacon_block, genesis_beacon_state, GenesisError};
|
||||
use slot_clock::SlotClock;
|
||||
use spec::ChainSpec;
|
||||
use ssz::ssz_encode;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
use types::Hash256;
|
||||
|
||||
pub use self::block_processing::Outcome as BlockProcessingOutcome;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct CheckPoint {
|
||||
block_root: Hash256,
|
||||
state_root: Hash256,
|
||||
slot: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum BeaconChainError {
|
||||
InsufficientValidators,
|
||||
GenesisError(GenesisError),
|
||||
DBError(String),
|
||||
}
|
||||
|
||||
pub struct BeaconChain<T: ClientDB + Sized, U: SlotClock> {
|
||||
pub block_store: Arc<BeaconBlockStore<T>>,
|
||||
pub state_store: Arc<BeaconStateStore<T>>,
|
||||
pub slot_clock: U,
|
||||
pub leaf_blocks: HashSet<Hash256>,
|
||||
pub canonical_leaf_block: Hash256,
|
||||
pub spec: ChainSpec,
|
||||
latest_attestation_targets: HashMap<usize, Hash256>,
|
||||
finalized_checkpoint: CheckPoint,
|
||||
}
|
||||
|
||||
impl<T, U> BeaconChain<T, U>
|
||||
where
|
||||
T: ClientDB,
|
||||
U: SlotClock,
|
||||
{
|
||||
pub fn genesis(
|
||||
state_store: Arc<BeaconStateStore<T>>,
|
||||
block_store: Arc<BeaconBlockStore<T>>,
|
||||
slot_clock: U,
|
||||
spec: ChainSpec,
|
||||
) -> Result<Self, BeaconChainError> {
|
||||
if spec.initial_validators.is_empty() {
|
||||
return Err(BeaconChainError::InsufficientValidators);
|
||||
}
|
||||
|
||||
let genesis_state = genesis_beacon_state(&spec)?;
|
||||
let state_root = genesis_state.canonical_root();
|
||||
state_store.put(&state_root, &ssz_encode(&genesis_state)[..])?;
|
||||
|
||||
let genesis_block = genesis_beacon_block(state_root, &spec);
|
||||
let block_root = genesis_block.canonical_root();
|
||||
block_store.put(&block_root, &ssz_encode(&genesis_block)[..])?;
|
||||
|
||||
let mut leaf_blocks = HashSet::new();
|
||||
leaf_blocks.insert(block_root);
|
||||
|
||||
let finalized_checkpoint = CheckPoint {
|
||||
block_root,
|
||||
state_root,
|
||||
slot: genesis_block.slot,
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
block_store,
|
||||
state_store,
|
||||
slot_clock,
|
||||
leaf_blocks,
|
||||
canonical_leaf_block: block_root,
|
||||
spec,
|
||||
latest_attestation_targets: HashMap::new(),
|
||||
finalized_checkpoint,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DBError> for BeaconChainError {
|
||||
fn from(e: DBError) -> BeaconChainError {
|
||||
BeaconChainError::DBError(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GenesisError> for BeaconChainError {
|
||||
fn from(e: GenesisError) -> BeaconChainError {
|
||||
BeaconChainError::GenesisError(e)
|
||||
}
|
||||
}
|
||||
170
beacon_node/beacon_chain/src/lmd_ghost.rs
Normal file
170
beacon_node/beacon_chain/src/lmd_ghost.rs
Normal file
@@ -0,0 +1,170 @@
|
||||
use super::{BeaconChain, SlotClock};
|
||||
use db::{
|
||||
stores::{BeaconBlockAtSlotError, BeaconBlockStore},
|
||||
ClientDB, DBError,
|
||||
};
|
||||
use slot_clock::TestingSlotClockError;
|
||||
use ssz::{ssz_encode, Encodable};
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
use types::{
|
||||
readers::{BeaconBlockReader, BeaconStateReader},
|
||||
validator_registry::get_active_validator_indices,
|
||||
BeaconBlock, Hash256,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Outcome {
|
||||
Something,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
DBError(String),
|
||||
MissingBeaconState(Hash256),
|
||||
InvalidBeaconState(Hash256),
|
||||
MissingBeaconBlock(Hash256),
|
||||
InvalidBeaconBlock(Hash256),
|
||||
}
|
||||
|
||||
impl<T, U> BeaconChain<T, U>
|
||||
where
|
||||
T: ClientDB,
|
||||
U: SlotClock,
|
||||
Error: From<<U as SlotClock>::Error>,
|
||||
{
|
||||
pub fn slow_lmd_ghost(&mut self, start_hash: &Hash256) -> Result<Hash256, Error> {
|
||||
let start = self
|
||||
.block_store
|
||||
.get_reader(&start_hash)?
|
||||
.ok_or(Error::MissingBeaconBlock(*start_hash))?;
|
||||
|
||||
let start_state_root = start.state_root();
|
||||
|
||||
let state = self
|
||||
.state_store
|
||||
.get_reader(&start_state_root)?
|
||||
.ok_or(Error::MissingBeaconState(start_state_root))?
|
||||
.into_beacon_state()
|
||||
.ok_or(Error::InvalidBeaconState(start_state_root))?;
|
||||
|
||||
let active_validator_indices =
|
||||
get_active_validator_indices(&state.validator_registry, start.slot());
|
||||
|
||||
let mut attestation_targets = Vec::with_capacity(active_validator_indices.len());
|
||||
for i in active_validator_indices {
|
||||
if let Some(target) = self.latest_attestation_targets.get(&i) {
|
||||
attestation_targets.push(target);
|
||||
}
|
||||
}
|
||||
|
||||
let mut head_hash = Hash256::zero();
|
||||
let mut head_vote_count = 0;
|
||||
|
||||
loop {
|
||||
let child_hashes_and_slots =
|
||||
get_child_hashes_and_slots(&self.block_store, &head_hash, &self.leaf_blocks)?;
|
||||
|
||||
if child_hashes_and_slots.len() == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
for (child_hash, child_slot) in child_hashes_and_slots {
|
||||
let vote_count = get_vote_count(
|
||||
&self.block_store,
|
||||
&attestation_targets[..],
|
||||
&child_hash,
|
||||
child_slot,
|
||||
)?;
|
||||
|
||||
if vote_count > head_vote_count {
|
||||
head_hash = child_hash;
|
||||
head_vote_count = vote_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(head_hash)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_vote_count<T: ClientDB>(
|
||||
block_store: &Arc<BeaconBlockStore<T>>,
|
||||
attestation_targets: &[&Hash256],
|
||||
block_root: &Hash256,
|
||||
slot: u64,
|
||||
) -> Result<u64, Error> {
|
||||
let mut count = 0;
|
||||
for target in attestation_targets {
|
||||
let (root_at_slot, _) = block_store
|
||||
.block_at_slot(&block_root, slot)?
|
||||
.ok_or(Error::MissingBeaconBlock(*block_root))?;
|
||||
if root_at_slot == *block_root {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
/// Starting from some `leaf_hashes`, recurse back down each branch until the `root_hash`, adding
|
||||
/// each `block_root` and `slot` to a HashSet.
|
||||
fn get_child_hashes_and_slots<T: ClientDB>(
|
||||
block_store: &Arc<BeaconBlockStore<T>>,
|
||||
root_hash: &Hash256,
|
||||
leaf_hashes: &HashSet<Hash256>,
|
||||
) -> Result<HashSet<(Hash256, u64)>, Error> {
|
||||
let mut hash_set = HashSet::new();
|
||||
|
||||
for leaf_hash in leaf_hashes {
|
||||
let mut current_hash = *leaf_hash;
|
||||
|
||||
loop {
|
||||
if let Some(block_reader) = block_store.get_reader(¤t_hash)? {
|
||||
let parent_root = block_reader.parent_root();
|
||||
|
||||
let new_hash = hash_set.insert((current_hash, block_reader.slot()));
|
||||
|
||||
// If the hash just added was already in the set, break the loop.
|
||||
//
|
||||
// In such a case, the present branch has merged with a branch that is already in
|
||||
// the set.
|
||||
if !new_hash {
|
||||
break;
|
||||
}
|
||||
|
||||
// The branch is exhausted if the parent of this block is the root_hash.
|
||||
if parent_root == *root_hash {
|
||||
break;
|
||||
}
|
||||
|
||||
current_hash = parent_root.clone();
|
||||
} else {
|
||||
return Err(Error::MissingBeaconBlock(current_hash));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(hash_set)
|
||||
}
|
||||
|
||||
impl From<DBError> for Error {
|
||||
fn from(e: DBError) -> Error {
|
||||
Error::DBError(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BeaconBlockAtSlotError> for Error {
|
||||
fn from(e: BeaconBlockAtSlotError) -> Error {
|
||||
match e {
|
||||
BeaconBlockAtSlotError::UnknownBeaconBlock(h) => Error::MissingBeaconBlock(h),
|
||||
BeaconBlockAtSlotError::InvalidBeaconBlock(h) => Error::InvalidBeaconBlock(h),
|
||||
BeaconBlockAtSlotError::DBError(msg) => Error::DBError(msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TestingSlotClockError> for Error {
|
||||
fn from(_: TestingSlotClockError) -> Error {
|
||||
unreachable!(); // Testing clock never throws an error.
|
||||
}
|
||||
}
|
||||
484
beacon_node/beacon_chain/src/state_transition.rs
Normal file
484
beacon_node/beacon_chain/src/state_transition.rs
Normal file
@@ -0,0 +1,484 @@
|
||||
use super::{BeaconChain, ClientDB, DBError, SlotClock};
|
||||
use bls::{AggregatePublicKey, AggregateSignature, PublicKey, Signature};
|
||||
use boolean_bitfield::BooleanBitfield;
|
||||
use slot_clock::{SystemTimeSlotClockError, TestingSlotClockError};
|
||||
use ssz::ssz_encode;
|
||||
use types::{
|
||||
readers::{BeaconBlockReader, BeaconStateReader},
|
||||
AttestationData, AttestationDataAndCustodyBit, BeaconBlock, BeaconState, Exit, ForkData,
|
||||
Hash256, PendingAttestationRecord, ProposalSignedData,
|
||||
};
|
||||
|
||||
// TODO: define elsehwere.
|
||||
const DOMAIN_ATTESTATION: u64 = 1;
|
||||
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,
|
||||
BadAttestation,
|
||||
NoBlockRoot,
|
||||
MaxDepositsExceeded,
|
||||
MaxExitsExceeded,
|
||||
BadExit,
|
||||
BadCustodyReseeds,
|
||||
BadCustodyChallenges,
|
||||
BadCustodyResponses,
|
||||
SlotClockError(SystemTimeSlotClockError),
|
||||
}
|
||||
|
||||
impl<T, U> BeaconChain<T, U>
|
||||
where
|
||||
T: ClientDB,
|
||||
U: SlotClock,
|
||||
{
|
||||
pub fn state_transition(
|
||||
&self,
|
||||
mut state: BeaconState,
|
||||
block: &BeaconBlock,
|
||||
) -> Result<BeaconState, Error> {
|
||||
ensure!(state.slot < block.slot, Error::StateAlreadyTransitioned);
|
||||
|
||||
for _ in state.slot..block.slot {
|
||||
self.per_slot_processing(&mut state, &block.parent_root)?;
|
||||
}
|
||||
|
||||
/*
|
||||
* Slot
|
||||
*/
|
||||
|
||||
ensure!(block.slot() == state.slot, Error::StateSlotMismatch);
|
||||
|
||||
/*
|
||||
* Proposer Signature
|
||||
*/
|
||||
|
||||
let block_without_signature_root = {
|
||||
let mut block_without_signature = block.clone();
|
||||
block_without_signature.signature = self.spec.empty_signature.clone();
|
||||
block_without_signature.canonical_root()
|
||||
};
|
||||
|
||||
let proposal_root = {
|
||||
let proposal = ProposalSignedData {
|
||||
slot: state.slot,
|
||||
shard: self.spec.beacon_chain_shard_number,
|
||||
block_root: block_without_signature_root,
|
||||
};
|
||||
hash_tree_root(&proposal)
|
||||
};
|
||||
|
||||
let block_proposer_index =
|
||||
get_beacon_proposer_index(&state, block.slot, self.spec.epoch_length)
|
||||
.ok_or(Error::NoBlockProducer)?;
|
||||
let block_proposer = &state.validator_registry[block_proposer_index];
|
||||
|
||||
ensure!(
|
||||
bls_verify(
|
||||
&block_proposer.pubkey,
|
||||
&proposal_root,
|
||||
&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
|
||||
);
|
||||
|
||||
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));
|
||||
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,
|
||||
&hash_tree_root(&proposer_slashing.proposal_data_1),
|
||||
&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,
|
||||
&hash_tree_root(&proposer_slashing.proposal_data_2),
|
||||
&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 {
|
||||
ensure!(
|
||||
attestation.data.slot + self.spec.min_attestation_inclusion_delay <= state.slot,
|
||||
Error::BadAttestation
|
||||
);
|
||||
ensure!(
|
||||
attestation.data.slot + self.spec.epoch_length >= state.slot,
|
||||
Error::BadAttestation
|
||||
);
|
||||
if state.justified_slot >= state.slot - (state.slot % self.spec.epoch_length) {
|
||||
ensure!(
|
||||
attestation.data.justified_slot == state.justified_slot,
|
||||
Error::BadAttestation
|
||||
);
|
||||
} else {
|
||||
ensure!(
|
||||
attestation.data.justified_slot == state.previous_justified_slot,
|
||||
Error::BadAttestation
|
||||
);
|
||||
}
|
||||
ensure!(
|
||||
attestation.data.justified_block_root
|
||||
== *get_block_root(
|
||||
&state,
|
||||
attestation.data.justified_slot,
|
||||
self.spec.latest_block_roots_length
|
||||
)
|
||||
.ok_or(Error::NoBlockRoot)?,
|
||||
Error::BadAttestation
|
||||
);
|
||||
ensure!(
|
||||
(attestation.data.latest_crosslink_root
|
||||
== state.latest_crosslinks[attestation.data.shard as usize].shard_block_root)
|
||||
|| (attestation.data.shard_block_root
|
||||
== state.latest_crosslinks[attestation.data.shard as usize]
|
||||
.shard_block_root),
|
||||
Error::BadAttestation
|
||||
);
|
||||
let participants = get_attestation_participants(
|
||||
&state,
|
||||
&attestation.data,
|
||||
&attestation.aggregation_bitfield,
|
||||
);
|
||||
let mut group_public_key = AggregatePublicKey::new();
|
||||
for participant in participants {
|
||||
group_public_key.add(
|
||||
state.validator_registry[participant as usize]
|
||||
.pubkey
|
||||
.as_raw(),
|
||||
)
|
||||
}
|
||||
let attestation_message = {
|
||||
let attestation_data_and_custody_bit = AttestationDataAndCustodyBit {
|
||||
data: attestation.data.clone(),
|
||||
custody_bit: false,
|
||||
};
|
||||
hash_tree_root(&attestation_data_and_custody_bit).to_vec()
|
||||
};
|
||||
// Signature verification.
|
||||
ensure!(
|
||||
bls_verify_aggregate(
|
||||
&group_public_key,
|
||||
&attestation_message[..],
|
||||
&attestation.aggregate_signature,
|
||||
get_domain(&state.fork_data, attestation.data.slot, DOMAIN_ATTESTATION)
|
||||
),
|
||||
Error::BadProposerSlashing
|
||||
);
|
||||
ensure!(
|
||||
attestation.data.shard_block_root == self.spec.zero_hash,
|
||||
Error::BadAttestation
|
||||
);
|
||||
let pending_attestation = PendingAttestationRecord {
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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(),
|
||||
};
|
||||
hash_tree_root(&exit_struct)
|
||||
};
|
||||
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
|
||||
);
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
fn per_slot_processing(
|
||||
&self,
|
||||
state: &mut BeaconState,
|
||||
previous_block_root: &Hash256,
|
||||
) -> Result<(), Error> {
|
||||
let epoch_length = self.spec.epoch_length;
|
||||
let latest_randao_mixes_length = self.spec.latest_randao_mixes_length;
|
||||
let latest_block_roots_length = self.spec.latest_block_roots_length;
|
||||
|
||||
// Misc counters.
|
||||
state.slot += 1;
|
||||
let block_proposer = get_beacon_proposer_index(&state, state.slot, epoch_length)
|
||||
.ok_or(Error::NoBlockProducer)?;
|
||||
state.validator_registry[block_proposer].proposer_slots += 1;
|
||||
state.latest_randao_mixes[(state.slot % latest_randao_mixes_length) as usize] =
|
||||
state.latest_randao_mixes[((state.slot - 1) % latest_randao_mixes_length) as usize];
|
||||
|
||||
// Block roots.
|
||||
state.latest_block_roots
|
||||
[((state.slot - 1) % self.spec.latest_block_roots_length) as usize] =
|
||||
*previous_block_root;
|
||||
|
||||
if state.slot % latest_block_roots_length == 0 {
|
||||
let root = merkle_root(&state.latest_block_roots[..]);
|
||||
state.batched_block_roots.push(root);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn initiate_validator_exit(_state: &BeaconState, _index: u32) {
|
||||
// TODO: stubbed out.
|
||||
}
|
||||
|
||||
fn get_attestation_participants(
|
||||
_state: &BeaconState,
|
||||
_attestation_data: &AttestationData,
|
||||
_aggregation_bitfield: &BooleanBitfield,
|
||||
) -> Vec<usize> {
|
||||
vec![0, 1]
|
||||
}
|
||||
|
||||
fn get_block_root(
|
||||
state: &BeaconState,
|
||||
slot: u64,
|
||||
latest_block_roots_length: u64,
|
||||
) -> Option<&Hash256> {
|
||||
// TODO: test
|
||||
if state.slot <= slot + latest_block_roots_length && slot <= state.slot {
|
||||
state
|
||||
.latest_block_roots
|
||||
.get((slot % latest_block_roots_length) as usize)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn penalize_validator(_state: &BeaconState, _proposer_index: usize) {
|
||||
// TODO: stubbed out.
|
||||
}
|
||||
|
||||
fn hash<T>(_input: &T) -> Hash256 {
|
||||
// TODO: stubbed out.
|
||||
Hash256::zero()
|
||||
}
|
||||
|
||||
fn get_domain(_fork: &ForkData, _slot: u64, _domain_type: u64) -> u64 {
|
||||
// TODO: stubbed out.
|
||||
0
|
||||
}
|
||||
|
||||
fn bls_verify(_pubkey: &PublicKey, _message: &[u8], _signature: &Signature, _domain: u64) -> bool {
|
||||
// TODO: stubbed out.
|
||||
true
|
||||
}
|
||||
|
||||
fn bls_verify_aggregate(
|
||||
_pubkey: &AggregatePublicKey,
|
||||
_message: &[u8],
|
||||
_signature: &AggregateSignature,
|
||||
_domain: u64,
|
||||
) -> bool {
|
||||
// TODO: stubbed out.
|
||||
true
|
||||
}
|
||||
|
||||
fn hash_tree_root<T>(_input: &T) -> Hash256 {
|
||||
// TODO: stubbed out.
|
||||
Hash256::zero()
|
||||
}
|
||||
|
||||
fn merkle_root(_input: &[Hash256]) -> Hash256 {
|
||||
// TODO: stubbed out.
|
||||
Hash256::zero()
|
||||
}
|
||||
|
||||
fn get_beacon_proposer_index(
|
||||
_state: &BeaconState,
|
||||
_slot: u64,
|
||||
_epoch_length: u64,
|
||||
) -> Option<usize> {
|
||||
// TODO: stubbed out.
|
||||
Some(0)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
29
beacon_node/beacon_chain/src/transition.rs
Normal file
29
beacon_node/beacon_chain/src/transition.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use super::BeaconChain;
|
||||
use db::ClientDB;
|
||||
use state_transition::{extend_active_state, StateTransitionError};
|
||||
use types::{ActiveState, BeaconBlock, CrystallizedState, Hash256};
|
||||
|
||||
impl<T, U> BeaconChain<T, U>
|
||||
where
|
||||
T: ClientDB + Sized,
|
||||
{
|
||||
pub(crate) fn transition_states(
|
||||
&self,
|
||||
act_state: &ActiveState,
|
||||
cry_state: &CrystallizedState,
|
||||
block: &BeaconBlock,
|
||||
block_hash: &Hash256,
|
||||
) -> Result<(ActiveState, Option<CrystallizedState>), StateTransitionError> {
|
||||
let state_recalc_distance = block
|
||||
.slot
|
||||
.checked_sub(cry_state.last_state_recalculation_slot)
|
||||
.ok_or(StateTransitionError::BlockSlotBeforeRecalcSlot)?;
|
||||
|
||||
if state_recalc_distance >= u64::from(self.spec.epoch_length) {
|
||||
panic!("Not implemented!")
|
||||
} else {
|
||||
let new_act_state = extend_active_state(act_state, block, block_hash)?;
|
||||
Ok((new_act_state, None))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user