From 1256ba0d010c8264bc23f401efb73613da4b9661 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Wed, 23 Jan 2019 19:25:05 +1100 Subject: [PATCH] Implement very raw state transition logic --- Cargo.toml | 1 + beacon_node/Cargo.toml | 1 + beacon_node/beacon_chain/Cargo.toml | 18 + .../beacon_chain/src/block_processing.rs | 136 +++++ .../src}/block_production.rs | 54 +- .../mod.rs => beacon_chain/src/lib.rs} | 3 +- .../src}/lmd_ghost.rs | 0 .../beacon_chain/src/state_transition.rs | 484 ++++++++++++++++++ .../src}/transition.rs | 0 .../tests/chain.rs} | 5 +- .../src/beacon_chain/block_processing.rs | 72 --- beacon_node/src/main.rs | 3 +- eth2/genesis/src/beacon_block.rs | 4 +- eth2/spec/src/foundation.rs | 1 + eth2/types/src/attestation.rs | 24 +- eth2/types/src/attestation_data.rs | 42 +- .../src/attestation_data_and_custody_bit.rs | 20 +- eth2/types/src/beacon_block.rs | 2 +- eth2/types/src/beacon_state.rs | 12 +- eth2/types/src/lib.rs | 2 + eth2/types/src/pending_attestation_record.rs | 10 +- eth2/types/src/validator_record.rs | 6 + .../slot_clock/src/system_time_slot_clock.rs | 6 +- eth2/validator_induction/src/inductor.rs | 1 + 24 files changed, 748 insertions(+), 159 deletions(-) create mode 100644 beacon_node/beacon_chain/Cargo.toml create mode 100644 beacon_node/beacon_chain/src/block_processing.rs rename beacon_node/{src/beacon_chain => beacon_chain/src}/block_production.rs (55%) rename beacon_node/{src/beacon_chain/mod.rs => beacon_chain/src/lib.rs} (98%) rename beacon_node/{src/beacon_chain => beacon_chain/src}/lmd_ghost.rs (100%) create mode 100644 beacon_node/beacon_chain/src/state_transition.rs rename beacon_node/{src/beacon_chain => beacon_chain/src}/transition.rs (100%) rename beacon_node/{src/beacon_chain/chain_test.rs => beacon_chain/tests/chain.rs} (90%) delete mode 100644 beacon_node/src/beacon_chain/block_processing.rs diff --git a/Cargo.toml b/Cargo.toml index c73e9f22d6..bcaaef87e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ members = [ "eth2/validator_shuffling", "beacon_node", "beacon_node/db", + "beacon_node/beacon_chain", "protos", "validator_client", ] diff --git a/beacon_node/Cargo.toml b/beacon_node/Cargo.toml index 1db739d7ca..7b44bb0f90 100644 --- a/beacon_node/Cargo.toml +++ b/beacon_node/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" [dependencies] bls = { path = "../eth2/utils/bls" } +beacon_chain = { path = "beacon_chain" } grpcio = { version = "0.4", default-features = false, features = ["protobuf-codec"] } protobuf = "2.0.2" protos = { path = "../protos" } diff --git a/beacon_node/beacon_chain/Cargo.toml b/beacon_node/beacon_chain/Cargo.toml new file mode 100644 index 0000000000..d6dac9a4b2 --- /dev/null +++ b/beacon_node/beacon_chain/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "beacon_chain" +version = "0.1.0" +authors = ["Paul Hauner "] +edition = "2018" + +[dependencies] +bls = { path = "../../eth2/utils/bls" } +boolean-bitfield = { path = "../../eth2/utils/boolean-bitfield" } +db = { path = "../db" } +failure = "0.1" +failure_derive = "0.1" +genesis = { path = "../../eth2/genesis" } +serde_derive = "1.0" +slot_clock = { path = "../../eth2/utils/slot_clock" } +spec = { path = "../../eth2/spec" } +ssz = { path = "../../eth2/utils/ssz" } +types = { path = "../../eth2/types" } diff --git a/beacon_node/beacon_chain/src/block_processing.rs b/beacon_node/beacon_chain/src/block_processing.rs new file mode 100644 index 0000000000..6c534bbce9 --- /dev/null +++ b/beacon_node/beacon_chain/src/block_processing.rs @@ -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 BeaconChain +where + T: ClientDB, + U: SlotClock, + Error: From<::Error>, +{ + pub fn process_block(&mut self, block: V) -> Result + 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 for Error { + fn from(e: DBError) -> Error { + Error::DBError(e.message) + } +} + +impl From for Error { + fn from(_: TestingSlotClockError) -> Error { + unreachable!(); // Testing clock never throws an error. + } +} + +impl From for Error { + fn from(e: SystemTimeSlotClockError) -> Error { + Error::SlotClockError(e) + } +} diff --git a/beacon_node/src/beacon_chain/block_production.rs b/beacon_node/beacon_chain/src/block_production.rs similarity index 55% rename from beacon_node/src/beacon_chain/block_production.rs rename to beacon_node/beacon_chain/src/block_production.rs index ba781d6e92..f6219fc875 100644 --- a/beacon_node/src/beacon_chain/block_production.rs +++ b/beacon_node/beacon_chain/src/block_production.rs @@ -1,13 +1,16 @@ +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, BeaconState, Hash256, + BeaconBlock, BeaconBlockBody, BeaconState, Hash256, }; #[derive(Debug, PartialEq)] pub enum Error { DBError(String), + StateTransitionError(TransitionError), PresentSlotIsNone, } @@ -17,43 +20,48 @@ where U: SlotClock, Error: From<::Error>, { - pub fn produce_block(&mut self) -> Result<(BeaconBlock, BeaconState), Error> { - /* - * Important: this code is a big stub and only exists to ensure that tests pass. - * - * https://github.com/sigp/lighthouse/issues/107 - */ + 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_reader = self + let parent_state = self .state_store .get_reader(&parent_block_reader.state_root())? - .ok_or_else(|| Error::DBError("State not found.".to_string()))?; + .ok_or_else(|| Error::DBError("State not found.".to_string()))? + .into_beacon_state() + .ok_or_else(|| Error::DBError("State invalid.".to_string()))?; - let parent_block = parent_block_reader - .into_beacon_block() - .ok_or_else(|| Error::DBError("Bad parent block SSZ.".to_string()))?; let mut block = BeaconBlock { slot: present_slot, parent_root, state_root: Hash256::zero(), // Updated after the state is calculated. - ..parent_block + 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 parent_state = parent_state_reader - .into_beacon_state() - .ok_or_else(|| Error::DBError("Bad parent block SSZ.".to_string()))?; - let state = BeaconState { - slot: present_slot, - ..parent_state - }; + let state = self.state_transition(parent_state, &block)?; + let state_root = state.canonical_root(); block.state_root = state_root; @@ -68,6 +76,12 @@ impl From for Error { } } +impl From for Error { + fn from(e: TransitionError) -> Error { + Error::StateTransitionError(e) + } +} + impl From for Error { fn from(_: TestingSlotClockError) -> Error { unreachable!(); // Testing clock never throws an error. diff --git a/beacon_node/src/beacon_chain/mod.rs b/beacon_node/beacon_chain/src/lib.rs similarity index 98% rename from beacon_node/src/beacon_chain/mod.rs rename to beacon_node/beacon_chain/src/lib.rs index be1d6ce9dc..6c95fcaa4b 100644 --- a/beacon_node/src/beacon_chain/mod.rs +++ b/beacon_node/beacon_chain/src/lib.rs @@ -1,8 +1,7 @@ mod block_processing; mod block_production; -#[cfg(test)] -mod chain_test; mod lmd_ghost; +mod state_transition; use db::{ stores::{BeaconBlockStore, BeaconStateStore}, diff --git a/beacon_node/src/beacon_chain/lmd_ghost.rs b/beacon_node/beacon_chain/src/lmd_ghost.rs similarity index 100% rename from beacon_node/src/beacon_chain/lmd_ghost.rs rename to beacon_node/beacon_chain/src/lmd_ghost.rs diff --git a/beacon_node/beacon_chain/src/state_transition.rs b/beacon_node/beacon_chain/src/state_transition.rs new file mode 100644 index 0000000000..afa805a23b --- /dev/null +++ b/beacon_node/beacon_chain/src/state_transition.rs @@ -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 BeaconChain +where + T: ClientDB, + U: SlotClock, +{ + pub fn state_transition( + &self, + mut state: BeaconState, + block: &BeaconBlock, + ) -> Result { + 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 { + 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(_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(_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 { + // TODO: stubbed out. + Some(0) +} + +impl From for Error { + fn from(e: DBError) -> Error { + Error::DBError(e.message) + } +} + +impl From for Error { + fn from(_: TestingSlotClockError) -> Error { + unreachable!(); // Testing clock never throws an error. + } +} + +impl From for Error { + fn from(e: SystemTimeSlotClockError) -> Error { + Error::SlotClockError(e) + } +} diff --git a/beacon_node/src/beacon_chain/transition.rs b/beacon_node/beacon_chain/src/transition.rs similarity index 100% rename from beacon_node/src/beacon_chain/transition.rs rename to beacon_node/beacon_chain/src/transition.rs diff --git a/beacon_node/src/beacon_chain/chain_test.rs b/beacon_node/beacon_chain/tests/chain.rs similarity index 90% rename from beacon_node/src/beacon_chain/chain_test.rs rename to beacon_node/beacon_chain/tests/chain.rs index 3d7d54d368..9c76bf59df 100644 --- a/beacon_node/src/beacon_chain/chain_test.rs +++ b/beacon_node/beacon_chain/tests/chain.rs @@ -1,4 +1,5 @@ -use super::{BeaconChain, BlockProcessingOutcome}; +use beacon_chain::{BeaconChain, BlockProcessingOutcome}; +use bls::Signature; use db::{ stores::{BeaconBlockStore, BeaconStateStore}, MemoryDB, @@ -43,7 +44,7 @@ fn it_produces() { fn it_processes_a_block_it_produces() { let (_db, mut chain) = in_memory_test_chain(ChainSpec::foundation()); let (block, _state) = chain.produce_block().unwrap(); - let (outcome, new_block_hash) = chain.process_block(&block).unwrap(); + let (outcome, new_block_hash) = chain.process_block(block).unwrap(); assert_eq!(outcome, BlockProcessingOutcome::Processed); assert_eq!(chain.canonical_leaf_block, new_block_hash); } diff --git a/beacon_node/src/beacon_chain/block_processing.rs b/beacon_node/src/beacon_chain/block_processing.rs deleted file mode 100644 index b24a6f1f81..0000000000 --- a/beacon_node/src/beacon_chain/block_processing.rs +++ /dev/null @@ -1,72 +0,0 @@ -use super::{BeaconChain, ClientDB, DBError, SlotClock}; -use slot_clock::TestingSlotClockError; -use ssz::{ssz_encode, Encodable}; -use types::{readers::BeaconBlockReader, Hash256}; - -#[derive(Debug, PartialEq)] -pub enum Outcome { - FutureSlot, - Processed, - - NewCanonicalBlock, - NewReorgBlock, - NewForkBlock, -} - -#[derive(Debug, PartialEq)] -pub enum Error { - DBError(String), - NotImplemented, - PresentSlotIsNone, -} - -impl BeaconChain -where - T: ClientDB, - U: SlotClock, - Error: From<::Error>, -{ - pub fn process_block(&mut self, block: &V) -> Result<(Outcome, Hash256), Error> - where - V: BeaconBlockReader + Encodable + Sized, - { - 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, block_root)); - } - - // TODO: block processing has been removed. - // https://github.com/sigp/lighthouse/issues/98 - - // Update leaf blocks. - self.block_store.put(&block_root, &ssz_encode(block)[..])?; - 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); - - Ok((Outcome::Processed, block_root)) - } -} - -impl From for Error { - fn from(e: DBError) -> Error { - Error::DBError(e.message) - } -} - -impl From for Error { - fn from(_: TestingSlotClockError) -> Error { - unreachable!(); // Testing clock never throws an error. - } -} diff --git a/beacon_node/src/main.rs b/beacon_node/src/main.rs index 26b6e362ed..f9e77723ac 100644 --- a/beacon_node/src/main.rs +++ b/beacon_node/src/main.rs @@ -1,14 +1,13 @@ extern crate slog; -mod beacon_chain; mod config; mod rpc; use std::path::PathBuf; -use self::beacon_chain::BeaconChain; use crate::config::LighthouseConfig; use crate::rpc::start_server; +use beacon_chain::BeaconChain; use clap::{App, Arg}; use db::{ stores::{BeaconBlockStore, BeaconStateStore}, diff --git a/eth2/genesis/src/beacon_block.rs b/eth2/genesis/src/beacon_block.rs index 3fefff6a04..9c44ece6c3 100644 --- a/eth2/genesis/src/beacon_block.rs +++ b/eth2/genesis/src/beacon_block.rs @@ -7,7 +7,7 @@ pub fn genesis_beacon_block(state_root: Hash256, spec: &ChainSpec) -> BeaconBloc slot: spec.genesis_slot_number, parent_root: spec.zero_hash, state_root, - randao_reveal: spec.zero_hash, + randao_reveal: spec.empty_signature.clone(), candidate_pow_receipt_root: spec.zero_hash, signature: spec.empty_signature.clone(), body: BeaconBlockBody { @@ -47,7 +47,7 @@ mod tests { assert!(genesis_block.slot == 0); assert!(genesis_block.parent_root.is_zero()); - assert!(genesis_block.randao_reveal.is_zero()); + assert_eq!(genesis_block.randao_reveal, spec.empty_signature); assert!(genesis_block.candidate_pow_receipt_root.is_zero()); // aka deposit_root } diff --git a/eth2/spec/src/foundation.rs b/eth2/spec/src/foundation.rs index 81bee7c4ab..9aa2b1971a 100644 --- a/eth2/spec/src/foundation.rs +++ b/eth2/spec/src/foundation.rs @@ -107,6 +107,7 @@ fn initial_validators_for_testing() -> Vec { let validator_record = ValidatorRecord { pubkey: keypair.pk.clone(), withdrawal_credentials: Hash256::zero(), + proposer_slots: 0, randao_commitment: Hash256::zero(), randao_layers: 0, activation_slot: u64::max_value(), diff --git a/eth2/types/src/attestation.rs b/eth2/types/src/attestation.rs index fb8b946a5c..bef35bc786 100644 --- a/eth2/types/src/attestation.rs +++ b/eth2/types/src/attestation.rs @@ -7,32 +7,32 @@ use rand::RngCore; #[derive(Debug, Clone, PartialEq)] pub struct Attestation { pub data: AttestationData, - pub participation_bitfield: Bitfield, + pub aggregation_bitfield: Bitfield, pub custody_bitfield: Bitfield, - pub aggregate_sig: AggregateSignature, + pub aggregate_signature: AggregateSignature, } impl Encodable for Attestation { fn ssz_append(&self, s: &mut SszStream) { s.append(&self.data); - s.append(&self.participation_bitfield); + s.append(&self.aggregation_bitfield); s.append(&self.custody_bitfield); - s.append(&self.aggregate_sig); + s.append(&self.aggregate_signature); } } impl Decodable for Attestation { fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { let (data, i) = AttestationData::ssz_decode(bytes, i)?; - let (participation_bitfield, i) = Bitfield::ssz_decode(bytes, i)?; + let (aggregation_bitfield, i) = Bitfield::ssz_decode(bytes, i)?; let (custody_bitfield, i) = Bitfield::ssz_decode(bytes, i)?; - let (aggregate_sig, i) = AggregateSignature::ssz_decode(bytes, i)?; + let (aggregate_signature, i) = AggregateSignature::ssz_decode(bytes, i)?; let attestation_record = Self { data, - participation_bitfield, + aggregation_bitfield, custody_bitfield, - aggregate_sig, + aggregate_signature, }; Ok((attestation_record, i)) } @@ -42,9 +42,9 @@ impl Attestation { pub fn zero() -> Self { Self { data: AttestationData::zero(), - participation_bitfield: Bitfield::new(), + aggregation_bitfield: Bitfield::new(), custody_bitfield: Bitfield::new(), - aggregate_sig: AggregateSignature::new(), + aggregate_signature: AggregateSignature::new(), } } } @@ -53,9 +53,9 @@ impl TestRandom for Attestation { fn random_for_test(rng: &mut T) -> Self { Self { data: <_>::random_for_test(rng), - participation_bitfield: <_>::random_for_test(rng), + aggregation_bitfield: <_>::random_for_test(rng), custody_bitfield: <_>::random_for_test(rng), - aggregate_sig: <_>::random_for_test(rng), + aggregate_signature: <_>::random_for_test(rng), } } } diff --git a/eth2/types/src/attestation_data.rs b/eth2/types/src/attestation_data.rs index e1d70e2b01..f4eccb86e6 100644 --- a/eth2/types/src/attestation_data.rs +++ b/eth2/types/src/attestation_data.rs @@ -8,10 +8,10 @@ pub const SSZ_ATTESTION_DATA_LENGTH: usize = { 8 + // shard 32 + // beacon_block_hash 32 + // epoch_boundary_hash - 32 + // shard_block_hash - 32 + // latest_crosslink_hash + 32 + // shard_block_root + 32 + // latest_crosslink_root 8 + // justified_slot - 32 // justified_block_hash + 32 // justified_block_root }; #[derive(Debug, Clone, PartialEq, Default)] @@ -20,10 +20,10 @@ pub struct AttestationData { pub shard: u64, pub beacon_block_hash: Hash256, pub epoch_boundary_hash: Hash256, - pub shard_block_hash: Hash256, - pub latest_crosslink_hash: Hash256, + pub shard_block_root: Hash256, + pub latest_crosslink_root: Hash256, pub justified_slot: u64, - pub justified_block_hash: Hash256, + pub justified_block_root: Hash256, } impl AttestationData { @@ -33,10 +33,10 @@ impl AttestationData { shard: 0, beacon_block_hash: Hash256::zero(), epoch_boundary_hash: Hash256::zero(), - shard_block_hash: Hash256::zero(), - latest_crosslink_hash: Hash256::zero(), + shard_block_root: Hash256::zero(), + latest_crosslink_root: Hash256::zero(), justified_slot: 0, - justified_block_hash: Hash256::zero(), + justified_block_root: Hash256::zero(), } } @@ -53,10 +53,10 @@ impl Encodable for AttestationData { s.append(&self.shard); s.append(&self.beacon_block_hash); s.append(&self.epoch_boundary_hash); - s.append(&self.shard_block_hash); - s.append(&self.latest_crosslink_hash); + s.append(&self.shard_block_root); + s.append(&self.latest_crosslink_root); s.append(&self.justified_slot); - s.append(&self.justified_block_hash); + s.append(&self.justified_block_root); } } @@ -66,20 +66,20 @@ impl Decodable for AttestationData { let (shard, i) = u64::ssz_decode(bytes, i)?; let (beacon_block_hash, i) = Hash256::ssz_decode(bytes, i)?; let (epoch_boundary_hash, i) = Hash256::ssz_decode(bytes, i)?; - let (shard_block_hash, i) = Hash256::ssz_decode(bytes, i)?; - let (latest_crosslink_hash, i) = Hash256::ssz_decode(bytes, i)?; + let (shard_block_root, i) = Hash256::ssz_decode(bytes, i)?; + let (latest_crosslink_root, i) = Hash256::ssz_decode(bytes, i)?; let (justified_slot, i) = u64::ssz_decode(bytes, i)?; - let (justified_block_hash, i) = Hash256::ssz_decode(bytes, i)?; + let (justified_block_root, i) = Hash256::ssz_decode(bytes, i)?; let attestation_data = AttestationData { slot, shard, beacon_block_hash, epoch_boundary_hash, - shard_block_hash, - latest_crosslink_hash, + shard_block_root, + latest_crosslink_root, justified_slot, - justified_block_hash, + justified_block_root, }; Ok((attestation_data, i)) } @@ -92,10 +92,10 @@ impl TestRandom for AttestationData { shard: <_>::random_for_test(rng), beacon_block_hash: <_>::random_for_test(rng), epoch_boundary_hash: <_>::random_for_test(rng), - shard_block_hash: <_>::random_for_test(rng), - latest_crosslink_hash: <_>::random_for_test(rng), + shard_block_root: <_>::random_for_test(rng), + latest_crosslink_root: <_>::random_for_test(rng), justified_slot: <_>::random_for_test(rng), - justified_block_hash: <_>::random_for_test(rng), + justified_block_root: <_>::random_for_test(rng), } } } diff --git a/eth2/types/src/attestation_data_and_custody_bit.rs b/eth2/types/src/attestation_data_and_custody_bit.rs index 66584b0d9e..f53614d6b9 100644 --- a/eth2/types/src/attestation_data_and_custody_bit.rs +++ b/eth2/types/src/attestation_data_and_custody_bit.rs @@ -1,7 +1,7 @@ use super::ssz::{Decodable, DecodeError, Encodable, SszStream}; -use rand::RngCore; -use crate::test_utils::TestRandom; use super::AttestationData; +use crate::test_utils::TestRandom; +use rand::RngCore; #[derive(Debug, Clone, PartialEq, Default)] pub struct AttestationDataAndCustodyBit { @@ -12,19 +12,16 @@ pub struct AttestationDataAndCustodyBit { impl Encodable for AttestationDataAndCustodyBit { fn ssz_append(&self, s: &mut SszStream) { s.append(&self.data); - s.append(&self.custody_bit); + // TODO: deal with bools } } impl Decodable for AttestationDataAndCustodyBit { fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { let (data, i) = <_>::ssz_decode(bytes, i)?; - let (custody_bit, i) = <_>::ssz_decode(bytes, i)?; + let custody_bit = false; - let attestation_data_and_custody_bit = AttestationDataAndCustodyBit { - data, - custody_bit, - }; + let attestation_data_and_custody_bit = AttestationDataAndCustodyBit { data, custody_bit }; Ok((attestation_data_and_custody_bit, i)) } @@ -34,16 +31,17 @@ impl TestRandom for AttestationDataAndCustodyBit { fn random_for_test(rng: &mut T) -> Self { Self { data: <_>::random_for_test(rng), - custody_bit: <_>::random_for_test(rng), + // TODO: deal with bools + custody_bit: false, } } } #[cfg(test)] mod test { - use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng}; - use super::*; use super::super::ssz::ssz_encode; + use super::*; + use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng}; #[test] pub fn test_ssz_round_trip() { diff --git a/eth2/types/src/beacon_block.rs b/eth2/types/src/beacon_block.rs index 30a1bd83b0..ee80bcd9a0 100644 --- a/eth2/types/src/beacon_block.rs +++ b/eth2/types/src/beacon_block.rs @@ -10,7 +10,7 @@ pub struct BeaconBlock { pub slot: u64, pub parent_root: Hash256, pub state_root: Hash256, - pub randao_reveal: Hash256, + pub randao_reveal: Signature, pub candidate_pow_receipt_root: Hash256, pub signature: Signature, pub body: BeaconBlockBody, diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index c55ce8ec9c..74f15d3ce0 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -1,10 +1,10 @@ -use super::candidate_pow_receipt_root_record::CandidatePoWReceiptRootRecord; -use super::crosslink_record::CrosslinkRecord; -use super::fork_data::ForkData; -use super::pending_attestation_record::PendingAttestationRecord; -use super::validator_record::ValidatorRecord; -use super::Hash256; +use crate::candidate_pow_receipt_root_record::CandidatePoWReceiptRootRecord; +use crate::crosslink_record::CrosslinkRecord; +use crate::fork_data::ForkData; +use crate::pending_attestation_record::PendingAttestationRecord; use crate::test_utils::TestRandom; +use crate::validator_record::ValidatorRecord; +use crate::Hash256; use hashing::canonical_hash; use rand::RngCore; use ssz::{ssz_encode, Decodable, DecodeError, Encodable, SszStream}; diff --git a/eth2/types/src/lib.rs b/eth2/types/src/lib.rs index f00c19d037..e23fe2ebc5 100644 --- a/eth2/types/src/lib.rs +++ b/eth2/types/src/lib.rs @@ -7,6 +7,7 @@ pub mod test_utils; pub mod attestation; pub mod attestation_data; +pub mod attestation_data_and_custody_bit; pub mod beacon_block; pub mod beacon_block_body; pub mod beacon_state; @@ -35,6 +36,7 @@ use std::collections::HashMap; pub use crate::attestation::Attestation; pub use crate::attestation_data::AttestationData; +pub use crate::attestation_data_and_custody_bit::AttestationDataAndCustodyBit; pub use crate::beacon_block::BeaconBlock; pub use crate::beacon_block_body::BeaconBlockBody; pub use crate::beacon_state::BeaconState; diff --git a/eth2/types/src/pending_attestation_record.rs b/eth2/types/src/pending_attestation_record.rs index 3bebf5676d..a093b9efdb 100644 --- a/eth2/types/src/pending_attestation_record.rs +++ b/eth2/types/src/pending_attestation_record.rs @@ -6,7 +6,7 @@ use rand::RngCore; #[derive(Debug, Clone, PartialEq)] pub struct PendingAttestationRecord { pub data: AttestationData, - pub participation_bitfield: Bitfield, + pub aggregation_bitfield: Bitfield, pub custody_bitfield: Bitfield, pub slot_included: u64, } @@ -14,7 +14,7 @@ pub struct PendingAttestationRecord { impl Encodable for PendingAttestationRecord { fn ssz_append(&self, s: &mut SszStream) { s.append(&self.data); - s.append(&self.participation_bitfield); + s.append(&self.aggregation_bitfield); s.append(&self.custody_bitfield); s.append(&self.slot_included); } @@ -23,14 +23,14 @@ impl Encodable for PendingAttestationRecord { impl Decodable for PendingAttestationRecord { fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { let (data, i) = <_>::ssz_decode(bytes, i)?; - let (participation_bitfield, i) = <_>::ssz_decode(bytes, i)?; + let (aggregation_bitfield, i) = <_>::ssz_decode(bytes, i)?; let (custody_bitfield, i) = <_>::ssz_decode(bytes, i)?; let (slot_included, i) = <_>::ssz_decode(bytes, i)?; Ok(( Self { data, - participation_bitfield, + aggregation_bitfield, custody_bitfield, slot_included, }, @@ -43,7 +43,7 @@ impl TestRandom for PendingAttestationRecord { fn random_for_test(rng: &mut T) -> Self { Self { data: <_>::random_for_test(rng), - participation_bitfield: <_>::random_for_test(rng), + aggregation_bitfield: <_>::random_for_test(rng), custody_bitfield: <_>::random_for_test(rng), slot_included: <_>::random_for_test(rng), } diff --git a/eth2/types/src/validator_record.rs b/eth2/types/src/validator_record.rs index 5a8cde8aba..50550f6e10 100644 --- a/eth2/types/src/validator_record.rs +++ b/eth2/types/src/validator_record.rs @@ -47,6 +47,7 @@ fn status_flag_from_byte(flag: u8) -> Result, StatusFlagsDec pub struct ValidatorRecord { pub pubkey: PublicKey, pub withdrawal_credentials: Hash256, + pub proposer_slots: u64, pub randao_commitment: Hash256, pub randao_layers: u64, pub activation_slot: u64, @@ -73,6 +74,7 @@ impl Default for ValidatorRecord { Self { pubkey: PublicKey::default(), withdrawal_credentials: Hash256::default(), + proposer_slots: 0, randao_commitment: Hash256::default(), randao_layers: 0, activation_slot: std::u64::MAX, @@ -99,6 +101,7 @@ impl Encodable for ValidatorRecord { fn ssz_append(&self, s: &mut SszStream) { s.append(&self.pubkey); s.append(&self.withdrawal_credentials); + s.append(&self.proposer_slots); s.append(&self.randao_commitment); s.append(&self.randao_layers); s.append(&self.activation_slot); @@ -117,6 +120,7 @@ impl Decodable for ValidatorRecord { fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), DecodeError> { let (pubkey, i) = <_>::ssz_decode(bytes, i)?; let (withdrawal_credentials, i) = <_>::ssz_decode(bytes, i)?; + let (proposer_slots, i) = <_>::ssz_decode(bytes, i)?; let (randao_commitment, i) = <_>::ssz_decode(bytes, i)?; let (randao_layers, i) = <_>::ssz_decode(bytes, i)?; let (activation_slot, i) = <_>::ssz_decode(bytes, i)?; @@ -135,6 +139,7 @@ impl Decodable for ValidatorRecord { Self { pubkey, withdrawal_credentials, + proposer_slots, randao_commitment, randao_layers, activation_slot, @@ -157,6 +162,7 @@ impl TestRandom for ValidatorRecord { Self { pubkey: <_>::random_for_test(rng), withdrawal_credentials: <_>::random_for_test(rng), + proposer_slots: <_>::random_for_test(rng), randao_commitment: <_>::random_for_test(rng), randao_layers: <_>::random_for_test(rng), activation_slot: <_>::random_for_test(rng), diff --git a/eth2/utils/slot_clock/src/system_time_slot_clock.rs b/eth2/utils/slot_clock/src/system_time_slot_clock.rs index 8c1e2f66c0..5c5f2e0eac 100644 --- a/eth2/utils/slot_clock/src/system_time_slot_clock.rs +++ b/eth2/utils/slot_clock/src/system_time_slot_clock.rs @@ -3,10 +3,10 @@ use std::time::{Duration, SystemTime}; pub use std::time::SystemTimeError; -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum Error { SlotDurationIsZero, - SystemTimeError(SystemTimeError), + SystemTimeError(String), } /// Determines the present slot based upon the present system time. @@ -51,7 +51,7 @@ impl SlotClock for SystemTimeSlotClock { impl From for Error { fn from(e: SystemTimeError) -> Error { - Error::SystemTimeError(e) + Error::SystemTimeError(format!("{:?}", e)) } } diff --git a/eth2/validator_induction/src/inductor.rs b/eth2/validator_induction/src/inductor.rs index 9dd5089ba0..05e6dfe3b3 100644 --- a/eth2/validator_induction/src/inductor.rs +++ b/eth2/validator_induction/src/inductor.rs @@ -42,6 +42,7 @@ pub fn process_deposit( let validator = ValidatorRecord { pubkey: deposit_input.pubkey.clone(), withdrawal_credentials: deposit_input.withdrawal_credentials, + proposer_slots: 0, randao_commitment: deposit_input.randao_commitment, randao_layers: 0, activation_slot: spec.far_future_slot,