From 5a2fe23957064c0577eb28d4f3d240b5a7b10ae4 Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Mon, 5 Aug 2019 12:11:09 +1000 Subject: [PATCH 001/151] Initiate shard node and behavior - step-by-step --- shard_node/Cargo.toml | 7 +++ shard_node/shard_chain/Cargo.toml | 7 +++ shard_node/shard_chain/src/lib.rs | 3 + shard_node/shard_chain/src/shard_chain.rs | 75 +++++++++++++++++++++++ 4 files changed, 92 insertions(+) create mode 100644 shard_node/Cargo.toml create mode 100644 shard_node/shard_chain/Cargo.toml create mode 100644 shard_node/shard_chain/src/lib.rs create mode 100644 shard_node/shard_chain/src/shard_chain.rs diff --git a/shard_node/Cargo.toml b/shard_node/Cargo.toml new file mode 100644 index 00000000000..e39d872edb3 --- /dev/null +++ b/shard_node/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "shard_node" +version = "0.1.0" +authors = ["will "] +edition = "2018" + +[dependencies] diff --git a/shard_node/shard_chain/Cargo.toml b/shard_node/shard_chain/Cargo.toml new file mode 100644 index 00000000000..41f5df3b8d4 --- /dev/null +++ b/shard_node/shard_chain/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "shard_chain" +version = "0.1.0" +authors = ["will "] +edition = "2018" + +[dependencies] diff --git a/shard_node/shard_chain/src/lib.rs b/shard_node/shard_chain/src/lib.rs new file mode 100644 index 00000000000..e7a11a969c0 --- /dev/null +++ b/shard_node/shard_chain/src/lib.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs new file mode 100644 index 00000000000..70f3ae15626 --- /dev/null +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -0,0 +1,75 @@ + + +pub trait ShardChainTypes { + type Store: store::Store; + type SlotClock: slot_clock::SlotClock; + type LmdGhost: LmdGhost; + type EthSpec: types::EthSpec; +} + +/// Represents the "Shard Chain" component of Ethereum 2.0. It holds a reference to a parent Beacon Chain +pub struct ShardChain { + pub spec: ChainSpec, + /// + /// Persistent storage for blocks, states, etc. Typically an on-disk store, such as LevelDB. + pub store: Arc, + /// Reports the current slot, typically based upon the system clock. + pub slot_clock: T::SlotClock, + /// Stores all operations (e.g., transactions) that are candidates for + /// inclusion in a block. + pub op_pool: OperationPool, + /// Stores a "snapshot" of the chain at the time the head-of-the-chain block was recieved. + canonical_head: RwLock>, + /// The same state from `self.canonical_head`, but updated at the start of each slot with a + /// skip slot if no block is recieved. This is effectively a cache that avoids repeating calls + /// to `per_slot_processing`. + state: RwLock>, + /// The root of the genesis block. + genesis_block_root: Hash256, + /// A state-machine that is updated with information from the network and chooses a canonical + /// head block. + pub fork_choice: ForkChoice, +} + +impl BeaconChain { + /// Instantiate a new Beacon Chain, from genesis. + pub fn from_genesis( + store: Arc, + slot_clock: T::SlotClock, + mut genesis_state: ShardState, + genesis_block: ShardBlock, + spec: ChainSpec, + ) -> Result { + genesis_state.build_all_caches(&spec)?; + + let state_root = genesis_state.canonical_root(); + store.put(&state_root, &genesis_state)?; + + let genesis_block_root = genesis_block.block_header().canonical_root(); + store.put(&genesis_block_root, &genesis_block)?; + + // Also store the genesis block under the `ZERO_HASH` key. + let genesis_block_root = genesis_block.block_header().canonical_root(); + store.put(&spec.zero_hash, &genesis_block)?; + + let canonical_head = RwLock::new(CheckPoint::new( + genesis_block.clone(), + genesis_block_root, + genesis_state.clone(), + state_root, + )); + + Ok(Self { + spec, + slot_clock, + op_pool: OperationPool::new(), + state: RwLock::new(genesis_state), + canonical_head, + genesis_block_root, + fork_choice: ForkChoice::new(store.clone(), &genesis_block, genesis_block_root), + metrics: Metrics::new()?, + store, + }) + } +} + From 62cf13f95ca595e486534bad8d71472cef40d2af Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Tue, 6 Aug 2019 13:50:41 +1000 Subject: [PATCH 002/151] Piece by piece - adding the store for the shard chain --- shard_node/store/Cargo.toml | 7 +++++++ shard_node/store/src/errors.rs | 30 ++++++++++++++++++++++++++++ shard_node/store/src/lib.rs | 3 +++ shard_node/store/src/memory_store.rs | 0 4 files changed, 40 insertions(+) create mode 100644 shard_node/store/Cargo.toml create mode 100644 shard_node/store/src/errors.rs create mode 100644 shard_node/store/src/lib.rs create mode 100644 shard_node/store/src/memory_store.rs diff --git a/shard_node/store/Cargo.toml b/shard_node/store/Cargo.toml new file mode 100644 index 00000000000..bf381d12b6b --- /dev/null +++ b/shard_node/store/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "store" +version = "0.1.0" +authors = ["will "] +edition = "2018" + +[dependencies] diff --git a/shard_node/store/src/errors.rs b/shard_node/store/src/errors.rs new file mode 100644 index 00000000000..815b35a8ed0 --- /dev/null +++ b/shard_node/store/src/errors.rs @@ -0,0 +1,30 @@ +use ssz::DecodeError; + +#[derive(Debug, PartialEq)] +pub enum Error { + SszDecodeError(DecodeError), + DBError { message: String }, +} + +impl From for Error { + fn from(e: DecodeError) -> Error { + Error::SszDecodeError(e) + } +} + +impl From for Error { + fn from(e: DBError) -> Error { + Error::DBError { message: e.message } + } +} + +#[derive(Debug)] +pub struct DBError { + pub message: String, +} + +impl DBError { + pub fn new(message: String) -> Self { + Self { message } + } +} diff --git a/shard_node/store/src/lib.rs b/shard_node/store/src/lib.rs new file mode 100644 index 00000000000..e7a11a969c0 --- /dev/null +++ b/shard_node/store/src/lib.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/shard_node/store/src/memory_store.rs b/shard_node/store/src/memory_store.rs new file mode 100644 index 00000000000..e69de29bb2d From e5c5558879e537f8beae82b4ab661c986416c70c Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Tue, 6 Aug 2019 14:59:51 +1000 Subject: [PATCH 003/151] Update chain spec to manage phase 1 constants as well --- eth2/types/src/chain_spec.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/eth2/types/src/chain_spec.rs b/eth2/types/src/chain_spec.rs index 6073fb32e77..81b53e1e232 100644 --- a/eth2/types/src/chain_spec.rs +++ b/eth2/types/src/chain_spec.rs @@ -8,8 +8,10 @@ use test_utils::{u8_from_hex_str, u8_to_hex_str}; /// Spec v0.6.3 pub enum Domain { BeaconProposer, + ShardProposer, Randao, Attestation, + ShardAttestation, Deposit, VoluntaryExit, Transfer, @@ -60,6 +62,7 @@ pub struct ChainSpec { */ pub genesis_time: u64, pub seconds_per_slot: u64, + pub shard_seconds_per_slot: u64, pub min_attestation_inclusion_delay: u64, pub min_seed_lookahead: Epoch, pub activation_exit_delay: u64, @@ -98,8 +101,10 @@ pub struct ChainSpec { * Use `ChainSpec::get_domain(..)` to access these values. */ domain_beacon_proposer: u32, + domain_shard_proposer: u32, domain_randao: u32, domain_attestation: u32, + domain_shard_attestation: u32, domain_deposit: u32, domain_voluntary_exit: u32, domain_transfer: u32, @@ -115,8 +120,10 @@ impl ChainSpec { pub fn get_domain(&self, epoch: Epoch, domain: Domain, fork: &Fork) -> u64 { let domain_constant = match domain { Domain::BeaconProposer => self.domain_beacon_proposer, + Domain::ShardProposer => self.domain_beacon_proposer, Domain::Randao => self.domain_randao, Domain::Attestation => self.domain_attestation, + Domain::ShardAttestation => self.domain_shard_attestation, Domain::Deposit => self.domain_deposit, Domain::VoluntaryExit => self.domain_voluntary_exit, Domain::Transfer => self.domain_transfer, @@ -172,6 +179,7 @@ impl ChainSpec { */ genesis_time: u64::from(u32::max_value()), seconds_per_slot: 6, + shard_seconds_per_slot: 3, min_attestation_inclusion_delay: 4, min_seed_lookahead: Epoch::new(1), activation_exit_delay: 4, @@ -205,11 +213,13 @@ impl ChainSpec { * Signature domains */ domain_beacon_proposer: 0, - domain_randao: 1, - domain_attestation: 2, - domain_deposit: 3, - domain_voluntary_exit: 4, - domain_transfer: 5, + domain_shard_proposer: 1, + domain_randao: 2, + domain_attestation: 3, + domain_shard_attestation: 4, + domain_deposit: 5, + domain_voluntary_exit: 6, + domain_transfer: 7, /* * Network specific From f443458cf475326e07ed916f58f8c1916e2ffb6f Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Tue, 6 Aug 2019 16:49:28 +1000 Subject: [PATCH 004/151] Remove beacon specific fields and add shard specific fields --- eth2/types/src/shard_state.rs | 878 ++++++++++++++++++++++++++++++++++ 1 file changed, 878 insertions(+) create mode 100644 eth2/types/src/shard_state.rs diff --git a/eth2/types/src/shard_state.rs b/eth2/types/src/shard_state.rs new file mode 100644 index 00000000000..b161840cf66 --- /dev/null +++ b/eth2/types/src/shard_state.rs @@ -0,0 +1,878 @@ +use self::committee_cache::get_active_validator_indices; +use crate::test_utils::TestRandom; +use crate::*; +use cached_tree_hash::{Error as TreeHashCacheError, TreeHashCache}; +use compare_fields_derive::CompareFields; +use fixed_len_vec::{typenum::Unsigned, FixedLenVec}; +use hashing::hash; +use int_to_bytes::{int_to_bytes32, int_to_bytes8}; +use pubkey_cache::PubkeyCache; +use serde_derive::{Deserialize, Serialize}; +use ssz::ssz_encode; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; +use tree_hash::TreeHash; +use tree_hash_derive::{CachedTreeHash, TreeHash}; + +pub use self::committee_cache::CommitteeCache; +pub use beacon_state_types::*; + +mod beacon_state_types; +mod committee_cache; +mod pubkey_cache; +mod tests; + +pub const CACHED_EPOCHS: usize = 3; +const MAX_RANDOM_BYTE: u64 = (1 << 8) - 1; + +#[derive(Debug, PartialEq)] +pub enum Error { + EpochOutOfBounds, + SlotOutOfBounds, + UnknownValidator, + UnableToDetermineProducer, + InvalidBitfield, + ValidatorIsWithdrawable, + UnableToShuffle, + TooManyValidators, + InsufficientValidators, + InsufficientRandaoMixes, + InsufficientBlockRoots, + InsufficientIndexRoots, + InsufficientAttestations, + InsufficientStateRoots, + NoCommitteeForShard, + NoCommitteeForSlot, + TreeHashCacheError(TreeHashCacheError), +} + +/// The state of the `BeaconChain` at some slot. +/// +/// Spec v0.6.3 +#[derive( + Debug, + PartialEq, + Clone, + Serialize, + Deserialize, + TestRandom, + Encode, + Decode, + TreeHash, + CachedTreeHash, + CompareFields, +)] +pub struct ShardState +where + T: EthSpec, +{ + // Misc + pub slot: Slot, + pub genesis_time: u64, + pub fork: Fork, + + // Attestations - split by epoch necessary or just from latest finalized crosslink? + pub previous_epoch_attestations: Vec, + pub current_epoch_attestations: Vec, + + // Latest beacon roots needed + // Latest crosslinks not needed since it will only duplicate data accessed via beacon roots + pub latest_beacon_roots: FixedLenVec, + + // Recent state + pub latest_block_roots: FixedLenVec, + #[compare_fields(as_slice)] + pub latest_state_roots: FixedLenVec, + pub latest_block_header: ShardBlockHeader, + pub historical_roots: Vec, +} + +impl ShardState { + /// Produce the first state of the Shard Chain. + /// + /// This does not fully build a genesis shard state, it omits processing of initial validator + /// deposits. To obtain a full genesis shard state, use the `ShardStateBuilder`. + /// + /// Spec v0.6.3 + pub fn genesis( + genesis_time: u64, + latest_eth1_data: Eth1Data, + spec: &ChainSpec, + ) -> BeaconState { + let initial_crosslink = Crosslink { + epoch: T::genesis_epoch(), + previous_crosslink_root: spec.zero_hash, + crosslink_data_root: spec.zero_hash, + }; + + BeaconState { + // Misc + slot: spec.genesis_slot, + genesis_time, + fork: Fork::genesis(T::genesis_epoch()), + + // Validator registry + validator_registry: vec![], // Set later in the function. + balances: vec![], // Set later in the function. + + // Randomness and committees + latest_randao_mixes: FixedLenVec::from(vec![ + spec.zero_hash; + T::LatestRandaoMixesLength::to_usize() + ]), + latest_start_shard: 0, + + // Finality + previous_epoch_attestations: vec![], + current_epoch_attestations: vec![], + previous_justified_epoch: T::genesis_epoch(), + current_justified_epoch: T::genesis_epoch(), + previous_justified_root: spec.zero_hash, + current_justified_root: spec.zero_hash, + justification_bitfield: 0, + finalized_epoch: T::genesis_epoch(), + finalized_root: spec.zero_hash, + + // Recent state + current_crosslinks: vec![initial_crosslink.clone(); T::ShardCount::to_usize()].into(), + previous_crosslinks: vec![initial_crosslink; T::ShardCount::to_usize()].into(), + latest_block_roots: vec![spec.zero_hash; T::SlotsPerHistoricalRoot::to_usize()].into(), + latest_state_roots: vec![spec.zero_hash; T::SlotsPerHistoricalRoot::to_usize()].into(), + latest_active_index_roots: vec![ + spec.zero_hash; + T::LatestActiveIndexRootsLength::to_usize() + ] + .into(), + latest_slashed_balances: vec![0; T::LatestSlashedExitLength::to_usize()].into(), + latest_block_header: BeaconBlock::empty(spec).temporary_block_header(spec), + historical_roots: vec![], + + /* + * PoW receipt root + */ + latest_eth1_data, + eth1_data_votes: vec![], + deposit_index: 0, + + /* + * Caching (not in spec) + */ + committee_caches: [ + CommitteeCache::default(), + CommitteeCache::default(), + CommitteeCache::default(), + ], + pubkey_cache: PubkeyCache::default(), + tree_hash_cache: TreeHashCache::default(), + exit_cache: ExitCache::default(), + } + } + + /// Returns the `tree_hash_root` of the state. + /// + /// Spec v0.6.3 + pub fn canonical_root(&self) -> Hash256 { + Hash256::from_slice(&self.tree_hash_root()[..]) + } + + pub fn historical_batch(&self) -> HistoricalBatch { + HistoricalBatch { + block_roots: self.latest_block_roots.clone(), + state_roots: self.latest_state_roots.clone(), + } + } + + /// If a validator pubkey exists in the validator registry, returns `Some(i)`, otherwise + /// returns `None`. + /// + /// Requires a fully up-to-date `pubkey_cache`, returns an error if this is not the case. + pub fn get_validator_index(&self, pubkey: &PublicKey) -> Result, Error> { + if self.pubkey_cache.len() == self.validator_registry.len() { + Ok(self.pubkey_cache.get(pubkey)) + } else { + Err(Error::PubkeyCacheIncomplete { + cache_len: self.pubkey_cache.len(), + registry_len: self.validator_registry.len(), + }) + } + } + + /// The epoch corresponding to `self.slot`. + /// + /// Spec v0.6.3 + pub fn current_epoch(&self) -> Epoch { + self.slot.epoch(T::slots_per_epoch()) + } + + /// The epoch prior to `self.current_epoch()`. + /// + /// If the current epoch is the genesis epoch, the genesis_epoch is returned. + /// + /// Spec v0.6.3 + pub fn previous_epoch(&self) -> Epoch { + let current_epoch = self.current_epoch(); + if current_epoch > T::genesis_epoch() { + current_epoch - 1 + } else { + current_epoch + } + } + + /// The epoch following `self.current_epoch()`. + /// + /// Spec v0.6.3 + pub fn next_epoch(&self) -> Epoch { + self.current_epoch() + 1 + } + + pub fn get_epoch_committee_count(&self, relative_epoch: RelativeEpoch) -> Result { + let cache = self.cache(relative_epoch)?; + + Ok(cache.epoch_committee_count() as u64) + } + + pub fn get_epoch_start_shard(&self, relative_epoch: RelativeEpoch) -> Result { + let cache = self.cache(relative_epoch)?; + + Ok(cache.epoch_start_shard()) + } + + pub fn next_epoch_start_shard(&self, spec: &ChainSpec) -> Result { + let cache = self.cache(RelativeEpoch::Current)?; + let active_validator_count = cache.active_validator_count(); + let shard_delta = T::get_shard_delta(active_validator_count, spec.target_committee_size); + + Ok((self.latest_start_shard + shard_delta) % T::ShardCount::to_u64()) + } + + /// Get the slot of an attestation. + /// + /// Note: Utilizes the cache and will fail if the appropriate cache is not initialized. + /// + /// Spec v0.6.3 + pub fn get_attestation_slot(&self, attestation_data: &AttestationData) -> Result { + let target_relative_epoch = + RelativeEpoch::from_epoch(self.current_epoch(), attestation_data.target_epoch)?; + + let cc = + self.get_crosslink_committee_for_shard(attestation_data.shard, target_relative_epoch)?; + + Ok(cc.slot) + } + + /// Return the cached active validator indices at some epoch. + /// + /// Note: the indices are shuffled (i.e., not in ascending order). + /// + /// Returns an error if that epoch is not cached, or the cache is not initialized. + pub fn get_cached_active_validator_indices( + &self, + relative_epoch: RelativeEpoch, + ) -> Result<&[usize], Error> { + let cache = self.cache(relative_epoch)?; + + Ok(&cache.active_validator_indices()) + } + + /// Returns the active validator indices for the given epoch. + /// + /// Does not utilize the cache, performs a full iteration over the validator registry. + /// + /// Spec v0.6.3 + pub fn get_active_validator_indices(&self, epoch: Epoch) -> Vec { + get_active_validator_indices(&self.validator_registry, epoch) + } + + /// Return the cached active validator indices at some epoch. + /// + /// Note: the indices are shuffled (i.e., not in ascending order). + /// + /// Returns an error if that epoch is not cached, or the cache is not initialized. + pub fn get_shuffling(&self, relative_epoch: RelativeEpoch) -> Result<&[usize], Error> { + let cache = self.cache(relative_epoch)?; + + Ok(cache.shuffling()) + } + + /// Returns the crosslink committees for some slot. + /// + /// Note: Utilizes the cache and will fail if the appropriate cache is not initialized. + /// + /// Spec v0.6.3 + pub fn get_crosslink_committees_at_slot( + &self, + slot: Slot, + ) -> Result, Error> { + let relative_epoch = RelativeEpoch::from_slot(self.slot, slot, T::slots_per_epoch())?; + let cache = self.cache(relative_epoch)?; + + cache + .get_crosslink_committees_for_slot(slot) + .ok_or_else(|| Error::NoCommitteeForSlot) + } + + /// Returns the crosslink committees for some shard in some cached epoch. + /// + /// Note: Utilizes the cache and will fail if the appropriate cache is not initialized. + /// + /// Spec v0.6.3 + pub fn get_crosslink_committee_for_shard( + &self, + shard: u64, + relative_epoch: RelativeEpoch, + ) -> Result { + let cache = self.cache(relative_epoch)?; + + let committee = cache + .get_crosslink_committee_for_shard(shard) + .ok_or_else(|| Error::NoCommitteeForShard)?; + + Ok(committee) + } + + /// Returns the beacon proposer index for the `slot` in the given `relative_epoch`. + /// + /// Spec v0.6.3 + // NOTE: be sure to test this bad boy. + pub fn get_beacon_proposer_index( + &self, + slot: Slot, + relative_epoch: RelativeEpoch, + spec: &ChainSpec, + ) -> Result { + let cache = self.cache(relative_epoch)?; + let epoch = relative_epoch.into_epoch(self.current_epoch()); + + let first_committee = cache + .first_committee_at_slot(slot) + .ok_or_else(|| Error::SlotOutOfBounds)?; + let seed = self.generate_seed(epoch, spec)?; + + let mut i = 0; + Ok(loop { + let candidate_index = first_committee[(epoch.as_usize() + i) % first_committee.len()]; + let random_byte = { + let mut preimage = seed.as_bytes().to_vec(); + preimage.append(&mut int_to_bytes8((i / 32) as u64)); + let hash = hash(&preimage); + hash[i % 32] + }; + let effective_balance = self.validator_registry[candidate_index].effective_balance; + if (effective_balance * MAX_RANDOM_BYTE) + >= (spec.max_effective_balance * u64::from(random_byte)) + { + break candidate_index; + } + i += 1; + }) + } + + /// Safely obtains the index for latest block roots, given some `slot`. + /// + /// Spec v0.6.3 + fn get_latest_block_roots_index(&self, slot: Slot) -> Result { + if (slot < self.slot) && (self.slot <= slot + self.latest_block_roots.len() as u64) { + Ok(slot.as_usize() % self.latest_block_roots.len()) + } else { + Err(BeaconStateError::SlotOutOfBounds) + } + } + + /// Return the block root at a recent `slot`. + /// + /// Spec v0.6.3 + pub fn get_block_root(&self, slot: Slot) -> Result<&Hash256, BeaconStateError> { + let i = self.get_latest_block_roots_index(slot)?; + Ok(&self.latest_block_roots[i]) + } + + /// Return the block root at a recent `epoch`. + /// + /// Spec v0.6.3 + // NOTE: the spec calls this get_block_root + pub fn get_block_root_at_epoch(&self, epoch: Epoch) -> Result<&Hash256, BeaconStateError> { + self.get_block_root(epoch.start_slot(T::slots_per_epoch())) + } + + /// Sets the block root for some given slot. + /// + /// Spec v0.6.3 + pub fn set_block_root( + &mut self, + slot: Slot, + block_root: Hash256, + ) -> Result<(), BeaconStateError> { + let i = self.get_latest_block_roots_index(slot)?; + self.latest_block_roots[i] = block_root; + Ok(()) + } + + /// Safely obtains the index for `latest_randao_mixes` + /// + /// Spec v0.6.3 + fn get_randao_mix_index(&self, epoch: Epoch) -> Result { + let current_epoch = self.current_epoch(); + let len = T::LatestRandaoMixesLength::to_u64(); + + if (epoch + len > current_epoch) & (epoch <= current_epoch) { + Ok(epoch.as_usize() % len as usize) + } else { + Err(Error::EpochOutOfBounds) + } + } + + /// XOR-assigns the existing `epoch` randao mix with the hash of the `signature`. + /// + /// # Errors: + /// + /// See `Self::get_randao_mix`. + /// + /// Spec v0.6.3 + pub fn update_randao_mix(&mut self, epoch: Epoch, signature: &Signature) -> Result<(), Error> { + let i = epoch.as_usize() % T::LatestRandaoMixesLength::to_usize(); + + let signature_hash = Hash256::from_slice(&hash(&ssz_encode(signature))); + + self.latest_randao_mixes[i] = *self.get_randao_mix(epoch)? ^ signature_hash; + + Ok(()) + } + + /// Return the randao mix at a recent ``epoch``. + /// + /// Spec v0.6.3 + pub fn get_randao_mix(&self, epoch: Epoch) -> Result<&Hash256, Error> { + let i = self.get_randao_mix_index(epoch)?; + Ok(&self.latest_randao_mixes[i]) + } + + /// Set the randao mix at a recent ``epoch``. + /// + /// Spec v0.6.3 + pub fn set_randao_mix(&mut self, epoch: Epoch, mix: Hash256) -> Result<(), Error> { + let i = self.get_randao_mix_index(epoch)?; + self.latest_randao_mixes[i] = mix; + Ok(()) + } + + /// Safely obtains the index for `latest_active_index_roots`, given some `epoch`. + /// + /// Spec v0.6.3 + fn get_active_index_root_index(&self, epoch: Epoch, spec: &ChainSpec) -> Result { + let current_epoch = self.current_epoch(); + + let lookahead = spec.activation_exit_delay; + let lookback = self.latest_active_index_roots.len() as u64 - lookahead; + + if (epoch + lookback > current_epoch) && (current_epoch + lookahead >= epoch) { + Ok(epoch.as_usize() % self.latest_active_index_roots.len()) + } else { + Err(Error::EpochOutOfBounds) + } + } + + /// Return the `active_index_root` at a recent `epoch`. + /// + /// Spec v0.6.3 + pub fn get_active_index_root(&self, epoch: Epoch, spec: &ChainSpec) -> Result { + let i = self.get_active_index_root_index(epoch, spec)?; + Ok(self.latest_active_index_roots[i]) + } + + /// Set the `active_index_root` at a recent `epoch`. + /// + /// Spec v0.6.3 + pub fn set_active_index_root( + &mut self, + epoch: Epoch, + index_root: Hash256, + spec: &ChainSpec, + ) -> Result<(), Error> { + let i = self.get_active_index_root_index(epoch, spec)?; + self.latest_active_index_roots[i] = index_root; + Ok(()) + } + + /// Replace `active_index_roots` with clones of `index_root`. + /// + /// Spec v0.6.3 + pub fn fill_active_index_roots_with(&mut self, index_root: Hash256) { + self.latest_active_index_roots = + vec![index_root; self.latest_active_index_roots.len()].into() + } + + /// Safely obtains the index for latest state roots, given some `slot`. + /// + /// Spec v0.6.3 + fn get_latest_state_roots_index(&self, slot: Slot) -> Result { + if (slot < self.slot) && (self.slot <= slot + Slot::from(self.latest_state_roots.len())) { + Ok(slot.as_usize() % self.latest_state_roots.len()) + } else { + Err(BeaconStateError::SlotOutOfBounds) + } + } + + /// Gets the state root for some slot. + /// + /// Spec v0.6.3 + pub fn get_state_root(&self, slot: Slot) -> Result<&Hash256, Error> { + let i = self.get_latest_state_roots_index(slot)?; + Ok(&self.latest_state_roots[i]) + } + + /// Gets the oldest (earliest slot) state root. + /// + /// Spec v0.6.3 + pub fn get_oldest_state_root(&self) -> Result<&Hash256, Error> { + let i = self + .get_latest_state_roots_index(self.slot - Slot::from(self.latest_state_roots.len()))?; + Ok(&self.latest_state_roots[i]) + } + + /// Sets the latest state root for slot. + /// + /// Spec v0.6.3 + pub fn set_state_root(&mut self, slot: Slot, state_root: Hash256) -> Result<(), Error> { + let i = self.get_latest_state_roots_index(slot)?; + self.latest_state_roots[i] = state_root; + Ok(()) + } + + /// Safely obtains the index for `latest_slashed_balances`, given some `epoch`. + /// + /// Spec v0.6.3 + fn get_slashed_balance_index(&self, epoch: Epoch) -> Result { + let i = epoch.as_usize() % self.latest_slashed_balances.len(); + + // NOTE: the validity of the epoch is not checked. It is not in the spec but it's probably + // useful to have. + if i < self.latest_slashed_balances.len() { + Ok(i) + } else { + Err(Error::InsufficientSlashedBalances) + } + } + + /// Gets the total slashed balances for some epoch. + /// + /// Spec v0.6.3 + pub fn get_slashed_balance(&self, epoch: Epoch) -> Result { + let i = self.get_slashed_balance_index(epoch)?; + Ok(self.latest_slashed_balances[i]) + } + + /// Sets the total slashed balances for some epoch. + /// + /// Spec v0.6.3 + pub fn set_slashed_balance(&mut self, epoch: Epoch, balance: u64) -> Result<(), Error> { + let i = self.get_slashed_balance_index(epoch)?; + self.latest_slashed_balances[i] = balance; + Ok(()) + } + + /// Get the attestations from the current or previous epoch. + /// + /// Spec v0.6.3 + pub fn get_matching_source_attestations( + &self, + epoch: Epoch, + ) -> Result<&[PendingAttestation], Error> { + if epoch == self.current_epoch() { + Ok(&self.current_epoch_attestations) + } else if epoch == self.previous_epoch() { + Ok(&self.previous_epoch_attestations) + } else { + Err(Error::EpochOutOfBounds) + } + } + + /// Get the current crosslink for a shard. + /// + /// Spec v0.6.3 + pub fn get_current_crosslink(&self, shard: u64) -> Result<&Crosslink, Error> { + self.current_crosslinks + .get(shard as usize) + .ok_or(Error::ShardOutOfBounds) + } + + /// Get the previous crosslink for a shard. + /// + /// Spec v0.6.3 + pub fn get_previous_crosslink(&self, shard: u64) -> Result<&Crosslink, Error> { + self.previous_crosslinks + .get(shard as usize) + .ok_or(Error::ShardOutOfBounds) + } + + /// Transform an attestation into the crosslink that it reinforces. + /// + /// Spec v0.6.3 + pub fn get_crosslink_from_attestation_data( + &self, + data: &AttestationData, + spec: &ChainSpec, + ) -> Result { + let current_crosslink_epoch = self.get_current_crosslink(data.shard)?.epoch; + Ok(Crosslink { + epoch: std::cmp::min( + data.target_epoch, + current_crosslink_epoch + spec.max_crosslink_epochs, + ), + previous_crosslink_root: data.previous_crosslink_root, + crosslink_data_root: data.crosslink_data_root, + }) + } + + /// Generate a seed for the given `epoch`. + /// + /// Spec v0.6.3 + pub fn generate_seed(&self, epoch: Epoch, spec: &ChainSpec) -> Result { + // Bypass the safe getter for RANDAO so we can gracefully handle the scenario where `epoch + // == 0`. + let randao = { + let i = epoch + T::latest_randao_mixes_length() as u64 - spec.min_seed_lookahead; + self.latest_randao_mixes[i.as_usize() % self.latest_randao_mixes.len()] + }; + let active_index_root = self.get_active_index_root(epoch, spec)?; + let epoch_bytes = int_to_bytes32(epoch.as_u64()); + + let mut preimage = [0; 32 * 3]; + preimage[0..32].copy_from_slice(&randao[..]); + preimage[32..64].copy_from_slice(&active_index_root[..]); + preimage[64..].copy_from_slice(&epoch_bytes); + + Ok(Hash256::from_slice(&hash(&preimage))) + } + + /// Return the effective balance (also known as "balance at stake") for a validator with the given ``index``. + /// + /// Spec v0.6.3 + pub fn get_effective_balance( + &self, + validator_index: usize, + _spec: &ChainSpec, + ) -> Result { + self.validator_registry + .get(validator_index) + .map(|v| v.effective_balance) + .ok_or_else(|| Error::UnknownValidator) + } + + /// Return the epoch at which an activation or exit triggered in ``epoch`` takes effect. + /// + /// Spec v0.6.3 + pub fn get_delayed_activation_exit_epoch(&self, epoch: Epoch, spec: &ChainSpec) -> Epoch { + epoch + 1 + spec.activation_exit_delay + } + + /// Return the churn limit for the current epoch (number of validators who can leave per epoch). + /// + /// Uses the epoch cache, and will error if it isn't initialized. + /// + /// Spec v0.6.3 + pub fn get_churn_limit(&self, spec: &ChainSpec) -> Result { + Ok(std::cmp::max( + spec.min_per_epoch_churn_limit, + self.cache(RelativeEpoch::Current)?.active_validator_count() as u64 + / spec.churn_limit_quotient, + )) + } + + /// Returns the `slot`, `shard` and `committee_index` for which a validator must produce an + /// attestation. + /// + /// Note: Utilizes the cache and will fail if the appropriate cache is not initialized. + /// + /// Spec v0.6.3 + pub fn get_attestation_duties( + &self, + validator_index: usize, + relative_epoch: RelativeEpoch, + ) -> Result, Error> { + let cache = self.cache(relative_epoch)?; + + Ok(cache.get_attestation_duties(validator_index)) + } + + /// Return the combined effective balance of an array of validators. + /// + /// Spec v0.6.3 + pub fn get_total_balance( + &self, + validator_indices: &[usize], + spec: &ChainSpec, + ) -> Result { + validator_indices.iter().try_fold(0_u64, |acc, i| { + self.get_effective_balance(*i, spec) + .and_then(|bal| Ok(bal + acc)) + }) + } + + /// Build all the caches, if they need to be built. + pub fn build_all_caches(&mut self, spec: &ChainSpec) -> Result<(), Error> { + self.build_committee_cache(RelativeEpoch::Previous, spec)?; + self.build_committee_cache(RelativeEpoch::Current, spec)?; + self.build_committee_cache(RelativeEpoch::Next, spec)?; + self.update_pubkey_cache()?; + self.update_tree_hash_cache()?; + self.exit_cache + .build_from_registry(&self.validator_registry, spec); + + Ok(()) + } + + /// Drop all caches on the state. + pub fn drop_all_caches(&mut self) { + self.drop_committee_cache(RelativeEpoch::Previous); + self.drop_committee_cache(RelativeEpoch::Current); + self.drop_committee_cache(RelativeEpoch::Next); + self.drop_pubkey_cache(); + self.drop_tree_hash_cache(); + self.exit_cache = ExitCache::default(); + } + + /// Build an epoch cache, unless it is has already been built. + pub fn build_committee_cache( + &mut self, + relative_epoch: RelativeEpoch, + spec: &ChainSpec, + ) -> Result<(), Error> { + let i = Self::cache_index(relative_epoch); + + if self.committee_caches[i] + .is_initialized_at(relative_epoch.into_epoch(self.current_epoch())) + { + Ok(()) + } else { + self.force_build_committee_cache(relative_epoch, spec) + } + } + + /// Always builds the previous epoch cache, even if it is already initialized. + pub fn force_build_committee_cache( + &mut self, + relative_epoch: RelativeEpoch, + spec: &ChainSpec, + ) -> Result<(), Error> { + let epoch = relative_epoch.into_epoch(self.current_epoch()); + + self.committee_caches[Self::cache_index(relative_epoch)] = + CommitteeCache::initialized(&self, epoch, spec)?; + Ok(()) + } + + /// Advances the cache for this state into the next epoch. + /// + /// This should be used if the `slot` of this state is advanced beyond an epoch boundary. + /// + /// Note: whilst this function will preserve already-built caches, it will not build any. + pub fn advance_caches(&mut self) { + let next = Self::cache_index(RelativeEpoch::Previous); + let current = Self::cache_index(RelativeEpoch::Current); + + let caches = &mut self.committee_caches[..]; + caches.rotate_left(1); + caches[next] = CommitteeCache::default(); + caches[current] = CommitteeCache::default(); + } + + fn cache_index(relative_epoch: RelativeEpoch) -> usize { + match relative_epoch { + RelativeEpoch::Previous => 0, + RelativeEpoch::Current => 1, + RelativeEpoch::Next => 2, + } + } + + /// Returns the cache for some `RelativeEpoch`. Returns an error if the cache has not been + /// initialized. + fn cache(&self, relative_epoch: RelativeEpoch) -> Result<&CommitteeCache, Error> { + let cache = &self.committee_caches[Self::cache_index(relative_epoch)]; + + if cache.is_initialized_at(relative_epoch.into_epoch(self.current_epoch())) { + Ok(cache) + } else { + Err(Error::CommitteeCacheUninitialized(relative_epoch)) + } + } + + /// Drops the cache, leaving it in an uninitialized state. + fn drop_committee_cache(&mut self, relative_epoch: RelativeEpoch) { + self.committee_caches[Self::cache_index(relative_epoch)] = CommitteeCache::default(); + } + + /// Updates the pubkey cache, if required. + /// + /// Adds all `pubkeys` from the `validator_registry` which are not already in the cache. Will + /// never re-add a pubkey. + pub fn update_pubkey_cache(&mut self) -> Result<(), Error> { + for (i, validator) in self + .validator_registry + .iter() + .enumerate() + .skip(self.pubkey_cache.len()) + { + let success = self.pubkey_cache.insert(validator.pubkey.clone(), i); + if !success { + return Err(Error::PubkeyCacheInconsistent); + } + } + + Ok(()) + } + + /// Completely drops the `pubkey_cache`, replacing it with a new, empty cache. + pub fn drop_pubkey_cache(&mut self) { + self.pubkey_cache = PubkeyCache::default() + } + + /// Update the tree hash cache, building it for the first time if it is empty. + /// + /// Returns the `tree_hash_root` resulting from the update. This root can be considered the + /// canonical root of `self`. + pub fn update_tree_hash_cache(&mut self) -> Result { + if self.tree_hash_cache.is_empty() { + self.tree_hash_cache = TreeHashCache::new(self)?; + } else { + // Move the cache outside of `self` to satisfy the borrow checker. + let mut cache = std::mem::replace(&mut self.tree_hash_cache, TreeHashCache::default()); + + cache.update(self)?; + + // Move the updated cache back into `self`. + self.tree_hash_cache = cache + } + + self.cached_tree_hash_root() + } + + /// Returns the tree hash root determined by the last execution of `self.update_tree_hash_cache(..)`. + /// + /// Note: does _not_ update the cache and may return an outdated root. + /// + /// Returns an error if the cache is not initialized or if an error is encountered during the + /// cache update. + pub fn cached_tree_hash_root(&self) -> Result { + self.tree_hash_cache + .tree_hash_root() + .and_then(|b| Ok(Hash256::from_slice(b))) + .map_err(Into::into) + } + + /// Completely drops the tree hash cache, replacing it with a new, empty cache. + pub fn drop_tree_hash_cache(&mut self) { + self.tree_hash_cache = TreeHashCache::default() + } +} + +impl From for Error { + fn from(e: RelativeEpochError) -> Error { + Error::RelativeEpochError(e) + } +} + +impl From for Error { + fn from(e: TreeHashCacheError) -> Error { + Error::TreeHashCacheError(e) + } +} From a0e7a581b61bac3e48fd787f050987dec393bf0c Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Tue, 6 Aug 2019 17:57:35 +1000 Subject: [PATCH 005/151] Begin narrowing down the functions required for the shard state --- eth2/types/src/shard_state.rs | 586 +--------------------------------- 1 file changed, 7 insertions(+), 579 deletions(-) diff --git a/eth2/types/src/shard_state.rs b/eth2/types/src/shard_state.rs index b161840cf66..7d5ab543a15 100644 --- a/eth2/types/src/shard_state.rs +++ b/eth2/types/src/shard_state.rs @@ -72,8 +72,8 @@ where pub fork: Fork, // Attestations - split by epoch necessary or just from latest finalized crosslink? - pub previous_epoch_attestations: Vec, - pub current_epoch_attestations: Vec, + // Going to need a way to request the attestation properly by epoch... play with this later + pub running_attestations: Vec, // Latest beacon roots needed // Latest crosslinks not needed since it will only duplicate data accessed via beacon roots @@ -96,42 +96,18 @@ impl ShardState { /// Spec v0.6.3 pub fn genesis( genesis_time: u64, - latest_eth1_data: Eth1Data, spec: &ChainSpec, - ) -> BeaconState { - let initial_crosslink = Crosslink { - epoch: T::genesis_epoch(), - previous_crosslink_root: spec.zero_hash, - crosslink_data_root: spec.zero_hash, - }; - - BeaconState { + ) -> ShardState { + ShardState { // Misc slot: spec.genesis_slot, + // will this genesis time not matter - can we just pull from the spec? genesis_time, fork: Fork::genesis(T::genesis_epoch()), - // Validator registry - validator_registry: vec![], // Set later in the function. - balances: vec![], // Set later in the function. - - // Randomness and committees - latest_randao_mixes: FixedLenVec::from(vec![ - spec.zero_hash; - T::LatestRandaoMixesLength::to_usize() - ]), - latest_start_shard: 0, - - // Finality + // Attestations previous_epoch_attestations: vec![], current_epoch_attestations: vec![], - previous_justified_epoch: T::genesis_epoch(), - current_justified_epoch: T::genesis_epoch(), - previous_justified_root: spec.zero_hash, - current_justified_root: spec.zero_hash, - justification_bitfield: 0, - finalized_epoch: T::genesis_epoch(), - finalized_root: spec.zero_hash, // Recent state current_crosslinks: vec![initial_crosslink.clone(); T::ShardCount::to_usize()].into(), @@ -182,69 +158,6 @@ impl ShardState { } } - /// If a validator pubkey exists in the validator registry, returns `Some(i)`, otherwise - /// returns `None`. - /// - /// Requires a fully up-to-date `pubkey_cache`, returns an error if this is not the case. - pub fn get_validator_index(&self, pubkey: &PublicKey) -> Result, Error> { - if self.pubkey_cache.len() == self.validator_registry.len() { - Ok(self.pubkey_cache.get(pubkey)) - } else { - Err(Error::PubkeyCacheIncomplete { - cache_len: self.pubkey_cache.len(), - registry_len: self.validator_registry.len(), - }) - } - } - - /// The epoch corresponding to `self.slot`. - /// - /// Spec v0.6.3 - pub fn current_epoch(&self) -> Epoch { - self.slot.epoch(T::slots_per_epoch()) - } - - /// The epoch prior to `self.current_epoch()`. - /// - /// If the current epoch is the genesis epoch, the genesis_epoch is returned. - /// - /// Spec v0.6.3 - pub fn previous_epoch(&self) -> Epoch { - let current_epoch = self.current_epoch(); - if current_epoch > T::genesis_epoch() { - current_epoch - 1 - } else { - current_epoch - } - } - - /// The epoch following `self.current_epoch()`. - /// - /// Spec v0.6.3 - pub fn next_epoch(&self) -> Epoch { - self.current_epoch() + 1 - } - - pub fn get_epoch_committee_count(&self, relative_epoch: RelativeEpoch) -> Result { - let cache = self.cache(relative_epoch)?; - - Ok(cache.epoch_committee_count() as u64) - } - - pub fn get_epoch_start_shard(&self, relative_epoch: RelativeEpoch) -> Result { - let cache = self.cache(relative_epoch)?; - - Ok(cache.epoch_start_shard()) - } - - pub fn next_epoch_start_shard(&self, spec: &ChainSpec) -> Result { - let cache = self.cache(RelativeEpoch::Current)?; - let active_validator_count = cache.active_validator_count(); - let shard_delta = T::get_shard_delta(active_validator_count, spec.target_committee_size); - - Ok((self.latest_start_shard + shard_delta) % T::ShardCount::to_u64()) - } - /// Get the slot of an attestation. /// /// Note: Utilizes the cache and will fail if the appropriate cache is not initialized. @@ -260,113 +173,6 @@ impl ShardState { Ok(cc.slot) } - /// Return the cached active validator indices at some epoch. - /// - /// Note: the indices are shuffled (i.e., not in ascending order). - /// - /// Returns an error if that epoch is not cached, or the cache is not initialized. - pub fn get_cached_active_validator_indices( - &self, - relative_epoch: RelativeEpoch, - ) -> Result<&[usize], Error> { - let cache = self.cache(relative_epoch)?; - - Ok(&cache.active_validator_indices()) - } - - /// Returns the active validator indices for the given epoch. - /// - /// Does not utilize the cache, performs a full iteration over the validator registry. - /// - /// Spec v0.6.3 - pub fn get_active_validator_indices(&self, epoch: Epoch) -> Vec { - get_active_validator_indices(&self.validator_registry, epoch) - } - - /// Return the cached active validator indices at some epoch. - /// - /// Note: the indices are shuffled (i.e., not in ascending order). - /// - /// Returns an error if that epoch is not cached, or the cache is not initialized. - pub fn get_shuffling(&self, relative_epoch: RelativeEpoch) -> Result<&[usize], Error> { - let cache = self.cache(relative_epoch)?; - - Ok(cache.shuffling()) - } - - /// Returns the crosslink committees for some slot. - /// - /// Note: Utilizes the cache and will fail if the appropriate cache is not initialized. - /// - /// Spec v0.6.3 - pub fn get_crosslink_committees_at_slot( - &self, - slot: Slot, - ) -> Result, Error> { - let relative_epoch = RelativeEpoch::from_slot(self.slot, slot, T::slots_per_epoch())?; - let cache = self.cache(relative_epoch)?; - - cache - .get_crosslink_committees_for_slot(slot) - .ok_or_else(|| Error::NoCommitteeForSlot) - } - - /// Returns the crosslink committees for some shard in some cached epoch. - /// - /// Note: Utilizes the cache and will fail if the appropriate cache is not initialized. - /// - /// Spec v0.6.3 - pub fn get_crosslink_committee_for_shard( - &self, - shard: u64, - relative_epoch: RelativeEpoch, - ) -> Result { - let cache = self.cache(relative_epoch)?; - - let committee = cache - .get_crosslink_committee_for_shard(shard) - .ok_or_else(|| Error::NoCommitteeForShard)?; - - Ok(committee) - } - - /// Returns the beacon proposer index for the `slot` in the given `relative_epoch`. - /// - /// Spec v0.6.3 - // NOTE: be sure to test this bad boy. - pub fn get_beacon_proposer_index( - &self, - slot: Slot, - relative_epoch: RelativeEpoch, - spec: &ChainSpec, - ) -> Result { - let cache = self.cache(relative_epoch)?; - let epoch = relative_epoch.into_epoch(self.current_epoch()); - - let first_committee = cache - .first_committee_at_slot(slot) - .ok_or_else(|| Error::SlotOutOfBounds)?; - let seed = self.generate_seed(epoch, spec)?; - - let mut i = 0; - Ok(loop { - let candidate_index = first_committee[(epoch.as_usize() + i) % first_committee.len()]; - let random_byte = { - let mut preimage = seed.as_bytes().to_vec(); - preimage.append(&mut int_to_bytes8((i / 32) as u64)); - let hash = hash(&preimage); - hash[i % 32] - }; - let effective_balance = self.validator_registry[candidate_index].effective_balance; - if (effective_balance * MAX_RANDOM_BYTE) - >= (spec.max_effective_balance * u64::from(random_byte)) - { - break candidate_index; - } - i += 1; - }) - } - /// Safely obtains the index for latest block roots, given some `slot`. /// /// Spec v0.6.3 @@ -386,14 +192,6 @@ impl ShardState { Ok(&self.latest_block_roots[i]) } - /// Return the block root at a recent `epoch`. - /// - /// Spec v0.6.3 - // NOTE: the spec calls this get_block_root - pub fn get_block_root_at_epoch(&self, epoch: Epoch) -> Result<&Hash256, BeaconStateError> { - self.get_block_root(epoch.start_slot(T::slots_per_epoch())) - } - /// Sets the block root for some given slot. /// /// Spec v0.6.3 @@ -407,100 +205,6 @@ impl ShardState { Ok(()) } - /// Safely obtains the index for `latest_randao_mixes` - /// - /// Spec v0.6.3 - fn get_randao_mix_index(&self, epoch: Epoch) -> Result { - let current_epoch = self.current_epoch(); - let len = T::LatestRandaoMixesLength::to_u64(); - - if (epoch + len > current_epoch) & (epoch <= current_epoch) { - Ok(epoch.as_usize() % len as usize) - } else { - Err(Error::EpochOutOfBounds) - } - } - - /// XOR-assigns the existing `epoch` randao mix with the hash of the `signature`. - /// - /// # Errors: - /// - /// See `Self::get_randao_mix`. - /// - /// Spec v0.6.3 - pub fn update_randao_mix(&mut self, epoch: Epoch, signature: &Signature) -> Result<(), Error> { - let i = epoch.as_usize() % T::LatestRandaoMixesLength::to_usize(); - - let signature_hash = Hash256::from_slice(&hash(&ssz_encode(signature))); - - self.latest_randao_mixes[i] = *self.get_randao_mix(epoch)? ^ signature_hash; - - Ok(()) - } - - /// Return the randao mix at a recent ``epoch``. - /// - /// Spec v0.6.3 - pub fn get_randao_mix(&self, epoch: Epoch) -> Result<&Hash256, Error> { - let i = self.get_randao_mix_index(epoch)?; - Ok(&self.latest_randao_mixes[i]) - } - - /// Set the randao mix at a recent ``epoch``. - /// - /// Spec v0.6.3 - pub fn set_randao_mix(&mut self, epoch: Epoch, mix: Hash256) -> Result<(), Error> { - let i = self.get_randao_mix_index(epoch)?; - self.latest_randao_mixes[i] = mix; - Ok(()) - } - - /// Safely obtains the index for `latest_active_index_roots`, given some `epoch`. - /// - /// Spec v0.6.3 - fn get_active_index_root_index(&self, epoch: Epoch, spec: &ChainSpec) -> Result { - let current_epoch = self.current_epoch(); - - let lookahead = spec.activation_exit_delay; - let lookback = self.latest_active_index_roots.len() as u64 - lookahead; - - if (epoch + lookback > current_epoch) && (current_epoch + lookahead >= epoch) { - Ok(epoch.as_usize() % self.latest_active_index_roots.len()) - } else { - Err(Error::EpochOutOfBounds) - } - } - - /// Return the `active_index_root` at a recent `epoch`. - /// - /// Spec v0.6.3 - pub fn get_active_index_root(&self, epoch: Epoch, spec: &ChainSpec) -> Result { - let i = self.get_active_index_root_index(epoch, spec)?; - Ok(self.latest_active_index_roots[i]) - } - - /// Set the `active_index_root` at a recent `epoch`. - /// - /// Spec v0.6.3 - pub fn set_active_index_root( - &mut self, - epoch: Epoch, - index_root: Hash256, - spec: &ChainSpec, - ) -> Result<(), Error> { - let i = self.get_active_index_root_index(epoch, spec)?; - self.latest_active_index_roots[i] = index_root; - Ok(()) - } - - /// Replace `active_index_roots` with clones of `index_root`. - /// - /// Spec v0.6.3 - pub fn fill_active_index_roots_with(&mut self, index_root: Hash256) { - self.latest_active_index_roots = - vec![index_root; self.latest_active_index_roots.len()].into() - } - /// Safely obtains the index for latest state roots, given some `slot`. /// /// Spec v0.6.3 @@ -538,292 +242,16 @@ impl ShardState { Ok(()) } - /// Safely obtains the index for `latest_slashed_balances`, given some `epoch`. - /// - /// Spec v0.6.3 - fn get_slashed_balance_index(&self, epoch: Epoch) -> Result { - let i = epoch.as_usize() % self.latest_slashed_balances.len(); - - // NOTE: the validity of the epoch is not checked. It is not in the spec but it's probably - // useful to have. - if i < self.latest_slashed_balances.len() { - Ok(i) - } else { - Err(Error::InsufficientSlashedBalances) - } - } - - /// Gets the total slashed balances for some epoch. - /// - /// Spec v0.6.3 - pub fn get_slashed_balance(&self, epoch: Epoch) -> Result { - let i = self.get_slashed_balance_index(epoch)?; - Ok(self.latest_slashed_balances[i]) - } - - /// Sets the total slashed balances for some epoch. - /// - /// Spec v0.6.3 - pub fn set_slashed_balance(&mut self, epoch: Epoch, balance: u64) -> Result<(), Error> { - let i = self.get_slashed_balance_index(epoch)?; - self.latest_slashed_balances[i] = balance; - Ok(()) - } - - /// Get the attestations from the current or previous epoch. - /// - /// Spec v0.6.3 - pub fn get_matching_source_attestations( - &self, - epoch: Epoch, - ) -> Result<&[PendingAttestation], Error> { - if epoch == self.current_epoch() { - Ok(&self.current_epoch_attestations) - } else if epoch == self.previous_epoch() { - Ok(&self.previous_epoch_attestations) - } else { - Err(Error::EpochOutOfBounds) - } - } - - /// Get the current crosslink for a shard. - /// - /// Spec v0.6.3 - pub fn get_current_crosslink(&self, shard: u64) -> Result<&Crosslink, Error> { - self.current_crosslinks - .get(shard as usize) - .ok_or(Error::ShardOutOfBounds) - } - - /// Get the previous crosslink for a shard. - /// - /// Spec v0.6.3 - pub fn get_previous_crosslink(&self, shard: u64) -> Result<&Crosslink, Error> { - self.previous_crosslinks - .get(shard as usize) - .ok_or(Error::ShardOutOfBounds) - } - - /// Transform an attestation into the crosslink that it reinforces. - /// - /// Spec v0.6.3 - pub fn get_crosslink_from_attestation_data( - &self, - data: &AttestationData, - spec: &ChainSpec, - ) -> Result { - let current_crosslink_epoch = self.get_current_crosslink(data.shard)?.epoch; - Ok(Crosslink { - epoch: std::cmp::min( - data.target_epoch, - current_crosslink_epoch + spec.max_crosslink_epochs, - ), - previous_crosslink_root: data.previous_crosslink_root, - crosslink_data_root: data.crosslink_data_root, - }) - } - - /// Generate a seed for the given `epoch`. - /// - /// Spec v0.6.3 - pub fn generate_seed(&self, epoch: Epoch, spec: &ChainSpec) -> Result { - // Bypass the safe getter for RANDAO so we can gracefully handle the scenario where `epoch - // == 0`. - let randao = { - let i = epoch + T::latest_randao_mixes_length() as u64 - spec.min_seed_lookahead; - self.latest_randao_mixes[i.as_usize() % self.latest_randao_mixes.len()] - }; - let active_index_root = self.get_active_index_root(epoch, spec)?; - let epoch_bytes = int_to_bytes32(epoch.as_u64()); - - let mut preimage = [0; 32 * 3]; - preimage[0..32].copy_from_slice(&randao[..]); - preimage[32..64].copy_from_slice(&active_index_root[..]); - preimage[64..].copy_from_slice(&epoch_bytes); - - Ok(Hash256::from_slice(&hash(&preimage))) - } - - /// Return the effective balance (also known as "balance at stake") for a validator with the given ``index``. - /// - /// Spec v0.6.3 - pub fn get_effective_balance( - &self, - validator_index: usize, - _spec: &ChainSpec, - ) -> Result { - self.validator_registry - .get(validator_index) - .map(|v| v.effective_balance) - .ok_or_else(|| Error::UnknownValidator) - } - - /// Return the epoch at which an activation or exit triggered in ``epoch`` takes effect. - /// - /// Spec v0.6.3 - pub fn get_delayed_activation_exit_epoch(&self, epoch: Epoch, spec: &ChainSpec) -> Epoch { - epoch + 1 + spec.activation_exit_delay - } - - /// Return the churn limit for the current epoch (number of validators who can leave per epoch). - /// - /// Uses the epoch cache, and will error if it isn't initialized. - /// - /// Spec v0.6.3 - pub fn get_churn_limit(&self, spec: &ChainSpec) -> Result { - Ok(std::cmp::max( - spec.min_per_epoch_churn_limit, - self.cache(RelativeEpoch::Current)?.active_validator_count() as u64 - / spec.churn_limit_quotient, - )) - } - - /// Returns the `slot`, `shard` and `committee_index` for which a validator must produce an - /// attestation. - /// - /// Note: Utilizes the cache and will fail if the appropriate cache is not initialized. - /// - /// Spec v0.6.3 - pub fn get_attestation_duties( - &self, - validator_index: usize, - relative_epoch: RelativeEpoch, - ) -> Result, Error> { - let cache = self.cache(relative_epoch)?; - - Ok(cache.get_attestation_duties(validator_index)) - } - - /// Return the combined effective balance of an array of validators. - /// - /// Spec v0.6.3 - pub fn get_total_balance( - &self, - validator_indices: &[usize], - spec: &ChainSpec, - ) -> Result { - validator_indices.iter().try_fold(0_u64, |acc, i| { - self.get_effective_balance(*i, spec) - .and_then(|bal| Ok(bal + acc)) - }) - } - + /// Do we need this since the tree hash really is just the balance set of everyone? /// Build all the caches, if they need to be built. pub fn build_all_caches(&mut self, spec: &ChainSpec) -> Result<(), Error> { - self.build_committee_cache(RelativeEpoch::Previous, spec)?; - self.build_committee_cache(RelativeEpoch::Current, spec)?; - self.build_committee_cache(RelativeEpoch::Next, spec)?; - self.update_pubkey_cache()?; self.update_tree_hash_cache()?; - self.exit_cache - .build_from_registry(&self.validator_registry, spec); - Ok(()) } /// Drop all caches on the state. pub fn drop_all_caches(&mut self) { - self.drop_committee_cache(RelativeEpoch::Previous); - self.drop_committee_cache(RelativeEpoch::Current); - self.drop_committee_cache(RelativeEpoch::Next); - self.drop_pubkey_cache(); self.drop_tree_hash_cache(); - self.exit_cache = ExitCache::default(); - } - - /// Build an epoch cache, unless it is has already been built. - pub fn build_committee_cache( - &mut self, - relative_epoch: RelativeEpoch, - spec: &ChainSpec, - ) -> Result<(), Error> { - let i = Self::cache_index(relative_epoch); - - if self.committee_caches[i] - .is_initialized_at(relative_epoch.into_epoch(self.current_epoch())) - { - Ok(()) - } else { - self.force_build_committee_cache(relative_epoch, spec) - } - } - - /// Always builds the previous epoch cache, even if it is already initialized. - pub fn force_build_committee_cache( - &mut self, - relative_epoch: RelativeEpoch, - spec: &ChainSpec, - ) -> Result<(), Error> { - let epoch = relative_epoch.into_epoch(self.current_epoch()); - - self.committee_caches[Self::cache_index(relative_epoch)] = - CommitteeCache::initialized(&self, epoch, spec)?; - Ok(()) - } - - /// Advances the cache for this state into the next epoch. - /// - /// This should be used if the `slot` of this state is advanced beyond an epoch boundary. - /// - /// Note: whilst this function will preserve already-built caches, it will not build any. - pub fn advance_caches(&mut self) { - let next = Self::cache_index(RelativeEpoch::Previous); - let current = Self::cache_index(RelativeEpoch::Current); - - let caches = &mut self.committee_caches[..]; - caches.rotate_left(1); - caches[next] = CommitteeCache::default(); - caches[current] = CommitteeCache::default(); - } - - fn cache_index(relative_epoch: RelativeEpoch) -> usize { - match relative_epoch { - RelativeEpoch::Previous => 0, - RelativeEpoch::Current => 1, - RelativeEpoch::Next => 2, - } - } - - /// Returns the cache for some `RelativeEpoch`. Returns an error if the cache has not been - /// initialized. - fn cache(&self, relative_epoch: RelativeEpoch) -> Result<&CommitteeCache, Error> { - let cache = &self.committee_caches[Self::cache_index(relative_epoch)]; - - if cache.is_initialized_at(relative_epoch.into_epoch(self.current_epoch())) { - Ok(cache) - } else { - Err(Error::CommitteeCacheUninitialized(relative_epoch)) - } - } - - /// Drops the cache, leaving it in an uninitialized state. - fn drop_committee_cache(&mut self, relative_epoch: RelativeEpoch) { - self.committee_caches[Self::cache_index(relative_epoch)] = CommitteeCache::default(); - } - - /// Updates the pubkey cache, if required. - /// - /// Adds all `pubkeys` from the `validator_registry` which are not already in the cache. Will - /// never re-add a pubkey. - pub fn update_pubkey_cache(&mut self) -> Result<(), Error> { - for (i, validator) in self - .validator_registry - .iter() - .enumerate() - .skip(self.pubkey_cache.len()) - { - let success = self.pubkey_cache.insert(validator.pubkey.clone(), i); - if !success { - return Err(Error::PubkeyCacheInconsistent); - } - } - - Ok(()) - } - - /// Completely drops the `pubkey_cache`, replacing it with a new, empty cache. - pub fn drop_pubkey_cache(&mut self) { - self.pubkey_cache = PubkeyCache::default() } /// Update the tree hash cache, building it for the first time if it is empty. From 11add850ec62ff125962261b9a57299cffab9578 Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Wed, 7 Aug 2019 13:40:00 +1000 Subject: [PATCH 006/151] Include a number of important shard types --- eth2/types/src/shard_attestation_data.rs | 0 eth2/types/src/shard_block.rs | 96 +++++++++++++++++++++ eth2/types/src/shard_block_body.rs | 36 ++++++++ eth2/types/src/shard_block_header.rs | 64 ++++++++++++++ eth2/types/src/shard_pending_attestation.rs | 0 5 files changed, 196 insertions(+) create mode 100644 eth2/types/src/shard_attestation_data.rs create mode 100644 eth2/types/src/shard_block.rs create mode 100644 eth2/types/src/shard_block_body.rs create mode 100644 eth2/types/src/shard_block_header.rs create mode 100644 eth2/types/src/shard_pending_attestation.rs diff --git a/eth2/types/src/shard_attestation_data.rs b/eth2/types/src/shard_attestation_data.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/eth2/types/src/shard_block.rs b/eth2/types/src/shard_block.rs new file mode 100644 index 00000000000..73cae803f9b --- /dev/null +++ b/eth2/types/src/shard_block.rs @@ -0,0 +1,96 @@ +use crate::test_utils::TestRandom; +use crate::*; +use bls::Signature; + +use serde_derive::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; +use tree_hash::{SignedRoot, TreeHash}; +use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash}; + +/// A block of the `BeaconChain`. +/// +/// Spec v0.6.3 +#[derive( + Debug, + PartialEq, + Clone, + Serialize, + Deserialize, + Encode, + Decode, + TreeHash, + CachedTreeHash, + TestRandom, + SignedRoot, +)] +pub struct ShardBlock { + pub slot: Slot, + pub previous_block_root: Hash256, + pub state_root: Hash256, + pub body: ShardBlockBody, + #[signed_root(skip_hashing)] + pub signature: Signature, +} + +impl ShardBlock { + /// Returns an empty block to be used during genesis. + /// + /// Spec v0.6.3 + pub fn empty(spec: &ChainSpec) -> BeaconBlock { + ShardBlock { + slot: spec.genesis_slot, + previous_block_root: spec.zero_hash, + state_root: spec.zero_hash, + body: ShardBlockBody { + graffiti: [0; 32], + attestations: vec![], + }, + signature: Signature::empty_signature(), + } + } + + /// Returns the `signed_root` of the block. + /// + /// Spec v0.6.3 + pub fn canonical_root(&self) -> Hash256 { + Hash256::from_slice(&self.signed_root()[..]) + } + + /// Returns a full `ShardBlockHeader` of this block. + /// + /// Note: This method is used instead of an `Into` impl to avoid a `Clone` of an entire block + /// when you want to have the block _and_ the header. + /// + /// Note: performs a full tree-hash of `self.body`. + /// + /// Spec v0.6.3 + pub fn block_header(&self) -> ShardBlockHeader { + ShardBlockHeader { + slot: self.slot, + previous_block_root: self.previous_block_root, + state_root: self.state_root, + block_body_root: Hash256::from_slice(&self.body.tree_hash_root()[..]), + signature: self.signature.clone(), + } + } + + /// Returns a "temporary" header, where the `state_root` is `spec.zero_hash`. + /// + /// Spec v0.6.3 + pub fn temporary_block_header(&self, spec: &ChainSpec) -> ShardBlockHeader { + ShardBlockHeader { + state_root: spec.zero_hash, + signature: Signature::empty_signature(), + ..self.block_header() + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + ssz_tests!(BeaconBlock); + cached_tree_hash_tests!(BeaconBlock); +} diff --git a/eth2/types/src/shard_block_body.rs b/eth2/types/src/shard_block_body.rs new file mode 100644 index 00000000000..897ee8a7afa --- /dev/null +++ b/eth2/types/src/shard_block_body.rs @@ -0,0 +1,36 @@ +use crate::test_utils::{graffiti_from_hex_str, TestRandom}; +use crate::*; + +use serde_derive::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; +use tree_hash_derive::{CachedTreeHash, TreeHash}; + +/// The body of a `BeaconChain` block, containing operations. +/// +/// Spec v0.6.3 +#[derive( + Debug, + PartialEq, + Clone, + Serialize, + Deserialize, + Encode, + Decode, + TreeHash, + CachedTreeHash, + TestRandom, +)] +pub struct ShardBlockBody { + #[serde(deserialize_with = "graffiti_from_hex_str")] + pub graffiti: [u8; 32], + pub attestations: Vec, +} + +#[cfg(test)] +mod tests { + use super::*; + + ssz_tests!(BeaconBlockBody); + cached_tree_hash_tests!(BeaconBlockBody); +} diff --git a/eth2/types/src/shard_block_header.rs b/eth2/types/src/shard_block_header.rs new file mode 100644 index 00000000000..779b88899c5 --- /dev/null +++ b/eth2/types/src/shard_block_header.rs @@ -0,0 +1,64 @@ +use crate::test_utils::TestRandom; +use crate::*; +use bls::Signature; + +use serde_derive::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; +use tree_hash::{SignedRoot, TreeHash}; +use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash}; + +/// A header of a `BeaconBlock`. +/// +/// Spec v0.6.3 +#[derive( + Debug, + PartialEq, + Clone, + Serialize, + Deserialize, + Encode, + Decode, + TreeHash, + CachedTreeHash, + TestRandom, + SignedRoot, +)] +pub struct ShardBlockHeader { + pub slot: Slot, + pub previous_block_root: Hash256, + pub state_root: Hash256, + pub block_body_root: Hash256, + #[signed_root(skip_hashing)] + pub signature: Signature, +} + +impl BeaconBlockHeader { + /// Returns the `tree_hash_root` of the header. + /// + /// Spec v0.6.3 + pub fn canonical_root(&self) -> Hash256 { + Hash256::from_slice(&self.signed_root()[..]) + } + + /// Given a `body`, consumes `self` and returns a complete `BeaconBlock`. + /// + /// Spec v0.6.3 + pub fn into_block(self, body: ShardBlockBody) -> ShardBlock { + ShardBlock { + slot: self.slot, + previous_block_root: self.previous_block_root, + state_root: self.state_root, + body, + signature: self.signature, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + ssz_tests!(BeaconBlockHeader); + cached_tree_hash_tests!(BeaconBlockHeader); +} diff --git a/eth2/types/src/shard_pending_attestation.rs b/eth2/types/src/shard_pending_attestation.rs new file mode 100644 index 00000000000..e69de29bb2d From 7a05ec9bf68fa8974e9b9cc73b58140c17d13ca9 Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Wed, 7 Aug 2019 14:44:47 +1000 Subject: [PATCH 007/151] Update shard chain logic with all the appropriate functions needed and comments for future required additiions --- shard_node/shard_chain/src/shard_chain.rs | 545 +++++++++++++++++++++- 1 file changed, 544 insertions(+), 1 deletion(-) diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 70f3ae15626..84705e34063 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -9,6 +9,8 @@ pub trait ShardChainTypes { /// Represents the "Shard Chain" component of Ethereum 2.0. It holds a reference to a parent Beacon Chain pub struct ShardChain { + // TODO... pub parentBeacon: BeaconChain... TODO + // pub shard_id: determine type, pub spec: ChainSpec, /// /// Persistent storage for blocks, states, etc. Typically an on-disk store, such as LevelDB. @@ -67,9 +69,550 @@ impl BeaconChain { canonical_head, genesis_block_root, fork_choice: ForkChoice::new(store.clone(), &genesis_block, genesis_block_root), - metrics: Metrics::new()?, store, }) } + + /// Returns the beacon block body for each beacon block root in `roots`. + /// + /// Fails if any root in `roots` does not have a corresponding block. + pub fn get_block_bodies(&self, roots: &[Hash256]) -> Result, Error> { + let bodies: Result, _> = roots + .iter() + .map(|root| match self.get_block(root)? { + Some(block) => Ok(block.body), + None => Err(Error::DBInconsistent(format!("Missing block: {}", root))), + }) + .collect(); + + Ok(bodies?) + } + + /// Returns the beacon block header for each beacon block root in `roots`. + /// + /// Fails if any root in `roots` does not have a corresponding block. + pub fn get_block_headers(&self, roots: &[Hash256]) -> Result, Error> { + let headers: Result, _> = roots + .iter() + .map(|root| match self.get_block(root)? { + Some(block) => Ok(block.block_header()), + None => Err(Error::DBInconsistent("Missing block".into())), + }) + .collect(); + + Ok(headers?) + } + /// Iterate in reverse (highest to lowest slot) through all blocks from the block at `slot` + /// through to the genesis block. + /// + /// Returns `None` for headers prior to genesis or when there is an error reading from `Store`. + /// + /// Contains duplicate headers when skip slots are encountered. + pub fn rev_iter_blocks(&self, slot: Slot) -> BlockIterator { + BlockIterator::owned(self.store.clone(), self.state.read().clone(), slot) + } + + /// Iterates in reverse (highest to lowest slot) through all block roots from `slot` through to + /// genesis. + /// + /// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. + /// + /// Contains duplicate roots when skip slots are encountered. + pub fn rev_iter_block_roots(&self, slot: Slot) -> BlockRootsIterator { + BlockRootsIterator::owned(self.store.clone(), self.state.read().clone(), slot) + } + + /// Iterates in reverse (highest to lowest slot) through all block roots from largest + /// `slot <= beacon_state.slot` through to genesis. + /// + /// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. + /// + /// Contains duplicate roots when skip slots are encountered. + pub fn rev_iter_best_block_roots( + &self, + slot: Slot, + ) -> BestBlockRootsIterator { + BestBlockRootsIterator::owned(self.store.clone(), self.state.read().clone(), slot) + } + + /// Iterates in reverse (highest to lowest slot) through all state roots from `slot` through to + /// genesis. + /// + /// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. + pub fn f(&self, slot: Slot) -> StateRootsIterator { + StateRootsIterator::owned(self.store.clone(), self.state.read().clone(), slot) + } + + /// Returns the block at the given root, if any. + /// + /// ## Errors + /// + /// May return a database error. + pub fn get_block(&self, block_root: &Hash256) -> Result, Error> { + Ok(self.store.get(block_root)?) + } + + /// Returns a read-lock guarded `BeaconState` which is the `canonical_head` that has been + /// updated to match the current slot clock. + pub fn current_state(&self) -> RwLockReadGuard> { + self.state.read() + } + + /// Returns a read-lock guarded `CheckPoint` struct for reading the head (as chosen by the + /// fork-choice rule). + /// + /// It is important to note that the `beacon_state` returned may not match the present slot. It + /// is the state as it was when the head block was received, which could be some slots prior to + /// now. + pub fn head(&self) -> RwLockReadGuard> { + self.canonical_head.read() + } + + /// Returns the slot of the highest block in the canonical chain. + pub fn best_slot(&self) -> Slot { + self.canonical_head.read().shard_block.slot + } + + /// Ensures the current canonical `BeaconState` has been transitioned to match the `slot_clock`. + pub fn catchup_state(&self) -> Result<(), Error> { + let spec = &self.spec; + + let present_slot = match self.slot_clock.present_slot() { + Ok(Some(slot)) => slot, + _ => return Err(Error::UnableToReadSlot), + }; + + if self.state.read().slot < present_slot { + let mut state = self.state.write(); + + // If required, transition the new state to the present slot. + for _ in state.slot.as_u64()..present_slot.as_u64() { + // per_slot_processing(&mut *state, spec)?; + // logic here to manage everything... add this in + } + + state.build_all_caches(spec)?; + } + + Ok(()) + } + + /// Build all of the caches on the current state. + /// + /// Ideally this shouldn't be required, however we leave it here for testing. + pub fn ensure_state_caches_are_built(&self) -> Result<(), Error> { + self.state.write().build_all_caches(&self.spec)?; + + Ok(()) + } + + /// Returns the validator index (if any) for the given public key. + /// + /// Information is retrieved from the present `beacon_state.validator_registry`. + pub fn validator_index(&self, pubkey: &PublicKey) -> Option { + // reference directly to beacon chain parent + // needs to make sure it is part of this particular shard + // for (i, validator) in self + // .head() + // .beacon_state + // .validator_registry + // .iter() + // .enumerate() + // { + // if validator.pubkey == *pubkey { + // return Some(i); + // } + // } + // None + } + + /// Reads the slot clock, returns `None` if the slot is unavailable. + /// + /// The slot might be unavailable due to an error with the system clock, or if the present time + /// is before genesis (i.e., a negative slot). + /// + /// This is distinct to `present_slot`, which simply reads the latest state. If a + /// call to `read_slot_clock` results in a higher slot than a call to `present_slot`, + /// `self.state` should undergo per slot processing. + pub fn read_slot_clock(&self) -> Option { + match self.slot_clock.present_slot() { + Ok(Some(some_slot)) => Some(some_slot), + Ok(None) => None, + _ => None, + } + } + + /// Reads the slot clock (see `self.read_slot_clock()` and returns the number of slots since + /// genesis. + pub fn slots_since_genesis(&self) -> Option { + let now = self.read_slot_clock()?; + let genesis_slot = self.spec.genesis_slot; + + if now < genesis_slot { + None + } else { + Some(SlotHeight::from(now.as_u64() - genesis_slot.as_u64())) + } + } + + /// Returns slot of the present state. + /// + /// This is distinct to `read_slot_clock`, which reads from the actual system clock. If + /// `self.state` has not been transitioned it is possible for the system clock to be on a + /// different slot to what is returned from this call. + pub fn present_slot(&self) -> Slot { + self.state.read().slot + } + + /// Returns the block proposer for a given slot. + /// + /// Information is read from the present `beacon_state` shuffling, only information from the + /// present epoch is available. + pub fn block_proposer(&self, slot: Slot) -> Result { + // Update to go to beacon chain for this information + // Ensures that the present state has been advanced to the present slot, skipping slots if + // blocks are not present. + // self.catchup_state()?; + + // // TODO: permit lookups of the proposer at any slot. + // let index = self.state.read().get_beacon_proposer_index( + // slot, + // RelativeEpoch::Current, + // &self.spec, + // )?; + + Ok(index) + } + + /// Produce an `AttestationData` that is valid for the present `slot` and given `shard`. + /// + /// Attests to the canonical chain. + pub fn produce_attestation_data(&self) -> Result { + let state = self.state.read(); + let head_block_root = self.head().shard_block_root; + let head_block_slot = self.head().shard_block.slot; + + self.produce_attestation_data_for_block(head_block_root, head_block_slot, &*state) + } + + /// Produce an `AttestationData` that attests to the chain denoted by `block_root` and `state`. + /// + /// Permits attesting to any arbitrary chain. Generally, the `produce_attestation_data` + /// function should be used as it attests to the canonical chain. + pub fn produce_attestation_data_for_block( + &self, + head_block_root: Hash256, + head_block_slot: Slot, + state: &ShardState, + ) -> Result { + + Ok(AttestationData { + shard_block_root: head_block_root, + }) + } + + /// Accept a new attestation from the network. + /// + /// If valid, the attestation is added to the `op_pool` and aggregated with another attestation + /// if possible. + pub fn process_attestation( + &self, + attestation: Attestation, + ) -> Result<(), AttestationValidationError> { + let result = self + .op_pool + .insert_attestation(attestation, &*self.state.read(), &self.spec); + + result + } + + // This needs to be written and implemented + pub fn process_transactions() -> {} + + /// Accept some block and attempt to add it to block DAG. + /// + /// Will accept blocks from prior slots, however it will reject any block from a future slot. + pub fn process_block(&self, block: ShardBlock) -> Result { + // In the future... need some logic here that will actually check to see + // if the slot has been part of a finalized crosslink on the beacon chain + // extra logic needed, but for our testnet/system we won't need this to the full degree + // let finalized_slot = self + // .state + // .read() + // .finalized_epoch + // .start_slot(T::EthSpec::slots_per_epoch()); + + // if block.slot <= finalized_slot { + // return Ok(BlockProcessingOutcome::FinalizedSlot); + // } + + if block.slot == 0 { + return Ok(BlockProcessingOutcome::GenesisBlock); + } + + let block_root = block.block_header().canonical_root(); + + if block_root == self.genesis_block_root { + return Ok(BlockProcessingOutcome::GenesisBlock); + } + + let present_slot = self + .read_slot_clock() + .ok_or_else(|| Error::UnableToReadSlot)?; + + if block.slot > present_slot { + return Ok(BlockProcessingOutcome::FutureSlot { + present_slot, + block_slot: block.slot, + }); + } + + if self.store.exists::(&block_root)? { + return Ok(BlockProcessingOutcome::BlockIsAlreadyKnown); + } + + // Load the blocks parent block from the database, returning invalid if that block is not + // found. + let parent_block_root = block.previous_block_root; + let parent_block: ShardBlock = match self.store.get(&parent_block_root)? { + Some(previous_block_root) => previous_block_root, + None => { + return Ok(BlockProcessingOutcome::ParentUnknown { + parent: parent_block_root, + }); + } + }; + + // Load the parent blocks state from the database, returning an error if it is not found. + // It is an error because if know the parent block we should also know the parent state. + let parent_state_root = parent_block.state_root; + let parent_state = self + .store + .get(&parent_state_root)? + .ok_or_else(|| Error::DBInconsistent(format!("Missing state {}", parent_state_root)))?; + + // Transition the parent state to the block slot. + let mut state: ShardState = parent_state; + for _ in state.slot.as_u64()..block.slot.as_u64() { + per_slot_processing(&mut state, &self.spec)?; + } + + // Apply the received block to its parent state (which has been transitioned into this + // slot). + match per_block_processing(&mut state, &block, &self.spec) { + Err(BlockProcessingError::ShardStateError(e)) => { + return Err(Error::ShardStateError(e)) + } + Err(e) => return Ok(BlockProcessingOutcome::PerBlockProcessingError(e)), + _ => {} + } + + let state_root = state.canonical_root(); + + if block.state_root != state_root { + return Ok(BlockProcessingOutcome::StateRootMismatch); + } + + // Store the block and state. + self.store.put(&block_root, &block)?; + self.store.put(&state_root, &state)?; + + // Register the new block with the fork choice service. + self.fork_choice.process_block(&state, &block, block_root)?; + + // Execute the fork choice algorithm, enthroning a new head if discovered. + // + // Note: in the future we may choose to run fork-choice less often, potentially based upon + // some heuristic around number of attestations seen for the block. + self.fork_choice()?; + Ok(BlockProcessingOutcome::Processed { block_root }) + } + + /// Produce a new block at the present slot. + /// + /// The produced block will not be inherently valid, it must be signed by a block producer. + /// Block signing is out of the scope of this function and should be done by a separate program. + pub fn produce_block( + &self, + ) -> Result<(ShardBlock, ShardState), BlockProductionError> { + let state = self.state.read().clone(); + let slot = self + .read_slot_clock() + .ok_or_else(|| BlockProductionError::UnableToReadSlot)?; + + self.produce_block_on_state(state, slot) + } + + /// Produce a block for some `slot` upon the given `state`. + /// + /// Typically the `self.produce_block()` function should be used, instead of calling this + /// function directly. This function is useful for purposefully creating forks or blocks at + /// non-current slots. + /// + /// The given state will be advanced to the given `produce_at_slot`, then a block will be + /// produced at that slot height. + pub fn produce_block_on_state( + &self, + mut state: ShardState, + produce_at_slot: Slot, + ) -> Result<(ShardBlock, ShardState), BlockProductionError> { + // If required, transition the new state to the present slot. + while state.slot < produce_at_slot { + per_slot_processing(&mut state, &self.spec)?; + } + + let previous_block_root = if state.slot > 0 { + *state + .get_block_root(state.slot - 1) + .map_err(|_| BlockProductionError::UnableToGetBlockRootFromState)? + } else { + state.latest_block_header.canonical_root() + }; + + let mut graffiti: [u8; 32] = [0; 32]; + graffiti.copy_from_slice(GRAFFITI.as_bytes()); + + let mut block = ShardBlock { + slot: state.slot, + previous_block_root, + state_root: Hash256::zero(), // Updated after the state is calculated. + signature: Signature::empty_signature(), // To be completed by a validator. + // need to add the attestations here + body: ShardBlockBody { + graffiti, + attestations: self.op_pool.get_attestations(&state, &self.spec), + }, + }; + + per_block_processing_without_verifying_block_signature(&mut state, &block, &self.spec)?; + + let state_root = state.canonical_root(); + + block.state_root = state_root; + + Ok((block, state)) + } + + /// Execute the fork choice algorithm and enthrone the result as the canonical head. + pub fn fork_choice(&self) -> Result<(), Error> { + // Determine the root of the block that is the head of the chain. + let shard_block_root = self.fork_choice.find_head(&self)?; + + // If a new head was chosen. + if shard_block_root != self.head().shard_block_root { + let shard_block: ShardBlock = self + .store + .get(&shard_block_root)? + .ok_or_else(|| Error::MissingShardBlock(shard_block_root))?; + + let shard_state_root = shard_block.state_root; + let shard_state: ShardState = self + .store + .get(&shard_state_root)? + .ok_or_else(|| Error::MissingShardState(shard_state_root))?; + + // Never revert back past a finalized epoch. + // NEED logic to make sure the slot is not coming from an older slot + // if new_finalized_epoch < old_finalized_epoch { + // Err(Error::RevertedFinalizedEpoch { + // previous_epoch: old_finalized_epoch, + // new_epoch: new_finalized_epoch, + // }) + // } else { + self.update_canonical_head(CheckPoint { + shard_block: shard_block, + shard_block_root, + shard_state, + shard_state_root, + })?; + + Ok(()) + } else { + Ok(()) + } + } + + /// Update the canonical head to `new_head`. + fn update_canonical_head(&self, new_head: CheckPoint) -> Result<(), Error> { + // Update the checkpoint that stores the head of the chain at the time it received the + // block. + *self.canonical_head.write() = new_head; + + // Update the always-at-the-present-slot state we keep around for performance gains. + *self.state.write() = { + let mut state = self.canonical_head.read().shard_state.clone(); + + let present_slot = match self.slot_clock.present_slot() { + Ok(Some(slot)) => slot, + _ => return Err(Error::UnableToReadSlot), + }; + + // If required, transition the new state to the present slot. + for _ in state.slot.as_u64()..present_slot.as_u64() { + per_slot_processing(&mut state, &self.spec)?; + } + + state.build_all_caches(&self.spec)?; + + state + }; + + // Save `self` to `self.store`. + self.persist()?; + + Ok(()) + } + + /// Called after `self` has had a new block finalized. + /// + /// Performs pruning and finality-based optimizations. + fn after_finalization( + &self, + old_finalized_epoch: Epoch, + finalized_block_root: Hash256, + ) -> Result<(), Error> { + // Need to build logic here to manage pruning for shard as wel + // let finalized_block = self + // .store + // .get::(&finalized_block_root)? + // .ok_or_else(|| Error::MissingBeaconBlock(finalized_block_root))?; + + // let new_finalized_epoch = finalized_block.slot.epoch(T::EthSpec::slots_per_epoch()); + + // if new_finalized_epoch < old_finalized_epoch { + // Err(Error::RevertedFinalizedEpoch { + // previous_epoch: old_finalized_epoch, + // new_epoch: new_finalized_epoch, + // }) + // } else { + // self.fork_choice + // .process_finalization(&finalized_block, finalized_block_root)?; + + // Ok(()) + // } + } + + /// Returns `true` if the given block root has not been processed. + pub fn is_new_block_root(&self, shard_block_root: &Hash256) -> Result { + Ok(!self.store.exists::(shard_block_root)?) + } +} + +impl From for Error { + fn from(e: DBError) -> Error { + Error::DBError(e) + } +} + +impl From for Error { + fn from(e: ForkChoiceError) -> Error { + Error::ForkChoiceError(e) + } +} + +impl From for Error { + fn from(e: BeaconStateError) -> Error { + Error::BeaconStateError(e) + } } From 276c67623621fd07856af0ad27853a092d889fbe Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Wed, 7 Aug 2019 14:50:58 +1000 Subject: [PATCH 008/151] Include shard chain checkpoint --- shard_node/shard_chain/src/checkpoint.rs | 44 ++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 shard_node/shard_chain/src/checkpoint.rs diff --git a/shard_node/shard_chain/src/checkpoint.rs b/shard_node/shard_chain/src/checkpoint.rs new file mode 100644 index 00000000000..99010fdcaf0 --- /dev/null +++ b/shard_node/shard_chain/src/checkpoint.rs @@ -0,0 +1,44 @@ +use serde_derive::Serialize; +use ssz_derive::{Decode, Encode}; +use types::{BeaconBlock, BeaconState, EthSpec, Hash256}; + +/// Represents some block and it's associated state. Generally, this will be used for tracking the +/// head, justified head and finalized head. +#[derive(Clone, Serialize, PartialEq, Debug, Encode, Decode)] +pub struct CheckPoint { + pub shard_block: ShardBlock, + pub shard_block_root: Hash256, + pub shard_state: ShardState, + pub shard_state_root: Hash256, +} + +impl CheckPoint { + /// Create a new checkpoint. + pub fn new( + shard_block: ShardBlock, + shard_block_root: Hash256, + shard_state: ShardState, + shard_state_root: Hash256, + ) -> Self { + Self { + shard_block, + shard_block_root, + shard_state, + shard_state_root, + } + } + + /// Update all fields of the checkpoint. + pub fn update( + &mut self, + shard_block: ShardBlock, + shard_block_root: Hash256, + shard_state: ShardState, + shard_state_root: Hash256, + ) { + self.shard_block = shard_block; + self.shard_block_root = shard_block_root; + self.shard_state = shard_state; + self.shard_state_root = shard_state_root; + } +} From 6cc277c0b9b8143d7883913dbe8afbd0afc1aa3a Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Thu, 8 Aug 2019 15:48:03 +1000 Subject: [PATCH 009/151] Included parent beacon chain logic --- shard_node/shard_chain/src/shard_chain.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 84705e34063..8ea1c43ef5d 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -1,5 +1,3 @@ - - pub trait ShardChainTypes { type Store: store::Store; type SlotClock: slot_clock::SlotClock; @@ -8,11 +6,10 @@ pub trait ShardChainTypes { } /// Represents the "Shard Chain" component of Ethereum 2.0. It holds a reference to a parent Beacon Chain -pub struct ShardChain { - // TODO... pub parentBeacon: BeaconChain... TODO - // pub shard_id: determine type, +pub struct ShardChain { + pub parent_beacon: Arc>, + pub shard: Shard, pub spec: ChainSpec, - /// /// Persistent storage for blocks, states, etc. Typically an on-disk store, such as LevelDB. pub store: Arc, /// Reports the current slot, typically based upon the system clock. @@ -33,7 +30,7 @@ pub struct ShardChain { pub fork_choice: ForkChoice, } -impl BeaconChain { +impl ShardChain { /// Instantiate a new Beacon Chain, from genesis. pub fn from_genesis( store: Arc, @@ -41,6 +38,8 @@ impl BeaconChain { mut genesis_state: ShardState, genesis_block: ShardBlock, spec: ChainSpec, + shard: Shard, + parent_beacon: Arc>, ) -> Result { genesis_state.build_all_caches(&spec)?; @@ -62,6 +61,8 @@ impl BeaconChain { )); Ok(Self { + parent_beacon, + shard, spec, slot_clock, op_pool: OperationPool::new(), From 64f90cfb4b62a846299824ec46880b6a6c347c20 Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Thu, 8 Aug 2019 16:08:17 +1000 Subject: [PATCH 010/151] Add rest of store logic for shard... all majorly duplicated :) --- shard_node/store/src/block_at_slot.rs | 191 +++++++++++++++++++ shard_node/store/src/iter.rs | 261 ++++++++++++++++++++++++++ 2 files changed, 452 insertions(+) create mode 100644 shard_node/store/src/block_at_slot.rs create mode 100644 shard_node/store/src/iter.rs diff --git a/shard_node/store/src/block_at_slot.rs b/shard_node/store/src/block_at_slot.rs new file mode 100644 index 00000000000..369e639dc65 --- /dev/null +++ b/shard_node/store/src/block_at_slot.rs @@ -0,0 +1,191 @@ +use super::*; +use ssz::{Decode, DecodeError}; + +fn get_block_bytes(store: &T, root: Hash256) -> Result>, Error> { + store.get_bytes(ShardBlock::db_column().into(), &root[..]) +} + +fn read_slot_from_block_bytes(bytes: &[u8]) -> Result { + let end = std::cmp::min(Slot::ssz_fixed_len(), bytes.len()); + + Slot::from_ssz_bytes(&bytes[0..end]) +} + +fn read_previous_block_root_from_block_bytes(bytes: &[u8]) -> Result { + let previous_bytes = Slot::ssz_fixed_len(); + let slice = bytes + .get(previous_bytes..previous_bytes + Hash256::ssz_fixed_len()) + .ok_or_else(|| DecodeError::BytesInvalid("Not enough bytes.".to_string()))?; + + Hash256::from_ssz_bytes(slice) +} + +pub fn get_block_at_preceeding_slot( + store: &T, + slot: Slot, + start_root: Hash256, +) -> Result, Error> { + Ok(match get_at_preceeding_slot(store, slot, start_root)? { + Some((hash, bytes)) => Some((hash, ShardBlock::from_ssz_bytes(&bytes)?)), + None => None, + }) +} + +fn get_at_preceeding_slot( + store: &T, + slot: Slot, + mut root: Hash256, +) -> Result)>, Error> { + loop { + if let Some(bytes) = get_block_bytes(store, root)? { + let this_slot = read_slot_from_block_bytes(&bytes)?; + + if this_slot == slot { + break Ok(Some((root, bytes))); + } else if this_slot < slot { + break Ok(None); + } else { + root = read_previous_block_root_from_block_bytes(&bytes)?; + } + } else { + break Ok(None); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ssz::Encode; + use tree_hash::TreeHash; + + #[test] + fn read_slot() { + let spec = MinimalEthSpec::default_spec(); + + let test_slot = |slot: Slot| { + let mut block = ShardBlock::empty(&spec); + block.slot = slot; + let bytes = block.as_ssz_bytes(); + assert_eq!(read_slot_from_block_bytes(&bytes).unwrap(), slot); + }; + + test_slot(Slot::new(0)); + test_slot(Slot::new(1)); + test_slot(Slot::new(42)); + test_slot(Slot::new(u64::max_value())); + } + + #[test] + fn bad_slot() { + for i in 0..8 { + assert!(read_slot_from_block_bytes(&vec![0; i]).is_err()); + } + } + + #[test] + fn read_previous_block_root() { + let spec = MinimalEthSpec::default_spec(); + + let test_root = |root: Hash256| { + let mut block = ShardBlock::empty(&spec); + block.previous_block_root = root; + let bytes = block.as_ssz_bytes(); + assert_eq!( + read_previous_block_root_from_block_bytes(&bytes).unwrap(), + root + ); + }; + + test_root(Hash256::random()); + test_root(Hash256::random()); + test_root(Hash256::random()); + } + + fn build_chain( + store: &impl Store, + slots: &[usize], + spec: &ChainSpec, + ) -> Vec<(Hash256, ShardBlock)> { + let mut blocks_and_roots: Vec<(Hash256, ShardBlock)> = vec![]; + + for (i, slot) in slots.iter().enumerate() { + let mut block = ShardBlock::empty(spec); + block.slot = Slot::from(*slot); + + if i > 0 { + block.previous_block_root = blocks_and_roots[i - 1].0; + } + + let root = Hash256::from_slice(&block.tree_hash_root()); + + store.put(&root, &block).unwrap(); + blocks_and_roots.push((root, block)); + } + + blocks_and_roots + } + + #[test] + fn chain_without_skips() { + let n: usize = 10; + let store = MemoryStore::open(); + let spec = MinimalEthSpec::default_spec(); + + let slots: Vec = (0..n).collect(); + let blocks_and_roots = build_chain(&store, &slots, &spec); + + for source in 1..n { + for target in 0..=source { + let (source_root, _source_block) = &blocks_and_roots[source]; + let (target_root, target_block) = &blocks_and_roots[target]; + + let (found_root, found_block) = store + .get_block_at_preceeding_slot(*source_root, target_block.slot) + .unwrap() + .unwrap(); + + assert_eq!(found_root, *target_root); + assert_eq!(found_block, *target_block); + } + } + } + + #[test] + fn chain_with_skips() { + let store = MemoryStore::open(); + let spec = MinimalEthSpec::default_spec(); + + let slots = vec![0, 1, 2, 5]; + + let blocks_and_roots = build_chain(&store, &slots, &spec); + + // Valid slots + for target in 0..3 { + let (source_root, _source_block) = &blocks_and_roots[3]; + let (target_root, target_block) = &blocks_and_roots[target]; + + let (found_root, found_block) = store + .get_block_at_preceeding_slot(*source_root, target_block.slot) + .unwrap() + .unwrap(); + + assert_eq!(found_root, *target_root); + assert_eq!(found_block, *target_block); + } + + // Slot that doesn't exist + let (source_root, _source_block) = &blocks_and_roots[3]; + assert!(store + .get_block_at_preceeding_slot(*source_root, Slot::new(3)) + .unwrap() + .is_none()); + + // Slot too high + let (source_root, _source_block) = &blocks_and_roots[3]; + assert!(store + .get_block_at_preceeding_slot(*source_root, Slot::new(3)) + .unwrap() + .is_none()); + } +} diff --git a/shard_node/store/src/iter.rs b/shard_node/store/src/iter.rs new file mode 100644 index 00000000000..09616455b6b --- /dev/null +++ b/shard_node/store/src/iter.rs @@ -0,0 +1,261 @@ +use crate::Store; +use std::borrow::Cow; +use std::sync::Arc; +use types::{ShardBlock, ShardState, ShardStateError, EthSpec, Hash256, Slot}; + +#[derive(Clone)] +pub struct StateRootsIterator<'a, T: EthSpec, U> { + store: Arc, + shard_state: Cow<'a, ShardState>, + slot: Slot, +} + +impl<'a, T: EthSpec, U: Store> StateRootsIterator<'a, T, U> { + pub fn new(store: Arc, shard_state: &'a ShardState, start_slot: Slot) -> Self { + Self { + store, + shard_state: Cow::Borrowed(shard_state), + slot: start_slot + 1, + } + } + + pub fn owned(store: Arc, beacon_state: BeaconState, start_slot: Slot) -> Self { + Self { + store, + shard_state: Cow::Owned(shard_state), + slot: start_slot + 1, + } + } +} + +impl<'a, T: EthSpec, U: Store> Iterator for StateRootsIterator<'a, T, U> { + type Item = (Hash256, Slot); + + fn next(&mut self) -> Option { + if (self.slot == 0) || (self.slot > self.shard_state.slot) { + return None; + } + + self.slot -= 1; + + match self.shard_state.get_state_root(self.slot) { + Ok(root) => Some((*root, self.slot)), + Err(ShardStateError::SlotOutOfBounds) => { + // Read a `BeaconState` from the store that has access to prior historical root. + let shard_state: ShardState = { + let new_state_root = self.shard_state.get_oldest_state_root().ok()?; + + self.store.get(&new_state_root).ok()? + }?; + + self.shard_state = Cow::Owned(shard_state); + + let root = self.shard_state.get_state_root(self.slot).ok()?; + + Some((*root, self.slot)) + } + _ => None, + } + } +} + +#[derive(Clone)] +/// Extends `BlockRootsIterator`, returning `BeaconBlock` instances, instead of their roots. +pub struct BlockIterator<'a, T: EthSpec, U> { + roots: BlockRootsIterator<'a, T, U>, +} + +impl<'a, T: EthSpec, U: Store> BlockIterator<'a, T, U> { + /// Create a new iterator over all blocks in the given `beacon_state` and prior states. + pub fn new(store: Arc, shard_state: &'a ShardState, start_slot: Slot) -> Self { + Self { + roots: BlockRootsIterator::new(store, shard_state, start_slot), + } + } + + /// Create a new iterator over all blocks in the given `beacon_state` and prior states. + pub fn owned(store: Arc, shard_state: ShardState, start_slot: Slot) -> Self { + Self { + roots: BlockRootsIterator::owned(store, shard_state, start_slot), + } + } +} + +impl<'a, T: EthSpec, U: Store> Iterator for BlockIterator<'a, T, U> { + type Item = ShardBlock; + + fn next(&mut self) -> Option { + let (root, _slot) = self.roots.next()?; + self.roots.store.get(&root).ok()? + } +} + +/// Iterates backwards through block roots. If any specified slot is unable to be retrieved, the +/// iterator returns `None` indefinitely. +/// +/// Uses the `latest_block_roots` field of `BeaconState` to as the source of block roots and will +/// perform a lookup on the `Store` for a prior `BeaconState` if `latest_block_roots` has been +/// exhausted. +/// +/// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. +/// +/// ## Notes +/// +/// See [`BestBlockRootsIterator`](struct.BestBlockRootsIterator.html), which has different +/// `start_slot` logic. +#[derive(Clone)] +pub struct BlockRootsIterator<'a, T: EthSpec, U> { + store: Arc, + shard_state: Cow<'a, ShardState>, + slot: Slot, +} + +impl<'a, T: EthSpec, U: Store> BlockRootsIterator<'a, T, U> { + /// Create a new iterator over all block roots in the given `shard_state` and prior states. + pub fn new(store: Arc, shard_state: &'a ShardState, start_slot: Slot) -> Self { + Self { + store, + shard_state: Cow::Borrowed(shard_state), + slot: start_slot + 1, + } + } + + /// Create a new iterator over all block roots in the given `beacon_state` and prior states. + pub fn owned(store: Arc, shard_state: ShardState, start_slot: Slot) -> Self { + Self { + store, + shard_state: Cow::Owned(shard_state), + slot: start_slot + 1, + } + } +} + +impl<'a, T: EthSpec, U: Store> Iterator for BlockRootsIterator<'a, T, U> { + type Item = (Hash256, Slot); + + fn next(&mut self) -> Option { + if (self.slot == 0) || (self.slot > self.shard_state.slot) { + return None; + } + + self.slot -= 1; + + match self.shard_state.get_block_root(self.slot) { + Ok(root) => Some((*root, self.slot)), + Err(ShardStateError::SlotOutOfBounds) => { + // Read a `BeaconState` from the store that has access to prior historical root. + let shard_state: ShardState = { + // Load the earliest state from disk. + let new_state_root = self.shard_state.get_oldest_state_root().ok()?; + + self.store.get(&new_state_root).ok()? + }?; + + self.shard_state = Cow::Owned(shard_state); + + let root = self.shard_state.get_block_root(self.slot).ok()?; + + Some((*root, self.slot)) + } + _ => None, + } + } +} + +/// Iterates backwards through block roots with `start_slot` highest possible value +/// `<= beacon_state.slot`. +/// +/// The distinction between `BestBlockRootsIterator` and `BlockRootsIterator` is: +/// +/// - `BestBlockRootsIterator` uses best-effort slot. When `start_slot` is greater than the latest available block root +/// on `beacon_state`, returns `Some(root, slot)` where `slot` is the latest available block +/// root. +/// - `BlockRootsIterator` is strict about `start_slot`. When `start_slot` is greater than the latest available block root +/// on `beacon_state`, returns `None`. +/// +/// This is distinct from `BestBlockRootsIterator`. +/// +/// Uses the `latest_block_roots` field of `BeaconState` to as the source of block roots and will +/// perform a lookup on the `Store` for a prior `BeaconState` if `latest_block_roots` has been +/// exhausted. +/// +/// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. +#[derive(Clone)] +pub struct BestBlockRootsIterator<'a, T: EthSpec, U> { + store: Arc, + shard_state: Cow<'a, ShardState>, + slot: Slot, +} + +impl<'a, T: EthSpec, U: Store> BestBlockRootsIterator<'a, T, U> { + /// Create a new iterator over all block roots in the given `beacon_state` and prior states. + pub fn new(store: Arc, beacon_state: &'a ShardState, start_slot: Slot) -> Self { + let mut slot = start_slot; + if slot >= shard_state.slot { + // Slot may be too high. + slot = shard_state.slot; + if shard_state.get_block_root(slot).is_err() { + slot -= 1; + } + } + + Self { + store, + shard_state: Cow::Borrowed(shard_state), + slot: slot + 1, + } + } + + /// Create a new iterator over all block roots in the given `beacon_state` and prior states. + pub fn owned(store: Arc, shard_state: ShardState, start_slot: Slot) -> Self { + let mut slot = start_slot; + if slot >= shard_state.slot { + // Slot may be too high. + slot = shard_state.slot; + // TODO: Use a function other than `get_block_root` as this will always return `Err()` + // for slot = state.slot. + if shard_state.get_block_root(slot).is_err() { + slot -= 1; + } + } + + Self { + store, + shard_state: Cow::Owned(shard_state), + slot: slot + 1, + } + } +} + +impl<'a, T: EthSpec, U: Store> Iterator for BestBlockRootsIterator<'a, T, U> { + type Item = (Hash256, Slot); + + fn next(&mut self) -> Option { + if self.slot == 0 { + // End of Iterator + return None; + } + + self.slot -= 1; + + match self.shard_state.get_block_root(self.slot) { + Ok(root) => Some((*root, self.slot)), + Err(ShardStateError::SlotOutOfBounds) => { + // Read a `BeaconState` from the store that has access to prior historical root. + let shard_state: ShardState = { + // Load the earliest state from disk. + let new_state_root = self.shard_state.get_oldest_state_root().ok()?; + + self.store.get(&new_state_root).ok()? + }?; + + self.shard_state = Cow::Owned(shard_state); + + let root = self.shard_state.get_block_root(self.slot).ok()?; + + Some((*root, self.slot)) + } + _ => None, + } + } +} \ No newline at end of file From c401a2dae6435c19daaf0bef0045853948f59ec5 Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Thu, 8 Aug 2019 21:43:50 +1000 Subject: [PATCH 011/151] Began trait functions for beacon wrapper and began fork choice rules --- beacon_node/beacon_chain/src/beacon_chain.rs | 61 ++++++++++++++++- eth2/types/src/beacon_state.rs | 11 +++ eth2/types/src/shard_state.rs | 71 +++++++------------- shard_node/shard_chain/src/fork_choice.rs | 0 4 files changed, 94 insertions(+), 49 deletions(-) create mode 100644 shard_node/shard_chain/src/fork_choice.rs diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 96ebe4b41a5..e865d31b2aa 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -243,7 +243,7 @@ impl BeaconChain { /// genesis. /// /// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. - pub fn rev_iter_state_roots(&self, slot: Slot) -> StateRootsIterator { + pub fn f(&self, slot: Slot) -> StateRootsIterator { StateRootsIterator::owned(self.store.clone(), self.state.read().clone(), slot) } @@ -925,6 +925,65 @@ impl BeaconChain { Ok(dump) } + + // Everything from this point on will techincally be moved into a trait + pub fn get_persistent_committee_at_block(&self, beacon_block_root: &Hash256, shard: Shard) -> Result { + let block: BeaconBlock = match self.store.get(&beacon_block_root)? { + Some(block_root) => block_root, + None => { + } + }; + let state_root = block.state_root; + let block_state = self + .store + .get(&state_root)? + .ok_or_else(|| Error::DBInconsistent(format!("Missing state {}", state_root)))?; + + let mut state: BeaconState = block_state; + state.get_crosslink_committee_for_shard(shard, state.current_epoch()) + } + + pub fn get_persistent_crosslink_committee(&self, shard: Shard) -> Result { + let current_state = self.current_state(); + current_state.get_crosslink_committee_for_shard(shard, current_state.current_epoch()) + } + + pub fn shard_block_proposer(&self, slot: Slot, shard: Shard) -> Result { + self.catchup_state()?; + let index = self.state.read().get_shard_proposer_index( + slot, + shard, + RelativeEpoch::Current, + &self.spec, + )?; + + Ok(index) + } + + // need to support lookup by epoch + pub fn get_current_crosslink(&self, shard: u64) -> Result<&Crosslink, Error> { + self.current_state().get_current_crosslink(shard) + } + + // need to support lookup by epoch + pub fn get_finalized_crosslink(&self, shard: Shard, epoch: Epoch) -> { + let current_state = self.current_state(); + let finalized_block_root = current_state.finalized_block_root; + + let finalized_block: BeaconBlock = match self.store.get(&finalized_block_root)? { + Some(block_root) => block_root, + None => { + } + }; + let state_root = finalized_block.state_root; + let finalized_state_root = self + .store + .get(&state_root)? + .ok_or_else(|| Error::DBInconsistent(format!("Missing state {}", state_root)))?; + + let mut state: BeaconState = finalized_state_root; + finalized_state_root().get_current_crosslink(shard) + } } impl From for Error { diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index 1be6eac2363..40054f997a2 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -431,6 +431,17 @@ impl BeaconState { }) } + // NEED TO ADD + pub fn get_shard_proposer_index() -> { + &self, + shard: Shard, + slot: Slot, + relative_epoch: RelativeEpoch, + spec: &ChainSpec, + ) -> Result { + // needs implementation + } + /// Safely obtains the index for latest block roots, given some `slot`. /// /// Spec v0.6.3 diff --git a/eth2/types/src/shard_state.rs b/eth2/types/src/shard_state.rs index 7d5ab543a15..7347a4134e4 100644 --- a/eth2/types/src/shard_state.rs +++ b/eth2/types/src/shard_state.rs @@ -32,11 +32,6 @@ pub enum Error { UnknownValidator, UnableToDetermineProducer, InvalidBitfield, - ValidatorIsWithdrawable, - UnableToShuffle, - TooManyValidators, - InsufficientValidators, - InsufficientRandaoMixes, InsufficientBlockRoots, InsufficientIndexRoots, InsufficientAttestations, @@ -73,11 +68,11 @@ where // Attestations - split by epoch necessary or just from latest finalized crosslink? // Going to need a way to request the attestation properly by epoch... play with this later - pub running_attestations: Vec, + pub running_attestations: Vec, - // Latest beacon roots needed + // Latest beacon root needed // Latest crosslinks not needed since it will only duplicate data accessed via beacon roots - pub latest_beacon_roots: FixedLenVec, + pub beacon_root: Hash256, // Recent state pub latest_block_roots: FixedLenVec, @@ -106,41 +101,18 @@ impl ShardState { fork: Fork::genesis(T::genesis_epoch()), // Attestations - previous_epoch_attestations: vec![], - current_epoch_attestations: vec![], + running_attestations: vec![], // Recent state - current_crosslinks: vec![initial_crosslink.clone(); T::ShardCount::to_usize()].into(), - previous_crosslinks: vec![initial_crosslink; T::ShardCount::to_usize()].into(), latest_block_roots: vec![spec.zero_hash; T::SlotsPerHistoricalRoot::to_usize()].into(), latest_state_roots: vec![spec.zero_hash; T::SlotsPerHistoricalRoot::to_usize()].into(), - latest_active_index_roots: vec![ - spec.zero_hash; - T::LatestActiveIndexRootsLength::to_usize() - ] - .into(), - latest_slashed_balances: vec![0; T::LatestSlashedExitLength::to_usize()].into(), - latest_block_header: BeaconBlock::empty(spec).temporary_block_header(spec), + latest_block_header: ShardBlock::empty(spec).temporary_block_header(spec), historical_roots: vec![], - /* - * PoW receipt root - */ - latest_eth1_data, - eth1_data_votes: vec![], - deposit_index: 0, - /* * Caching (not in spec) */ - committee_caches: [ - CommitteeCache::default(), - CommitteeCache::default(), - CommitteeCache::default(), - ], - pubkey_cache: PubkeyCache::default(), tree_hash_cache: TreeHashCache::default(), - exit_cache: ExitCache::default(), } } @@ -159,18 +131,20 @@ impl ShardState { } /// Get the slot of an attestation. - /// - /// Note: Utilizes the cache and will fail if the appropriate cache is not initialized. - /// + /// Adds logic to iterate through latest block roots and match to the attestation data + /// /// Spec v0.6.3 - pub fn get_attestation_slot(&self, attestation_data: &AttestationData) -> Result { - let target_relative_epoch = - RelativeEpoch::from_epoch(self.current_epoch(), attestation_data.target_epoch)?; - - let cc = - self.get_crosslink_committee_for_shard(attestation_data.shard, target_relative_epoch)?; - - Ok(cc.slot) + pub fn get_attestation_slot(&self, attestation_data: &ShardAttestationData) -> Result { + let block_root = attestation_data.shard_block_root + + for (i, root) in self.latest_block_roots.iter().enumerate() { + if root == block_root { + // calculate logic here + Ok(slot) + } + } + + Ok(slot) } /// Safely obtains the index for latest block roots, given some `slot`. @@ -180,14 +154,15 @@ impl ShardState { if (slot < self.slot) && (self.slot <= slot + self.latest_block_roots.len() as u64) { Ok(slot.as_usize() % self.latest_block_roots.len()) } else { - Err(BeaconStateError::SlotOutOfBounds) + // Update proper error here + Err(ShardStateError::SlotOutOfBounds) } } /// Return the block root at a recent `slot`. /// /// Spec v0.6.3 - pub fn get_block_root(&self, slot: Slot) -> Result<&Hash256, BeaconStateError> { + pub fn get_block_root(&self, slot: Slot) -> Result<&Hash256, ShardStateError> { let i = self.get_latest_block_roots_index(slot)?; Ok(&self.latest_block_roots[i]) } @@ -199,7 +174,7 @@ impl ShardState { &mut self, slot: Slot, block_root: Hash256, - ) -> Result<(), BeaconStateError> { + ) -> Result<(), ShardStateError> { let i = self.get_latest_block_roots_index(slot)?; self.latest_block_roots[i] = block_root; Ok(()) @@ -212,7 +187,7 @@ impl ShardState { if (slot < self.slot) && (self.slot <= slot + Slot::from(self.latest_state_roots.len())) { Ok(slot.as_usize() % self.latest_state_roots.len()) } else { - Err(BeaconStateError::SlotOutOfBounds) + Err(ShardStateError::SlotOutOfBounds) } } diff --git a/shard_node/shard_chain/src/fork_choice.rs b/shard_node/shard_chain/src/fork_choice.rs new file mode 100644 index 00000000000..e69de29bb2d From 2ff15d232b6066d297382bfa90798945cab5c9b1 Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Thu, 8 Aug 2019 22:41:02 +1000 Subject: [PATCH 012/151] Build fork choice logic --- eth2/shard_lmd_ghost/cargo.toml | 0 eth2/shard_lmd_ghost/src/lib.rs | 46 ++ eth2/shard_lmd_ghost/src/reduced_tree.rs | 650 ++++++++++++++++++ .../src/common/get_attesting_indices.rs | 44 ++ shard_node/shard_chain/src/fork_choice.rs | 139 ++++ shard_node/shard_chain/src/shard_chain.rs | 32 +- 6 files changed, 895 insertions(+), 16 deletions(-) create mode 100644 eth2/shard_lmd_ghost/cargo.toml create mode 100644 eth2/shard_lmd_ghost/src/lib.rs create mode 100644 eth2/shard_lmd_ghost/src/reduced_tree.rs diff --git a/eth2/shard_lmd_ghost/cargo.toml b/eth2/shard_lmd_ghost/cargo.toml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/eth2/shard_lmd_ghost/src/lib.rs b/eth2/shard_lmd_ghost/src/lib.rs new file mode 100644 index 00000000000..d3e5104ebee --- /dev/null +++ b/eth2/shard_lmd_ghost/src/lib.rs @@ -0,0 +1,46 @@ +mod reduced_tree; + +use std::sync::Arc; +use store::Store; +use types::{ShardBlock, EthSpec, Hash256, Slot}; + +pub use reduced_tree::ThreadSafeReducedTree; + +pub type Result = std::result::Result; + +pub trait LmdGhost: Send + Sync { + /// Create a new instance, with the given `store` and `finalized_root`. + fn new(store: Arc, finalized_block: &ShardBlock, finalized_root: Hash256) -> Self; + + /// Process an attestation message from some validator that attests to some `block_hash` + /// representing a block at some `block_slot`. + fn process_attestation( + &self, + validator_index: usize, + block_hash: Hash256, + block_slot: Slot, + ) -> Result<()>; + + /// Process a block that was seen on the network. + fn process_block(&self, block: &ShardBlock, block_hash: Hash256) -> Result<()>; + + /// Returns the head of the chain, starting the search at `start_block_root` and moving upwards + /// (in block height). + fn find_head( + &self, + start_block_slot: Slot, + start_block_root: Hash256, + weight: F, + ) -> Result + where + F: Fn(usize) -> Option + Copy; + + /// Provide an indication that the blockchain has been finalized at the given `finalized_block`. + /// + /// `finalized_block_root` must be the root of `finalized_block`. + fn update_finalized_root( + &self, + finalized_block: &ShardBlock, + finalized_block_root: Hash256, + ) -> Result<()>; +} diff --git a/eth2/shard_lmd_ghost/src/reduced_tree.rs b/eth2/shard_lmd_ghost/src/reduced_tree.rs new file mode 100644 index 00000000000..7e382d6999f --- /dev/null +++ b/eth2/shard_lmd_ghost/src/reduced_tree.rs @@ -0,0 +1,650 @@ +//! An implementation of "reduced tree" LMD GHOST fork choice. +//! +//! This algorithm was concieved at IC3 Cornell, 2019. +//! +//! This implementation is incomplete and has known bugs. Do not use in production. +use super::{LmdGhost, Result as SuperResult}; +use parking_lot::RwLock; +use std::collections::HashMap; +use std::marker::PhantomData; +use std::sync::Arc; +use store::{iter::BestBlockRootsIterator, Error as StoreError, Store}; +use types::{ShardBlock, ShardState, EthSpec, Hash256, Slot}; + +type Result = std::result::Result; + +#[derive(Debug, PartialEq)] +pub enum Error { + MissingNode(Hash256), + MissingBlock(Hash256), + MissingState(Hash256), + MissingChild(Hash256), + NotInTree(Hash256), + NoCommonAncestor((Hash256, Hash256)), + StoreError(StoreError), + ValidatorWeightUnknown(usize), +} + +impl From for Error { + fn from(e: StoreError) -> Error { + Error::StoreError(e) + } +} + +pub struct ThreadSafeReducedTree { + core: RwLock>, +} + +impl LmdGhost for ThreadSafeReducedTree +where + T: Store, + E: EthSpec, +{ + fn new(store: Arc, genesis_block: &ShardBlock, genesis_root: Hash256) -> Self { + ThreadSafeReducedTree { + core: RwLock::new(ReducedTree::new(store, genesis_block, genesis_root)), + } + } + + fn process_attestation( + &self, + validator_index: usize, + block_hash: Hash256, + block_slot: Slot, + ) -> SuperResult<()> { + self.core + .write() + .process_message(validator_index, block_hash, block_slot) + .map_err(|e| format!("process_attestation failed: {:?}", e)) + } + + /// Process a block that was seen on the network. + fn process_block(&self, block: &ShardBlock, block_hash: Hash256) -> SuperResult<()> { + self.core + .write() + .add_weightless_node(block.slot, block_hash) + .map_err(|e| format!("process_block failed: {:?}", e)) + } + + fn find_head( + &self, + start_block_slot: Slot, + start_block_root: Hash256, + weight_fn: F, + ) -> SuperResult + where + F: Fn(usize) -> Option + Copy, + { + self.core + .write() + .update_weights_and_find_head(start_block_slot, start_block_root, weight_fn) + .map_err(|e| format!("find_head failed: {:?}", e)) + } + + fn update_finalized_root(&self, new_block: &ShardBlock, new_root: Hash256) -> SuperResult<()> { + self.core + .write() + .update_root(new_block.slot, new_root) + .map_err(|e| format!("update_finalized_root failed: {:?}", e)) + } +} + +struct ReducedTree { + store: Arc, + /// Stores all nodes of the tree, keyed by the block hash contained in the node. + nodes: HashMap, + /// Maps validator indices to their latest votes. + latest_votes: ElasticList>, + /// Stores the root of the tree, used for pruning. + root: (Hash256, Slot), + _phantom: PhantomData, +} + +impl ReducedTree +where + T: Store, + E: EthSpec, +{ + pub fn new(store: Arc, genesis_block: &ShardBlock, genesis_root: Hash256) -> Self { + let mut nodes = HashMap::new(); + + // Insert the genesis node. + nodes.insert( + genesis_root, + Node { + block_hash: genesis_root, + ..Node::default() + }, + ); + + Self { + store, + nodes, + latest_votes: ElasticList::default(), + root: (genesis_root, genesis_block.slot), + _phantom: PhantomData, + } + } + + pub fn update_root(&mut self, new_slot: Slot, new_root: Hash256) -> Result<()> { + if !self.nodes.contains_key(&new_root) { + let node = Node { + block_hash: new_root, + voters: vec![], + ..Node::default() + }; + + self.add_node(node)?; + } + + self.retain_subtree(self.root.0, new_root)?; + + self.root = (new_root, new_slot); + + let root_node = self.get_mut_node(new_root)?; + root_node.parent_hash = None; + + Ok(()) + } + + /// Removes `current_hash` and all decendants, except `subtree_hash` and all nodes + /// which have `subtree_hash` as an ancestor. + /// + /// In effect, prunes the tree so that only decendants of `subtree_hash` exist. + fn retain_subtree(&mut self, current_hash: Hash256, subtree_hash: Hash256) -> Result<()> { + if current_hash != subtree_hash { + let children = self.get_node(current_hash)?.children.clone(); + + for child_hash in children { + self.retain_subtree(child_hash, subtree_hash)?; + } + + self.nodes.remove(¤t_hash); + } + + Ok(()) + } + + pub fn process_message( + &mut self, + validator_index: usize, + block_hash: Hash256, + slot: Slot, + ) -> Result<()> { + if slot >= self.root_slot() { + if let Some(previous_vote) = self.latest_votes.get(validator_index) { + // Note: it is possible to do a cheap equivocation check here: + // + // slashable = (previous_vote.slot == slot) && (previous_vote.hash != block_hash) + + if previous_vote.slot < slot { + self.remove_latest_message(validator_index)?; + } else { + return Ok(()); + } + } + + self.latest_votes.insert( + validator_index, + Some(Vote { + slot, + hash: block_hash, + }), + ); + + self.add_latest_message(validator_index, block_hash)?; + } + + Ok(()) + } + + pub fn update_weights_and_find_head( + &mut self, + start_block_slot: Slot, + start_block_root: Hash256, + weight_fn: F, + ) -> Result + where + F: Fn(usize) -> Option + Copy, + { + // It is possible that the given `start_block_root` is not in the reduced tree. + // + // In this case, we add a weightless node at `start_block_root`. + if !self.nodes.contains_key(&start_block_root) { + self.add_weightless_node(start_block_slot, start_block_root)?; + }; + + let _root_weight = self.update_weight(start_block_root, weight_fn)?; + + let start_node = self.get_node(start_block_root)?; + let head_node = self.find_head_from(start_node)?; + + Ok(head_node.block_hash) + } + + fn find_head_from<'a>(&'a self, start_node: &'a Node) -> Result<&'a Node> { + if start_node.does_not_have_children() { + Ok(start_node) + } else { + let children = start_node + .children + .iter() + .map(|hash| self.get_node(*hash)) + .collect::>>()?; + + // TODO: check if `max_by` is `O(n^2)`. + let best_child = children + .iter() + .max_by(|a, b| { + if a.weight != b.weight { + a.weight.cmp(&b.weight) + } else { + a.block_hash.cmp(&b.block_hash) + } + }) + // There can only be no maximum if there are no children. This code path is guarded + // against that condition. + .expect("There must be a maximally weighted node."); + + self.find_head_from(best_child) + } + } + + fn update_weight(&mut self, start_block_root: Hash256, weight_fn: F) -> Result + where + F: Fn(usize) -> Option + Copy, + { + let weight = { + let node = self.get_node(start_block_root)?.clone(); + + let mut weight = 0; + + for &child in &node.children { + weight += self.update_weight(child, weight_fn)?; + } + + for &voter in &node.voters { + weight += weight_fn(voter).ok_or_else(|| Error::ValidatorWeightUnknown(voter))?; + } + + weight + }; + + let node = self.get_mut_node(start_block_root)?; + node.weight = weight; + + Ok(weight) + } + + fn remove_latest_message(&mut self, validator_index: usize) -> Result<()> { + if self.latest_votes.get(validator_index).is_some() { + // Unwrap is safe as prior `if` statements ensures the result is `Some`. + let vote = self.latest_votes.get(validator_index).unwrap(); + + let should_delete = { + self.get_mut_node(vote.hash)?.remove_voter(validator_index); + let node = self.get_node(vote.hash)?.clone(); + + if let Some(parent_hash) = node.parent_hash { + if node.has_votes() || node.children.len() > 1 { + // A node with votes or more than one child is never removed. + false + } else if node.children.len() == 1 { + // A node which has only one child may be removed. + // + // Load the child of the node and set it's parent to be the parent of this + // node (viz., graft the node's child to the node's parent) + let child = self.get_mut_node(node.children[0])?; + child.parent_hash = node.parent_hash; + + // Graft the parent of this node to it's child. + if let Some(parent_hash) = node.parent_hash { + let parent = self.get_mut_node(parent_hash)?; + parent.replace_child(node.block_hash, node.children[0])?; + } + + true + } else if node.children.is_empty() { + // A node which has no children may be deleted and potentially it's parent + // too. + self.maybe_delete_node(parent_hash)?; + + true + } else { + // It is impossible for a node to have a number of children that is not 0, 1 or + // greater than one. + // + // This code is strictly unnecessary, however we keep it for readability. + unreachable!(); + } + } else { + // A node without a parent is the genesis/finalized node and should never be removed. + false + } + }; + + if should_delete { + self.nodes.remove(&vote.hash); + } + + self.latest_votes.insert(validator_index, Some(vote)); + } + + Ok(()) + } + + fn maybe_delete_node(&mut self, hash: Hash256) -> Result<()> { + let should_delete = { + let node = self.get_node(hash)?.clone(); + + if let Some(parent_hash) = node.parent_hash { + if (node.children.len() == 1) && !node.has_votes() { + // Graft the child to it's grandparent. + let child_hash = { + let child_node = self.get_mut_node(node.children[0])?; + child_node.parent_hash = node.parent_hash; + + child_node.block_hash + }; + + // Graft the grandparent to it's grandchild. + let parent_node = self.get_mut_node(parent_hash)?; + parent_node.replace_child(node.block_hash, child_hash)?; + + true + } else { + false + } + } else { + // A node without a parent is the genesis node and should not be deleted. + false + } + }; + + if should_delete { + self.nodes.remove(&hash); + } + + Ok(()) + } + + fn add_latest_message(&mut self, validator_index: usize, hash: Hash256) -> Result<()> { + if let Ok(node) = self.get_mut_node(hash) { + node.add_voter(validator_index); + } else { + let node = Node { + block_hash: hash, + voters: vec![validator_index], + ..Node::default() + }; + + self.add_node(node)?; + } + + Ok(()) + } + + fn add_weightless_node(&mut self, slot: Slot, hash: Hash256) -> Result<()> { + if slot >= self.root_slot() && !self.nodes.contains_key(&hash) { + let node = Node { + block_hash: hash, + ..Node::default() + }; + + self.add_node(node)?; + + if let Some(parent_hash) = self.get_node(hash)?.parent_hash { + self.maybe_delete_node(parent_hash)?; + } + } + + Ok(()) + } + + fn add_node(&mut self, mut node: Node) -> Result<()> { + // Find the highest (by slot) ancestor of the given hash/block that is in the reduced tree. + let mut prev_in_tree = { + let hash = self + .find_prev_in_tree(node.block_hash) + .ok_or_else(|| Error::NotInTree(node.block_hash))?; + self.get_mut_node(hash)?.clone() + }; + + let mut added = false; + + if !prev_in_tree.children.is_empty() { + for &child_hash in &prev_in_tree.children { + if self + .iter_ancestors(child_hash)? + .any(|(ancestor, _slot)| ancestor == node.block_hash) + { + let child = self.get_mut_node(child_hash)?; + + child.parent_hash = Some(node.block_hash); + node.children.push(child_hash); + prev_in_tree.replace_child(child_hash, node.block_hash)?; + node.parent_hash = Some(prev_in_tree.block_hash); + + added = true; + + break; + } + } + + if !added { + for &child_hash in &prev_in_tree.children { + let ancestor_hash = + self.find_least_common_ancestor(node.block_hash, child_hash)?; + + if ancestor_hash != prev_in_tree.block_hash { + let child = self.get_mut_node(child_hash)?; + let common_ancestor = Node { + block_hash: ancestor_hash, + parent_hash: Some(prev_in_tree.block_hash), + children: vec![node.block_hash, child_hash], + ..Node::default() + }; + child.parent_hash = Some(common_ancestor.block_hash); + node.parent_hash = Some(common_ancestor.block_hash); + + prev_in_tree.replace_child(child_hash, ancestor_hash)?; + + self.nodes + .insert(common_ancestor.block_hash, common_ancestor); + + added = true; + + break; + } + } + } + } + + if !added { + node.parent_hash = Some(prev_in_tree.block_hash); + prev_in_tree.children.push(node.block_hash); + } + + // Update `prev_in_tree`. A mutable reference was not maintained to satisfy the borrow + // checker. + // + // This is not an ideal solution and results in unnecessary memory copies -- a better + // solution is certainly possible. + self.nodes.insert(prev_in_tree.block_hash, prev_in_tree); + self.nodes.insert(node.block_hash, node); + + Ok(()) + } + + /// For the given block `hash`, find it's highest (by slot) ancestor that exists in the reduced + /// tree. + fn find_prev_in_tree(&mut self, hash: Hash256) -> Option { + self.iter_ancestors(hash) + .ok()? + .find(|(root, _slot)| self.nodes.contains_key(root)) + .and_then(|(root, _slot)| Some(root)) + } + + /// For the given `child` block hash, return the block's ancestor at the given `target` slot. + fn find_ancestor_at_slot(&self, child: Hash256, target: Slot) -> Result { + let (root, slot) = self + .iter_ancestors(child)? + .find(|(_block, slot)| *slot <= target) + .ok_or_else(|| Error::NotInTree(child))?; + + // Explicitly check that the slot is the target in the case that the given child has a slot + // above target. + if slot == target { + Ok(root) + } else { + Err(Error::NotInTree(child)) + } + } + + /// For the two given block roots (`a_root` and `b_root`), find the first block they share in + /// the tree. Viz, find the block that these two distinct blocks forked from. + fn find_least_common_ancestor(&self, a_root: Hash256, b_root: Hash256) -> Result { + // If the blocks behind `a_root` and `b_root` are not at the same slot, take the highest + // block (by slot) down to be equal with the lower slot. + // + // The result is two roots which identify two blocks at the same height. + let (a_root, b_root) = { + let a = self.get_block(a_root)?; + let b = self.get_block(b_root)?; + + if a.slot > b.slot { + (self.find_ancestor_at_slot(a_root, b.slot)?, b_root) + } else if b.slot > a.slot { + (a_root, self.find_ancestor_at_slot(b_root, a.slot)?) + } else { + (a_root, b_root) + } + }; + + let ((a_root, _a_slot), (_b_root, _b_slot)) = self + .iter_ancestors(a_root)? + .zip(self.iter_ancestors(b_root)?) + .find(|((a_root, _), (b_root, _))| a_root == b_root) + .ok_or_else(|| Error::NoCommonAncestor((a_root, b_root)))?; + + Ok(a_root) + } + + fn iter_ancestors(&self, child: Hash256) -> Result> { + let block = self.get_block(child)?; + let state = self.get_state(block.state_root)?; + + Ok(BestBlockRootsIterator::owned( + self.store.clone(), + state, + block.slot - 1, + )) + } + + fn get_node(&self, hash: Hash256) -> Result<&Node> { + self.nodes + .get(&hash) + .ok_or_else(|| Error::MissingNode(hash)) + } + + fn get_mut_node(&mut self, hash: Hash256) -> Result<&mut Node> { + self.nodes + .get_mut(&hash) + .ok_or_else(|| Error::MissingNode(hash)) + } + + fn get_block(&self, block_root: Hash256) -> Result { + self.store + .get::(&block_root)? + .ok_or_else(|| Error::MissingBlock(block_root)) + } + + fn get_state(&self, state_root: Hash256) -> Result> { + self.store + .get::>(&state_root)? + .ok_or_else(|| Error::MissingState(state_root)) + } + + fn root_slot(&self) -> Slot { + self.root.1 + } +} + +#[derive(Default, Clone, Debug)] +pub struct Node { + pub parent_hash: Option, + pub children: Vec, + pub weight: u64, + pub block_hash: Hash256, + pub voters: Vec, +} + +impl Node { + pub fn does_not_have_children(&self) -> bool { + self.children.is_empty() + } + + pub fn replace_child(&mut self, old: Hash256, new: Hash256) -> Result<()> { + let i = self + .children + .iter() + .position(|&c| c == old) + .ok_or_else(|| Error::MissingChild(old))?; + self.children[i] = new; + + Ok(()) + } + + pub fn remove_voter(&mut self, voter: usize) -> Option { + let i = self.voters.iter().position(|&v| v == voter)?; + Some(self.voters.remove(i)) + } + + pub fn add_voter(&mut self, voter: usize) { + self.voters.push(voter); + } + + pub fn has_votes(&self) -> bool { + !self.voters.is_empty() + } +} + +#[derive(Debug, Clone, Copy)] +pub struct Vote { + hash: Hash256, + slot: Slot, +} + +/// A Vec-wrapper which will grow to match any request. +/// +/// E.g., a `get` or `insert` to an out-of-bounds element will cause the Vec to grow (using +/// Default) to the smallest size required to fulfill the request. +#[derive(Default, Clone, Debug)] +pub struct ElasticList(Vec); + +impl ElasticList +where + T: Default, +{ + fn ensure(&mut self, i: usize) { + if self.0.len() <= i { + self.0.resize_with(i + 1, Default::default); + } + } + + pub fn get(&mut self, i: usize) -> &T { + self.ensure(i); + &self.0[i] + } + + pub fn insert(&mut self, i: usize, element: T) { + self.ensure(i); + self.0[i] = element; + } +} + +impl From for String { + fn from(e: Error) -> String { + format!("{:?}", e) + } +} diff --git a/eth2/state_processing/src/common/get_attesting_indices.rs b/eth2/state_processing/src/common/get_attesting_indices.rs index c627c366bfa..fe047d77ee2 100644 --- a/eth2/state_processing/src/common/get_attesting_indices.rs +++ b/eth2/state_processing/src/common/get_attesting_indices.rs @@ -44,3 +44,47 @@ pub fn get_attesting_indices_unsorted( }) .collect()) } + + +pub fn get_shard_attesting_indices( + shard: Shard, + state: &BeaconState, + attestation_data: &AttestationData, + bitfield: &Bitfield, +) -> Result, BeaconStateError> { + get_attesting_indices_unsorted(shard, state, attestation_data, bitfield).map(|mut indices| { + // Fast unstable sort is safe because validator indices are unique + indices.sort_unstable(); + indices + }) +} + +/// Returns validator indices which participated in the attestation, unsorted. +/// +/// Spec v0.6.3 +pub fn get_shard_attesting_indices_unsorted( + shard: Shard, + state: &BeaconState, + attestation_data: &AttestationData, + bitfield: &Bitfield, +) -> Result, BeaconStateError> { + let target_relative_epoch = + RelativeEpoch::from_epoch(state.current_epoch(), attestation_data.target_epoch)?; + + let committee = + state.get_crosslink_committee_for_shard(shard, target_relative_epoch)?; + + if !verify_bitfield_length(&bitfield, committee.committee.len()) { + return Err(BeaconStateError::InvalidBitfield); + } + + Ok(committee + .committee + .iter() + .enumerate() + .filter_map(|(i, validator_index)| match bitfield.get(i) { + Ok(true) => Some(*validator_index), + _ => None, + }) + .collect()) +} diff --git a/shard_node/shard_chain/src/fork_choice.rs b/shard_node/shard_chain/src/fork_choice.rs index e69de29bb2d..06e0439d414 100644 --- a/shard_node/shard_chain/src/fork_choice.rs +++ b/shard_node/shard_chain/src/fork_choice.rs @@ -0,0 +1,139 @@ +type Result = std::result::Result; + +#[derive(Debug, PartialEq)] +pub enum Error { + MissingBlock(Hash256), + MissingState(Hash256), + BackendError(String), + ShardStateError(ShardStateError), + StoreError(StoreError), +} + +pub struct ForkChoice { + backend: T::ShardLmdGhost, + /// Used for resolving the `0x00..00` alias back to genesis. + /// + /// Does not necessarily need to be the _actual_ genesis, it suffices to be the finalized root + /// whenever the struct was instantiated. + genesis_block_root: Hash256, +} + +impl ForkChoice { + /// Instantiate a new fork chooser. + /// + /// "Genesis" does not necessarily need to be the absolute genesis, it can be some finalized + /// block. + pub fn new( + store: Arc, + genesis_block: &ShardBlock, + genesis_block_root: Hash256, + ) -> Self { + Self { + backend: T::ShardLmdGhost::new(store, genesis_block, genesis_block_root), + genesis_block_root, + } + } + + // general pseudocode here + pub fn find_head(&self, chain: &ShardChain) -> Result { + let beacon_state = chain.get_beacon_state(); + let finalized_epoch = beacon_state.finalized_epoch; + let start_block_root = chain.get_crosslink(finalized_epoch); + let start_block_slot = chain.get_block(start_block_root).slot; + + // A function that returns the weight for some validator index. + let weight = |validator_index: usize| -> Option { + beacon_state + .validator_registry + .get(validator_index) + .map(|v| v.effective_balance) + }; + + self.backend + .find_head(start_block_slot, start_block_root, weight) + .map_err(Into::into) + } + + /// Process all attestations in the given `block`. + /// + /// Assumes the block (and therefore it's attestations) are valid. It is a logic error to + /// provide an invalid block. + pub fn process_block( + &self, + beacon_state: &BeaconState, + block: &ShardBlock, + block_root: Hash256, + ) -> Result<()> { + // Note: we never count the block as a latest message, only attestations. + // + // I (Paul H) do not have an explicit reference to this, but I derive it from this + // document: + // + // https://github.com/ethereum/eth2.0-specs/blob/v0.7.0/specs/core/0_fork-choice.md + for attestation in &block.body.attestations { + self.process_attestation_from_block(beacon_state, attestation, block)?; + } + + self.backend.process_block(block, block_root)?; + + Ok(()) + } + + fn process_attestation_from_block( + &self, + beacon_state: &BeaconState, + attestation: &Attestation, + block: &ShardBlock + ) -> Result<()> { + // Note: `get_attesting_indices_unsorted` requires that the beacon state caches be built. + let validator_indices = get_shard_attesting_indices_unsorted( + block.slot, + beacon_state, + &attestation.data, + &attestation.aggregation_bitfield, + )?; + + let block_hash = attestation.data.target_root; + + if block_hash != Hash256::zero() { + for validator_index in validator_indices { + self.backend + .process_attestation(validator_index, block_hash, block.slot)?; + } + } + + Ok(()) + } + + /// Inform the fork choice that the given block (and corresponding root) have been finalized so + /// it may prune it's storage. + /// + /// `finalized_block_root` must be the root of `finalized_block`. + pub fn process_finalization( + &self, + finalized_block: &BeaconBlock, + finalized_block_root: Hash256, + ) -> Result<()> { + self.backend + .update_finalized_root(finalized_block, finalized_block_root) + .map_err(Into::into) + } +} + +impl From for Error { + fn from(e: ShardStateError) -> Error { + Error::ShardStateError(e) + } +} + +impl From for Error { + fn from(e: StoreError) -> Error { + Error::StoreError(e) + } +} + +impl From for Error { + fn from(e: String) -> Error { + Error::BackendError(e) + } +} diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 8ea1c43ef5d..cbcc65ab098 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -213,18 +213,20 @@ impl ShardChain Option { // reference directly to beacon chain parent // needs to make sure it is part of this particular shard - // for (i, validator) in self - // .head() - // .beacon_state - // .validator_registry - // .iter() - // .enumerate() - // { - // if validator.pubkey == *pubkey { - // return Some(i); - // } - // } - // None + for (i, validator) in self + .parent_beacon + .current_state() + .validator_registry + .iter() + .enumerate() + { + if validator.pubkey == *pubkey { + if self.parent_beacon.current_state().get_attestation_duties(i).shard = self.shard { + return Some(i); + } + } + } + None } /// Reads the slot clock, returns `None` if the slot is unavailable. @@ -417,6 +419,7 @@ impl ShardChain ShardChain ShardChain Result<(), Error> { - // Need to build logic here to manage pruning for shard as wel + // Need to build logic here to manage pruning for shard as well // let finalized_block = self // .store // .get::(&finalized_block_root)? From 2759a8a557246c14b6b98da20912f15a2b618409 Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Wed, 14 Aug 2019 11:25:49 +0200 Subject: [PATCH 013/151] Include proper dependencies and errors file --- shard_node/shard_chain/src/checkpoint.rs | 2 +- shard_node/shard_chain/src/errors.rs | 51 +++++++++++++++++++++++ shard_node/shard_chain/src/shard_chain.rs | 44 +++++++++++++++---- 3 files changed, 87 insertions(+), 10 deletions(-) create mode 100644 shard_node/shard_chain/src/errors.rs diff --git a/shard_node/shard_chain/src/checkpoint.rs b/shard_node/shard_chain/src/checkpoint.rs index 99010fdcaf0..e39e2dbd1e1 100644 --- a/shard_node/shard_chain/src/checkpoint.rs +++ b/shard_node/shard_chain/src/checkpoint.rs @@ -1,6 +1,6 @@ use serde_derive::Serialize; use ssz_derive::{Decode, Encode}; -use types::{BeaconBlock, BeaconState, EthSpec, Hash256}; +use types::{ShardBlock, ShardState, EthSpec, Hash256}; /// Represents some block and it's associated state. Generally, this will be used for tracking the /// head, justified head and finalized head. diff --git a/shard_node/shard_chain/src/errors.rs b/shard_node/shard_chain/src/errors.rs new file mode 100644 index 00000000000..926d0bda338 --- /dev/null +++ b/shard_node/shard_chain/src/errors.rs @@ -0,0 +1,51 @@ +use crate::fork_choice::Error as ForkChoiceError; +use crate::metrics::Error as MetricsError; +use state_processing::BlockProcessingError; +use state_processing::SlotProcessingError; +use types::*; + +macro_rules! easy_from_to { + ($from: ident, $to: ident) => { + impl From<$from> for $to { + fn from(e: $from) -> $to { + $to::$from(e) + } + } + }; +} + +#[derive(Debug, PartialEq)] +pub enum ShardChainError { + InsufficientValidators, + BadRecentBlockRoots, + UnableToReadSlot, + ShardStateError(BeaconStateError), + DBInconsistent(String), + DBError(store::Error), + ForkChoiceError(ForkChoiceError), + MissingShardBlock(Hash256), + MissingShardState(Hash256), + SlotProcessingError(SlotProcessingError), + MetricsError(String), +} + +easy_from_to!(SlotProcessingError, ShardChainError); + +impl From for ShardChainError { + fn from(e: MetricsError) -> ShardChainError { + ShardChainError::MetricsError(format!("{:?}", e)) + } +} + +#[derive(Debug, PartialEq)] +pub enum BlockProductionError { + UnableToGetBlockRootFromState, + UnableToReadSlot, + SlotProcessingError(SlotProcessingError), + BlockProcessingError(BlockProcessingError), + ShardStateError(ShardStateError), +} + +easy_from_to!(BlockProcessingError, BlockProductionError); +easy_from_to!(ShardStateError, BlockProductionError); +easy_from_to!(SlotProcessingError, BlockProductionError); diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index cbcc65ab098..4193df7b1d3 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -1,3 +1,29 @@ +use crate::checkpoint::CheckPoint; +use crate::errors::{ShardChainError as Error, BlockProductionError}; +use crate::fork_choice::{Error as ForkChoiceError, ForkChoice}; +use lmd_ghost::LmdGhost; +// use operation_pool::{OperationPool}; +use parking_lot::{RwLock, RwLockReadGuard}; +use slot_clock::SlotClock; +use state_processing::per_block_processing::errors::{ + AttestationValidationError, AttesterSlashingValidationError, +}; +use state_processing::{ + per_block_processing, per_block_processing_without_verifying_block_signature, + per_slot_processing, BlockProcessingError, +}; +use std::sync::Arc; +// use store::iter::{BestBlockRootsIterator, BlockIterator, BlockRootsIterator, StateRootsIterator}; +use store::{Error as DBError, Store}; +use tree_hash::TreeHash; +use types::*; + +// Text included in blocks. +// Must be 32-bytes or panic. +// +// |-------must be this long------| +pub const GRAFFITI: &str = "sigp/lighthouse-0.0.0-prerelease"; + pub trait ShardChainTypes { type Store: store::Store; type SlotClock: slot_clock::SlotClock; @@ -6,10 +32,10 @@ pub trait ShardChainTypes { } /// Represents the "Shard Chain" component of Ethereum 2.0. It holds a reference to a parent Beacon Chain -pub struct ShardChain { +pub struct ShardChain { pub parent_beacon: Arc>, pub shard: Shard, - pub spec: ChainSpec, + pub spec: ChaitnSpec, /// Persistent storage for blocks, states, etc. Typically an on-disk store, such as LevelDB. pub store: Arc, /// Reports the current slot, typically based upon the system clock. @@ -31,7 +57,7 @@ pub struct ShardChain ShardChain { - /// Instantiate a new Beacon Chain, from genesis. + /// Instantiate a new Shard Chain, from genesis. pub fn from_genesis( store: Arc, slot_clock: T::SlotClock, @@ -271,18 +297,18 @@ impl ShardChain Result { + pub fn block_proposer(&self, slot: Slot, shard: Shard) -> Result { // Update to go to beacon chain for this information // Ensures that the present state has been advanced to the present slot, skipping slots if // blocks are not present. // self.catchup_state()?; // // TODO: permit lookups of the proposer at any slot. - // let index = self.state.read().get_beacon_proposer_index( - // slot, - // RelativeEpoch::Current, - // &self.spec, - // )?; + let index = self.parent_beacon.get_shard_proposer_index( + slot, + shard, + &self.spec, + )?; Ok(index) } From 012677408677b051995d8d35bb202848085848ad Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Fri, 16 Aug 2019 12:24:17 +0200 Subject: [PATCH 014/151] Attestation data for a shard needs to include a target slot --- eth2/types/src/shard_attestation_data.rs | 43 ++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/eth2/types/src/shard_attestation_data.rs b/eth2/types/src/shard_attestation_data.rs index e69de29bb2d..38be878397a 100644 --- a/eth2/types/src/shard_attestation_data.rs +++ b/eth2/types/src/shard_attestation_data.rs @@ -0,0 +1,43 @@ +use crate::test_utils::TestRandom; +use crate::{Epoch, Hash256}; + +use serde_derive::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; +use tree_hash::TreeHash; +use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash}; + +/// The data upon which an attestation is based. +/// +/// Spec v0.6.3 +#[derive( + Debug, + Clone, + PartialEq, + Eq, + Default, + Serialize, + Deserialize, + Hash, + Encode, + Decode, + TreeHash, + CachedTreeHash, + TestRandom, + SignedRoot, +)] +pub struct ShardAttestationData { + // LMD GHOST vote + pub shard_block_root: Hash256, + + // Need to indicate which slot the attestation is for + pub target_slot: Slot +} + +#[cfg(test)] +mod tests { + use super::*; + + ssz_tests!(ShardAttestationData); + cached_tree_hash_tests!(ShardAttestationData); +} From 0433d72a4685aa2a0862fc51fe53a1e00cc76f7e Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Fri, 16 Aug 2019 12:24:47 +0200 Subject: [PATCH 015/151] Initialize the shard operation pool --- eth2/shard_operation_pool/Cargo.toml | 7 + eth2/shard_operation_pool/src/attestation.rs | 0 .../src/attestation_id.rs | 38 +++++ eth2/shard_operation_pool/src/lib.rs | 135 ++++++++++++++++++ eth2/shard_operation_pool/src/max_cover.rs | 0 5 files changed, 180 insertions(+) create mode 100644 eth2/shard_operation_pool/Cargo.toml create mode 100644 eth2/shard_operation_pool/src/attestation.rs create mode 100644 eth2/shard_operation_pool/src/attestation_id.rs create mode 100644 eth2/shard_operation_pool/src/lib.rs create mode 100644 eth2/shard_operation_pool/src/max_cover.rs diff --git a/eth2/shard_operation_pool/Cargo.toml b/eth2/shard_operation_pool/Cargo.toml new file mode 100644 index 00000000000..94cb27beaad --- /dev/null +++ b/eth2/shard_operation_pool/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "shard_operation_pool" +version = "0.1.0" +authors = ["will "] +edition = "2018" + +[dependencies] diff --git a/eth2/shard_operation_pool/src/attestation.rs b/eth2/shard_operation_pool/src/attestation.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/eth2/shard_operation_pool/src/attestation_id.rs b/eth2/shard_operation_pool/src/attestation_id.rs new file mode 100644 index 00000000000..a79023a699c --- /dev/null +++ b/eth2/shard_operation_pool/src/attestation_id.rs @@ -0,0 +1,38 @@ +use int_to_bytes::int_to_bytes8; +use ssz::ssz_encode; +use ssz_derive::{Decode, Encode}; +use types::{AttestationData, BeaconState, ChainSpec, Domain, Epoch, EthSpec}; + +/// Serialized `AttestationData` augmented with a domain to encode the fork info. +#[derive(PartialEq, Eq, Clone, Hash, Debug, PartialOrd, Ord, Encode, Decode)] +pub struct AttestationId { + v: Vec, +} + +/// Number of domain bytes that the end of an attestation ID is padded with. +const DOMAIN_BYTES_LEN: usize = 8; + +impl AttestationId { + pub fn from_data( + attestation: &AttestationData, + state: &BeaconState, + spec: &ChainSpec, + ) -> Self { + let mut bytes = ssz_encode(attestation); + let epoch = attestation.target_epoch; + bytes.extend_from_slice(&AttestationId::compute_domain_bytes(epoch, state, spec)); + AttestationId { v: bytes } + } + + pub fn compute_domain_bytes( + epoch: Epoch, + state: &BeaconState, + spec: &ChainSpec, + ) -> Vec { + int_to_bytes8(spec.get_domain(epoch, Domain::Attestation, &state.fork)) + } + + pub fn domain_bytes_match(&self, domain_bytes: &[u8]) -> bool { + &self.v[self.v.len() - DOMAIN_BYTES_LEN..] == domain_bytes + } +} diff --git a/eth2/shard_operation_pool/src/lib.rs b/eth2/shard_operation_pool/src/lib.rs new file mode 100644 index 00000000000..18cc026a899 --- /dev/null +++ b/eth2/shard_operation_pool/src/lib.rs @@ -0,0 +1,135 @@ +mod attestation; +mod attestation_id; +mod max_cover; +mod persistence; + +pub use persistence::PersistedOperationPool; + +use attestation::{earliest_attestation_validators, AttMaxCover}; +use attestation_id::AttestationId; +use itertools::Itertools; +use max_cover::maximum_cover; +use parking_lot::RwLock; +use state_processing::per_block_processing::{ + get_slashable_indices_modular, validate_attestation, + validate_attestation_time_independent_only, verify_attester_slashing, verify_exit, + verify_exit_time_independent_only, verify_proposer_slashing, verify_transfer, + verify_transfer_time_independent_only, +}; +use std::collections::{btree_map::Entry, hash_map, BTreeMap, HashMap, HashSet}; +use std::marker::PhantomData; +use types::{ + ShardAttestation, BeaconState, ShardState, ChainSpec, EthSpec, Validator +}; + +#[derive(Default, Debug)] +pub struct OperationPool { + /// Map from attestation ID (see below) to vectors of attestations. + attestations: RwLock>>, + // NOTE: We assume that there is only one deposit per index + // because the Eth1 data is updated (at most) once per epoch, + // and the spec doesn't seem to accomodate for re-orgs on a time-frame + // longer than an epoch + _phantom: PhantomData, +} + +impl OperationPool { + /// Create a new operation pool. + pub fn new() -> Self { + Self::default() + } + + /// Insert an attestation into the pool, aggregating it with existing attestations if possible. + pub fn insert_attestation( + &self, + attestation: Shardttestation, + state: &BeaconState, + shard_state: &ShardState, + spec: &ChainSpec, + ) -> () { + let id = AttestationId::from_data(&attestation.data, state, spec); + + // Take a write lock on the attestations map. + let mut attestations = self.attestations.write(); + + let existing_attestations = match attestations.entry(id) { + hash_map::Entry::Vacant(entry) => { + entry.insert(vec![attestation]); + return Ok(()); + } + hash_map::Entry::Occupied(entry) => entry.into_mut(), + }; + + let mut aggregated = false; + for existing_attestation in existing_attestations.iter_mut() { + if existing_attestation.signers_disjoint_from(&attestation) { + existing_attestation.aggregate(&attestation); + aggregated = true; + } else if *existing_attestation == attestation { + aggregated = true; + } + } + + if !aggregated { + existing_attestations.push(attestation); + } + + Ok(()) + } + + /// Total number of attestations in the pool, including attestations for the same data. + pub fn num_attestations(&self) -> usize { + self.attestations.read().values().map(Vec::len).sum() + } + + /// Get a list of attestations for inclusion in a block. + pub fn get_attestations(&self, state: &ShardState, spec: &ChainSpec) -> Vec { + // Attestations for the current fork, which may be from the current or previous epoch. + let current_slot = state.slot(); + let domain_bytes = AttestationId::compute_domain_bytes(epoch, state, spec); + let reader = self.attestations.read(); + let valid_attestations = reader + .iter() + .filter(|(key, _)| key.domain_bytes_match(&domain_bytes)) + .flat_map(|(_, attestations)| attestations) + // remove valid check for now... + .map(|att| AttMaxCover::new(att, earliest_attestation_validators(att, state))); + + maximum_cover(valid_attestations, spec.max_attestations as usize) + } + + /// Remove attestations which are too old to be included in a block. + pub fn prune_attestations(&self, finalized_state: &BeaconState) { + // We know we can include an attestation if: + // state.slot <= attestation_slot + SLOTS_PER_EPOCH + // We approximate this check using the attestation's epoch, to avoid computing + // the slot or relying on the committee cache of the finalized state. + self.attestations.write().retain(|_, attestations| { + // All the attestations in this bucket have the same data, so we only need to + // check the first one. + attestations.first().map_or(false, |att| { + finalized_state.current_epoch() <= att.data.target_epoch + 1 + }) + }); + } +} + +/// Filter up to a maximum number of operations out of an iterator. +fn filter_limit_operations<'a, T: 'a, I, F>(operations: I, filter: F, limit: u64) -> Vec +where + I: IntoIterator, + F: Fn(&T) -> bool, + T: Clone, +{ + operations + .into_iter() + .filter(|x| filter(*x)) + .take(limit as usize) + .cloned() + .collect() +} + +/// Compare two operation pools. +impl PartialEq for OperationPool { + fn eq(&self, other: &Self) -> bool { *self.attestations.read() == *other.attestations.read()} +} diff --git a/eth2/shard_operation_pool/src/max_cover.rs b/eth2/shard_operation_pool/src/max_cover.rs new file mode 100644 index 00000000000..e69de29bb2d From e1d2c04762efaef3eed038559351feef891d76d8 Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Fri, 16 Aug 2019 12:47:45 +0200 Subject: [PATCH 016/151] bring operation pool logic/pseudocoding in check with requirements --- .../src/attestation_id.rs | 10 ++++++---- eth2/shard_operation_pool/src/lib.rs | 19 +++++-------------- eth2/shard_operation_pool/src/max_cover.rs | 0 3 files changed, 11 insertions(+), 18 deletions(-) delete mode 100644 eth2/shard_operation_pool/src/max_cover.rs diff --git a/eth2/shard_operation_pool/src/attestation_id.rs b/eth2/shard_operation_pool/src/attestation_id.rs index a79023a699c..76cbc628773 100644 --- a/eth2/shard_operation_pool/src/attestation_id.rs +++ b/eth2/shard_operation_pool/src/attestation_id.rs @@ -10,12 +10,12 @@ pub struct AttestationId { } /// Number of domain bytes that the end of an attestation ID is padded with. -const DOMAIN_BYTES_LEN: usize = 8; +const DOMAIN_BYTES_LEN: usize = 16; impl AttestationId { pub fn from_data( attestation: &AttestationData, - state: &BeaconState, + state: &ShardState, spec: &ChainSpec, ) -> Self { let mut bytes = ssz_encode(attestation); @@ -26,10 +26,12 @@ impl AttestationId { pub fn compute_domain_bytes( epoch: Epoch, - state: &BeaconState, + state: &ShardState, spec: &ChainSpec, + slot: Slot ) -> Vec { - int_to_bytes8(spec.get_domain(epoch, Domain::Attestation, &state.fork)) + // also pseudocoded here + int_to_bytes8(spec.get_domain(epoch, Domain::Attestation, &state.fork)) + int_to_bytes8(slot) } pub fn domain_bytes_match(&self, domain_bytes: &[u8]) -> bool { diff --git a/eth2/shard_operation_pool/src/lib.rs b/eth2/shard_operation_pool/src/lib.rs index 18cc026a899..376dfb8393b 100644 --- a/eth2/shard_operation_pool/src/lib.rs +++ b/eth2/shard_operation_pool/src/lib.rs @@ -1,14 +1,10 @@ -mod attestation; mod attestation_id; -mod max_cover; mod persistence; pub use persistence::PersistedOperationPool; -use attestation::{earliest_attestation_validators, AttMaxCover}; use attestation_id::AttestationId; use itertools::Itertools; -use max_cover::maximum_cover; use parking_lot::RwLock; use state_processing::per_block_processing::{ get_slashable_indices_modular, validate_attestation, @@ -19,13 +15,13 @@ use state_processing::per_block_processing::{ use std::collections::{btree_map::Entry, hash_map, BTreeMap, HashMap, HashSet}; use std::marker::PhantomData; use types::{ - ShardAttestation, BeaconState, ShardState, ChainSpec, EthSpec, Validator + Attestation, BeaconState, ShardState, ChainSpec, EthSpec, Validator }; #[derive(Default, Debug)] pub struct OperationPool { /// Map from attestation ID (see below) to vectors of attestations. - attestations: RwLock>>, + attestations: RwLock>>, // NOTE: We assume that there is only one deposit per index // because the Eth1 data is updated (at most) once per epoch, // and the spec doesn't seem to accomodate for re-orgs on a time-frame @@ -42,9 +38,8 @@ impl OperationPool { /// Insert an attestation into the pool, aggregating it with existing attestations if possible. pub fn insert_attestation( &self, - attestation: Shardttestation, - state: &BeaconState, - shard_state: &ShardState, + attestation: Attestation, + state: &ShardState, spec: &ChainSpec, ) -> () { let id = AttestationId::from_data(&attestation.data, state, spec); @@ -91,11 +86,7 @@ impl OperationPool { let valid_attestations = reader .iter() .filter(|(key, _)| key.domain_bytes_match(&domain_bytes)) - .flat_map(|(_, attestations)| attestations) - // remove valid check for now... - .map(|att| AttMaxCover::new(att, earliest_attestation_validators(att, state))); - - maximum_cover(valid_attestations, spec.max_attestations as usize) + .flat_map(|(_, attestations)| attestations); } /// Remove attestations which are too old to be included in a block. diff --git a/eth2/shard_operation_pool/src/max_cover.rs b/eth2/shard_operation_pool/src/max_cover.rs deleted file mode 100644 index e69de29bb2d..00000000000 From 5631314213a8ff0b5da0024ef894a86494c3632a Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Mon, 19 Aug 2019 15:02:27 +0200 Subject: [PATCH 017/151] add Slot logic --- eth2/types/src/slot_epoch.rs | 26 +++++++++++++++++++++++++- eth2/types/src/slot_height.rs | 19 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/eth2/types/src/slot_epoch.rs b/eth2/types/src/slot_epoch.rs index 9a7808da4c3..251a3dad823 100644 --- a/eth2/types/src/slot_epoch.rs +++ b/eth2/types/src/slot_epoch.rs @@ -26,12 +26,36 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssi #[serde(transparent)] pub struct Slot(u64); +#[derive(Eq, Debug, Clone, Copy, Default, Serialize, Deserialize)] +#[serde(transparent)] +pub struct ShardSlot(u64); + + #[derive(Eq, Debug, Clone, Copy, Default, Serialize, Deserialize)] pub struct Epoch(u64); impl_common!(Slot); +impl_common!(ShardSlot); impl_common!(Epoch); +impl ShardSlot { + pub fn new(slot: u64) -> Slot { + ShardSlot(slot) + } + + pub fn epoch(self, slots_per_epoch: u64, slots_per_beacon_slot: u64) -> { + Epoch::from(self.0 / slots_per_epoch / slots_per_beacon_slot) + } + + pub fn height(self, genesis_slot: Slot) -> SlotHeight { + ShardSlotHeight::from(self.0.saturating_sub(genesis_slot.as_u64())) + } + + pub fn max_value() -> Slot { + Slot(u64::max_value()) + } +} + impl Slot { pub fn new(slot: u64) -> Slot { Slot(slot) @@ -42,7 +66,7 @@ impl Slot { } pub fn height(self, genesis_slot: Slot) -> SlotHeight { - SlotHeight::from(self.0.saturating_sub(genesis_slot.as_u64())) + ShardSlotHeight::from(self.0.saturating_sub(genesis_slot.as_u64())) } pub fn max_value() -> Slot { diff --git a/eth2/types/src/slot_height.rs b/eth2/types/src/slot_height.rs index 47db392a9e0..bb0a206e33c 100644 --- a/eth2/types/src/slot_height.rs +++ b/eth2/types/src/slot_height.rs @@ -12,6 +12,7 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssi /// Beacon block height, effectively `Slot/GENESIS_START_BLOCK`. #[derive(Eq, Debug, Clone, Copy, Default, Serialize)] pub struct SlotHeight(u64); +pub struct ShardSlotHeight(u64); impl_common!(SlotHeight); @@ -33,6 +34,24 @@ impl SlotHeight { } } +impl ShardSlotHeight { + pub fn new(slot: u64) -> SlotHeight { + ShardSlotHeight(slot) + } + + pub fn slot(self, genesis_slot: Slot) -> Slot { + ShardSlot::from(self.0.saturating_add(genesis_slot.as_u64())) + } + + pub fn epoch(self, genesis_slot: u64, slots_per_epoch: u64, slots_per_beacon_slot: u64) -> Epoch { + Epoch::from(self.0.saturating_add(genesis_slot) / slots_per_epoch / slots_per_beacon_slot ) + } + + pub fn max_value() -> SlotHeight { + SlotHeight(u64::max_value()) + } +} + #[cfg(test)] mod slot_height_tests { use super::*; From b3d6f58aa7921409445e496eb119519656b7dfee Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Wed, 21 Aug 2019 15:08:59 +0200 Subject: [PATCH 018/151] Include period committee structure --- eth2/types/src/period_committee.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 eth2/types/src/period_committee.rs diff --git a/eth2/types/src/period_committee.rs b/eth2/types/src/period_committee.rs new file mode 100644 index 00000000000..23e23267ec8 --- /dev/null +++ b/eth2/types/src/period_committee.rs @@ -0,0 +1,26 @@ +use crate::*; +use tree_hash_derive::{CachedTreeHash, TreeHash}; + +#[derive(Default, Clone, Debug, PartialEq, TreeHash, CachedTreeHash)] +pub struct PeriodCommittee<'a> { + pub slot: Slot, + pub shard: Shard, + pub committee: &'a [usize], +} + +impl<'a> PeriodCommittee<'a> { + pub fn into_owned(self) -> OwnedPeriodCommittee { + OwnedPeriodCommittee { + slot: self.slot, + shard: self.shard, + committee: self.committee.to_vec(), + } + } +} + +#[derive(Default, Clone, Debug, PartialEq, TreeHash, CachedTreeHash)] +pub struct OwnedPeriodCommittee { + pub slot: Slot, + pub shard: Shard, + pub committee: Vec, +} From 4e8115706fc5bf8a71df76c982a9f6df70996355 Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Wed, 21 Aug 2019 15:09:32 +0200 Subject: [PATCH 019/151] Include logic around start period for committee --- eth2/types/src/slot_epoch.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/eth2/types/src/slot_epoch.rs b/eth2/types/src/slot_epoch.rs index 251a3dad823..689b6b844a8 100644 --- a/eth2/types/src/slot_epoch.rs +++ b/eth2/types/src/slot_epoch.rs @@ -43,10 +43,15 @@ impl ShardSlot { ShardSlot(slot) } - pub fn epoch(self, slots_per_epoch: u64, slots_per_beacon_slot: u64) -> { + pub fn epoch(self, slots_per_epoch: u64, slots_per_beacon_slot: u64) -> Epoch { Epoch::from(self.0 / slots_per_epoch / slots_per_beacon_slot) } + pub fn shard_period_start_epoch(self, slots_per_epoch: u64, slots_per_beacon_slot: u64, epochs_per_shard_period: u64) -> Epoch { + let epoch = self::Epoch(slots_per_epoch, slots_per_beacon_slot) + Epoch::from(epoch - (epoch % epochs_per_shard_period) - lookback * epochs_per_shard_period) + } + pub fn height(self, genesis_slot: Slot) -> SlotHeight { ShardSlotHeight::from(self.0.saturating_sub(genesis_slot.as_u64())) } From 295efcd4221b63e0aef6fafb6bdc276a9b1b74c5 Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Wed, 21 Aug 2019 15:11:03 +0200 Subject: [PATCH 020/151] Fix reference to spec --- shard_node/shard_chain/src/shard_chain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 4193df7b1d3..7a19b04d564 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -35,7 +35,7 @@ pub trait ShardChainTypes { pub struct ShardChain { pub parent_beacon: Arc>, pub shard: Shard, - pub spec: ChaitnSpec, + pub spec: ChainSpec, /// Persistent storage for blocks, states, etc. Typically an on-disk store, such as LevelDB. pub store: Arc, /// Reports the current slot, typically based upon the system clock. From 0a331cb404a8d2606ac82b91366b866904f657f0 Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Wed, 21 Aug 2019 15:24:39 +0200 Subject: [PATCH 021/151] Updated to calculate a period committee :) --- eth2/types/src/beacon_state.rs | 27 +++++++++++++++++++ .../types/src/beacon_state/committee_cache.rs | 16 +++++++++++ eth2/types/src/chain_spec.rs | 1 + 3 files changed, 44 insertions(+) diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index 40054f997a2..9990f9ecc87 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -295,6 +295,12 @@ impl BeaconState { Ok(cache.epoch_committee_count() as u64) } + pub fn get_period_committee_count(&self, relative_epoch: RelativeEpoch) -> Result { + let cache = self.cache(relative_epoch)?; + + Ok(cache.period_committee_count() as u64) + } + pub fn get_epoch_start_shard(&self, relative_epoch: RelativeEpoch) -> Result { let cache = self.cache(relative_epoch)?; @@ -394,6 +400,27 @@ impl BeaconState { Ok(committee) } + + pub fn get_persistent_committee_for_shard( + &self, + shard: u64, + relative_epoch: RelativeEpoch, + ) -> Result { + let cache = self.cache(relative_epoch)?; + + let committee = cache + .get_period_committee_for_shard(shard) + .ok_or_else(|| Error::NoCommitteeForShard)?; + + Ok(committee) + } + + + pub fn get_beacon_proposer_index(){ + + } + + /// Returns the beacon proposer index for the `slot` in the given `relative_epoch`. /// /// Spec v0.6.3 diff --git a/eth2/types/src/beacon_state/committee_cache.rs b/eth2/types/src/beacon_state/committee_cache.rs index 54564d95d58..6d124b4bbc9 100644 --- a/eth2/types/src/beacon_state/committee_cache.rs +++ b/eth2/types/src/beacon_state/committee_cache.rs @@ -18,6 +18,7 @@ pub struct CommitteeCache { shuffling_start_shard: u64, shard_count: u64, committee_count: usize, + period_committee_count: usize, slots_per_epoch: u64, } @@ -50,6 +51,11 @@ impl CommitteeCache { spec.target_committee_size, ) as usize; + let period_committee_count = T::get_epoch_committee_count( + active_validator_indices.len(), + spec.target_period_committee_size, + ) as usize; + let shuffling_start_shard = Self::compute_start_shard(state, relative_epoch, active_validator_indices.len(), spec); @@ -79,6 +85,7 @@ impl CommitteeCache { shuffling, shard_count: T::shard_count() as u64, committee_count, + period_committee_count, slots_per_epoch: T::slots_per_epoch(), shuffling_positions, }) @@ -165,6 +172,15 @@ impl CommitteeCache { }) } + pub fn get_period_committee_for_shard(&self, shard: Shard) -> Option { + let committee = self.get_crosslink_committee_for_shard(shard)?.into_owned(); + + Some(PeriodCommittee { + shard, + committee[:self.period_committee_count], + }) + } + /// Returns the `AttestationDuty` for the given `validator_index`. /// /// Returns `None` if the `validator_index` does not exist, does not have duties or `Self` is diff --git a/eth2/types/src/chain_spec.rs b/eth2/types/src/chain_spec.rs index 81b53e1e232..875c0d53b11 100644 --- a/eth2/types/src/chain_spec.rs +++ b/eth2/types/src/chain_spec.rs @@ -147,6 +147,7 @@ impl ChainSpec { * Misc */ target_committee_size: 128, + target_period_committee_size: 128, max_indices_per_attestation: 4096, min_per_epoch_churn_limit: 4, churn_limit_quotient: 65_536, From 0075869d8f0f9510c7ea1a09f00c4bf26319de8e Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Wed, 21 Aug 2019 16:15:57 +0200 Subject: [PATCH 022/151] Setup function for proposer index --- eth2/types/src/beacon_state.rs | 40 +++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index 9990f9ecc87..b377b2989cf 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -415,12 +415,6 @@ impl BeaconState { Ok(committee) } - - pub fn get_beacon_proposer_index(){ - - } - - /// Returns the beacon proposer index for the `slot` in the given `relative_epoch`. /// /// Spec v0.6.3 @@ -458,15 +452,41 @@ impl BeaconState { }) } - // NEED TO ADD + /// Returns the shard proposer index for the given `relative_epoch` pub fn get_shard_proposer_index() -> { &self, - shard: Shard, - slot: Slot, relative_epoch: RelativeEpoch, + shard: Shard, + slot: ShardSlot, spec: &ChainSpec, ) -> Result { - // needs implementation + let cache = self.cache(relative_epoch)?; + let epoch = relative_epoch.into_epoch(self.current_epoch()); + + let committee = cache + .get_period_committee_for_shard(shard) + .ok_or_else(|| Error::SlotOutOfBounds)?; + let seed = self.generate_seed(epoch, spec)?; + + let mut i = 0; + Ok(loop { + let candidate_index = committee[(slot.as_usize() + i) % committee.len()]; + let random_byte = { + let mut preimage = seed.as_bytes().to_vec(); + preimage.append(&mut int_to_bytes8((i / 32) as u64)); + preimage.append(&mut int_to_bytes8(shard as u64)); + preimage.append(&mut int_to_bytes8(slot as u64)); + let hash = hash(&preimage); + hash[i % 32] + }; + let effective_balance = self.validator_registry[candidate_index].effective_balance; + if (effective_balance * MAX_RANDOM_BYTE) + >= (spec.max_effective_balance * u64::from(random_byte)) + { + break candidate_index; + } + i += 1; + }) } /// Safely obtains the index for latest block roots, given some `slot`. From 7a739d23b509f156f911db5993a8bb89481b37da Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Fri, 23 Aug 2019 11:47:47 +0200 Subject: [PATCH 023/151] Removed specific shard committee logic and pass through an argument for the period committee --- eth2/types/src/beacon_state/committee_cache.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/eth2/types/src/beacon_state/committee_cache.rs b/eth2/types/src/beacon_state/committee_cache.rs index 6d124b4bbc9..2df04fec275 100644 --- a/eth2/types/src/beacon_state/committee_cache.rs +++ b/eth2/types/src/beacon_state/committee_cache.rs @@ -18,7 +18,6 @@ pub struct CommitteeCache { shuffling_start_shard: u64, shard_count: u64, committee_count: usize, - period_committee_count: usize, slots_per_epoch: u64, } @@ -51,11 +50,6 @@ impl CommitteeCache { spec.target_committee_size, ) as usize; - let period_committee_count = T::get_epoch_committee_count( - active_validator_indices.len(), - spec.target_period_committee_size, - ) as usize; - let shuffling_start_shard = Self::compute_start_shard(state, relative_epoch, active_validator_indices.len(), spec); @@ -172,12 +166,12 @@ impl CommitteeCache { }) } - pub fn get_period_committee_for_shard(&self, shard: Shard) -> Option { + pub fn get_period_committee_for_shard(&self, shard: Shard, committee_size: u64) -> Option { let committee = self.get_crosslink_committee_for_shard(shard)?.into_owned(); Some(PeriodCommittee { shard, - committee[:self.period_committee_count], + committee[:committee_size], }) } From d8902e3568bf7081c04d760811733dad40b592c1 Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Fri, 23 Aug 2019 12:02:19 +0200 Subject: [PATCH 024/151] added additional phase 1 required variables/values --- eth2/types/src/chain_spec.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/eth2/types/src/chain_spec.rs b/eth2/types/src/chain_spec.rs index 875c0d53b11..284a05877c1 100644 --- a/eth2/types/src/chain_spec.rs +++ b/eth2/types/src/chain_spec.rs @@ -191,6 +191,13 @@ impl ChainSpec { max_crosslink_epochs: 64, min_epochs_to_inactivity_penalty: 4, + /* + * Phase 1 specific values, fork epoch and slot are hardcoded to values for now + */ + epochs_per_shard_period: 256, + phase_1_fork_epoch: 600, + phase_1_fork_slot: 38_400, + /* * Reward and penalty quotients */ From 03411219d413ee35a2f51beaf4894403f1cc5599 Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Fri, 23 Aug 2019 12:07:05 +0200 Subject: [PATCH 025/151] Include lookahead on slot to epcoh calculation --- eth2/types/src/slot_epoch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth2/types/src/slot_epoch.rs b/eth2/types/src/slot_epoch.rs index 689b6b844a8..638741f5e49 100644 --- a/eth2/types/src/slot_epoch.rs +++ b/eth2/types/src/slot_epoch.rs @@ -47,7 +47,7 @@ impl ShardSlot { Epoch::from(self.0 / slots_per_epoch / slots_per_beacon_slot) } - pub fn shard_period_start_epoch(self, slots_per_epoch: u64, slots_per_beacon_slot: u64, epochs_per_shard_period: u64) -> Epoch { + pub fn shard_period_start_epoch(self, slots_per_epoch: u64, slots_per_beacon_slot: u64, epochs_per_shard_period: u64, lookback: u64) -> Epoch { let epoch = self::Epoch(slots_per_epoch, slots_per_beacon_slot) Epoch::from(epoch - (epoch % epochs_per_shard_period) - lookback * epochs_per_shard_period) } From abf4aec5704f6031bdf5e03756756f89d96d239d Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Fri, 23 Aug 2019 12:57:46 +0200 Subject: [PATCH 026/151] Drastically simplify shard state --- eth2/types/src/shard_state.rs | 120 +++++----------------------------- 1 file changed, 17 insertions(+), 103 deletions(-) diff --git a/eth2/types/src/shard_state.rs b/eth2/types/src/shard_state.rs index 7347a4134e4..065d77a1fec 100644 --- a/eth2/types/src/shard_state.rs +++ b/eth2/types/src/shard_state.rs @@ -62,24 +62,25 @@ where T: EthSpec, { // Misc - pub slot: Slot, - pub genesis_time: u64, + pub slot: ShardSlot, pub fork: Fork, - // Attestations - split by epoch necessary or just from latest finalized crosslink? - // Going to need a way to request the attestation properly by epoch... play with this later - pub running_attestations: Vec, + // Attestations - Update to bitfield but simply one attestation included + pub attestation: ShardPendingAttestation, // Latest beacon root needed - // Latest crosslinks not needed since it will only duplicate data accessed via beacon roots pub beacon_root: Hash256, - // Recent state - pub latest_block_roots: FixedLenVec, - #[compare_fields(as_slice)] - pub latest_state_roots: FixedLenVec, - pub latest_block_header: ShardBlockHeader, - pub historical_roots: Vec, + // Parent root + pub parent_root: Hash256, + + // Caching (not in the spec) + #[serde(default)] + #[ssz(skip_serializing)] + #[ssz(skip_deserializing)] + #[tree_hash(skip_hashing)] + #[test_random(default)] + pub committee_caches: [CommitteeCache; CACHED_EPOCHS], } impl ShardState { @@ -100,8 +101,8 @@ impl ShardState { genesis_time, fork: Fork::genesis(T::genesis_epoch()), - // Attestations - running_attestations: vec![], + // Attestation + attestation: vec![], // Recent state latest_block_roots: vec![spec.zero_hash; T::SlotsPerHistoricalRoot::to_usize()].into(), @@ -130,102 +131,15 @@ impl ShardState { } } - /// Get the slot of an attestation. - /// Adds logic to iterate through latest block roots and match to the attestation data - /// - /// Spec v0.6.3 - pub fn get_attestation_slot(&self, attestation_data: &ShardAttestationData) -> Result { - let block_root = attestation_data.shard_block_root - - for (i, root) in self.latest_block_roots.iter().enumerate() { - if root == block_root { - // calculate logic here - Ok(slot) - } - } - - Ok(slot) - } - - /// Safely obtains the index for latest block roots, given some `slot`. - /// - /// Spec v0.6.3 - fn get_latest_block_roots_index(&self, slot: Slot) -> Result { - if (slot < self.slot) && (self.slot <= slot + self.latest_block_roots.len() as u64) { - Ok(slot.as_usize() % self.latest_block_roots.len()) - } else { - // Update proper error here - Err(ShardStateError::SlotOutOfBounds) - } - } - - /// Return the block root at a recent `slot`. - /// - /// Spec v0.6.3 - pub fn get_block_root(&self, slot: Slot) -> Result<&Hash256, ShardStateError> { - let i = self.get_latest_block_roots_index(slot)?; - Ok(&self.latest_block_roots[i]) - } - - /// Sets the block root for some given slot. - /// - /// Spec v0.6.3 - pub fn set_block_root( - &mut self, - slot: Slot, - block_root: Hash256, - ) -> Result<(), ShardStateError> { - let i = self.get_latest_block_roots_index(slot)?; - self.latest_block_roots[i] = block_root; - Ok(()) - } - - /// Safely obtains the index for latest state roots, given some `slot`. - /// - /// Spec v0.6.3 - fn get_latest_state_roots_index(&self, slot: Slot) -> Result { - if (slot < self.slot) && (self.slot <= slot + Slot::from(self.latest_state_roots.len())) { - Ok(slot.as_usize() % self.latest_state_roots.len()) - } else { - Err(ShardStateError::SlotOutOfBounds) - } - } - - /// Gets the state root for some slot. - /// - /// Spec v0.6.3 - pub fn get_state_root(&self, slot: Slot) -> Result<&Hash256, Error> { - let i = self.get_latest_state_roots_index(slot)?; - Ok(&self.latest_state_roots[i]) - } - - /// Gets the oldest (earliest slot) state root. - /// - /// Spec v0.6.3 - pub fn get_oldest_state_root(&self) -> Result<&Hash256, Error> { - let i = self - .get_latest_state_roots_index(self.slot - Slot::from(self.latest_state_roots.len()))?; - Ok(&self.latest_state_roots[i]) - } - - /// Sets the latest state root for slot. - /// - /// Spec v0.6.3 - pub fn set_state_root(&mut self, slot: Slot, state_root: Hash256) -> Result<(), Error> { - let i = self.get_latest_state_roots_index(slot)?; - self.latest_state_roots[i] = state_root; - Ok(()) - } - /// Do we need this since the tree hash really is just the balance set of everyone? /// Build all the caches, if they need to be built. - pub fn build_all_caches(&mut self, spec: &ChainSpec) -> Result<(), Error> { + pub fn build_cache(&mut self, spec: &ChainSpec) -> Result<(), Error> { self.update_tree_hash_cache()?; Ok(()) } /// Drop all caches on the state. - pub fn drop_all_caches(&mut self) { + pub fn drop_cache(&mut self) { self.drop_tree_hash_cache(); } From 757d17bddedd9776cc120a1b5b1027a838e92209 Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Fri, 23 Aug 2019 13:08:51 +0200 Subject: [PATCH 027/151] Simplify shard eth 2 types --- eth2/types/src/shard_block.rs | 4 ++-- eth2/types/src/shard_block_body.rs | 8 ++++---- eth2/types/src/shard_block_header.rs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/eth2/types/src/shard_block.rs b/eth2/types/src/shard_block.rs index 73cae803f9b..1a2f7617d83 100644 --- a/eth2/types/src/shard_block.rs +++ b/eth2/types/src/shard_block.rs @@ -8,7 +8,7 @@ use test_random_derive::TestRandom; use tree_hash::{SignedRoot, TreeHash}; use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash}; -/// A block of the `BeaconChain`. +/// A block of the `ShardChain`. /// /// Spec v0.6.3 #[derive( @@ -25,7 +25,7 @@ use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash}; SignedRoot, )] pub struct ShardBlock { - pub slot: Slot, + pub slot: ShardSlot, pub previous_block_root: Hash256, pub state_root: Hash256, pub body: ShardBlockBody, diff --git a/eth2/types/src/shard_block_body.rs b/eth2/types/src/shard_block_body.rs index 897ee8a7afa..7505961b478 100644 --- a/eth2/types/src/shard_block_body.rs +++ b/eth2/types/src/shard_block_body.rs @@ -6,7 +6,7 @@ use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::{CachedTreeHash, TreeHash}; -/// The body of a `BeaconChain` block, containing operations. +/// The body of a `ShardChain` block, containing operations. /// /// Spec v0.6.3 #[derive( @@ -24,13 +24,13 @@ use tree_hash_derive::{CachedTreeHash, TreeHash}; pub struct ShardBlockBody { #[serde(deserialize_with = "graffiti_from_hex_str")] pub graffiti: [u8; 32], - pub attestations: Vec, + pub attestation: ShardAttestation, } #[cfg(test)] mod tests { use super::*; - ssz_tests!(BeaconBlockBody); - cached_tree_hash_tests!(BeaconBlockBody); + ssz_tests!(ShardBlockBody); + cached_tree_hash_tests!(ShardBlockBody); } diff --git a/eth2/types/src/shard_block_header.rs b/eth2/types/src/shard_block_header.rs index 779b88899c5..04b8f0a5182 100644 --- a/eth2/types/src/shard_block_header.rs +++ b/eth2/types/src/shard_block_header.rs @@ -25,7 +25,7 @@ use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash}; SignedRoot, )] pub struct ShardBlockHeader { - pub slot: Slot, + pub slot: ShardSlot, pub previous_block_root: Hash256, pub state_root: Hash256, pub block_body_root: Hash256, From 6b57c9fc0eed5e26bae70b956c30be1ab0e348a1 Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Fri, 23 Aug 2019 13:32:14 +0200 Subject: [PATCH 028/151] Fill in persistent committee/stored committee logic --- eth2/types/src/shard_state.rs | 40 +++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/eth2/types/src/shard_state.rs b/eth2/types/src/shard_state.rs index 065d77a1fec..76920ad6af0 100644 --- a/eth2/types/src/shard_state.rs +++ b/eth2/types/src/shard_state.rs @@ -63,7 +63,6 @@ where { // Misc pub slot: ShardSlot, - pub fork: Fork, // Attestations - Update to bitfield but simply one attestation included pub attestation: ShardPendingAttestation, @@ -80,7 +79,7 @@ where #[ssz(skip_deserializing)] #[tree_hash(skip_hashing)] #[test_random(default)] - pub committee_caches: [CommitteeCache; CACHED_EPOCHS], + pub committees: [PeriodCommittee; 3], } impl ShardState { @@ -97,23 +96,20 @@ impl ShardState { ShardState { // Misc slot: spec.genesis_slot, - // will this genesis time not matter - can we just pull from the spec? - genesis_time, fork: Fork::genesis(T::genesis_epoch()), // Attestation - attestation: vec![], + attestation: ShardPendingAttestation::default(), - // Recent state - latest_block_roots: vec![spec.zero_hash; T::SlotsPerHistoricalRoot::to_usize()].into(), - latest_state_roots: vec![spec.zero_hash; T::SlotsPerHistoricalRoot::to_usize()].into(), - latest_block_header: ShardBlock::empty(spec).temporary_block_header(spec), - historical_roots: vec![], + // Roots + beacon_root: Hash256::default(), + parent_root: Hash256::default(), /* - * Caching (not in spec) + * Cacheing (not in spec) */ tree_hash_cache: TreeHashCache::default(), + committees: [PeriodCommittee::default(); 3], } } @@ -124,11 +120,23 @@ impl ShardState { Hash256::from_slice(&self.tree_hash_root()[..]) } - pub fn historical_batch(&self) -> HistoricalBatch { - HistoricalBatch { - block_roots: self.latest_block_roots.clone(), - state_roots: self.latest_state_roots.clone(), - } + pub fn get_earlier_committee(&self) -> PeriodCommittee { + self.committees[0] + } + + pub fn get_later_committee(&self) -> PeriodCommittee { + self.committees[1] + } + + pub fn get_next_committee(&self) -> PeriodCommittee { + self.committees[2] + } + + pub fn get_persistent_committee(&self) -> PersistentCommittee { + let earlier_committee = self.get_earlier_committee(); + let later_committee = self.get_later_committee(); + + // finish logic here - fairly simple } /// Do we need this since the tree hash really is just the balance set of everyone? From 3c994bd737c67b93465b9d03d52fde888c2e381c Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Fri, 23 Aug 2019 13:41:03 +0200 Subject: [PATCH 029/151] Persistent committe data structure (maybe remove later) --- eth2/types/src/period_committee.rs | 9 ++++----- eth2/types/src/persistent_committee.rs | 25 +++++++++++++++++++++++++ eth2/types/src/shard_state.rs | 7 +++++-- 3 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 eth2/types/src/persistent_committee.rs diff --git a/eth2/types/src/period_committee.rs b/eth2/types/src/period_committee.rs index 23e23267ec8..d0ce2e46b6b 100644 --- a/eth2/types/src/period_committee.rs +++ b/eth2/types/src/period_committee.rs @@ -1,9 +1,8 @@ use crate::*; -use tree_hash_derive::{CachedTreeHash, TreeHash}; -#[derive(Default, Clone, Debug, PartialEq, TreeHash, CachedTreeHash)] +#[derive(Default, Clone, Debug, PartialEq)] pub struct PeriodCommittee<'a> { - pub slot: Slot, + pub epoch: Epoch, pub shard: Shard, pub committee: &'a [usize], } @@ -11,7 +10,7 @@ pub struct PeriodCommittee<'a> { impl<'a> PeriodCommittee<'a> { pub fn into_owned(self) -> OwnedPeriodCommittee { OwnedPeriodCommittee { - slot: self.slot, + epoch: self.epoch, shard: self.shard, committee: self.committee.to_vec(), } @@ -20,7 +19,7 @@ impl<'a> PeriodCommittee<'a> { #[derive(Default, Clone, Debug, PartialEq, TreeHash, CachedTreeHash)] pub struct OwnedPeriodCommittee { - pub slot: Slot, + pub epoch: Epoch, pub shard: Shard, pub committee: Vec, } diff --git a/eth2/types/src/persistent_committee.rs b/eth2/types/src/persistent_committee.rs new file mode 100644 index 00000000000..00f064300f2 --- /dev/null +++ b/eth2/types/src/persistent_committee.rs @@ -0,0 +1,25 @@ +use crate::*; + +#[derive(Default, Clone, Debug, PartialEq)] +pub struct PersistentCommittee<'a> { + pub epoch: Epoch, + pub shard: Shard, + pub committee: &'a [usize], +} + +impl<'a> PersistentCommittee<'a> { + pub fn into_owned(self) -> OwnedPeriodCommittee { + OwnedPeriodCommittee { + epoch: self.epoch, + shard: self.shard, + committee: self.committee.to_vec(), + } + } +} + +#[derive(Default, Clone, Debug, PartialEq, TreeHash, CachedTreeHash)] +pub struct OwnedPersistentCommittee { + pub slot: Epoch, + pub shard: Shard, + pub committee: Vec, +} diff --git a/eth2/types/src/shard_state.rs b/eth2/types/src/shard_state.rs index 76920ad6af0..a4de9f87084 100644 --- a/eth2/types/src/shard_state.rs +++ b/eth2/types/src/shard_state.rs @@ -133,9 +133,12 @@ impl ShardState { } pub fn get_persistent_committee(&self) -> PersistentCommittee { - let earlier_committee = self.get_earlier_committee(); - let later_committee = self.get_later_committee(); + let earlier_committee = self.get_earlier_committee().to_owned().committee; + let later_committee = self.get_later_committee().to_owned().committee; + let persistent_committee_indexes = { + // loop through properly + } // finish logic here - fairly simple } From dfdd108ae2adb3abc0136d34ef7b74dbdeab36e5 Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Fri, 23 Aug 2019 13:52:11 +0200 Subject: [PATCH 030/151] For now keep the get_shard_proposer function in the beacon state --- eth2/types/src/beacon_state.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index b377b2989cf..bfd20da525c 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -390,7 +390,7 @@ impl BeaconState { &self, shard: u64, relative_epoch: RelativeEpoch, - ) -> Result { + ) -> Result { let cache = self.cache(relative_epoch)?; let committee = cache @@ -401,7 +401,7 @@ impl BeaconState { } - pub fn get_persistent_committee_for_shard( + pub fn get_period_committee_for_shard( &self, shard: u64, relative_epoch: RelativeEpoch, @@ -458,14 +458,10 @@ impl BeaconState { relative_epoch: RelativeEpoch, shard: Shard, slot: ShardSlot, + committee: PersistentCommittee spec: &ChainSpec, ) -> Result { - let cache = self.cache(relative_epoch)?; let epoch = relative_epoch.into_epoch(self.current_epoch()); - - let committee = cache - .get_period_committee_for_shard(shard) - .ok_or_else(|| Error::SlotOutOfBounds)?; let seed = self.generate_seed(epoch, spec)?; let mut i = 0; From 7bc5a5018d94166a5009d9b35c6d6f75ac233136 Mon Sep 17 00:00:00 2001 From: William Villanueva Date: Fri, 23 Aug 2019 21:57:56 +0200 Subject: [PATCH 031/151] compiler error part 1 - rust noob --- beacon_node/beacon_chain/src/beacon_chain.rs | 108 +++++++++--------- .../src/common/get_attesting_indices.rs | 2 +- eth2/types/src/beacon_state.rs | 94 +++++++-------- .../types/src/beacon_state/committee_cache.rs | 8 +- eth2/types/src/chain_spec.rs | 9 ++ eth2/types/src/lib.rs | 6 +- eth2/types/src/period_committee.rs | 2 +- eth2/types/src/persistent_committee.rs | 8 +- eth2/types/src/slot_epoch.rs | 10 +- eth2/types/src/slot_height.rs | 13 ++- 10 files changed, 138 insertions(+), 122 deletions(-) diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index e865d31b2aa..8dab8d13cb2 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -243,7 +243,7 @@ impl BeaconChain { /// genesis. /// /// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. - pub fn f(&self, slot: Slot) -> StateRootsIterator { + pub fn rev_iter_state_roots(&self, slot: Slot) -> StateRootsIterator { StateRootsIterator::owned(self.store.clone(), self.state.read().clone(), slot) } @@ -927,63 +927,63 @@ impl BeaconChain { } // Everything from this point on will techincally be moved into a trait - pub fn get_persistent_committee_at_block(&self, beacon_block_root: &Hash256, shard: Shard) -> Result { - let block: BeaconBlock = match self.store.get(&beacon_block_root)? { - Some(block_root) => block_root, - None => { - } - }; - let state_root = block.state_root; - let block_state = self - .store - .get(&state_root)? - .ok_or_else(|| Error::DBInconsistent(format!("Missing state {}", state_root)))?; - - let mut state: BeaconState = block_state; - state.get_crosslink_committee_for_shard(shard, state.current_epoch()) - } - - pub fn get_persistent_crosslink_committee(&self, shard: Shard) -> Result { - let current_state = self.current_state(); - current_state.get_crosslink_committee_for_shard(shard, current_state.current_epoch()) - } - - pub fn shard_block_proposer(&self, slot: Slot, shard: Shard) -> Result { - self.catchup_state()?; - let index = self.state.read().get_shard_proposer_index( - slot, - shard, - RelativeEpoch::Current, - &self.spec, - )?; - - Ok(index) - } + // pub fn get_persistent_committee_at_block(&self, beacon_block_root: &Hash256, shard: Shard) -> Result { + // let block: BeaconBlock = match self.store.get(&beacon_block_root)? { + // Some(block_root) => block_root, + // None => { + // } + // }; + // let state_root = block.state_root; + // let block_state = self + // .store + // .get(&state_root)? + // .ok_or_else(|| Error::DBInconsistent(format!("Missing state {}", state_root)))?; + + // let mut state: BeaconState = block_state; + // state.get_crosslink_committee_for_shard(shard, state.current_epoch()) + // } + + // pub fn get_persistent_crosslink_committee(&self, shard: Shard) -> Result { + // let current_state = self.current_state(); + // current_state.get_crosslink_committee_for_shard(shard, current_state.current_epoch()) + // } + + // pub fn shard_block_proposer(&self, slot: Slot, shard: Shard) -> Result { + // self.catchup_state()?; + // let index = self.state.read().get_shard_proposer_index( + // slot, + // shard, + // RelativeEpoch::Current, + // &self.spec, + // )?; + + // Ok(index) + // } // need to support lookup by epoch - pub fn get_current_crosslink(&self, shard: u64) -> Result<&Crosslink, Error> { - self.current_state().get_current_crosslink(shard) - } + // pub fn get_current_crosslink(&self, shard: u64) -> Result<&Crosslink, Error> { + // self.current_state().get_current_crosslink(shard) + // } // need to support lookup by epoch - pub fn get_finalized_crosslink(&self, shard: Shard, epoch: Epoch) -> { - let current_state = self.current_state(); - let finalized_block_root = current_state.finalized_block_root; - - let finalized_block: BeaconBlock = match self.store.get(&finalized_block_root)? { - Some(block_root) => block_root, - None => { - } - }; - let state_root = finalized_block.state_root; - let finalized_state_root = self - .store - .get(&state_root)? - .ok_or_else(|| Error::DBInconsistent(format!("Missing state {}", state_root)))?; - - let mut state: BeaconState = finalized_state_root; - finalized_state_root().get_current_crosslink(shard) - } + // pub fn get_finalized_crosslink(&self, shard: Shard, epoch: Epoch) -> { + // let current_state = self.current_state(); + // let finalized_block_root = current_state.finalized_block_root; + + // let finalized_block: BeaconBlock = match self.store.get(&finalized_block_root)? { + // Some(block_root) => block_root, + // None => { + // } + // }; + // let state_root = finalized_block.state_root; + // let finalized_state_root = self + // .store + // .get(&state_root)? + // .ok_or_else(|| Error::DBInconsistent(format!("Missing state {}", state_root)))?; + + // let mut state: BeaconState = finalized_state_root; + // finalized_state_root().get_current_crosslink(shard) + // } } impl From for Error { diff --git a/eth2/state_processing/src/common/get_attesting_indices.rs b/eth2/state_processing/src/common/get_attesting_indices.rs index fe047d77ee2..6d9c824d7d4 100644 --- a/eth2/state_processing/src/common/get_attesting_indices.rs +++ b/eth2/state_processing/src/common/get_attesting_indices.rs @@ -52,7 +52,7 @@ pub fn get_shard_attesting_indices( attestation_data: &AttestationData, bitfield: &Bitfield, ) -> Result, BeaconStateError> { - get_attesting_indices_unsorted(shard, state, attestation_data, bitfield).map(|mut indices| { + get_shard_attesting_indices_unsorted(shard, state, attestation_data, bitfield).map(|mut indices| { // Fast unstable sort is safe because validator indices are unique indices.sort_unstable(); indices diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index bfd20da525c..584f2a4b98c 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -295,11 +295,11 @@ impl BeaconState { Ok(cache.epoch_committee_count() as u64) } - pub fn get_period_committee_count(&self, relative_epoch: RelativeEpoch) -> Result { - let cache = self.cache(relative_epoch)?; + // pub fn get_period_committee_count(&self, relative_epoch: RelativeEpoch) -> Result { + // let cache = self.cache(relative_epoch)?; - Ok(cache.period_committee_count() as u64) - } + // Ok(cache.period_committee_count() as u64) + // } pub fn get_epoch_start_shard(&self, relative_epoch: RelativeEpoch) -> Result { let cache = self.cache(relative_epoch)?; @@ -390,7 +390,7 @@ impl BeaconState { &self, shard: u64, relative_epoch: RelativeEpoch, - ) -> Result { + ) -> Result { let cache = self.cache(relative_epoch)?; let committee = cache @@ -401,19 +401,19 @@ impl BeaconState { } - pub fn get_period_committee_for_shard( - &self, - shard: u64, - relative_epoch: RelativeEpoch, - ) -> Result { - let cache = self.cache(relative_epoch)?; + // pub fn get_period_committee_for_shard( + // &self, + // shard: u64, + // relative_epoch: RelativeEpoch, + // ) -> Result { + // let cache = self.cache(relative_epoch)?; - let committee = cache - .get_period_committee_for_shard(shard) - .ok_or_else(|| Error::NoCommitteeForShard)?; + // let committee = cache + // .get_period_committee_for_shard(shard) + // .ok_or_else(|| Error::NoCommitteeForShard)?; - Ok(committee) - } + // Ok(committee) + // } /// Returns the beacon proposer index for the `slot` in the given `relative_epoch`. /// @@ -453,37 +453,37 @@ impl BeaconState { } /// Returns the shard proposer index for the given `relative_epoch` - pub fn get_shard_proposer_index() -> { - &self, - relative_epoch: RelativeEpoch, - shard: Shard, - slot: ShardSlot, - committee: PersistentCommittee - spec: &ChainSpec, - ) -> Result { - let epoch = relative_epoch.into_epoch(self.current_epoch()); - let seed = self.generate_seed(epoch, spec)?; - - let mut i = 0; - Ok(loop { - let candidate_index = committee[(slot.as_usize() + i) % committee.len()]; - let random_byte = { - let mut preimage = seed.as_bytes().to_vec(); - preimage.append(&mut int_to_bytes8((i / 32) as u64)); - preimage.append(&mut int_to_bytes8(shard as u64)); - preimage.append(&mut int_to_bytes8(slot as u64)); - let hash = hash(&preimage); - hash[i % 32] - }; - let effective_balance = self.validator_registry[candidate_index].effective_balance; - if (effective_balance * MAX_RANDOM_BYTE) - >= (spec.max_effective_balance * u64::from(random_byte)) - { - break candidate_index; - } - i += 1; - }) - } + // pub fn get_shard_proposer_index() -> { + // &self, + // relative_epoch: RelativeEpoch, + // shard: Shard, + // slot: ShardSlot, + // committee: PersistentCommittee + // spec: &ChainSpec, + // ) -> Result { + // let epoch = relative_epoch.into_epoch(self.current_epoch()); + // let seed = self.generate_seed(epoch, spec)?; + + // let mut i = 0; + // Ok(loop { + // let candidate_index = committee[(slot.as_usize() + i) % committee.len()]; + // let random_byte = { + // let mut preimage = seed.as_bytes().to_vec(); + // preimage.append(&mut int_to_bytes8((i / 32) as u64)); + // preimage.append(&mut int_to_bytes8(shard as u64)); + // preimage.append(&mut int_to_bytes8(slot as u64)); + // let hash = hash(&preimage); + // hash[i % 32] + // }; + // let effective_balance = self.validator_registry[candidate_index].effective_balance; + // if (effective_balance * MAX_RANDOM_BYTE) + // >= (spec.max_effective_balance * u64::from(random_byte)) + // { + // break candidate_index; + // } + // i += 1; + // }) + // } /// Safely obtains the index for latest block roots, given some `slot`. /// diff --git a/eth2/types/src/beacon_state/committee_cache.rs b/eth2/types/src/beacon_state/committee_cache.rs index 2df04fec275..fce104ddb36 100644 --- a/eth2/types/src/beacon_state/committee_cache.rs +++ b/eth2/types/src/beacon_state/committee_cache.rs @@ -79,7 +79,6 @@ impl CommitteeCache { shuffling, shard_count: T::shard_count() as u64, committee_count, - period_committee_count, slots_per_epoch: T::slots_per_epoch(), shuffling_positions, }) @@ -166,12 +165,13 @@ impl CommitteeCache { }) } - pub fn get_period_committee_for_shard(&self, shard: Shard, committee_size: u64) -> Option { - let committee = self.get_crosslink_committee_for_shard(shard)?.into_owned(); + pub fn get_period_committee_for_shard(&self, shard: Shard, committee_size: u64) -> Option { + let committee = self.get_crosslink_committee_for_shard(shard)?.committee; Some(PeriodCommittee { shard, - committee[:committee_size], + epoch: self.initialized_epoch?, + committee: &committee[..committee_size as usize], }) } diff --git a/eth2/types/src/chain_spec.rs b/eth2/types/src/chain_spec.rs index 284a05877c1..6749b620199 100644 --- a/eth2/types/src/chain_spec.rs +++ b/eth2/types/src/chain_spec.rs @@ -27,6 +27,7 @@ pub struct ChainSpec { * Misc */ pub target_committee_size: usize, + pub target_period_committee_size: usize, pub max_indices_per_attestation: u64, pub min_per_epoch_churn_limit: u64, pub churn_limit_quotient: u64, @@ -73,6 +74,13 @@ pub struct ChainSpec { pub max_crosslink_epochs: u64, pub min_epochs_to_inactivity_penalty: u64, + /* + * Phase 1 specific values, fork epoch and slot are hardcoded to values for now + */ + epochs_per_shard_period: u64, + phase_1_fork_epoch: u64, + phase_1_fork_slot: u64, + /* * Reward and penalty quotients */ @@ -250,6 +258,7 @@ impl ChainSpec { Self { target_committee_size: 4, + target_period_committee_size: 4, shuffle_round_count: 10, min_attestation_inclusion_delay: 2, slots_per_eth1_voting_period: 16, diff --git a/eth2/types/src/lib.rs b/eth2/types/src/lib.rs index 2406c3a18e1..7637f078737 100644 --- a/eth2/types/src/lib.rs +++ b/eth2/types/src/lib.rs @@ -23,6 +23,8 @@ pub mod free_attestation; pub mod historical_batch; pub mod indexed_attestation; pub mod pending_attestation; +pub mod period_committee; +pub mod persistent_committee; pub mod proposer_slashing; pub mod transfer; pub mod voluntary_exit; @@ -56,10 +58,12 @@ pub use crate::free_attestation::FreeAttestation; pub use crate::historical_batch::HistoricalBatch; pub use crate::indexed_attestation::IndexedAttestation; pub use crate::pending_attestation::PendingAttestation; +pub use crate::period_committee::PeriodCommittee; +pub use crate::persistent_committee::PersistentCommittee; pub use crate::proposer_slashing::ProposerSlashing; pub use crate::relative_epoch::{Error as RelativeEpochError, RelativeEpoch}; pub use crate::slot_epoch::{Epoch, Slot}; -pub use crate::slot_height::SlotHeight; +pub use crate::slot_height::{SlotHeight, ShardSlotHeight}; pub use crate::transfer::Transfer; pub use crate::validator::Validator; pub use crate::voluntary_exit::VoluntaryExit; diff --git a/eth2/types/src/period_committee.rs b/eth2/types/src/period_committee.rs index d0ce2e46b6b..f79540c171d 100644 --- a/eth2/types/src/period_committee.rs +++ b/eth2/types/src/period_committee.rs @@ -17,7 +17,7 @@ impl<'a> PeriodCommittee<'a> { } } -#[derive(Default, Clone, Debug, PartialEq, TreeHash, CachedTreeHash)] +#[derive(Default, Clone, Debug, PartialEq)] pub struct OwnedPeriodCommittee { pub epoch: Epoch, pub shard: Shard, diff --git a/eth2/types/src/persistent_committee.rs b/eth2/types/src/persistent_committee.rs index 00f064300f2..da5ebdb3d8a 100644 --- a/eth2/types/src/persistent_committee.rs +++ b/eth2/types/src/persistent_committee.rs @@ -8,8 +8,8 @@ pub struct PersistentCommittee<'a> { } impl<'a> PersistentCommittee<'a> { - pub fn into_owned(self) -> OwnedPeriodCommittee { - OwnedPeriodCommittee { + pub fn into_owned(self) -> OwnedPersistentCommittee { + OwnedPersistentCommittee { epoch: self.epoch, shard: self.shard, committee: self.committee.to_vec(), @@ -17,9 +17,9 @@ impl<'a> PersistentCommittee<'a> { } } -#[derive(Default, Clone, Debug, PartialEq, TreeHash, CachedTreeHash)] +#[derive(Default, Clone, Debug, PartialEq)] pub struct OwnedPersistentCommittee { - pub slot: Epoch, + pub epoch: Epoch, pub shard: Shard, pub committee: Vec, } diff --git a/eth2/types/src/slot_epoch.rs b/eth2/types/src/slot_epoch.rs index 638741f5e49..207263cfa46 100644 --- a/eth2/types/src/slot_epoch.rs +++ b/eth2/types/src/slot_epoch.rs @@ -10,7 +10,7 @@ //! implement `Into`, however this would allow operations between `Slots` and `Epochs` which //! may lead to programming errors which are not detected by the compiler. -use crate::slot_height::SlotHeight; +use crate::slot_height::{SlotHeight, ShardSlotHeight}; use crate::test_utils::TestRandom; use rand::RngCore; use serde_derive::{Deserialize, Serialize}; @@ -39,7 +39,7 @@ impl_common!(ShardSlot); impl_common!(Epoch); impl ShardSlot { - pub fn new(slot: u64) -> Slot { + pub fn new(slot: u64) -> ShardSlot { ShardSlot(slot) } @@ -48,11 +48,11 @@ impl ShardSlot { } pub fn shard_period_start_epoch(self, slots_per_epoch: u64, slots_per_beacon_slot: u64, epochs_per_shard_period: u64, lookback: u64) -> Epoch { - let epoch = self::Epoch(slots_per_epoch, slots_per_beacon_slot) + let epoch = self.epoch(slots_per_epoch, slots_per_beacon_slot); Epoch::from(epoch - (epoch % epochs_per_shard_period) - lookback * epochs_per_shard_period) } - pub fn height(self, genesis_slot: Slot) -> SlotHeight { + pub fn height(self, genesis_slot: Slot) -> ShardSlotHeight { ShardSlotHeight::from(self.0.saturating_sub(genesis_slot.as_u64())) } @@ -71,7 +71,7 @@ impl Slot { } pub fn height(self, genesis_slot: Slot) -> SlotHeight { - ShardSlotHeight::from(self.0.saturating_sub(genesis_slot.as_u64())) + SlotHeight::from(self.0.saturating_sub(genesis_slot.as_u64())) } pub fn max_value() -> Slot { diff --git a/eth2/types/src/slot_height.rs b/eth2/types/src/slot_height.rs index bb0a206e33c..a3b88e31fa8 100644 --- a/eth2/types/src/slot_height.rs +++ b/eth2/types/src/slot_height.rs @@ -1,4 +1,4 @@ -use crate::slot_epoch::{Epoch, Slot}; +use crate::slot_epoch::{Epoch, Slot, ShardSlot}; use crate::test_utils::TestRandom; use rand::RngCore; @@ -12,9 +12,12 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssi /// Beacon block height, effectively `Slot/GENESIS_START_BLOCK`. #[derive(Eq, Debug, Clone, Copy, Default, Serialize)] pub struct SlotHeight(u64); + +#[derive(Eq, Debug, Clone, Copy, Default, Serialize)] pub struct ShardSlotHeight(u64); impl_common!(SlotHeight); +impl_common!(ShardSlotHeight); impl SlotHeight { pub fn new(slot: u64) -> SlotHeight { @@ -35,11 +38,11 @@ impl SlotHeight { } impl ShardSlotHeight { - pub fn new(slot: u64) -> SlotHeight { + pub fn new(slot: u64) -> ShardSlotHeight { ShardSlotHeight(slot) } - pub fn slot(self, genesis_slot: Slot) -> Slot { + pub fn slot(self, genesis_slot: Slot) -> ShardSlot { ShardSlot::from(self.0.saturating_add(genesis_slot.as_u64())) } @@ -47,8 +50,8 @@ impl ShardSlotHeight { Epoch::from(self.0.saturating_add(genesis_slot) / slots_per_epoch / slots_per_beacon_slot ) } - pub fn max_value() -> SlotHeight { - SlotHeight(u64::max_value()) + pub fn max_value() -> ShardSlotHeight { + ShardSlotHeight(u64::max_value()) } } From f85e097fa6016436a794e5e4bb94a2a1ee67626d Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Mon, 2 Sep 2019 16:13:42 -0400 Subject: [PATCH 032/151] Compiler part 2. Add relative period. --- eth2/types/src/lib.rs | 8 ++++- eth2/types/src/relative_period.rs | 12 +++++++ eth2/types/src/shard_attestation_data.rs | 4 +-- eth2/types/src/shard_block_body.rs | 8 ----- eth2/types/src/shard_pending_attestation.rs | 37 +++++++++++++++++++++ 5 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 eth2/types/src/relative_period.rs diff --git a/eth2/types/src/lib.rs b/eth2/types/src/lib.rs index 7637f078737..4bb965048ce 100644 --- a/eth2/types/src/lib.rs +++ b/eth2/types/src/lib.rs @@ -26,6 +26,9 @@ pub mod pending_attestation; pub mod period_committee; pub mod persistent_committee; pub mod proposer_slashing; +pub mod relative_period; +pub mod shard_attestation_data; +pub mod shard_pending_attestation; pub mod transfer; pub mod voluntary_exit; #[macro_use] @@ -62,7 +65,10 @@ pub use crate::period_committee::PeriodCommittee; pub use crate::persistent_committee::PersistentCommittee; pub use crate::proposer_slashing::ProposerSlashing; pub use crate::relative_epoch::{Error as RelativeEpochError, RelativeEpoch}; -pub use crate::slot_epoch::{Epoch, Slot}; +pub use crate::relative_period::RelativePeriod; +pub use crate::shard_attestation_data::ShardAttestationData; +pub use crate::shard_pending_attestation::ShardPendingAttestation; +pub use crate::slot_epoch::{Epoch, Slot, ShardSlot}; pub use crate::slot_height::{SlotHeight, ShardSlotHeight}; pub use crate::transfer::Transfer; pub use crate::validator::Validator; diff --git a/eth2/types/src/relative_period.rs b/eth2/types/src/relative_period.rs new file mode 100644 index 00000000000..54a61e92a38 --- /dev/null +++ b/eth2/types/src/relative_period.rs @@ -0,0 +1,12 @@ +use crate::*; + + +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum RelativePeriod { + /// The prior period. + Previous, + /// The current period. + Current, + /// The next period. + Next, +} diff --git a/eth2/types/src/shard_attestation_data.rs b/eth2/types/src/shard_attestation_data.rs index 38be878397a..07c4819eac2 100644 --- a/eth2/types/src/shard_attestation_data.rs +++ b/eth2/types/src/shard_attestation_data.rs @@ -1,5 +1,5 @@ use crate::test_utils::TestRandom; -use crate::{Epoch, Hash256}; +use crate::{Hash256, ShardSlot}; use serde_derive::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; @@ -31,7 +31,7 @@ pub struct ShardAttestationData { pub shard_block_root: Hash256, // Need to indicate which slot the attestation is for - pub target_slot: Slot + pub target_slot: ShardSlot, } #[cfg(test)] diff --git a/eth2/types/src/shard_block_body.rs b/eth2/types/src/shard_block_body.rs index 7505961b478..154d9a73809 100644 --- a/eth2/types/src/shard_block_body.rs +++ b/eth2/types/src/shard_block_body.rs @@ -26,11 +26,3 @@ pub struct ShardBlockBody { pub graffiti: [u8; 32], pub attestation: ShardAttestation, } - -#[cfg(test)] -mod tests { - use super::*; - - ssz_tests!(ShardBlockBody); - cached_tree_hash_tests!(ShardBlockBody); -} diff --git a/eth2/types/src/shard_pending_attestation.rs b/eth2/types/src/shard_pending_attestation.rs index e69de29bb2d..71ae0cee468 100644 --- a/eth2/types/src/shard_pending_attestation.rs +++ b/eth2/types/src/shard_pending_attestation.rs @@ -0,0 +1,37 @@ +use crate::test_utils::TestRandom; +use crate::{ShardAttestationData, Bitfield}; + +use serde_derive::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; +use tree_hash_derive::{CachedTreeHash, TreeHash}; + +/// An attestation that has been included in the state but not yet fully processed. +/// +/// Spec v0.6.3 +#[derive( + Debug, + Clone, + PartialEq, + Serialize, + Deserialize, + Encode, + Decode, + TreeHash, + CachedTreeHash, + TestRandom, +)] +pub struct ShardPendingAttestation { + pub aggregation_bitfield: Bitfield, + pub data: ShardAttestationData, + pub inclusion_delay: u64, + pub proposer_index: u64, +} + +#[cfg(test)] +mod tests { + use super::*; + + ssz_tests!(ShardPendingAttestation); + cached_tree_hash_tests!(ShardPendingAttestation); +} From 134f469122d714ec1fd30731a8d0cf7f72be26d9 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Mon, 2 Sep 2019 17:47:38 -0400 Subject: [PATCH 033/151] Include period struct --- eth2/types/src/period.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 eth2/types/src/period.rs diff --git a/eth2/types/src/period.rs b/eth2/types/src/period.rs new file mode 100644 index 00000000000..35fc055a19a --- /dev/null +++ b/eth2/types/src/period.rs @@ -0,0 +1,20 @@ +use serde_derive::{Deserialize, Serialize}; +use ssz::{ssz_encode, Decode, DecodeError, Encode}; +use std::fmt; +use std::iter::Iterator; + +#[derive(Eq, Debug, Clone, Copy, Default, Serialize, Deserialize)] +#[serde(transparent)] +pub struct Period(u64); + +impl_common!(Period); + +impl Period { + pub fn new(period: u64) -> Period { + Period(period) + } + + pub fn max_value() -> Slot { + Period(u64::max_value()) + } +} From d79cb3a294fb09b0f1bbca5ff4e066df3d5b44eb Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Mon, 2 Sep 2019 18:00:24 -0400 Subject: [PATCH 034/151] Epochs can be converted into a Period --- eth2/types/src/slot_epoch.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/eth2/types/src/slot_epoch.rs b/eth2/types/src/slot_epoch.rs index 207263cfa46..4e32bf12fc8 100644 --- a/eth2/types/src/slot_epoch.rs +++ b/eth2/types/src/slot_epoch.rs @@ -47,11 +47,6 @@ impl ShardSlot { Epoch::from(self.0 / slots_per_epoch / slots_per_beacon_slot) } - pub fn shard_period_start_epoch(self, slots_per_epoch: u64, slots_per_beacon_slot: u64, epochs_per_shard_period: u64, lookback: u64) -> Epoch { - let epoch = self.epoch(slots_per_epoch, slots_per_beacon_slot); - Epoch::from(epoch - (epoch % epochs_per_shard_period) - lookback * epochs_per_shard_period) - } - pub fn height(self, genesis_slot: Slot) -> ShardSlotHeight { ShardSlotHeight::from(self.0.saturating_sub(genesis_slot.as_u64())) } @@ -84,6 +79,10 @@ impl Epoch { Epoch(slot) } + pub fn period(self, epochs_per_period: u64) -> Period { + Period::from(self.0 / epochs_per_period) + } + pub fn max_value() -> Epoch { Epoch(u64::max_value()) } From c7e1013f23fd632a4304e2d5cc73d9a41b855388 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Mon, 2 Sep 2019 18:04:42 -0400 Subject: [PATCH 035/151] Include relative period logic --- eth2/types/src/relative_period.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/eth2/types/src/relative_period.rs b/eth2/types/src/relative_period.rs index 54a61e92a38..b55e3f95db9 100644 --- a/eth2/types/src/relative_period.rs +++ b/eth2/types/src/relative_period.rs @@ -1,6 +1,12 @@ use crate::*; +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum Error { + PeriodTooLow { base: Period, other: Period }, + PeriodTooHigh { base: Period, other: Period }, +} + #[derive(Debug, PartialEq, Clone, Copy)] pub enum RelativePeriod { /// The prior period. @@ -10,3 +16,28 @@ pub enum RelativePeriod { /// The next period. Next, } + +impl RelativePeriod { + pub fn into_period(self, base: Period) -> Period { + match self { + // Due to saturating nature of epoch, check for current first. + RelativePeriod::Current => base, + RelativePeriod::Previous => base - 1, + RelativePeriod::Next => base + 1, + } + } + + pub fn from_epoch(base: Period, other: Period) -> Result { + if other == base { + Ok(RelativePeriod::Current) + } else if other == base - 1 { + Ok(RelativePeriod::Previous) + } else if other == base + 1 { + Ok(RelativePeriod::Next) + } else if other < base { + Err(Error::PeriodTooLow { base, other }) + } else { + Err(Error::PeriodTooHigh { base, other }) + } + } +} From 24976ba3ceaa788220d48fa38e5b3b997969c29f Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Tue, 3 Sep 2019 11:27:45 -0400 Subject: [PATCH 036/151] Update chain spec to include period committee roots --- eth2/types/src/chain_spec.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/eth2/types/src/chain_spec.rs b/eth2/types/src/chain_spec.rs index 6749b620199..6ebf6c2ead2 100644 --- a/eth2/types/src/chain_spec.rs +++ b/eth2/types/src/chain_spec.rs @@ -78,6 +78,7 @@ pub struct ChainSpec { * Phase 1 specific values, fork epoch and slot are hardcoded to values for now */ epochs_per_shard_period: u64, + period_committee_root_length: u64, phase_1_fork_epoch: u64, phase_1_fork_slot: u64, @@ -203,6 +204,7 @@ impl ChainSpec { * Phase 1 specific values, fork epoch and slot are hardcoded to values for now */ epochs_per_shard_period: 256, + period_committee_root_length: 256, phase_1_fork_epoch: 600, phase_1_fork_slot: 38_400, From aa086aea0b6cd5be3d08fd23960aeef3493be6d9 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Tue, 3 Sep 2019 11:28:29 -0400 Subject: [PATCH 037/151] Period committee uses period vs. epoch now --- eth2/types/src/period_committee.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/eth2/types/src/period_committee.rs b/eth2/types/src/period_committee.rs index f79540c171d..ac72b0ba096 100644 --- a/eth2/types/src/period_committee.rs +++ b/eth2/types/src/period_committee.rs @@ -1,8 +1,8 @@ use crate::*; -#[derive(Default, Clone, Debug, PartialEq)] +#[derive(Default, Clone, Debug, PartialEq, TreeHash, CachedTreeHash)] pub struct PeriodCommittee<'a> { - pub epoch: Epoch, + pub period: Period, pub shard: Shard, pub committee: &'a [usize], } @@ -10,7 +10,7 @@ pub struct PeriodCommittee<'a> { impl<'a> PeriodCommittee<'a> { pub fn into_owned(self) -> OwnedPeriodCommittee { OwnedPeriodCommittee { - epoch: self.epoch, + period: self.period, shard: self.shard, committee: self.committee.to_vec(), } @@ -19,7 +19,7 @@ impl<'a> PeriodCommittee<'a> { #[derive(Default, Clone, Debug, PartialEq)] pub struct OwnedPeriodCommittee { - pub epoch: Epoch, + pub period: Period, pub shard: Shard, pub committee: Vec, } From 6e4cdc30015f8c37d1c333e72ae2535c41819500 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Tue, 3 Sep 2019 12:51:46 -0400 Subject: [PATCH 038/151] Support PeriodCommittees in the beacon state - with cache and historic roots :) --- eth2/types/src/beacon_state.rs | 86 +++++++------------ .../src/beacon_state/beacon_state_types.rs | 5 +- .../types/src/beacon_state/committee_cache.rs | 10 --- eth2/types/src/chain_spec.rs | 2 +- eth2/types/src/lib.rs | 4 +- eth2/types/src/period.rs | 9 +- eth2/types/src/period_committee.rs | 23 +---- eth2/types/src/relative_period.rs | 2 +- eth2/types/src/slot_epoch.rs | 1 + 9 files changed, 55 insertions(+), 87 deletions(-) diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index 584f2a4b98c..061492a90ed 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -94,6 +94,7 @@ where // Randomness and committees pub latest_randao_mixes: FixedLenVec, + pub period_committee_roots: FixedLenVec, pub latest_start_shard: u64, // Finality @@ -136,6 +137,12 @@ where #[ssz(skip_deserializing)] #[tree_hash(skip_hashing)] #[test_random(default)] + pub period_caches: [PeriodCommittee; 3], + #[serde(default)] + #[ssz(skip_serializing)] + #[ssz(skip_deserializing)] + #[tree_hash(skip_hashing)] + #[test_random(default)] pub pubkey_cache: PubkeyCache, #[serde(skip_serializing, skip_deserializing)] #[ssz(skip_serializing)] @@ -185,6 +192,11 @@ impl BeaconState { T::LatestRandaoMixesLength::to_usize() ]), latest_start_shard: 0, + // Update to proper variables + period_committee_roots: FixedLenVec::from(vec![ + spec.zero_hash; + T::PeriodCommitteeRootsLength::to_usize() + ]), // Finality previous_epoch_attestations: vec![], @@ -226,6 +238,11 @@ impl BeaconState { CommitteeCache::default(), CommitteeCache::default(), ], + period_caches: [ + PeriodCommittee::default(), + PeriodCommittee::default(), + PeriodCommittee::default(), + ], pubkey_cache: PubkeyCache::default(), tree_hash_cache: TreeHashCache::default(), exit_cache: ExitCache::default(), @@ -295,12 +312,6 @@ impl BeaconState { Ok(cache.epoch_committee_count() as u64) } - // pub fn get_period_committee_count(&self, relative_epoch: RelativeEpoch) -> Result { - // let cache = self.cache(relative_epoch)?; - - // Ok(cache.period_committee_count() as u64) - // } - pub fn get_epoch_start_shard(&self, relative_epoch: RelativeEpoch) -> Result { let cache = self.cache(relative_epoch)?; @@ -400,25 +411,27 @@ impl BeaconState { Ok(committee) } + pub fn period_index( + &self, + relative_period: RelativePeriod, + ) -> usize { + match relative_period { + RelativePeriod::Previous => 0, + RelativePeriod::Current => 1, + RelativePeriod::Next => 2, + } + } - // pub fn get_period_committee_for_shard( - // &self, - // shard: u64, - // relative_epoch: RelativeEpoch, - // ) -> Result { - // let cache = self.cache(relative_epoch)?; - - // let committee = cache - // .get_period_committee_for_shard(shard) - // .ok_or_else(|| Error::NoCommitteeForShard)?; - - // Ok(committee) - // } + pub fn get_period_committee( + &self, + relative_period: RelativePeriod, + ) -> &PeriodCommittee { + &self.period_caches[self.period_index(relative_period)] + } /// Returns the beacon proposer index for the `slot` in the given `relative_epoch`. /// /// Spec v0.6.3 - // NOTE: be sure to test this bad boy. pub fn get_beacon_proposer_index( &self, slot: Slot, @@ -452,39 +465,6 @@ impl BeaconState { }) } - /// Returns the shard proposer index for the given `relative_epoch` - // pub fn get_shard_proposer_index() -> { - // &self, - // relative_epoch: RelativeEpoch, - // shard: Shard, - // slot: ShardSlot, - // committee: PersistentCommittee - // spec: &ChainSpec, - // ) -> Result { - // let epoch = relative_epoch.into_epoch(self.current_epoch()); - // let seed = self.generate_seed(epoch, spec)?; - - // let mut i = 0; - // Ok(loop { - // let candidate_index = committee[(slot.as_usize() + i) % committee.len()]; - // let random_byte = { - // let mut preimage = seed.as_bytes().to_vec(); - // preimage.append(&mut int_to_bytes8((i / 32) as u64)); - // preimage.append(&mut int_to_bytes8(shard as u64)); - // preimage.append(&mut int_to_bytes8(slot as u64)); - // let hash = hash(&preimage); - // hash[i % 32] - // }; - // let effective_balance = self.validator_registry[candidate_index].effective_balance; - // if (effective_balance * MAX_RANDOM_BYTE) - // >= (spec.max_effective_balance * u64::from(random_byte)) - // { - // break candidate_index; - // } - // i += 1; - // }) - // } - /// Safely obtains the index for latest block roots, given some `slot`. /// /// Spec v0.6.3 diff --git a/eth2/types/src/beacon_state/beacon_state_types.rs b/eth2/types/src/beacon_state/beacon_state_types.rs index 7e4a0425832..6be430a0a99 100644 --- a/eth2/types/src/beacon_state/beacon_state_types.rs +++ b/eth2/types/src/beacon_state/beacon_state_types.rs @@ -1,5 +1,5 @@ use crate::*; -use fixed_len_vec::typenum::{Unsigned, U0, U1024, U64, U8, U8192}; +use fixed_len_vec::typenum::{Unsigned, U0, U1024, U64, U8, U8192, U256}; use serde_derive::{Deserialize, Serialize}; use std::fmt::Debug; @@ -7,6 +7,7 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq { type ShardCount: Unsigned + Clone + Sync + Send + Debug + PartialEq; type SlotsPerHistoricalRoot: Unsigned + Clone + Sync + Send + Debug + PartialEq; type LatestRandaoMixesLength: Unsigned + Clone + Sync + Send + Debug + PartialEq; + type PeriodCommitteeRootsLength: Unsigned + Clone + Sync + Send + Debug + PartialEq; type LatestActiveIndexRootsLength: Unsigned + Clone + Sync + Send + Debug + PartialEq; type LatestSlashedExitLength: Unsigned + Clone + Sync + Send + Debug + PartialEq; /// Note: `SlotsPerEpoch` is not necessarily required to be a compile-time constant. We include @@ -111,6 +112,7 @@ impl EthSpec for MainnetEthSpec { type ShardCount = U1024; type SlotsPerHistoricalRoot = U8192; type LatestRandaoMixesLength = U8192; + type PeriodCommitteeRootsLength = U256; type LatestActiveIndexRootsLength = U8192; type LatestSlashedExitLength = U8192; type SlotsPerEpoch = U64; @@ -134,6 +136,7 @@ pub struct MinimalEthSpec; impl EthSpec for MinimalEthSpec { type ShardCount = U8; type SlotsPerHistoricalRoot = U64; + type PeriodCommitteeRootsLength = U64; type LatestRandaoMixesLength = U64; type LatestActiveIndexRootsLength = U64; type LatestSlashedExitLength = U64; diff --git a/eth2/types/src/beacon_state/committee_cache.rs b/eth2/types/src/beacon_state/committee_cache.rs index fce104ddb36..54564d95d58 100644 --- a/eth2/types/src/beacon_state/committee_cache.rs +++ b/eth2/types/src/beacon_state/committee_cache.rs @@ -165,16 +165,6 @@ impl CommitteeCache { }) } - pub fn get_period_committee_for_shard(&self, shard: Shard, committee_size: u64) -> Option { - let committee = self.get_crosslink_committee_for_shard(shard)?.committee; - - Some(PeriodCommittee { - shard, - epoch: self.initialized_epoch?, - committee: &committee[..committee_size as usize], - }) - } - /// Returns the `AttestationDuty` for the given `validator_index`. /// /// Returns `None` if the `validator_index` does not exist, does not have duties or `Self` is diff --git a/eth2/types/src/chain_spec.rs b/eth2/types/src/chain_spec.rs index 6ebf6c2ead2..9728e78ce32 100644 --- a/eth2/types/src/chain_spec.rs +++ b/eth2/types/src/chain_spec.rs @@ -157,6 +157,7 @@ impl ChainSpec { */ target_committee_size: 128, target_period_committee_size: 128, + period_committee_root_length: 256, max_indices_per_attestation: 4096, min_per_epoch_churn_limit: 4, churn_limit_quotient: 65_536, @@ -204,7 +205,6 @@ impl ChainSpec { * Phase 1 specific values, fork epoch and slot are hardcoded to values for now */ epochs_per_shard_period: 256, - period_committee_root_length: 256, phase_1_fork_epoch: 600, phase_1_fork_slot: 38_400, diff --git a/eth2/types/src/lib.rs b/eth2/types/src/lib.rs index 4bb965048ce..419510ddc73 100644 --- a/eth2/types/src/lib.rs +++ b/eth2/types/src/lib.rs @@ -26,14 +26,15 @@ pub mod pending_attestation; pub mod period_committee; pub mod persistent_committee; pub mod proposer_slashing; -pub mod relative_period; pub mod shard_attestation_data; pub mod shard_pending_attestation; pub mod transfer; pub mod voluntary_exit; #[macro_use] pub mod slot_epoch_macros; +pub mod period; pub mod relative_epoch; +pub mod relative_period; pub mod slot_epoch; pub mod slot_height; pub mod validator; @@ -61,6 +62,7 @@ pub use crate::free_attestation::FreeAttestation; pub use crate::historical_batch::HistoricalBatch; pub use crate::indexed_attestation::IndexedAttestation; pub use crate::pending_attestation::PendingAttestation; +pub use crate::period::Period; pub use crate::period_committee::PeriodCommittee; pub use crate::persistent_committee::PersistentCommittee; pub use crate::proposer_slashing::ProposerSlashing; diff --git a/eth2/types/src/period.rs b/eth2/types/src/period.rs index 35fc055a19a..3e3fdd51bd9 100644 --- a/eth2/types/src/period.rs +++ b/eth2/types/src/period.rs @@ -2,6 +2,13 @@ use serde_derive::{Deserialize, Serialize}; use ssz::{ssz_encode, Decode, DecodeError, Encode}; use std::fmt; use std::iter::Iterator; +use slog; +use std::cmp::{Ord, Ordering}; +use std::hash::{Hash, Hasher}; +use rand::RngCore; +use crate::test_utils::TestRandom; +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign}; + #[derive(Eq, Debug, Clone, Copy, Default, Serialize, Deserialize)] #[serde(transparent)] @@ -14,7 +21,7 @@ impl Period { Period(period) } - pub fn max_value() -> Slot { + pub fn max_value() -> Period { Period(u64::max_value()) } } diff --git a/eth2/types/src/period_committee.rs b/eth2/types/src/period_committee.rs index ac72b0ba096..510a1d650dc 100644 --- a/eth2/types/src/period_committee.rs +++ b/eth2/types/src/period_committee.rs @@ -1,24 +1,9 @@ use crate::*; +use tree_hash_derive::{CachedTreeHash, TreeHash}; +use serde_derive::{Deserialize, Serialize}; -#[derive(Default, Clone, Debug, PartialEq, TreeHash, CachedTreeHash)] -pub struct PeriodCommittee<'a> { - pub period: Period, - pub shard: Shard, - pub committee: &'a [usize], -} - -impl<'a> PeriodCommittee<'a> { - pub fn into_owned(self) -> OwnedPeriodCommittee { - OwnedPeriodCommittee { - period: self.period, - shard: self.shard, - committee: self.committee.to_vec(), - } - } -} - -#[derive(Default, Clone, Debug, PartialEq)] -pub struct OwnedPeriodCommittee { +#[derive(Default, Clone, Debug, PartialEq, TreeHash, Serialize, Deserialize, CachedTreeHash)] +pub struct PeriodCommittee { pub period: Period, pub shard: Shard, pub committee: Vec, diff --git a/eth2/types/src/relative_period.rs b/eth2/types/src/relative_period.rs index b55e3f95db9..53a2f7ca885 100644 --- a/eth2/types/src/relative_period.rs +++ b/eth2/types/src/relative_period.rs @@ -27,7 +27,7 @@ impl RelativePeriod { } } - pub fn from_epoch(base: Period, other: Period) -> Result { + pub fn from_period(base: Period, other: Period) -> Result { if other == base { Ok(RelativePeriod::Current) } else if other == base - 1 { diff --git a/eth2/types/src/slot_epoch.rs b/eth2/types/src/slot_epoch.rs index 4e32bf12fc8..ea652d6d1cb 100644 --- a/eth2/types/src/slot_epoch.rs +++ b/eth2/types/src/slot_epoch.rs @@ -11,6 +11,7 @@ //! may lead to programming errors which are not detected by the compiler. use crate::slot_height::{SlotHeight, ShardSlotHeight}; +use crate::period::Period; use crate::test_utils::TestRandom; use rand::RngCore; use serde_derive::{Deserialize, Serialize}; From 4fd666873d629edcc87d7dfdf0548d1c41c7201e Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Wed, 4 Sep 2019 12:47:35 -0400 Subject: [PATCH 039/151] Define period committee cache --- eth2/types/src/beacon_state.rs | 3 +++ .../beacon_state/period_committee_cache.rs | 27 +++++++++++++++++++ eth2/types/src/chain_spec.rs | 8 +++--- 3 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 eth2/types/src/beacon_state/period_committee_cache.rs diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index 061492a90ed..8e0a38e7d9f 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -16,10 +16,12 @@ use tree_hash::TreeHash; use tree_hash_derive::{CachedTreeHash, TreeHash}; pub use self::committee_cache::CommitteeCache; +pub use self::period_committee_cache::PeriodCommitteeCache; pub use beacon_state_types::*; mod beacon_state_types; mod committee_cache; +mod period_committee_cache; mod exit_cache; mod pubkey_cache; mod tests; @@ -32,6 +34,7 @@ pub enum Error { EpochOutOfBounds, SlotOutOfBounds, ShardOutOfBounds, + NoPeriodBoundary, UnknownValidator, UnableToDetermineProducer, InvalidBitfield, diff --git a/eth2/types/src/beacon_state/period_committee_cache.rs b/eth2/types/src/beacon_state/period_committee_cache.rs new file mode 100644 index 00000000000..b5d07d178da --- /dev/null +++ b/eth2/types/src/beacon_state/period_committee_cache.rs @@ -0,0 +1,27 @@ +use super::BeaconState; +use crate::*; +use serde_derive::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; + +/// Computes and stores the shuffling for an epoch. Provides various getters to allow callers to +/// read the committees for the given epoch. +#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize, Encode, Decode)] +pub struct PeriodCommitteeCache { + pub committee: Vec, +} + +impl PeriodCommitteeCache { + pub fn initialize( + state: &BeaconState, + spec: &ChainSpec, + shard: u64, + ) -> Result { + let current_epoch = state.current_epoch(); + if current_epoch % spec.epochs_per_shard_period != 0 { + return Err(Error::NoPeriodBoundary); + } + + let committee = state.get_crosslink_committee_for_shard(shard, RelativeEpoch::Current)?.committee[..spec.target_period_committee_size].to_vec(); + Ok(PeriodCommitteeCache{committee}) + } +} diff --git a/eth2/types/src/chain_spec.rs b/eth2/types/src/chain_spec.rs index 9728e78ce32..ed8d6f8c2c3 100644 --- a/eth2/types/src/chain_spec.rs +++ b/eth2/types/src/chain_spec.rs @@ -77,10 +77,10 @@ pub struct ChainSpec { /* * Phase 1 specific values, fork epoch and slot are hardcoded to values for now */ - epochs_per_shard_period: u64, - period_committee_root_length: u64, - phase_1_fork_epoch: u64, - phase_1_fork_slot: u64, + pub epochs_per_shard_period: u64, + pub period_committee_root_length: u64, + pub phase_1_fork_epoch: u64, + pub phase_1_fork_slot: u64, /* * Reward and penalty quotients From b34f78fa4e3f03dfe6a012c16858d7623f2558dc Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Wed, 4 Sep 2019 12:52:47 -0400 Subject: [PATCH 040/151] Update period committee to have owned/sliced --- eth2/types/src/period_committee.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/eth2/types/src/period_committee.rs b/eth2/types/src/period_committee.rs index 510a1d650dc..12ed2fcf034 100644 --- a/eth2/types/src/period_committee.rs +++ b/eth2/types/src/period_committee.rs @@ -3,8 +3,25 @@ use tree_hash_derive::{CachedTreeHash, TreeHash}; use serde_derive::{Deserialize, Serialize}; #[derive(Default, Clone, Debug, PartialEq, TreeHash, Serialize, Deserialize, CachedTreeHash)] -pub struct PeriodCommittee { +pub struct PeriodCommittee<'a> { pub period: Period, pub shard: Shard, + pub committee: &'a [usize], +} + +impl<'a> PeriodCommittee<'a> { + pub fn into_owned(self) -> OwnedPeriodCommittee { + OwnedPeriodCommittee { + slot: self.slot, + shard: self.shard, + committee: self.committee.to_vec(), + } + } +} + +#[derive(Default, Clone, Debug, PartialEq, TreeHash, CachedTreeHash)] +pub struct OwnedPeriodCommittee { + pub slot: Slot, + pub shard: Shard, pub committee: Vec, } From d2d72898d6d6c7fb8b2574793e7dd7269c7ab0ba Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Wed, 4 Sep 2019 13:08:23 -0400 Subject: [PATCH 041/151] Update beacon state to use period committee caches --- eth2/types/src/beacon_state.rs | 10 +++++----- eth2/types/src/period_committee.rs | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index 8e0a38e7d9f..5024ac3e790 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -140,7 +140,7 @@ where #[ssz(skip_deserializing)] #[tree_hash(skip_hashing)] #[test_random(default)] - pub period_caches: [PeriodCommittee; 3], + pub period_caches: [PeriodCommitteeCache; 3], #[serde(default)] #[ssz(skip_serializing)] #[ssz(skip_deserializing)] @@ -242,9 +242,9 @@ impl BeaconState { CommitteeCache::default(), ], period_caches: [ - PeriodCommittee::default(), - PeriodCommittee::default(), - PeriodCommittee::default(), + PeriodCommitteeCache::default(), + PeriodCommitteeCache::default(), + PeriodCommitteeCache::default(), ], pubkey_cache: PubkeyCache::default(), tree_hash_cache: TreeHashCache::default(), @@ -428,7 +428,7 @@ impl BeaconState { pub fn get_period_committee( &self, relative_period: RelativePeriod, - ) -> &PeriodCommittee { + ) -> &PeriodCommitteeCache { &self.period_caches[self.period_index(relative_period)] } diff --git a/eth2/types/src/period_committee.rs b/eth2/types/src/period_committee.rs index 12ed2fcf034..2863da41e86 100644 --- a/eth2/types/src/period_committee.rs +++ b/eth2/types/src/period_committee.rs @@ -2,7 +2,7 @@ use crate::*; use tree_hash_derive::{CachedTreeHash, TreeHash}; use serde_derive::{Deserialize, Serialize}; -#[derive(Default, Clone, Debug, PartialEq, TreeHash, Serialize, Deserialize, CachedTreeHash)] +#[derive(Default, Clone, Debug, PartialEq, TreeHash, CachedTreeHash)] pub struct PeriodCommittee<'a> { pub period: Period, pub shard: Shard, @@ -12,7 +12,7 @@ pub struct PeriodCommittee<'a> { impl<'a> PeriodCommittee<'a> { pub fn into_owned(self) -> OwnedPeriodCommittee { OwnedPeriodCommittee { - slot: self.slot, + period: self.period, shard: self.shard, committee: self.committee.to_vec(), } @@ -21,7 +21,7 @@ impl<'a> PeriodCommittee<'a> { #[derive(Default, Clone, Debug, PartialEq, TreeHash, CachedTreeHash)] pub struct OwnedPeriodCommittee { - pub slot: Slot, + pub period: Period, pub shard: Shard, pub committee: Vec, } From 387583e1fc9320f025dff62cc30180766d244f60 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Wed, 4 Sep 2019 15:46:49 -0400 Subject: [PATCH 042/151] Period committee is a vec of vectors. Not sure if this is the best way, but it is a start :) --- .../src/beacon_state/period_committee_cache.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/eth2/types/src/beacon_state/period_committee_cache.rs b/eth2/types/src/beacon_state/period_committee_cache.rs index b5d07d178da..a386445ecc4 100644 --- a/eth2/types/src/beacon_state/period_committee_cache.rs +++ b/eth2/types/src/beacon_state/period_committee_cache.rs @@ -7,7 +7,7 @@ use ssz_derive::{Decode, Encode}; /// read the committees for the given epoch. #[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize, Encode, Decode)] pub struct PeriodCommitteeCache { - pub committee: Vec, + committees: Vec>, } impl PeriodCommitteeCache { @@ -21,7 +21,14 @@ impl PeriodCommitteeCache { return Err(Error::NoPeriodBoundary); } - let committee = state.get_crosslink_committee_for_shard(shard, RelativeEpoch::Current)?.committee[..spec.target_period_committee_size].to_vec(); - Ok(PeriodCommitteeCache{committee}) + let shard_count = T::shard_count(); + let mut committees = Vec::with_capacity(shard_count); + for n in 0..shard_count { + committees.push( + state.get_crosslink_committee_for_shard(n as u64, RelativeEpoch::Current)?.committee[..spec.target_period_committee_size].to_vec() + ); + } + + Ok(PeriodCommitteeCache{committees}) } } From 2eed9ac23c969d0c0a32ac22d7677b1cb456115d Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Wed, 4 Sep 2019 16:52:28 -0400 Subject: [PATCH 043/151] Period committee initialization is correct --- .../beacon_state/period_committee_cache.rs | 17 ++++++++------ eth2/types/src/period_committee.rs | 22 +++---------------- 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/eth2/types/src/beacon_state/period_committee_cache.rs b/eth2/types/src/beacon_state/period_committee_cache.rs index a386445ecc4..099633a283b 100644 --- a/eth2/types/src/beacon_state/period_committee_cache.rs +++ b/eth2/types/src/beacon_state/period_committee_cache.rs @@ -3,11 +3,9 @@ use crate::*; use serde_derive::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; -/// Computes and stores the shuffling for an epoch. Provides various getters to allow callers to -/// read the committees for the given epoch. #[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize, Encode, Decode)] pub struct PeriodCommitteeCache { - committees: Vec>, + committees: Vec, } impl PeriodCommitteeCache { @@ -22,11 +20,16 @@ impl PeriodCommitteeCache { } let shard_count = T::shard_count(); - let mut committees = Vec::with_capacity(shard_count); + let mut committees: Vec = Vec::with_capacity(shard_count); + for n in 0..shard_count { - committees.push( - state.get_crosslink_committee_for_shard(n as u64, RelativeEpoch::Current)?.committee[..spec.target_period_committee_size].to_vec() - ); + let committee_indices = state.get_crosslink_committee_for_shard(n as u64, RelativeEpoch::Current)?.committee[..spec.target_period_committee_size].to_vec(); + let period_committee = PeriodCommittee { + shard: n as u64, + period: current_epoch.period(spec.epochs_per_shard_period), + committee: committee_indices, + }; + committees.push(period_committee); } Ok(PeriodCommitteeCache{committees}) diff --git a/eth2/types/src/period_committee.rs b/eth2/types/src/period_committee.rs index 2863da41e86..f7139dea43a 100644 --- a/eth2/types/src/period_committee.rs +++ b/eth2/types/src/period_committee.rs @@ -1,26 +1,10 @@ use crate::*; use tree_hash_derive::{CachedTreeHash, TreeHash}; use serde_derive::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; -#[derive(Default, Clone, Debug, PartialEq, TreeHash, CachedTreeHash)] -pub struct PeriodCommittee<'a> { - pub period: Period, - pub shard: Shard, - pub committee: &'a [usize], -} - -impl<'a> PeriodCommittee<'a> { - pub fn into_owned(self) -> OwnedPeriodCommittee { - OwnedPeriodCommittee { - period: self.period, - shard: self.shard, - committee: self.committee.to_vec(), - } - } -} - -#[derive(Default, Clone, Debug, PartialEq, TreeHash, CachedTreeHash)] -pub struct OwnedPeriodCommittee { +#[derive(Default, Clone, Debug, PartialEq, TreeHash, CachedTreeHash, Serialize, Deserialize, Decode, Encode)] +pub struct PeriodCommittee { pub period: Period, pub shard: Shard, pub committee: Vec, From 5a7990f2fa602f674c74889532524f1f45828f23 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Wed, 4 Sep 2019 17:34:04 -0400 Subject: [PATCH 044/151] Change persistent committee to naturally a owned type --- eth2/types/src/persistent_committee.rs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/eth2/types/src/persistent_committee.rs b/eth2/types/src/persistent_committee.rs index da5ebdb3d8a..ecdbb081bbd 100644 --- a/eth2/types/src/persistent_committee.rs +++ b/eth2/types/src/persistent_committee.rs @@ -2,23 +2,6 @@ use crate::*; #[derive(Default, Clone, Debug, PartialEq)] pub struct PersistentCommittee<'a> { - pub epoch: Epoch, - pub shard: Shard, - pub committee: &'a [usize], -} - -impl<'a> PersistentCommittee<'a> { - pub fn into_owned(self) -> OwnedPersistentCommittee { - OwnedPersistentCommittee { - epoch: self.epoch, - shard: self.shard, - committee: self.committee.to_vec(), - } - } -} - -#[derive(Default, Clone, Debug, PartialEq)] -pub struct OwnedPersistentCommittee { pub epoch: Epoch, pub shard: Shard, pub committee: Vec, From 676c1b55594fdb48f82223ba8d84a2a90be9df65 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Thu, 5 Sep 2019 11:45:51 -0400 Subject: [PATCH 045/151] Remove ref in persistent committee struct --- eth2/types/src/persistent_committee.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth2/types/src/persistent_committee.rs b/eth2/types/src/persistent_committee.rs index ecdbb081bbd..70476f4306c 100644 --- a/eth2/types/src/persistent_committee.rs +++ b/eth2/types/src/persistent_committee.rs @@ -1,7 +1,7 @@ use crate::*; #[derive(Default, Clone, Debug, PartialEq)] -pub struct PersistentCommittee<'a> { +pub struct PersistentCommittee { pub epoch: Epoch, pub shard: Shard, pub committee: Vec, From 0b9a2305e38bfc8c318125b1940db09d26035366 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Thu, 5 Sep 2019 12:07:30 -0400 Subject: [PATCH 046/151] Period Committee Cache has get committee call --- eth2/types/src/beacon_state/period_committee_cache.rs | 9 ++++++++- eth2/types/src/lib.rs | 4 ++-- .../src/{persistent_committee.rs => shard_committee.rs} | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) rename eth2/types/src/{persistent_committee.rs => shard_committee.rs} (80%) diff --git a/eth2/types/src/beacon_state/period_committee_cache.rs b/eth2/types/src/beacon_state/period_committee_cache.rs index 099633a283b..98191cfdb18 100644 --- a/eth2/types/src/beacon_state/period_committee_cache.rs +++ b/eth2/types/src/beacon_state/period_committee_cache.rs @@ -12,7 +12,6 @@ impl PeriodCommitteeCache { pub fn initialize( state: &BeaconState, spec: &ChainSpec, - shard: u64, ) -> Result { let current_epoch = state.current_epoch(); if current_epoch % spec.epochs_per_shard_period != 0 { @@ -34,4 +33,12 @@ impl PeriodCommitteeCache { Ok(PeriodCommitteeCache{committees}) } + + pub fn get_period_committee(&self, shard: u64) -> Result<&PeriodCommittee, Error> { + if (self.committees.len() - 1) < shard as usize { + return Err(Error::ShardOutOfBounds) + } + + Ok(&self.committees[shard as usize]) + } } diff --git a/eth2/types/src/lib.rs b/eth2/types/src/lib.rs index 419510ddc73..e335a053d58 100644 --- a/eth2/types/src/lib.rs +++ b/eth2/types/src/lib.rs @@ -24,7 +24,7 @@ pub mod historical_batch; pub mod indexed_attestation; pub mod pending_attestation; pub mod period_committee; -pub mod persistent_committee; +pub mod shard_committee; pub mod proposer_slashing; pub mod shard_attestation_data; pub mod shard_pending_attestation; @@ -64,7 +64,7 @@ pub use crate::indexed_attestation::IndexedAttestation; pub use crate::pending_attestation::PendingAttestation; pub use crate::period::Period; pub use crate::period_committee::PeriodCommittee; -pub use crate::persistent_committee::PersistentCommittee; +pub use crate::shard_committee::ShardCommittee; pub use crate::proposer_slashing::ProposerSlashing; pub use crate::relative_epoch::{Error as RelativeEpochError, RelativeEpoch}; pub use crate::relative_period::RelativePeriod; diff --git a/eth2/types/src/persistent_committee.rs b/eth2/types/src/shard_committee.rs similarity index 80% rename from eth2/types/src/persistent_committee.rs rename to eth2/types/src/shard_committee.rs index 70476f4306c..e7f71e12ef2 100644 --- a/eth2/types/src/persistent_committee.rs +++ b/eth2/types/src/shard_committee.rs @@ -1,7 +1,7 @@ use crate::*; #[derive(Default, Clone, Debug, PartialEq)] -pub struct PersistentCommittee { +pub struct ShardCommittee { pub epoch: Epoch, pub shard: Shard, pub committee: Vec, From 9d4dd954048a486beb578fdc3997dd525edb5066 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Thu, 5 Sep 2019 12:10:47 -0400 Subject: [PATCH 047/151] fmt - rearranged --- eth2/types/src/beacon_state/period_committee_cache.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/eth2/types/src/beacon_state/period_committee_cache.rs b/eth2/types/src/beacon_state/period_committee_cache.rs index 98191cfdb18..8bf9fc5afa0 100644 --- a/eth2/types/src/beacon_state/period_committee_cache.rs +++ b/eth2/types/src/beacon_state/period_committee_cache.rs @@ -22,7 +22,10 @@ impl PeriodCommitteeCache { let mut committees: Vec = Vec::with_capacity(shard_count); for n in 0..shard_count { - let committee_indices = state.get_crosslink_committee_for_shard(n as u64, RelativeEpoch::Current)?.committee[..spec.target_period_committee_size].to_vec(); + let committee_indices = state + .get_crosslink_committee_for_shard(n as u64, RelativeEpoch::Current)? + .committee[..spec.target_period_committee_size] + .to_vec(); let period_committee = PeriodCommittee { shard: n as u64, period: current_epoch.period(spec.epochs_per_shard_period), @@ -31,12 +34,12 @@ impl PeriodCommitteeCache { committees.push(period_committee); } - Ok(PeriodCommitteeCache{committees}) + Ok(PeriodCommitteeCache { committees }) } pub fn get_period_committee(&self, shard: u64) -> Result<&PeriodCommittee, Error> { if (self.committees.len() - 1) < shard as usize { - return Err(Error::ShardOutOfBounds) + return Err(Error::ShardOutOfBounds); } Ok(&self.committees[shard as usize]) From 97a973c8f81c399a363ef7042a6cd7757f4eb539 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Thu, 5 Sep 2019 20:35:13 -0400 Subject: [PATCH 048/151] Get shard committee function --- beacon_node/beacon_chain/src/beacon_chain.rs | 59 -------------------- eth2/types/src/beacon_state.rs | 42 +++++++++++++- 2 files changed, 40 insertions(+), 61 deletions(-) diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 8dab8d13cb2..96ebe4b41a5 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -925,65 +925,6 @@ impl BeaconChain { Ok(dump) } - - // Everything from this point on will techincally be moved into a trait - // pub fn get_persistent_committee_at_block(&self, beacon_block_root: &Hash256, shard: Shard) -> Result { - // let block: BeaconBlock = match self.store.get(&beacon_block_root)? { - // Some(block_root) => block_root, - // None => { - // } - // }; - // let state_root = block.state_root; - // let block_state = self - // .store - // .get(&state_root)? - // .ok_or_else(|| Error::DBInconsistent(format!("Missing state {}", state_root)))?; - - // let mut state: BeaconState = block_state; - // state.get_crosslink_committee_for_shard(shard, state.current_epoch()) - // } - - // pub fn get_persistent_crosslink_committee(&self, shard: Shard) -> Result { - // let current_state = self.current_state(); - // current_state.get_crosslink_committee_for_shard(shard, current_state.current_epoch()) - // } - - // pub fn shard_block_proposer(&self, slot: Slot, shard: Shard) -> Result { - // self.catchup_state()?; - // let index = self.state.read().get_shard_proposer_index( - // slot, - // shard, - // RelativeEpoch::Current, - // &self.spec, - // )?; - - // Ok(index) - // } - - // need to support lookup by epoch - // pub fn get_current_crosslink(&self, shard: u64) -> Result<&Crosslink, Error> { - // self.current_state().get_current_crosslink(shard) - // } - - // need to support lookup by epoch - // pub fn get_finalized_crosslink(&self, shard: Shard, epoch: Epoch) -> { - // let current_state = self.current_state(); - // let finalized_block_root = current_state.finalized_block_root; - - // let finalized_block: BeaconBlock = match self.store.get(&finalized_block_root)? { - // Some(block_root) => block_root, - // None => { - // } - // }; - // let state_root = finalized_block.state_root; - // let finalized_state_root = self - // .store - // .get(&state_root)? - // .ok_or_else(|| Error::DBInconsistent(format!("Missing state {}", state_root)))?; - - // let mut state: BeaconState = finalized_state_root; - // finalized_state_root().get_current_crosslink(shard) - // } } impl From for Error { diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index 5024ac3e790..e91b0c5a646 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -428,10 +428,48 @@ impl BeaconState { pub fn get_period_committee( &self, relative_period: RelativePeriod, - ) -> &PeriodCommitteeCache { - &self.period_caches[self.period_index(relative_period)] + shard: u64, + ) -> Result<&PeriodCommittee, Error> { + self.period_caches[self.period_index(relative_period)].get_period_committee(shard) } + pub fn get_shard_committee( + &self, + epoch: Epoch, + shard: u64, + ) -> Result { + let earlier_committee = &self.get_period_committee(RelativePeriod::Previous, shard)?.committee; + let later_committee = &self.get_period_committee(RelativePeriod::Current, shard)?.committee; + + let mut union = Vec::new(); + + for &member in earlier_committee { + if member as u64 % T::default_spec().epochs_per_shard_period < member as u64 % T::default_spec().epochs_per_shard_period { + union.push(member); + } + } + + for &member in later_committee { + if member as u64 % T::default_spec().epochs_per_shard_period >= member as u64 % T::default_spec().epochs_per_shard_period { + union.push(member); + } + } + + union.dedup(); + + Ok(ShardCommittee{ + epoch: epoch, + shard: shard, + committee: union, + }) + } + + // pub fn get_shard_proposer_index( + // &self, + // epoch, + // shard, + // ) + /// Returns the beacon proposer index for the `slot` in the given `relative_epoch`. /// /// Spec v0.6.3 From 92d3e0129deacbc36ad5cebb042635f271e8fb17 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Thu, 5 Sep 2019 21:19:15 -0400 Subject: [PATCH 049/151] Shard proposer index, Shard committee logic completed :) --- eth2/types/src/beacon_state.rs | 57 ++++++++++++++++--- .../src/beacon_state/beacon_state_types.rs | 4 +- 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index e91b0c5a646..644953dea17 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -34,6 +34,7 @@ pub enum Error { EpochOutOfBounds, SlotOutOfBounds, ShardOutOfBounds, + PeriodOutOfBounds, NoPeriodBoundary, UnknownValidator, UnableToDetermineProducer, @@ -438,19 +439,28 @@ impl BeaconState { epoch: Epoch, shard: u64, ) -> Result { + let spec = T::default_spec(); + let current_epoch = self.current_epoch(); + let current_period = current_epoch.period(spec.epochs_per_shard_period); + let target_period = epoch.period(spec.epochs_per_shard_period); + + if target_period != current_period { + return Err(BeaconStateError::PeriodOutOfBounds); + } + let earlier_committee = &self.get_period_committee(RelativePeriod::Previous, shard)?.committee; let later_committee = &self.get_period_committee(RelativePeriod::Current, shard)?.committee; let mut union = Vec::new(); for &member in earlier_committee { - if member as u64 % T::default_spec().epochs_per_shard_period < member as u64 % T::default_spec().epochs_per_shard_period { + if member as u64 % spec.epochs_per_shard_period < member as u64 % spec.epochs_per_shard_period { union.push(member); } } for &member in later_committee { - if member as u64 % T::default_spec().epochs_per_shard_period >= member as u64 % T::default_spec().epochs_per_shard_period { + if member as u64 % spec.epochs_per_shard_period >= member as u64 % spec.epochs_per_shard_period { union.push(member); } } @@ -464,11 +474,44 @@ impl BeaconState { }) } - // pub fn get_shard_proposer_index( - // &self, - // epoch, - // shard, - // ) + pub fn get_shard_proposer_index( + &self, + epoch: Epoch, + shard: u64, + slot: ShardSlot, + ) -> Result { + let spec = T::default_spec(); + let current_epoch = self.current_epoch(); + let current_period = current_epoch.period(spec.epochs_per_shard_period); + let target_period = epoch.period(spec.epochs_per_shard_period); + + if target_period != current_period { + return Err(BeaconStateError::PeriodOutOfBounds); + } + + let seed = self.generate_seed(epoch, &spec)?; + let committee = self.get_shard_committee(epoch, shard)?.committee; + + let mut i = 0; + Ok(loop { + let candidate_index = committee[(slot.as_usize() + i) % committee.len()]; + let random_byte = { + let mut preimage = seed.as_bytes().to_vec(); + preimage.append(&mut int_to_bytes8((i / 32) as u64)); + preimage.append(&mut int_to_bytes8(shard)); + preimage.append(&mut int_to_bytes8(slot.into())); + let hash = hash(&preimage); + hash[i % 32] + }; + let effective_balance = self.validator_registry[candidate_index].effective_balance; + if (effective_balance * MAX_RANDOM_BYTE) + >= (spec.max_effective_balance * u64::from(random_byte)) + { + break candidate_index; + } + i += 1; + }) + } /// Returns the beacon proposer index for the `slot` in the given `relative_epoch`. /// diff --git a/eth2/types/src/beacon_state/beacon_state_types.rs b/eth2/types/src/beacon_state/beacon_state_types.rs index 6be430a0a99..81dadd350a7 100644 --- a/eth2/types/src/beacon_state/beacon_state_types.rs +++ b/eth2/types/src/beacon_state/beacon_state_types.rs @@ -137,8 +137,8 @@ impl EthSpec for MinimalEthSpec { type ShardCount = U8; type SlotsPerHistoricalRoot = U64; type PeriodCommitteeRootsLength = U64; - type LatestRandaoMixesLength = U64; - type LatestActiveIndexRootsLength = U64; + type LatestRandaoMixesLength = U1024; + type LatestActiveIndexRootsLength = U1024; type LatestSlashedExitLength = U64; type SlotsPerEpoch = U8; type GenesisEpoch = U0; From e9c6b9d70ece5329c0168180e85716422de0d7ae Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Fri, 6 Sep 2019 14:11:28 -0400 Subject: [PATCH 050/151] Shard block header compile - minor changes to store iter/pending att --- eth2/types/src/lib.rs | 2 + eth2/types/src/shard_block_header.rs | 32 +++++------- eth2/types/src/shard_pending_attestation.rs | 1 - shard_node/store/src/iter.rs | 58 +++++---------------- 4 files changed, 27 insertions(+), 66 deletions(-) diff --git a/eth2/types/src/lib.rs b/eth2/types/src/lib.rs index e335a053d58..f170e153f9a 100644 --- a/eth2/types/src/lib.rs +++ b/eth2/types/src/lib.rs @@ -27,6 +27,7 @@ pub mod period_committee; pub mod shard_committee; pub mod proposer_slashing; pub mod shard_attestation_data; +pub mod shard_block_header; pub mod shard_pending_attestation; pub mod transfer; pub mod voluntary_exit; @@ -69,6 +70,7 @@ pub use crate::proposer_slashing::ProposerSlashing; pub use crate::relative_epoch::{Error as RelativeEpochError, RelativeEpoch}; pub use crate::relative_period::RelativePeriod; pub use crate::shard_attestation_data::ShardAttestationData; +pub use crate::shard_block_header::ShardBlockHeader; pub use crate::shard_pending_attestation::ShardPendingAttestation; pub use crate::slot_epoch::{Epoch, Slot, ShardSlot}; pub use crate::slot_height::{SlotHeight, ShardSlotHeight}; diff --git a/eth2/types/src/shard_block_header.rs b/eth2/types/src/shard_block_header.rs index 04b8f0a5182..8507f33229b 100644 --- a/eth2/types/src/shard_block_header.rs +++ b/eth2/types/src/shard_block_header.rs @@ -8,9 +8,6 @@ use test_random_derive::TestRandom; use tree_hash::{SignedRoot, TreeHash}; use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash}; -/// A header of a `BeaconBlock`. -/// -/// Spec v0.6.3 #[derive( Debug, PartialEq, @@ -27,32 +24,27 @@ use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash}; pub struct ShardBlockHeader { pub slot: ShardSlot, pub previous_block_root: Hash256, + pub beacon_block_root: Hash256, pub state_root: Hash256, - pub block_body_root: Hash256, + pub body_root: Hash256, #[signed_root(skip_hashing)] pub signature: Signature, } -impl BeaconBlockHeader { - /// Returns the `tree_hash_root` of the header. - /// - /// Spec v0.6.3 +impl ShardBlockHeader { pub fn canonical_root(&self) -> Hash256 { Hash256::from_slice(&self.signed_root()[..]) } - /// Given a `body`, consumes `self` and returns a complete `BeaconBlock`. - /// - /// Spec v0.6.3 - pub fn into_block(self, body: ShardBlockBody) -> ShardBlock { - ShardBlock { - slot: self.slot, - previous_block_root: self.previous_block_root, - state_root: self.state_root, - body, - signature: self.signature, - } - } + // pub fn into_block(self, body: ShardBlockBody) -> ShardBlock { + // ShardBlock { + // slot: self.slot, + // previous_block_root: self.previous_block_root, + // state_root: self.state_root, + // body, + // signature: self.signature, + // } + // } } #[cfg(test)] diff --git a/eth2/types/src/shard_pending_attestation.rs b/eth2/types/src/shard_pending_attestation.rs index 71ae0cee468..309b23b82bf 100644 --- a/eth2/types/src/shard_pending_attestation.rs +++ b/eth2/types/src/shard_pending_attestation.rs @@ -24,7 +24,6 @@ use tree_hash_derive::{CachedTreeHash, TreeHash}; pub struct ShardPendingAttestation { pub aggregation_bitfield: Bitfield, pub data: ShardAttestationData, - pub inclusion_delay: u64, pub proposer_index: u64, } diff --git a/shard_node/store/src/iter.rs b/shard_node/store/src/iter.rs index 09616455b6b..084da2fb7eb 100644 --- a/shard_node/store/src/iter.rs +++ b/shard_node/store/src/iter.rs @@ -1,13 +1,13 @@ use crate::Store; use std::borrow::Cow; use std::sync::Arc; -use types::{ShardBlock, ShardState, ShardStateError, EthSpec, Hash256, Slot}; +use types::{ShardBlock, ShardState, ShardStateError, EthSpec, Hash256, ShardSlot}; #[derive(Clone)] pub struct StateRootsIterator<'a, T: EthSpec, U> { store: Arc, shard_state: Cow<'a, ShardState>, - slot: Slot, + slot: ShardSlot, } impl<'a, T: EthSpec, U: Store> StateRootsIterator<'a, T, U> { @@ -67,14 +67,14 @@ pub struct BlockIterator<'a, T: EthSpec, U> { impl<'a, T: EthSpec, U: Store> BlockIterator<'a, T, U> { /// Create a new iterator over all blocks in the given `beacon_state` and prior states. - pub fn new(store: Arc, shard_state: &'a ShardState, start_slot: Slot) -> Self { + pub fn new(store: Arc, shard_state: &'a ShardState, start_slot: ShardSlot) -> Self { Self { roots: BlockRootsIterator::new(store, shard_state, start_slot), } } /// Create a new iterator over all blocks in the given `beacon_state` and prior states. - pub fn owned(store: Arc, shard_state: ShardState, start_slot: Slot) -> Self { + pub fn owned(store: Arc, shard_state: ShardState, start_slot: ShardSlot) -> Self { Self { roots: BlockRootsIterator::owned(store, shard_state, start_slot), } @@ -90,29 +90,16 @@ impl<'a, T: EthSpec, U: Store> Iterator for BlockIterator<'a, T, U> { } } -/// Iterates backwards through block roots. If any specified slot is unable to be retrieved, the -/// iterator returns `None` indefinitely. -/// -/// Uses the `latest_block_roots` field of `BeaconState` to as the source of block roots and will -/// perform a lookup on the `Store` for a prior `BeaconState` if `latest_block_roots` has been -/// exhausted. -/// -/// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. -/// -/// ## Notes -/// -/// See [`BestBlockRootsIterator`](struct.BestBlockRootsIterator.html), which has different -/// `start_slot` logic. #[derive(Clone)] pub struct BlockRootsIterator<'a, T: EthSpec, U> { store: Arc, shard_state: Cow<'a, ShardState>, - slot: Slot, + slot: ShardSlot, } impl<'a, T: EthSpec, U: Store> BlockRootsIterator<'a, T, U> { /// Create a new iterator over all block roots in the given `shard_state` and prior states. - pub fn new(store: Arc, shard_state: &'a ShardState, start_slot: Slot) -> Self { + pub fn new(store: Arc, shard_state: &'a ShardState, start_slot: ShardSlot) -> Self { Self { store, shard_state: Cow::Borrowed(shard_state), @@ -121,7 +108,7 @@ impl<'a, T: EthSpec, U: Store> BlockRootsIterator<'a, T, U> { } /// Create a new iterator over all block roots in the given `beacon_state` and prior states. - pub fn owned(store: Arc, shard_state: ShardState, start_slot: Slot) -> Self { + pub fn owned(store: Arc, shard_state: ShardState, start_slot: ShardSlot) -> Self { Self { store, shard_state: Cow::Owned(shard_state), @@ -131,7 +118,7 @@ impl<'a, T: EthSpec, U: Store> BlockRootsIterator<'a, T, U> { } impl<'a, T: EthSpec, U: Store> Iterator for BlockRootsIterator<'a, T, U> { - type Item = (Hash256, Slot); + type Item = (Hash256, ShardSlot); fn next(&mut self) -> Option { if (self.slot == 0) || (self.slot > self.shard_state.slot) { @@ -143,7 +130,7 @@ impl<'a, T: EthSpec, U: Store> Iterator for BlockRootsIterator<'a, T, U> { match self.shard_state.get_block_root(self.slot) { Ok(root) => Some((*root, self.slot)), Err(ShardStateError::SlotOutOfBounds) => { - // Read a `BeaconState` from the store that has access to prior historical root. + // Read a `ShardState` from the store that has access to prior historical root. let shard_state: ShardState = { // Load the earliest state from disk. let new_state_root = self.shard_state.get_oldest_state_root().ok()?; @@ -162,34 +149,15 @@ impl<'a, T: EthSpec, U: Store> Iterator for BlockRootsIterator<'a, T, U> { } } -/// Iterates backwards through block roots with `start_slot` highest possible value -/// `<= beacon_state.slot`. -/// -/// The distinction between `BestBlockRootsIterator` and `BlockRootsIterator` is: -/// -/// - `BestBlockRootsIterator` uses best-effort slot. When `start_slot` is greater than the latest available block root -/// on `beacon_state`, returns `Some(root, slot)` where `slot` is the latest available block -/// root. -/// - `BlockRootsIterator` is strict about `start_slot`. When `start_slot` is greater than the latest available block root -/// on `beacon_state`, returns `None`. -/// -/// This is distinct from `BestBlockRootsIterator`. -/// -/// Uses the `latest_block_roots` field of `BeaconState` to as the source of block roots and will -/// perform a lookup on the `Store` for a prior `BeaconState` if `latest_block_roots` has been -/// exhausted. -/// -/// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. #[derive(Clone)] pub struct BestBlockRootsIterator<'a, T: EthSpec, U> { store: Arc, shard_state: Cow<'a, ShardState>, - slot: Slot, + slot: ShardSlot, } impl<'a, T: EthSpec, U: Store> BestBlockRootsIterator<'a, T, U> { - /// Create a new iterator over all block roots in the given `beacon_state` and prior states. - pub fn new(store: Arc, beacon_state: &'a ShardState, start_slot: Slot) -> Self { + pub fn new(store: Arc, beacon_state: &'a ShardState, start_slot: ShardSlot) -> Self { let mut slot = start_slot; if slot >= shard_state.slot { // Slot may be too high. @@ -207,7 +175,7 @@ impl<'a, T: EthSpec, U: Store> BestBlockRootsIterator<'a, T, U> { } /// Create a new iterator over all block roots in the given `beacon_state` and prior states. - pub fn owned(store: Arc, shard_state: ShardState, start_slot: Slot) -> Self { + pub fn owned(store: Arc, shard_state: ShardState, start_slot: ShardSlot) -> Self { let mut slot = start_slot; if slot >= shard_state.slot { // Slot may be too high. @@ -228,7 +196,7 @@ impl<'a, T: EthSpec, U: Store> BestBlockRootsIterator<'a, T, U> { } impl<'a, T: EthSpec, U: Store> Iterator for BestBlockRootsIterator<'a, T, U> { - type Item = (Hash256, Slot); + type Item = (Hash256, ShardSlot); fn next(&mut self) -> Option { if self.slot == 0 { From 27de61055d4166954bd04d56d54a1599c3e3485a Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Mon, 9 Sep 2019 15:31:06 -0400 Subject: [PATCH 051/151] Shard Block is more in line with intended spec. --- eth2/types/src/lib.rs | 4 ++- eth2/types/src/shard_block.rs | 42 +++++------------------------- eth2/types/src/shard_block_body.rs | 28 -------------------- 3 files changed, 10 insertions(+), 64 deletions(-) delete mode 100644 eth2/types/src/shard_block_body.rs diff --git a/eth2/types/src/lib.rs b/eth2/types/src/lib.rs index f170e153f9a..d7f022af468 100644 --- a/eth2/types/src/lib.rs +++ b/eth2/types/src/lib.rs @@ -24,9 +24,10 @@ pub mod historical_batch; pub mod indexed_attestation; pub mod pending_attestation; pub mod period_committee; -pub mod shard_committee; pub mod proposer_slashing; +pub mod shard_committee; pub mod shard_attestation_data; +pub mod shard_block; pub mod shard_block_header; pub mod shard_pending_attestation; pub mod transfer; @@ -70,6 +71,7 @@ pub use crate::proposer_slashing::ProposerSlashing; pub use crate::relative_epoch::{Error as RelativeEpochError, RelativeEpoch}; pub use crate::relative_period::RelativePeriod; pub use crate::shard_attestation_data::ShardAttestationData; +pub use crate::shard_block::ShardBlock; pub use crate::shard_block_header::ShardBlockHeader; pub use crate::shard_pending_attestation::ShardPendingAttestation; pub use crate::slot_epoch::{Epoch, Slot, ShardSlot}; diff --git a/eth2/types/src/shard_block.rs b/eth2/types/src/shard_block.rs index 1a2f7617d83..36f11021258 100644 --- a/eth2/types/src/shard_block.rs +++ b/eth2/types/src/shard_block.rs @@ -8,9 +8,6 @@ use test_random_derive::TestRandom; use tree_hash::{SignedRoot, TreeHash}; use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash}; -/// A block of the `ShardChain`. -/// -/// Spec v0.6.3 #[derive( Debug, PartialEq, @@ -25,48 +22,34 @@ use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash}; SignedRoot, )] pub struct ShardBlock { + pub shard: u64, pub slot: ShardSlot, pub previous_block_root: Hash256, pub state_root: Hash256, - pub body: ShardBlockBody, + pub attestation: ShardAttestation, #[signed_root(skip_hashing)] pub signature: Signature, } impl ShardBlock { - /// Returns an empty block to be used during genesis. - /// - /// Spec v0.6.3 - pub fn empty(spec: &ChainSpec) -> BeaconBlock { + pub fn empty(spec: &ChainSpec, shard: u64) -> BeaconBlock { ShardBlock { - slot: spec.genesis_slot, + shard, + slot: spec.phase_1_fork_slot, previous_block_root: spec.zero_hash, state_root: spec.zero_hash, - body: ShardBlockBody { - graffiti: [0; 32], - attestations: vec![], - }, + attestation: ShardAttestation::default(), signature: Signature::empty_signature(), } } - /// Returns the `signed_root` of the block. - /// - /// Spec v0.6.3 pub fn canonical_root(&self) -> Hash256 { Hash256::from_slice(&self.signed_root()[..]) } - /// Returns a full `ShardBlockHeader` of this block. - /// - /// Note: This method is used instead of an `Into` impl to avoid a `Clone` of an entire block - /// when you want to have the block _and_ the header. - /// - /// Note: performs a full tree-hash of `self.body`. - /// - /// Spec v0.6.3 pub fn block_header(&self) -> ShardBlockHeader { ShardBlockHeader { + shard: self.shard, slot: self.slot, previous_block_root: self.previous_block_root, state_root: self.state_root, @@ -75,9 +58,6 @@ impl ShardBlock { } } - /// Returns a "temporary" header, where the `state_root` is `spec.zero_hash`. - /// - /// Spec v0.6.3 pub fn temporary_block_header(&self, spec: &ChainSpec) -> ShardBlockHeader { ShardBlockHeader { state_root: spec.zero_hash, @@ -86,11 +66,3 @@ impl ShardBlock { } } } - -#[cfg(test)] -mod tests { - use super::*; - - ssz_tests!(BeaconBlock); - cached_tree_hash_tests!(BeaconBlock); -} diff --git a/eth2/types/src/shard_block_body.rs b/eth2/types/src/shard_block_body.rs deleted file mode 100644 index 154d9a73809..00000000000 --- a/eth2/types/src/shard_block_body.rs +++ /dev/null @@ -1,28 +0,0 @@ -use crate::test_utils::{graffiti_from_hex_str, TestRandom}; -use crate::*; - -use serde_derive::{Deserialize, Serialize}; -use ssz_derive::{Decode, Encode}; -use test_random_derive::TestRandom; -use tree_hash_derive::{CachedTreeHash, TreeHash}; - -/// The body of a `ShardChain` block, containing operations. -/// -/// Spec v0.6.3 -#[derive( - Debug, - PartialEq, - Clone, - Serialize, - Deserialize, - Encode, - Decode, - TreeHash, - CachedTreeHash, - TestRandom, -)] -pub struct ShardBlockBody { - #[serde(deserialize_with = "graffiti_from_hex_str")] - pub graffiti: [u8; 32], - pub attestation: ShardAttestation, -} From 26007422622c6d0e71a6c7a305978e35480844a1 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Mon, 9 Sep 2019 17:47:49 -0400 Subject: [PATCH 052/151] Added shard attestation --- eth2/types/src/shard_attestation.rs | 62 +++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 eth2/types/src/shard_attestation.rs diff --git a/eth2/types/src/shard_attestation.rs b/eth2/types/src/shard_attestation.rs new file mode 100644 index 00000000000..f30ebd28169 --- /dev/null +++ b/eth2/types/src/shard_attestation.rs @@ -0,0 +1,62 @@ +use super::{AggregateSignature, ShardAttestationData, Bitfield}; +use crate::test_utils::TestRandom; + +use serde_derive::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; +use tree_hash::TreeHash; +use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash}; + +/// Details an attestation that can be slashable. +/// +/// Spec v0.6.3 +#[derive( + Debug, + Clone, + PartialEq, + Serialize, + Deserialize, + Encode, + Decode, + TreeHash, + CachedTreeHash, + TestRandom, + SignedRoot, +)] +pub struct ShardAttestation { + pub aggregation_bitfield: Bitfield, + pub data: ShardAttestationData, + pub custody_bitfield: Bitfield, + #[signed_root(skip_hashing)] + pub signature: AggregateSignature, +} + +impl Attestation { + /// Are the aggregation bitfields of these attestations disjoint? + pub fn signers_disjoint_from(&self, other: &ShardAttestation) -> bool { + self.aggregation_bitfield + .intersection(&other.aggregation_bitfield) + .is_zero() + } + + /// Aggregate another Attestation into this one. + /// + /// The aggregation bitfields must be disjoint, and the data must be the same. + pub fn aggregate(&mut self, other: &ShardAttestation) { + debug_assert_eq!(self.data, other.data); + debug_assert!(self.signers_disjoint_from(other)); + + self.aggregation_bitfield + .union_inplace(&other.aggregation_bitfield); + self.custody_bitfield.union_inplace(&other.custody_bitfield); + self.signature.add_aggregate(&other.signature); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + ssz_tests!(ShardAttestation); + cached_tree_hash_tests!(ShardAttestation); +} From 269531ef92d881e47c84204b2549e8c3b88866a4 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Mon, 9 Sep 2019 17:48:53 -0400 Subject: [PATCH 053/151] Op pool logic on attestation being situated --- eth2/shard_operation_pool/src/attestation_id.rs | 10 +++++----- eth2/types/src/shard_attestation.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eth2/shard_operation_pool/src/attestation_id.rs b/eth2/shard_operation_pool/src/attestation_id.rs index 76cbc628773..b4c258d2c6b 100644 --- a/eth2/shard_operation_pool/src/attestation_id.rs +++ b/eth2/shard_operation_pool/src/attestation_id.rs @@ -14,23 +14,23 @@ const DOMAIN_BYTES_LEN: usize = 16; impl AttestationId { pub fn from_data( - attestation: &AttestationData, + attestation: &ShardAttestationData, state: &ShardState, spec: &ChainSpec, ) -> Self { let mut bytes = ssz_encode(attestation); - let epoch = attestation.target_epoch; - bytes.extend_from_slice(&AttestationId::compute_domain_bytes(epoch, state, spec)); + let slot = attestation.target_slot; + let epoch = slot.epoch(); + bytes.extend_from_slice(&AttestationId::compute_domain_bytes(epoch, slot, state, spec)); AttestationId { v: bytes } } pub fn compute_domain_bytes( epoch: Epoch, + slot: ShardSlot, state: &ShardState, spec: &ChainSpec, - slot: Slot ) -> Vec { - // also pseudocoded here int_to_bytes8(spec.get_domain(epoch, Domain::Attestation, &state.fork)) + int_to_bytes8(slot) } diff --git a/eth2/types/src/shard_attestation.rs b/eth2/types/src/shard_attestation.rs index f30ebd28169..2b004e95ccd 100644 --- a/eth2/types/src/shard_attestation.rs +++ b/eth2/types/src/shard_attestation.rs @@ -31,7 +31,7 @@ pub struct ShardAttestation { pub signature: AggregateSignature, } -impl Attestation { +impl ShardAttestation { /// Are the aggregation bitfields of these attestations disjoint? pub fn signers_disjoint_from(&self, other: &ShardAttestation) -> bool { self.aggregation_bitfield From a6f54fff73d287aa579e00d2526bf12f837da30f Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Mon, 9 Sep 2019 18:23:06 -0400 Subject: [PATCH 054/151] Shard Operation Pool logic adjusted for shards --- Cargo.toml | 1 + eth2/shard_operation_pool/Cargo.toml | 8 ++++ eth2/shard_operation_pool/src/attestation.rs | 0 eth2/shard_operation_pool/src/lib.rs | 41 +++++--------------- 4 files changed, 18 insertions(+), 32 deletions(-) delete mode 100644 eth2/shard_operation_pool/src/attestation.rs diff --git a/Cargo.toml b/Cargo.toml index 22ec6fd9858..321a24d9ef9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "eth2/lmd_ghost", "eth2/operation_pool", + "eth2/shard_operation_pool", "eth2/state_processing", "eth2/types", "eth2/utils/bls", diff --git a/eth2/shard_operation_pool/Cargo.toml b/eth2/shard_operation_pool/Cargo.toml index 94cb27beaad..23c4cfb358b 100644 --- a/eth2/shard_operation_pool/Cargo.toml +++ b/eth2/shard_operation_pool/Cargo.toml @@ -5,3 +5,11 @@ authors = ["will "] edition = "2018" [dependencies] +boolean-bitfield = { path = "../utils/boolean-bitfield" } +int_to_bytes = { path = "../utils/int_to_bytes" } +itertools = "0.8" +parking_lot = "0.7" +types = { path = "../types" } +state_processing = { path = "../state_processing" } +eth2_ssz = { path = "../utils/ssz" } +eth2_ssz_derive = { path = "../utils/ssz_derive" } diff --git a/eth2/shard_operation_pool/src/attestation.rs b/eth2/shard_operation_pool/src/attestation.rs deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/eth2/shard_operation_pool/src/lib.rs b/eth2/shard_operation_pool/src/lib.rs index 376dfb8393b..c1bc2039a4c 100644 --- a/eth2/shard_operation_pool/src/lib.rs +++ b/eth2/shard_operation_pool/src/lib.rs @@ -1,35 +1,21 @@ mod attestation_id; -mod persistence; - -pub use persistence::PersistedOperationPool; use attestation_id::AttestationId; use itertools::Itertools; use parking_lot::RwLock; -use state_processing::per_block_processing::{ - get_slashable_indices_modular, validate_attestation, - validate_attestation_time_independent_only, verify_attester_slashing, verify_exit, - verify_exit_time_independent_only, verify_proposer_slashing, verify_transfer, - verify_transfer_time_independent_only, -}; use std::collections::{btree_map::Entry, hash_map, BTreeMap, HashMap, HashSet}; use std::marker::PhantomData; use types::{ - Attestation, BeaconState, ShardState, ChainSpec, EthSpec, Validator + ShardAttestation, ShardState, ChainSpec, EthSpec, Validator }; #[derive(Default, Debug)] -pub struct OperationPool { - /// Map from attestation ID (see below) to vectors of attestations. - attestations: RwLock>>, - // NOTE: We assume that there is only one deposit per index - // because the Eth1 data is updated (at most) once per epoch, - // and the spec doesn't seem to accomodate for re-orgs on a time-frame - // longer than an epoch +pub struct ShardOperationPool { + attestations: RwLock>>, _phantom: PhantomData, } -impl OperationPool { +impl ShardOperationPool { /// Create a new operation pool. pub fn new() -> Self { Self::default() @@ -38,7 +24,7 @@ impl OperationPool { /// Insert an attestation into the pool, aggregating it with existing attestations if possible. pub fn insert_attestation( &self, - attestation: Attestation, + attestation: ShardAttestation, state: &ShardState, spec: &ChainSpec, ) -> () { @@ -78,7 +64,7 @@ impl OperationPool { } /// Get a list of attestations for inclusion in a block. - pub fn get_attestations(&self, state: &ShardState, spec: &ChainSpec) -> Vec { + pub fn get_attestations(&self, state: &ShardState, spec: &ChainSpec) -> Vec { // Attestations for the current fork, which may be from the current or previous epoch. let current_slot = state.slot(); let domain_bytes = AttestationId::compute_domain_bytes(epoch, state, spec); @@ -89,23 +75,15 @@ impl OperationPool { .flat_map(|(_, attestations)| attestations); } - /// Remove attestations which are too old to be included in a block. - pub fn prune_attestations(&self, finalized_state: &BeaconState) { - // We know we can include an attestation if: - // state.slot <= attestation_slot + SLOTS_PER_EPOCH - // We approximate this check using the attestation's epoch, to avoid computing - // the slot or relying on the committee cache of the finalized state. + pub fn prune_attestations(&self, finalized_state: &ShardState) { self.attestations.write().retain(|_, attestations| { - // All the attestations in this bucket have the same data, so we only need to - // check the first one. attestations.first().map_or(false, |att| { - finalized_state.current_epoch() <= att.data.target_epoch + 1 + finalized_state.current_slot() <= att.data.target_slot }) }); } } -/// Filter up to a maximum number of operations out of an iterator. fn filter_limit_operations<'a, T: 'a, I, F>(operations: I, filter: F, limit: u64) -> Vec where I: IntoIterator, @@ -120,7 +98,6 @@ where .collect() } -/// Compare two operation pools. -impl PartialEq for OperationPool { +impl PartialEq for ShardOperationPool { fn eq(&self, other: &Self) -> bool { *self.attestations.read() == *other.attestations.read()} } From 7fa6e0e56c3edb3f8f9d9beb01d98eb1fd328081 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Tue, 10 Sep 2019 12:27:35 -0400 Subject: [PATCH 055/151] Add op pool into cargo.toml and include some shard attestation changes. --- Cargo.toml | 1 - eth2/types/src/lib.rs | 2 ++ eth2/types/src/shard_attestation.rs | 1 + eth2/types/src/shard_block.rs | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 321a24d9ef9..22ec6fd9858 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,6 @@ members = [ "eth2/lmd_ghost", "eth2/operation_pool", - "eth2/shard_operation_pool", "eth2/state_processing", "eth2/types", "eth2/utils/bls", diff --git a/eth2/types/src/lib.rs b/eth2/types/src/lib.rs index d7f022af468..cd303b8bb7b 100644 --- a/eth2/types/src/lib.rs +++ b/eth2/types/src/lib.rs @@ -26,6 +26,7 @@ pub mod pending_attestation; pub mod period_committee; pub mod proposer_slashing; pub mod shard_committee; +pub mod shard_attestation; pub mod shard_attestation_data; pub mod shard_block; pub mod shard_block_header; @@ -70,6 +71,7 @@ pub use crate::shard_committee::ShardCommittee; pub use crate::proposer_slashing::ProposerSlashing; pub use crate::relative_epoch::{Error as RelativeEpochError, RelativeEpoch}; pub use crate::relative_period::RelativePeriod; +pub use crate::shard_attestation::ShardAttestation; pub use crate::shard_attestation_data::ShardAttestationData; pub use crate::shard_block::ShardBlock; pub use crate::shard_block_header::ShardBlockHeader; diff --git a/eth2/types/src/shard_attestation.rs b/eth2/types/src/shard_attestation.rs index 2b004e95ccd..a30a7fa4a2d 100644 --- a/eth2/types/src/shard_attestation.rs +++ b/eth2/types/src/shard_attestation.rs @@ -12,6 +12,7 @@ use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash}; /// Spec v0.6.3 #[derive( Debug, + Default, Clone, PartialEq, Serialize, diff --git a/eth2/types/src/shard_block.rs b/eth2/types/src/shard_block.rs index 36f11021258..74e89683473 100644 --- a/eth2/types/src/shard_block.rs +++ b/eth2/types/src/shard_block.rs @@ -35,7 +35,7 @@ impl ShardBlock { pub fn empty(spec: &ChainSpec, shard: u64) -> BeaconBlock { ShardBlock { shard, - slot: spec.phase_1_fork_slot, + slot: spec.phase_1_fork_slot as ShardSlot, previous_block_root: spec.zero_hash, state_root: spec.zero_hash, attestation: ShardAttestation::default(), From 31b7211026cfaba16e9361b513dd9b8ee22e8fb0 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Tue, 10 Sep 2019 14:49:18 -0400 Subject: [PATCH 056/151] Shard block types compiling --- eth2/types/src/shard_block.rs | 7 ++++--- eth2/types/src/shard_block_header.rs | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/eth2/types/src/shard_block.rs b/eth2/types/src/shard_block.rs index 74e89683473..9ce46b1dc7d 100644 --- a/eth2/types/src/shard_block.rs +++ b/eth2/types/src/shard_block.rs @@ -32,10 +32,10 @@ pub struct ShardBlock { } impl ShardBlock { - pub fn empty(spec: &ChainSpec, shard: u64) -> BeaconBlock { + pub fn empty(spec: &ChainSpec, shard: u64) -> ShardBlock { ShardBlock { shard, - slot: spec.phase_1_fork_slot as ShardSlot, + slot: ShardSlot::from(spec.phase_1_fork_slot), previous_block_root: spec.zero_hash, state_root: spec.zero_hash, attestation: ShardAttestation::default(), @@ -51,9 +51,10 @@ impl ShardBlock { ShardBlockHeader { shard: self.shard, slot: self.slot, + beacon_block_root: Hash256::default(), previous_block_root: self.previous_block_root, state_root: self.state_root, - block_body_root: Hash256::from_slice(&self.body.tree_hash_root()[..]), + body_root: Hash256::default(), signature: self.signature.clone(), } } diff --git a/eth2/types/src/shard_block_header.rs b/eth2/types/src/shard_block_header.rs index 8507f33229b..994ffcb4e58 100644 --- a/eth2/types/src/shard_block_header.rs +++ b/eth2/types/src/shard_block_header.rs @@ -23,6 +23,7 @@ use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash}; )] pub struct ShardBlockHeader { pub slot: ShardSlot, + pub shard: u64, pub previous_block_root: Hash256, pub beacon_block_root: Hash256, pub state_root: Hash256, From 71c6a3c9c33cc05aa81ee9abd3b8132cfab317a1 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Thu, 12 Sep 2019 13:46:48 -0500 Subject: [PATCH 057/151] Shard state functioning --- eth2/types/src/lib.rs | 2 + eth2/types/src/shard_state.rs | 128 +++------------------------------- 2 files changed, 11 insertions(+), 119 deletions(-) diff --git a/eth2/types/src/lib.rs b/eth2/types/src/lib.rs index cd303b8bb7b..999f1d5eafb 100644 --- a/eth2/types/src/lib.rs +++ b/eth2/types/src/lib.rs @@ -31,6 +31,7 @@ pub mod shard_attestation_data; pub mod shard_block; pub mod shard_block_header; pub mod shard_pending_attestation; +pub mod shard_state; pub mod transfer; pub mod voluntary_exit; #[macro_use] @@ -76,6 +77,7 @@ pub use crate::shard_attestation_data::ShardAttestationData; pub use crate::shard_block::ShardBlock; pub use crate::shard_block_header::ShardBlockHeader; pub use crate::shard_pending_attestation::ShardPendingAttestation; +pub use crate::shard_state::ShardState; pub use crate::slot_epoch::{Epoch, Slot, ShardSlot}; pub use crate::slot_height::{SlotHeight, ShardSlotHeight}; pub use crate::transfer::Transfer; diff --git a/eth2/types/src/shard_state.rs b/eth2/types/src/shard_state.rs index a4de9f87084..f0f6c5b8705 100644 --- a/eth2/types/src/shard_state.rs +++ b/eth2/types/src/shard_state.rs @@ -1,12 +1,8 @@ -use self::committee_cache::get_active_validator_indices; use crate::test_utils::TestRandom; use crate::*; use cached_tree_hash::{Error as TreeHashCacheError, TreeHashCache}; use compare_fields_derive::CompareFields; -use fixed_len_vec::{typenum::Unsigned, FixedLenVec}; use hashing::hash; -use int_to_bytes::{int_to_bytes32, int_to_bytes8}; -use pubkey_cache::PubkeyCache; use serde_derive::{Deserialize, Serialize}; use ssz::ssz_encode; use ssz_derive::{Decode, Encode}; @@ -14,36 +10,11 @@ use test_random_derive::TestRandom; use tree_hash::TreeHash; use tree_hash_derive::{CachedTreeHash, TreeHash}; -pub use self::committee_cache::CommitteeCache; -pub use beacon_state_types::*; - -mod beacon_state_types; -mod committee_cache; -mod pubkey_cache; -mod tests; - -pub const CACHED_EPOCHS: usize = 3; -const MAX_RANDOM_BYTE: u64 = (1 << 8) - 1; - #[derive(Debug, PartialEq)] pub enum Error { - EpochOutOfBounds, - SlotOutOfBounds, - UnknownValidator, - UnableToDetermineProducer, - InvalidBitfield, - InsufficientBlockRoots, - InsufficientIndexRoots, - InsufficientAttestations, - InsufficientStateRoots, - NoCommitteeForShard, - NoCommitteeForSlot, TreeHashCacheError(TreeHashCacheError), } -/// The state of the `BeaconChain` at some slot. -/// -/// Spec v0.6.3 #[derive( Debug, PartialEq, @@ -57,129 +28,55 @@ pub enum Error { CachedTreeHash, CompareFields, )] -pub struct ShardState -where - T: EthSpec, -{ - // Misc +pub struct ShardState{ + pub shard: u64, pub slot: ShardSlot, - // Attestations - Update to bitfield but simply one attestation included - pub attestation: ShardPendingAttestation, - - // Latest beacon root needed - pub beacon_root: Hash256, - - // Parent root - pub parent_root: Hash256, - - // Caching (not in the spec) - #[serde(default)] + #[serde(skip_serializing, skip_deserializing)] #[ssz(skip_serializing)] #[ssz(skip_deserializing)] #[tree_hash(skip_hashing)] #[test_random(default)] - pub committees: [PeriodCommittee; 3], + pub tree_hash_cache: TreeHashCache, } -impl ShardState { - /// Produce the first state of the Shard Chain. - /// - /// This does not fully build a genesis shard state, it omits processing of initial validator - /// deposits. To obtain a full genesis shard state, use the `ShardStateBuilder`. - /// - /// Spec v0.6.3 +impl ShardState { pub fn genesis( - genesis_time: u64, spec: &ChainSpec, - ) -> ShardState { + shard: u64, + ) -> ShardState { ShardState { - // Misc - slot: spec.genesis_slot, - fork: Fork::genesis(T::genesis_epoch()), - - // Attestation - attestation: ShardPendingAttestation::default(), - - // Roots - beacon_root: Hash256::default(), - parent_root: Hash256::default(), - - /* - * Cacheing (not in spec) - */ + shard, + slot: ShardSlot::from(spec.phase_1_fork_slot), tree_hash_cache: TreeHashCache::default(), - committees: [PeriodCommittee::default(); 3], } } - /// Returns the `tree_hash_root` of the state. - /// - /// Spec v0.6.3 pub fn canonical_root(&self) -> Hash256 { Hash256::from_slice(&self.tree_hash_root()[..]) } - pub fn get_earlier_committee(&self) -> PeriodCommittee { - self.committees[0] - } - - pub fn get_later_committee(&self) -> PeriodCommittee { - self.committees[1] - } - - pub fn get_next_committee(&self) -> PeriodCommittee { - self.committees[2] - } - - pub fn get_persistent_committee(&self) -> PersistentCommittee { - let earlier_committee = self.get_earlier_committee().to_owned().committee; - let later_committee = self.get_later_committee().to_owned().committee; - - let persistent_committee_indexes = { - // loop through properly - } - // finish logic here - fairly simple - } - - /// Do we need this since the tree hash really is just the balance set of everyone? - /// Build all the caches, if they need to be built. pub fn build_cache(&mut self, spec: &ChainSpec) -> Result<(), Error> { self.update_tree_hash_cache()?; Ok(()) } - /// Drop all caches on the state. pub fn drop_cache(&mut self) { self.drop_tree_hash_cache(); } - /// Update the tree hash cache, building it for the first time if it is empty. - /// - /// Returns the `tree_hash_root` resulting from the update. This root can be considered the - /// canonical root of `self`. pub fn update_tree_hash_cache(&mut self) -> Result { if self.tree_hash_cache.is_empty() { self.tree_hash_cache = TreeHashCache::new(self)?; } else { - // Move the cache outside of `self` to satisfy the borrow checker. let mut cache = std::mem::replace(&mut self.tree_hash_cache, TreeHashCache::default()); - cache.update(self)?; - - // Move the updated cache back into `self`. self.tree_hash_cache = cache } self.cached_tree_hash_root() } - /// Returns the tree hash root determined by the last execution of `self.update_tree_hash_cache(..)`. - /// - /// Note: does _not_ update the cache and may return an outdated root. - /// - /// Returns an error if the cache is not initialized or if an error is encountered during the - /// cache update. pub fn cached_tree_hash_root(&self) -> Result { self.tree_hash_cache .tree_hash_root() @@ -187,18 +84,11 @@ impl ShardState { .map_err(Into::into) } - /// Completely drops the tree hash cache, replacing it with a new, empty cache. pub fn drop_tree_hash_cache(&mut self) { self.tree_hash_cache = TreeHashCache::default() } } -impl From for Error { - fn from(e: RelativeEpochError) -> Error { - Error::RelativeEpochError(e) - } -} - impl From for Error { fn from(e: TreeHashCacheError) -> Error { Error::TreeHashCacheError(e) From 1652446dfd93f66d9828d26d2dbd584926f311c6 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Thu, 12 Sep 2019 15:22:07 -0500 Subject: [PATCH 058/151] Operation pool setup/compiling properly --- Cargo.toml | 1 + .../src/attestation_id.rs | 16 ++++++---- eth2/shard_operation_pool/src/lib.rs | 30 +++++++++++-------- eth2/types/src/chain_spec.rs | 13 ++++++++ eth2/types/src/shard_state.rs | 18 +++++++++-- 5 files changed, 57 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 22ec6fd9858..321a24d9ef9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "eth2/lmd_ghost", "eth2/operation_pool", + "eth2/shard_operation_pool", "eth2/state_processing", "eth2/types", "eth2/utils/bls", diff --git a/eth2/shard_operation_pool/src/attestation_id.rs b/eth2/shard_operation_pool/src/attestation_id.rs index b4c258d2c6b..3ab40751126 100644 --- a/eth2/shard_operation_pool/src/attestation_id.rs +++ b/eth2/shard_operation_pool/src/attestation_id.rs @@ -1,7 +1,7 @@ use int_to_bytes::int_to_bytes8; use ssz::ssz_encode; use ssz_derive::{Decode, Encode}; -use types::{AttestationData, BeaconState, ChainSpec, Domain, Epoch, EthSpec}; +use types::{BeaconState, ShardAttestationData, ShardSlot, ShardState, ChainSpec, Domain, Epoch, EthSpec}; /// Serialized `AttestationData` augmented with a domain to encode the fork info. #[derive(PartialEq, Eq, Clone, Hash, Debug, PartialOrd, Ord, Encode, Decode)] @@ -15,23 +15,27 @@ const DOMAIN_BYTES_LEN: usize = 16; impl AttestationId { pub fn from_data( attestation: &ShardAttestationData, - state: &ShardState, + beacon_state: &BeaconState, spec: &ChainSpec, ) -> Self { let mut bytes = ssz_encode(attestation); let slot = attestation.target_slot; - let epoch = slot.epoch(); - bytes.extend_from_slice(&AttestationId::compute_domain_bytes(epoch, slot, state, spec)); + let epoch = slot.epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot); + bytes.extend_from_slice(&AttestationId::compute_domain_bytes(epoch, slot, beacon_state, spec)); AttestationId { v: bytes } } pub fn compute_domain_bytes( epoch: Epoch, slot: ShardSlot, - state: &ShardState, + beacon_state: &BeaconState, spec: &ChainSpec, ) -> Vec { - int_to_bytes8(spec.get_domain(epoch, Domain::Attestation, &state.fork)) + int_to_bytes8(slot) + let mut domain_bytes = int_to_bytes8(spec.get_domain(epoch, Domain::Attestation, &beacon_state.fork)); + let mut slot_identifying_bytes = int_to_bytes8(slot.into()); + + domain_bytes.append(&mut slot_identifying_bytes); + domain_bytes } pub fn domain_bytes_match(&self, domain_bytes: &[u8]) -> bool { diff --git a/eth2/shard_operation_pool/src/lib.rs b/eth2/shard_operation_pool/src/lib.rs index c1bc2039a4c..7db43ac8619 100644 --- a/eth2/shard_operation_pool/src/lib.rs +++ b/eth2/shard_operation_pool/src/lib.rs @@ -6,7 +6,7 @@ use parking_lot::RwLock; use std::collections::{btree_map::Entry, hash_map, BTreeMap, HashMap, HashSet}; use std::marker::PhantomData; use types::{ - ShardAttestation, ShardState, ChainSpec, EthSpec, Validator + BeaconState, ShardAttestation, ShardSlot, ShardState, ChainSpec, EthSpec, Validator }; #[derive(Default, Debug)] @@ -25,10 +25,10 @@ impl ShardOperationPool { pub fn insert_attestation( &self, attestation: ShardAttestation, - state: &ShardState, + beacon_state: &BeaconState, spec: &ChainSpec, ) -> () { - let id = AttestationId::from_data(&attestation.data, state, spec); + let id = AttestationId::from_data(&attestation.data, beacon_state, spec); // Take a write lock on the attestations map. let mut attestations = self.attestations.write(); @@ -36,7 +36,7 @@ impl ShardOperationPool { let existing_attestations = match attestations.entry(id) { hash_map::Entry::Vacant(entry) => { entry.insert(vec![attestation]); - return Ok(()); + return (); } hash_map::Entry::Occupied(entry) => entry.into_mut(), }; @@ -55,7 +55,7 @@ impl ShardOperationPool { existing_attestations.push(attestation); } - Ok(()) + () } /// Total number of attestations in the pool, including attestations for the same data. @@ -64,21 +64,27 @@ impl ShardOperationPool { } /// Get a list of attestations for inclusion in a block. - pub fn get_attestations(&self, state: &ShardState, spec: &ChainSpec) -> Vec { - // Attestations for the current fork, which may be from the current or previous epoch. - let current_slot = state.slot(); - let domain_bytes = AttestationId::compute_domain_bytes(epoch, state, spec); + pub fn get_attestations(&self, state: &ShardState, beacon_state: &BeaconState, spec: &ChainSpec) -> Vec { + // enforce the right beacon state is being passed through + let attesting_slot = ShardSlot::from(state.slot - 1); + let epoch = attesting_slot.epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot); + let domain_bytes = AttestationId::compute_domain_bytes(epoch, attesting_slot, beacon_state, spec); let reader = self.attestations.read(); - let valid_attestations = reader + + let attestations: Vec = reader .iter() .filter(|(key, _)| key.domain_bytes_match(&domain_bytes)) - .flat_map(|(_, attestations)| attestations); + .flat_map(|(_, attestations)| attestations) + .cloned() + .collect(); + + attestations } pub fn prune_attestations(&self, finalized_state: &ShardState) { self.attestations.write().retain(|_, attestations| { attestations.first().map_or(false, |att| { - finalized_state.current_slot() <= att.data.target_slot + finalized_state.slot <= att.data.target_slot }) }); } diff --git a/eth2/types/src/chain_spec.rs b/eth2/types/src/chain_spec.rs index ed8d6f8c2c3..2221f0eaf3c 100644 --- a/eth2/types/src/chain_spec.rs +++ b/eth2/types/src/chain_spec.rs @@ -74,6 +74,12 @@ pub struct ChainSpec { pub max_crosslink_epochs: u64, pub min_epochs_to_inactivity_penalty: u64, + /* + * Additional Time Parameters + */ + pub slots_per_epoch: u64, + pub shard_slots_per_beacon_slot: u64, + /* * Phase 1 specific values, fork epoch and slot are hardcoded to values for now */ @@ -201,6 +207,12 @@ impl ChainSpec { max_crosslink_epochs: 64, min_epochs_to_inactivity_penalty: 4, + /* + * Additional Time Parameters + */ + slots_per_epoch: 64, + shard_slots_per_beacon_slot: 2, + /* * Phase 1 specific values, fork epoch and slot are hardcoded to values for now */ @@ -267,6 +279,7 @@ impl ChainSpec { genesis_slot, chain_id: 2, // lighthouse testnet chain id boot_nodes, + slots_per_epoch: 8, ..ChainSpec::mainnet() } } diff --git a/eth2/types/src/shard_state.rs b/eth2/types/src/shard_state.rs index f0f6c5b8705..d2fd4e6d4d2 100644 --- a/eth2/types/src/shard_state.rs +++ b/eth2/types/src/shard_state.rs @@ -8,6 +8,7 @@ use ssz::ssz_encode; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash::TreeHash; +use std::marker::PhantomData; use tree_hash_derive::{CachedTreeHash, TreeHash}; #[derive(Debug, PartialEq)] @@ -28,7 +29,10 @@ pub enum Error { CachedTreeHash, CompareFields, )] -pub struct ShardState{ +pub struct ShardState +where + T: EthSpec, +{ pub shard: u64, pub slot: ShardSlot, @@ -38,17 +42,25 @@ pub struct ShardState{ #[tree_hash(skip_hashing)] #[test_random(default)] pub tree_hash_cache: TreeHashCache, + + #[serde(skip_serializing, skip_deserializing)] + #[ssz(skip_serializing)] + #[ssz(skip_deserializing)] + #[tree_hash(skip_hashing)] + #[test_random(default)] + _phantom: PhantomData, } -impl ShardState { +impl ShardState { pub fn genesis( spec: &ChainSpec, shard: u64, - ) -> ShardState { + ) -> ShardState { ShardState { shard, slot: ShardSlot::from(spec.phase_1_fork_slot), tree_hash_cache: TreeHashCache::default(), + _phantom: PhantomData, } } From 167a2d80129b044cdaa4eb5f05944c6c107d0935 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Thu, 12 Sep 2019 16:33:14 -0500 Subject: [PATCH 059/151] Shard node store functioning and compiling :) --- Cargo.toml | 1 + shard_node/shard_store/Cargo.toml | 20 ++ shard_node/shard_store/src/block_at_slot.rs | 54 +++++ .../{store => shard_store}/src/errors.rs | 0 shard_node/shard_store/src/impls.rs | 16 ++ shard_node/{store => shard_store}/src/iter.rs | 8 +- shard_node/shard_store/src/lib.rs | 221 ++++++++++++++++++ shard_node/shard_store/src/memory_store.rs | 3 + shard_node/store/Cargo.toml | 7 - shard_node/store/src/block_at_slot.rs | 191 --------------- shard_node/store/src/lib.rs | 3 - shard_node/store/src/memory_store.rs | 0 12 files changed, 319 insertions(+), 205 deletions(-) create mode 100644 shard_node/shard_store/Cargo.toml create mode 100644 shard_node/shard_store/src/block_at_slot.rs rename shard_node/{store => shard_store}/src/errors.rs (100%) create mode 100644 shard_node/shard_store/src/impls.rs rename shard_node/{store => shard_store}/src/iter.rs (96%) create mode 100644 shard_node/shard_store/src/lib.rs create mode 100644 shard_node/shard_store/src/memory_store.rs delete mode 100644 shard_node/store/Cargo.toml delete mode 100644 shard_node/store/src/block_at_slot.rs delete mode 100644 shard_node/store/src/lib.rs delete mode 100644 shard_node/store/src/memory_store.rs diff --git a/Cargo.toml b/Cargo.toml index 321a24d9ef9..e5ae7a1455f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ members = [ "beacon_node/rpc", "beacon_node/version", "beacon_node/beacon_chain", + "shard_node/shard_store", "tests/ef_tests", "protos", "validator_client", diff --git a/shard_node/shard_store/Cargo.toml b/shard_node/shard_store/Cargo.toml new file mode 100644 index 00000000000..1ea26400680 --- /dev/null +++ b/shard_node/shard_store/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "shard_store" +version = "0.1.0" +authors = ["will "] +edition = "2018" + +[dev-dependencies] +tempfile = "3" + +[dependencies] +blake2-rfc = "0.2.18" +bls = { path = "../../eth2/utils/bls" } +bytes = "0.4.10" +db-key = "0.0.5" +leveldb = "0.8.4" +parking_lot = "0.7" +eth2_ssz = { path = "../../eth2/utils/ssz" } +eth2_ssz_derive = { path = "../../eth2/utils/ssz_derive" } +tree_hash = { path = "../../eth2/utils/tree_hash" } +types = { path = "../../eth2/types" } diff --git a/shard_node/shard_store/src/block_at_slot.rs b/shard_node/shard_store/src/block_at_slot.rs new file mode 100644 index 00000000000..fdd6b3f2059 --- /dev/null +++ b/shard_node/shard_store/src/block_at_slot.rs @@ -0,0 +1,54 @@ +use super::*; +use ssz::{Decode, DecodeError}; + +fn get_block_bytes(store: &T, root: Hash256) -> Result>, Error> { + store.get_bytes(ShardBlock::db_column().into(), &root[..]) +} + +fn read_slot_from_block_bytes(bytes: &[u8]) -> Result { + let end = std::cmp::min(ShardSlot::ssz_fixed_len(), bytes.len()); + + ShardSlot::from_ssz_bytes(&bytes[0..end]) +} + +fn read_previous_block_root_from_block_bytes(bytes: &[u8]) -> Result { + let previous_bytes = ShardSlot::ssz_fixed_len(); + let slice = bytes + .get(previous_bytes..previous_bytes + Hash256::ssz_fixed_len()) + .ok_or_else(|| DecodeError::BytesInvalid("Not enough bytes.".to_string()))?; + + Hash256::from_ssz_bytes(slice) +} + +pub fn get_block_at_preceeding_slot( + store: &T, + slot: ShardSlot, + start_root: Hash256, +) -> Result, Error> { + Ok(match get_at_preceeding_slot(store, slot, start_root)? { + Some((hash, bytes)) => Some((hash, ShardBlock::from_ssz_bytes(&bytes)?)), + None => None, + }) +} + +fn get_at_preceeding_slot( + store: &T, + slot: ShardSlot, + mut root: Hash256, +) -> Result)>, Error> { + loop { + if let Some(bytes) = get_block_bytes(store, root)? { + let this_slot = read_slot_from_block_bytes(&bytes)?; + + if this_slot == slot { + break Ok(Some((root, bytes))); + } else if this_slot < slot { + break Ok(None); + } else { + root = read_previous_block_root_from_block_bytes(&bytes)?; + } + } else { + break Ok(None); + } + } +} diff --git a/shard_node/store/src/errors.rs b/shard_node/shard_store/src/errors.rs similarity index 100% rename from shard_node/store/src/errors.rs rename to shard_node/shard_store/src/errors.rs diff --git a/shard_node/shard_store/src/impls.rs b/shard_node/shard_store/src/impls.rs new file mode 100644 index 00000000000..8ff8f974ff6 --- /dev/null +++ b/shard_node/shard_store/src/impls.rs @@ -0,0 +1,16 @@ +use crate::*; +use ssz::{Decode, Encode}; + +impl StoreItem for ShardBlock { + fn db_column() -> DBColumn { + DBColumn::ShardBlock + } + + fn as_store_bytes(&self) -> Vec { + self.as_ssz_bytes() + } + + fn from_store_bytes(bytes: &mut [u8]) -> Result { + Self::from_ssz_bytes(bytes).map_err(Into::into) + } +} diff --git a/shard_node/store/src/iter.rs b/shard_node/shard_store/src/iter.rs similarity index 96% rename from shard_node/store/src/iter.rs rename to shard_node/shard_store/src/iter.rs index 084da2fb7eb..ce524582f5c 100644 --- a/shard_node/store/src/iter.rs +++ b/shard_node/shard_store/src/iter.rs @@ -11,7 +11,7 @@ pub struct StateRootsIterator<'a, T: EthSpec, U> { } impl<'a, T: EthSpec, U: Store> StateRootsIterator<'a, T, U> { - pub fn new(store: Arc, shard_state: &'a ShardState, start_slot: Slot) -> Self { + pub fn new(store: Arc, shard_state: &'a ShardState, start_slot: ShardSlot) -> Self { Self { store, shard_state: Cow::Borrowed(shard_state), @@ -19,7 +19,7 @@ impl<'a, T: EthSpec, U: Store> StateRootsIterator<'a, T, U> { } } - pub fn owned(store: Arc, beacon_state: BeaconState, start_slot: Slot) -> Self { + pub fn owned(store: Arc, shard_state: ShardState, start_slot: ShardSlot) -> Self { Self { store, shard_state: Cow::Owned(shard_state), @@ -29,7 +29,7 @@ impl<'a, T: EthSpec, U: Store> StateRootsIterator<'a, T, U> { } impl<'a, T: EthSpec, U: Store> Iterator for StateRootsIterator<'a, T, U> { - type Item = (Hash256, Slot); + type Item = (Hash256, ShardSlot); fn next(&mut self) -> Option { if (self.slot == 0) || (self.slot > self.shard_state.slot) { @@ -157,7 +157,7 @@ pub struct BestBlockRootsIterator<'a, T: EthSpec, U> { } impl<'a, T: EthSpec, U: Store> BestBlockRootsIterator<'a, T, U> { - pub fn new(store: Arc, beacon_state: &'a ShardState, start_slot: ShardSlot) -> Self { + pub fn new(store: Arc, shard_state: &'a ShardState, start_slot: ShardSlot) -> Self { let mut slot = start_slot; if slot >= shard_state.slot { // Slot may be too high. diff --git a/shard_node/shard_store/src/lib.rs b/shard_node/shard_store/src/lib.rs new file mode 100644 index 00000000000..79ca4c3c20a --- /dev/null +++ b/shard_node/shard_store/src/lib.rs @@ -0,0 +1,221 @@ +//! Storage functionality for Lighthouse. +//! +//! Provides the following stores: +//! +//! - `DiskStore`: an on-disk store backed by leveldb. Used in production. +//! - `MemoryStore`: an in-memory store backed by a hash-map. Used for testing. +//! +//! Provides a simple API for storing/retrieving all types that sometimes needs type-hints. See +//! tests for implementation examples. + +mod impls; +mod block_at_slot; +mod errors; +mod memory_store; + +pub use memory_store::MemoryStore; +pub use errors::Error; +pub use types::*; + +/// An object capable of storing and retrieving objects implementing `StoreItem`. +/// +/// A `Store` is fundamentally backed by a key-value database, however it provides support for +/// columns. A simple column implementation might involve prefixing a key with some bytes unique to +/// each column. +pub trait Store: Sync + Send + Sized { + /// Store an item in `Self`. + fn put(&self, key: &Hash256, item: &impl StoreItem) -> Result<(), Error> { + item.db_put(self, key) + } + + /// Retrieve an item from `Self`. + fn get(&self, key: &Hash256) -> Result, Error> { + I::db_get(self, key) + } + + /// Returns `true` if the given key represents an item in `Self`. + fn exists(&self, key: &Hash256) -> Result { + I::db_exists(self, key) + } + + /// Remove an item from `Self`. + fn delete(&self, key: &Hash256) -> Result<(), Error> { + I::db_delete(self, key) + } + + /// Given the root of an existing block in the store (`start_block_root`), return a parent + /// block with the specified `slot`. + /// + /// Returns `None` if no parent block exists at that slot, or if `slot` is greater than the + /// slot of `start_block_root`. + fn get_block_at_preceeding_slot( + &self, + start_block_root: Hash256, + slot: ShardSlot, + ) -> Result, Error> { + block_at_slot::get_block_at_preceeding_slot(self, slot, start_block_root) + } + + /// Retrieve some bytes in `column` with `key`. + fn get_bytes(&self, column: &str, key: &[u8]) -> Result>, Error>; + + /// Store some `value` in `column`, indexed with `key`. + fn put_bytes(&self, column: &str, key: &[u8], value: &[u8]) -> Result<(), Error>; + + /// Return `true` if `key` exists in `column`. + fn key_exists(&self, column: &str, key: &[u8]) -> Result; + + /// Removes `key` from `column`. + fn key_delete(&self, column: &str, key: &[u8]) -> Result<(), Error>; +} + +/// A unique column identifier. +pub enum DBColumn { + ShardBlock, + ShardState, + ShardChain, +} + +impl<'a> Into<&'a str> for DBColumn { + /// Returns a `&str` that can be used for keying a key-value data base. + fn into(self) -> &'a str { + match self { + DBColumn::ShardBlock => &"blk", + DBColumn::ShardState => &"ste", + DBColumn::ShardChain => &"bch", + } + } +} + +/// An item that may be stored in a `Store`. +/// +/// Provides default methods that are suitable for most applications, however when overridden they +/// provide full customizability of `Store` operations. +pub trait StoreItem: Sized { + /// Identifies which column this item should be placed in. + fn db_column() -> DBColumn; + + /// Serialize `self` as bytes. + fn as_store_bytes(&self) -> Vec; + + /// De-serialize `self` from bytes. + fn from_store_bytes(bytes: &mut [u8]) -> Result; + + /// Store `self`. + fn db_put(&self, store: &impl Store, key: &Hash256) -> Result<(), Error> { + let column = Self::db_column().into(); + let key = key.as_bytes(); + + store + .put_bytes(column, key, &self.as_store_bytes()) + .map_err(Into::into) + } + + /// Retrieve an instance of `Self`. + fn db_get(store: &impl Store, key: &Hash256) -> Result, Error> { + let column = Self::db_column().into(); + let key = key.as_bytes(); + + match store.get_bytes(column, key)? { + Some(mut bytes) => Ok(Some(Self::from_store_bytes(&mut bytes[..])?)), + None => Ok(None), + } + } + + /// Return `true` if an instance of `Self` exists in `Store`. + fn db_exists(store: &impl Store, key: &Hash256) -> Result { + let column = Self::db_column().into(); + let key = key.as_bytes(); + + store.key_exists(column, key) + } + + /// Delete `self` from the `Store`. + fn db_delete(store: &impl Store, key: &Hash256) -> Result<(), Error> { + let column = Self::db_column().into(); + let key = key.as_bytes(); + + store.key_delete(column, key) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ssz::{Decode, Encode}; + use ssz_derive::{Decode, Encode}; + use tempfile::tempdir; + + #[derive(PartialEq, Debug, Encode, Decode)] + struct StorableThing { + a: u64, + b: u64, + } + + impl StoreItem for StorableThing { + fn db_column() -> DBColumn { + DBColumn::ShardBlock + } + + fn as_store_bytes(&self) -> Vec { + self.as_ssz_bytes() + } + + fn from_store_bytes(bytes: &mut [u8]) -> Result { + Self::from_ssz_bytes(bytes).map_err(Into::into) + } + } + + fn test_impl(store: impl Store) { + let key = Hash256::random(); + let item = StorableThing { a: 1, b: 42 }; + + assert_eq!(store.exists::(&key), Ok(false)); + + store.put(&key, &item).unwrap(); + + assert_eq!(store.exists::(&key), Ok(true)); + + let retrieved = store.get(&key).unwrap().unwrap(); + assert_eq!(item, retrieved); + + store.delete::(&key).unwrap(); + + assert_eq!(store.exists::(&key), Ok(false)); + + assert_eq!(store.get::(&key), Ok(None)); + } + + #[test] + fn diskdb() { + let dir = tempdir().unwrap(); + let path = dir.path(); + let store = DiskStore::open(&path).unwrap(); + + test_impl(store); + } + + #[test] + fn memorydb() { + let store = MemoryStore::open(); + + test_impl(store); + } + + #[test] + fn exists() { + let store = MemoryStore::open(); + let key = Hash256::random(); + let item = StorableThing { a: 1, b: 42 }; + + assert_eq!(store.exists::(&key).unwrap(), false); + + store.put(&key, &item).unwrap(); + + assert_eq!(store.exists::(&key).unwrap(), true); + + store.delete::(&key).unwrap(); + + assert_eq!(store.exists::(&key).unwrap(), false); + } +} diff --git a/shard_node/shard_store/src/memory_store.rs b/shard_node/shard_store/src/memory_store.rs new file mode 100644 index 00000000000..1ed70211181 --- /dev/null +++ b/shard_node/shard_store/src/memory_store.rs @@ -0,0 +1,3 @@ +pub struct MemoryStore { + pub hello: u64, +} diff --git a/shard_node/store/Cargo.toml b/shard_node/store/Cargo.toml deleted file mode 100644 index bf381d12b6b..00000000000 --- a/shard_node/store/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "store" -version = "0.1.0" -authors = ["will "] -edition = "2018" - -[dependencies] diff --git a/shard_node/store/src/block_at_slot.rs b/shard_node/store/src/block_at_slot.rs deleted file mode 100644 index 369e639dc65..00000000000 --- a/shard_node/store/src/block_at_slot.rs +++ /dev/null @@ -1,191 +0,0 @@ -use super::*; -use ssz::{Decode, DecodeError}; - -fn get_block_bytes(store: &T, root: Hash256) -> Result>, Error> { - store.get_bytes(ShardBlock::db_column().into(), &root[..]) -} - -fn read_slot_from_block_bytes(bytes: &[u8]) -> Result { - let end = std::cmp::min(Slot::ssz_fixed_len(), bytes.len()); - - Slot::from_ssz_bytes(&bytes[0..end]) -} - -fn read_previous_block_root_from_block_bytes(bytes: &[u8]) -> Result { - let previous_bytes = Slot::ssz_fixed_len(); - let slice = bytes - .get(previous_bytes..previous_bytes + Hash256::ssz_fixed_len()) - .ok_or_else(|| DecodeError::BytesInvalid("Not enough bytes.".to_string()))?; - - Hash256::from_ssz_bytes(slice) -} - -pub fn get_block_at_preceeding_slot( - store: &T, - slot: Slot, - start_root: Hash256, -) -> Result, Error> { - Ok(match get_at_preceeding_slot(store, slot, start_root)? { - Some((hash, bytes)) => Some((hash, ShardBlock::from_ssz_bytes(&bytes)?)), - None => None, - }) -} - -fn get_at_preceeding_slot( - store: &T, - slot: Slot, - mut root: Hash256, -) -> Result)>, Error> { - loop { - if let Some(bytes) = get_block_bytes(store, root)? { - let this_slot = read_slot_from_block_bytes(&bytes)?; - - if this_slot == slot { - break Ok(Some((root, bytes))); - } else if this_slot < slot { - break Ok(None); - } else { - root = read_previous_block_root_from_block_bytes(&bytes)?; - } - } else { - break Ok(None); - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use ssz::Encode; - use tree_hash::TreeHash; - - #[test] - fn read_slot() { - let spec = MinimalEthSpec::default_spec(); - - let test_slot = |slot: Slot| { - let mut block = ShardBlock::empty(&spec); - block.slot = slot; - let bytes = block.as_ssz_bytes(); - assert_eq!(read_slot_from_block_bytes(&bytes).unwrap(), slot); - }; - - test_slot(Slot::new(0)); - test_slot(Slot::new(1)); - test_slot(Slot::new(42)); - test_slot(Slot::new(u64::max_value())); - } - - #[test] - fn bad_slot() { - for i in 0..8 { - assert!(read_slot_from_block_bytes(&vec![0; i]).is_err()); - } - } - - #[test] - fn read_previous_block_root() { - let spec = MinimalEthSpec::default_spec(); - - let test_root = |root: Hash256| { - let mut block = ShardBlock::empty(&spec); - block.previous_block_root = root; - let bytes = block.as_ssz_bytes(); - assert_eq!( - read_previous_block_root_from_block_bytes(&bytes).unwrap(), - root - ); - }; - - test_root(Hash256::random()); - test_root(Hash256::random()); - test_root(Hash256::random()); - } - - fn build_chain( - store: &impl Store, - slots: &[usize], - spec: &ChainSpec, - ) -> Vec<(Hash256, ShardBlock)> { - let mut blocks_and_roots: Vec<(Hash256, ShardBlock)> = vec![]; - - for (i, slot) in slots.iter().enumerate() { - let mut block = ShardBlock::empty(spec); - block.slot = Slot::from(*slot); - - if i > 0 { - block.previous_block_root = blocks_and_roots[i - 1].0; - } - - let root = Hash256::from_slice(&block.tree_hash_root()); - - store.put(&root, &block).unwrap(); - blocks_and_roots.push((root, block)); - } - - blocks_and_roots - } - - #[test] - fn chain_without_skips() { - let n: usize = 10; - let store = MemoryStore::open(); - let spec = MinimalEthSpec::default_spec(); - - let slots: Vec = (0..n).collect(); - let blocks_and_roots = build_chain(&store, &slots, &spec); - - for source in 1..n { - for target in 0..=source { - let (source_root, _source_block) = &blocks_and_roots[source]; - let (target_root, target_block) = &blocks_and_roots[target]; - - let (found_root, found_block) = store - .get_block_at_preceeding_slot(*source_root, target_block.slot) - .unwrap() - .unwrap(); - - assert_eq!(found_root, *target_root); - assert_eq!(found_block, *target_block); - } - } - } - - #[test] - fn chain_with_skips() { - let store = MemoryStore::open(); - let spec = MinimalEthSpec::default_spec(); - - let slots = vec![0, 1, 2, 5]; - - let blocks_and_roots = build_chain(&store, &slots, &spec); - - // Valid slots - for target in 0..3 { - let (source_root, _source_block) = &blocks_and_roots[3]; - let (target_root, target_block) = &blocks_and_roots[target]; - - let (found_root, found_block) = store - .get_block_at_preceeding_slot(*source_root, target_block.slot) - .unwrap() - .unwrap(); - - assert_eq!(found_root, *target_root); - assert_eq!(found_block, *target_block); - } - - // Slot that doesn't exist - let (source_root, _source_block) = &blocks_and_roots[3]; - assert!(store - .get_block_at_preceeding_slot(*source_root, Slot::new(3)) - .unwrap() - .is_none()); - - // Slot too high - let (source_root, _source_block) = &blocks_and_roots[3]; - assert!(store - .get_block_at_preceeding_slot(*source_root, Slot::new(3)) - .unwrap() - .is_none()); - } -} diff --git a/shard_node/store/src/lib.rs b/shard_node/store/src/lib.rs deleted file mode 100644 index e7a11a969c0..00000000000 --- a/shard_node/store/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} diff --git a/shard_node/store/src/memory_store.rs b/shard_node/store/src/memory_store.rs deleted file mode 100644 index e69de29bb2d..00000000000 From 61cc293c90213f554233f06ff5a6df6aaedd246c Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Thu, 12 Sep 2019 17:14:45 -0500 Subject: [PATCH 060/151] Placeholder for epoch period committee state transition --- eth2/state_processing/src/per_epoch_processing.rs | 2 ++ .../src/per_epoch_processing/process_period_committee.rs | 0 2 files changed, 2 insertions(+) create mode 100644 eth2/state_processing/src/per_epoch_processing/process_period_committee.rs diff --git a/eth2/state_processing/src/per_epoch_processing.rs b/eth2/state_processing/src/per_epoch_processing.rs index be421340817..4dbdf60f039 100644 --- a/eth2/state_processing/src/per_epoch_processing.rs +++ b/eth2/state_processing/src/per_epoch_processing.rs @@ -61,6 +61,8 @@ pub fn per_epoch_processing( // Slashings. process_slashings(state, validator_statuses.total_balances.current_epoch, spec)?; + // process_period_committee(state)?; + // Final updates. process_final_updates(state, spec)?; diff --git a/eth2/state_processing/src/per_epoch_processing/process_period_committee.rs b/eth2/state_processing/src/per_epoch_processing/process_period_committee.rs new file mode 100644 index 00000000000..e69de29bb2d From f363ca731d0fda83ae0c61dd0758111b068a41a8 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Thu, 12 Sep 2019 17:23:02 -0500 Subject: [PATCH 061/151] Basic shard block/state processing functions --- eth2/state_processing/src/lib.rs | 4 ++++ .../src/per_shard_block_processing.rs | 10 ++++++++++ .../src/per_shard_slot_processing.rs | 12 ++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 eth2/state_processing/src/per_shard_block_processing.rs create mode 100644 eth2/state_processing/src/per_shard_slot_processing.rs diff --git a/eth2/state_processing/src/lib.rs b/eth2/state_processing/src/lib.rs index e040c152579..1caa5255268 100644 --- a/eth2/state_processing/src/lib.rs +++ b/eth2/state_processing/src/lib.rs @@ -6,6 +6,8 @@ pub mod get_genesis_state; pub mod per_block_processing; pub mod per_epoch_processing; pub mod per_slot_processing; +pub mod per_shard_block_processing; +pub mod per_shard_slot_processing; pub use get_genesis_state::get_genesis_beacon_state; pub use per_block_processing::{ @@ -14,3 +16,5 @@ pub use per_block_processing::{ }; pub use per_epoch_processing::{errors::EpochProcessingError, per_epoch_processing}; pub use per_slot_processing::{per_slot_processing, Error as SlotProcessingError}; +pub use per_shard_block_processing::per_shard_block_processing; +pub use per_shard_slot_processing::per_shard_slot_processing; diff --git a/eth2/state_processing/src/per_shard_block_processing.rs b/eth2/state_processing/src/per_shard_block_processing.rs new file mode 100644 index 00000000000..d983a801648 --- /dev/null +++ b/eth2/state_processing/src/per_shard_block_processing.rs @@ -0,0 +1,10 @@ +use types::*; + +pub fn per_shard_block_processing( + beacon_state: &BeaconState, + state: &mut ShardState, + block: &ShardBlock, + spec: &ChainSpec, +) -> Result<(), Error> { + Ok(()) +} diff --git a/eth2/state_processing/src/per_shard_slot_processing.rs b/eth2/state_processing/src/per_shard_slot_processing.rs new file mode 100644 index 00000000000..ef1931d2e5e --- /dev/null +++ b/eth2/state_processing/src/per_shard_slot_processing.rs @@ -0,0 +1,12 @@ +use crate::*; +use types::*; + +pub fn per_shard_slot_processing( + state: &mut ShardState, + spec: &ChainSpec, +) -> Result<(), Error> { + // period_processing logic + state.slot += 1; + + Ok(()) +} From f833bad37db57315f684b0e1d0ca44ad302f052f Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Thu, 12 Sep 2019 20:10:25 -0500 Subject: [PATCH 062/151] Remove cargo from shard node for now --- shard_node/Cargo.toml | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 shard_node/Cargo.toml diff --git a/shard_node/Cargo.toml b/shard_node/Cargo.toml deleted file mode 100644 index e39d872edb3..00000000000 --- a/shard_node/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "shard_node" -version = "0.1.0" -authors = ["will "] -edition = "2018" - -[dependencies] From 6e8258eadabad5c994879d25fcd65d76f2fcd202 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Thu, 12 Sep 2019 22:11:20 -0500 Subject: [PATCH 063/151] Inclue all state transition functions for period committees and roots --- .../src/per_epoch_processing.rs | 5 ++- .../process_period_committee.rs | 31 +++++++++++++++++++ eth2/types/src/beacon_state.rs | 7 +++++ .../beacon_state/period_committee_cache.rs | 4 +-- 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/eth2/state_processing/src/per_epoch_processing.rs b/eth2/state_processing/src/per_epoch_processing.rs index 4dbdf60f039..f321153e125 100644 --- a/eth2/state_processing/src/per_epoch_processing.rs +++ b/eth2/state_processing/src/per_epoch_processing.rs @@ -1,5 +1,6 @@ use apply_rewards::process_rewards_and_penalties; use errors::EpochProcessingError as Error; +use process_period_committee::process_period_committee; use process_slashings::process_slashings; use registry_updates::process_registry_updates; use std::collections::HashMap; @@ -10,6 +11,7 @@ use winning_root::{winning_root, WinningRoot}; pub mod apply_rewards; pub mod errors; +pub mod process_period_committee; pub mod process_slashings; pub mod registry_updates; pub mod tests; @@ -61,7 +63,8 @@ pub fn per_epoch_processing( // Slashings. process_slashings(state, validator_statuses.total_balances.current_epoch, spec)?; - // process_period_committee(state)?; + // Set period committees + process_period_committee(state, spec)?; // Final updates. process_final_updates(state, spec)?; diff --git a/eth2/state_processing/src/per_epoch_processing/process_period_committee.rs b/eth2/state_processing/src/per_epoch_processing/process_period_committee.rs index e69de29bb2d..083126345d5 100644 --- a/eth2/state_processing/src/per_epoch_processing/process_period_committee.rs +++ b/eth2/state_processing/src/per_epoch_processing/process_period_committee.rs @@ -0,0 +1,31 @@ +use super::Error; +use tree_hash::TreeHash; +use types::*; + +pub fn process_period_committee( + state: &mut BeaconState, + spec: &ChainSpec, +) -> Result<(), Error> { + let current_epoch = state.current_epoch(); + + if (current_epoch + 1) % spec.epochs_per_shard_period != 0 { + return Ok(()); + } + + let shard_fork_period = ShardSlot::from(spec.phase_1_fork_slot) + .epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot) + .period(spec.epochs_per_shard_period); + let current_period = current_epoch.period(spec.epochs_per_shard_period); + + if current_period - shard_fork_period + 2 >= 0 { + state.advance_period_cache(spec); + state.period_committee_roots[(spec.period_committee_root_length - 1) as usize] = + Hash256::from_slice( + &state.period_caches[state.period_index(RelativePeriod::Next)] + .committees + .tree_hash_root()[..], + ); + } + + Ok(()) +} diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index 644953dea17..d2ca20f7744 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -890,6 +890,13 @@ impl BeaconState { }) } + pub fn advance_period_cache(&mut self, spec: &ChainSpec) -> Result<(), Error> { + let next_committee = PeriodCommitteeCache::initialize(self, spec)?; + self.period_caches.rotate_left(1); + self.period_caches[self.period_index(RelativePeriod::Next)] = next_committee; + Ok(()) + } + /// Build all the caches, if they need to be built. pub fn build_all_caches(&mut self, spec: &ChainSpec) -> Result<(), Error> { self.build_committee_cache(RelativeEpoch::Previous, spec)?; diff --git a/eth2/types/src/beacon_state/period_committee_cache.rs b/eth2/types/src/beacon_state/period_committee_cache.rs index 8bf9fc5afa0..2b1ed6b36d1 100644 --- a/eth2/types/src/beacon_state/period_committee_cache.rs +++ b/eth2/types/src/beacon_state/period_committee_cache.rs @@ -5,7 +5,7 @@ use ssz_derive::{Decode, Encode}; #[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize, Encode, Decode)] pub struct PeriodCommitteeCache { - committees: Vec, + pub committees: Vec, } impl PeriodCommitteeCache { @@ -14,7 +14,7 @@ impl PeriodCommitteeCache { spec: &ChainSpec, ) -> Result { let current_epoch = state.current_epoch(); - if current_epoch % spec.epochs_per_shard_period != 0 { + if (current_epoch + 1) % spec.epochs_per_shard_period != 0 { return Err(Error::NoPeriodBoundary); } From a2654d5784035aa91dc7cb7bcd45d6cce01f368b Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Thu, 12 Sep 2019 22:20:53 -0500 Subject: [PATCH 064/151] mock part for period processing on the shard --- .../per_epoch_processing/process_period_committee.rs | 2 +- .../state_processing/src/per_shard_slot_processing.rs | 11 ++++++++++- eth2/types/src/shard_state.rs | 7 ++----- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/eth2/state_processing/src/per_epoch_processing/process_period_committee.rs b/eth2/state_processing/src/per_epoch_processing/process_period_committee.rs index 083126345d5..df7d34866ec 100644 --- a/eth2/state_processing/src/per_epoch_processing/process_period_committee.rs +++ b/eth2/state_processing/src/per_epoch_processing/process_period_committee.rs @@ -19,7 +19,7 @@ pub fn process_period_committee( if current_period - shard_fork_period + 2 >= 0 { state.advance_period_cache(spec); - state.period_committee_roots[(spec.period_committee_root_length - 1) as usize] = + state.period_committee_roots[(current_period.as_u64() % spec.period_committee_root_length) as usize] = Hash256::from_slice( &state.period_caches[state.period_index(RelativePeriod::Next)] .committees diff --git a/eth2/state_processing/src/per_shard_slot_processing.rs b/eth2/state_processing/src/per_shard_slot_processing.rs index ef1931d2e5e..0c0b54bca21 100644 --- a/eth2/state_processing/src/per_shard_slot_processing.rs +++ b/eth2/state_processing/src/per_shard_slot_processing.rs @@ -5,7 +5,16 @@ pub fn per_shard_slot_processing( state: &mut ShardState, spec: &ChainSpec, ) -> Result<(), Error> { - // period_processing logic + if (state + .slot + .epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot) + + 1) + % spec.epochs_per_shard_period + == 0 + { + // include period processing here :) + } + state.slot += 1; Ok(()) diff --git a/eth2/types/src/shard_state.rs b/eth2/types/src/shard_state.rs index d2fd4e6d4d2..1ddf80dd09e 100644 --- a/eth2/types/src/shard_state.rs +++ b/eth2/types/src/shard_state.rs @@ -6,9 +6,9 @@ use hashing::hash; use serde_derive::{Deserialize, Serialize}; use ssz::ssz_encode; use ssz_derive::{Decode, Encode}; +use std::marker::PhantomData; use test_random_derive::TestRandom; use tree_hash::TreeHash; -use std::marker::PhantomData; use tree_hash_derive::{CachedTreeHash, TreeHash}; #[derive(Debug, PartialEq)] @@ -52,10 +52,7 @@ where } impl ShardState { - pub fn genesis( - spec: &ChainSpec, - shard: u64, - ) -> ShardState { + pub fn genesis(spec: &ChainSpec, shard: u64) -> ShardState { ShardState { shard, slot: ShardSlot::from(spec.phase_1_fork_slot), From bcb0a62bff188501105c0ea3851b8baf556979e0 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Fri, 13 Sep 2019 12:46:05 -0500 Subject: [PATCH 065/151] ItemStore container also holds period cache --- beacon_node/store/src/impls/beacon_state.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/beacon_node/store/src/impls/beacon_state.rs b/beacon_node/store/src/impls/beacon_state.rs index 591663fe051..d4a1228727f 100644 --- a/beacon_node/store/src/impls/beacon_state.rs +++ b/beacon_node/store/src/impls/beacon_state.rs @@ -2,27 +2,34 @@ use crate::*; use ssz::{Decode, DecodeError, Encode}; use ssz_derive::{Decode, Encode}; use std::convert::TryInto; -use types::beacon_state::{CommitteeCache, CACHED_EPOCHS}; +use types::beacon_state::{CommitteeCache, PeriodCommitteeCache, CACHED_EPOCHS, CACHED_PERIODS}; /// A container for storing `BeaconState` components. #[derive(Encode, Decode)] struct StorageContainer { state_bytes: Vec, committee_caches_bytes: Vec>, + period_caches_bytes: Vec>, } impl StorageContainer { /// Create a new instance for storing a `BeaconState`. pub fn new(state: &BeaconState) -> Self { let mut committee_caches_bytes = vec![]; + let mut period_caches_bytes = vec![]; for cache in state.committee_caches[..].iter() { committee_caches_bytes.push(cache.as_ssz_bytes()); } + for cache in state.period_caches[..].iter() { + period_caches_bytes.push(cache.as_ssz_bytes()); + } + Self { state_bytes: state.as_ssz_bytes(), committee_caches_bytes, + period_caches_bytes, } } } @@ -43,6 +50,16 @@ impl TryInto> for StorageContainer { state.committee_caches[i] = CommitteeCache::from_ssz_bytes(bytes)?; } + for i in 0..CACHED_PERIODS { + let bytes = &self.period_caches_bytes.get(i).ok_or_else(|| { + Error::SszDecodeError(DecodeError::BytesInvalid( + "Insufficient period committees for BeaconState".to_string(), + )) + })?; + + state.period_caches[i] = PeriodCommitteeCache::from_ssz_bytes(bytes)?; + } + Ok(state) } } From fdf085dafd645b3241bc07d043a775f41ac197cb Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Fri, 13 Sep 2019 19:17:44 -0500 Subject: [PATCH 066/151] Shard state now implements shardspec --- eth2/types/src/shard_state.rs | 14 ++++++- .../src/shard_state/shard_state_types.rs | 40 +++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 eth2/types/src/shard_state/shard_state_types.rs diff --git a/eth2/types/src/shard_state.rs b/eth2/types/src/shard_state.rs index 1ddf80dd09e..802d8f101bf 100644 --- a/eth2/types/src/shard_state.rs +++ b/eth2/types/src/shard_state.rs @@ -2,6 +2,7 @@ use crate::test_utils::TestRandom; use crate::*; use cached_tree_hash::{Error as TreeHashCacheError, TreeHashCache}; use compare_fields_derive::CompareFields; +use fixed_len_vec::FixedLenVec; use hashing::hash; use serde_derive::{Deserialize, Serialize}; use ssz::ssz_encode; @@ -11,6 +12,10 @@ use test_random_derive::TestRandom; use tree_hash::TreeHash; use tree_hash_derive::{CachedTreeHash, TreeHash}; +pub use shard_state_types::*; + +mod shard_state_types; + #[derive(Debug, PartialEq)] pub enum Error { TreeHashCacheError(TreeHashCacheError), @@ -31,10 +36,11 @@ pub enum Error { )] pub struct ShardState where - T: EthSpec, + T: ShardSpec, { pub shard: u64, pub slot: ShardSlot, + pub history_accumulator: FixedLenVec, #[serde(skip_serializing, skip_deserializing)] #[ssz(skip_serializing)] @@ -51,11 +57,15 @@ where _phantom: PhantomData, } -impl ShardState { +impl ShardState { pub fn genesis(spec: &ChainSpec, shard: u64) -> ShardState { ShardState { shard, slot: ShardSlot::from(spec.phase_1_fork_slot), + history_accumulator: FixedLenVec::from(vec![ + spec.zero_hash; + T::HistoryAccumulatorDepth::to_usize() + ]), tree_hash_cache: TreeHashCache::default(), _phantom: PhantomData, } diff --git a/eth2/types/src/shard_state/shard_state_types.rs b/eth2/types/src/shard_state/shard_state_types.rs new file mode 100644 index 00000000000..dfbe6d9236a --- /dev/null +++ b/eth2/types/src/shard_state/shard_state_types.rs @@ -0,0 +1,40 @@ +use crate::*; +use fixed_len_vec::typenum::{U64}; +use serde_derive::{Deserialize, Serialize}; +use std::fmt::Debug; + +pub trait ShardSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq { + type HistoryAccumulatorDepth: Unsigned + Clone + Sync + Send + Debug + PartialEq; + + fn default_spec() -> ChainSpec; + + fn history_accumulator_depth() -> usize { + Self::HistoryAccumulatorDepth::to_usize() + } +} + +#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)] +pub struct MainnetShardSpec; + +impl ShardSpec for MainnetShardSpec { + type HistoryAccumulatorDepth = U64; + + fn default_spec() -> ChainSpec { + ChainSpec::mainnet() + } +} + +pub type FoundationShardState = ShardState; + +#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)] +pub struct MinimalShardSpec; + +impl ShardSpec for MinimalShardSpec { + type HistoryAccumulatorDepth = U64; + + fn default_spec() -> ChainSpec { + ChainSpec::minimal() + } +} + +pub type MinimalShardState = ShardState; From 7699f24494670431375b63e560b4e4502814e6fc Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Fri, 13 Sep 2019 19:18:57 -0500 Subject: [PATCH 067/151] Shard Slot Processing used and corrected --- .../src/per_shard_slot_processing.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/eth2/state_processing/src/per_shard_slot_processing.rs b/eth2/state_processing/src/per_shard_slot_processing.rs index 0c0b54bca21..052a07b74fe 100644 --- a/eth2/state_processing/src/per_shard_slot_processing.rs +++ b/eth2/state_processing/src/per_shard_slot_processing.rs @@ -1,7 +1,8 @@ use crate::*; use types::*; +use tree_hash::TreeHash; -pub fn per_shard_slot_processing( +pub fn per_shard_slot_processing( state: &mut ShardState, spec: &ChainSpec, ) -> Result<(), Error> { @@ -15,7 +16,23 @@ pub fn per_shard_slot_processing( // include period processing here :) } + process_shard_slot(state, spec); + state.slot += 1; Ok(()) } + +// need to put this in separate directory (process slots) +fn process_shard_slot( + state: &mut ShardState, + spec: &ChainSpec, +) -> () { + let previous_state_root = Hash256::from_slice(&state.tree_hash_root()[..]); + let mut depth = 0; + + while (state.slot.as_u64() % u64::pow(2, depth as u32) == 0 as u64) && (depth < T::history_accumulator_depth() as u64) { + state.history_accumulator[depth as usize] = previous_state_root; + depth += 1; + } +} From 8d85f39a79bc32e386e4bb751460e1306a2e4e76 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Fri, 13 Sep 2019 21:02:01 -0500 Subject: [PATCH 068/151] Fields follow names closely to spec - added latest header to state --- eth2/types/src/shard_block.rs | 13 +++++++----- eth2/types/src/shard_block_header.rs | 30 ++++++++++++++++------------ 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/eth2/types/src/shard_block.rs b/eth2/types/src/shard_block.rs index 9ce46b1dc7d..09276ccf3ae 100644 --- a/eth2/types/src/shard_block.rs +++ b/eth2/types/src/shard_block.rs @@ -24,8 +24,10 @@ use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash}; pub struct ShardBlock { pub shard: u64, pub slot: ShardSlot, - pub previous_block_root: Hash256, + pub beacon_block_root: Hash256, + pub parent_root: Hash256, pub state_root: Hash256, + // add body pub attestation: ShardAttestation, #[signed_root(skip_hashing)] pub signature: Signature, @@ -36,7 +38,8 @@ impl ShardBlock { ShardBlock { shard, slot: ShardSlot::from(spec.phase_1_fork_slot), - previous_block_root: spec.zero_hash, + beacon_block_root: spec.zero_hash, + parent_root: spec.zero_hash, state_root: spec.zero_hash, attestation: ShardAttestation::default(), signature: Signature::empty_signature(), @@ -51,10 +54,10 @@ impl ShardBlock { ShardBlockHeader { shard: self.shard, slot: self.slot, - beacon_block_root: Hash256::default(), - previous_block_root: self.previous_block_root, + beacon_block_root: self.beacon_block_root, + parent_root: self.parent_root, state_root: self.state_root, - body_root: Hash256::default(), + attestation: self.attestation.clone(), signature: self.signature.clone(), } } diff --git a/eth2/types/src/shard_block_header.rs b/eth2/types/src/shard_block_header.rs index 994ffcb4e58..e38364653c5 100644 --- a/eth2/types/src/shard_block_header.rs +++ b/eth2/types/src/shard_block_header.rs @@ -24,10 +24,11 @@ use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash}; pub struct ShardBlockHeader { pub slot: ShardSlot, pub shard: u64, - pub previous_block_root: Hash256, + pub parent_root: Hash256, pub beacon_block_root: Hash256, pub state_root: Hash256, - pub body_root: Hash256, + // need to add body + pub attestation: ShardAttestation, #[signed_root(skip_hashing)] pub signature: Signature, } @@ -37,21 +38,24 @@ impl ShardBlockHeader { Hash256::from_slice(&self.signed_root()[..]) } - // pub fn into_block(self, body: ShardBlockBody) -> ShardBlock { - // ShardBlock { - // slot: self.slot, - // previous_block_root: self.previous_block_root, - // state_root: self.state_root, - // body, - // signature: self.signature, - // } - // } + pub fn into_block(self) -> ShardBlock { + // add body logic + ShardBlock { + shard: self.shard, + slot: self.slot, + beacon_block_root: self.beacon_block_root, + parent_root: self.parent_root, + state_root: self.state_root, + attestation: self.attestation, + signature: self.signature, + } + } } #[cfg(test)] mod tests { use super::*; - ssz_tests!(BeaconBlockHeader); - cached_tree_hash_tests!(BeaconBlockHeader); + ssz_tests!(ShardBlockHeader); + cached_tree_hash_tests!(ShardBlockHeader); } From 6cb3997f284eeaa2f4ae3f2891829d203279fa6d Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Fri, 13 Sep 2019 21:53:11 -0500 Subject: [PATCH 069/151] Shard State holds shard block header --- eth2/types/src/shard_block_header.rs | 12 ++++++++++++ eth2/types/src/shard_state.rs | 2 ++ 2 files changed, 14 insertions(+) diff --git a/eth2/types/src/shard_block_header.rs b/eth2/types/src/shard_block_header.rs index e38364653c5..6377c489ce4 100644 --- a/eth2/types/src/shard_block_header.rs +++ b/eth2/types/src/shard_block_header.rs @@ -34,6 +34,18 @@ pub struct ShardBlockHeader { } impl ShardBlockHeader { + pub fn empty(spec: &ChainSpec, shard: u64) -> ShardBlockHeader { + ShardBlockHeader { + shard, + slot: ShardSlot::from(spec.phase_1_fork_slot), + beacon_block_root: spec.zero_hash, + parent_root: spec.zero_hash, + state_root: spec.zero_hash, + attestation: ShardAttestation::default(), + signature: Signature::empty_signature(), + } + } + pub fn canonical_root(&self) -> Hash256 { Hash256::from_slice(&self.signed_root()[..]) } diff --git a/eth2/types/src/shard_state.rs b/eth2/types/src/shard_state.rs index 802d8f101bf..38ebaf2c4cd 100644 --- a/eth2/types/src/shard_state.rs +++ b/eth2/types/src/shard_state.rs @@ -41,6 +41,7 @@ where pub shard: u64, pub slot: ShardSlot, pub history_accumulator: FixedLenVec, + pub latest_block_header: ShardBlockHeader, #[serde(skip_serializing, skip_deserializing)] #[ssz(skip_serializing)] @@ -66,6 +67,7 @@ impl ShardState { spec.zero_hash; T::HistoryAccumulatorDepth::to_usize() ]), + latest_block_header: ShardBlockHeader::empty(spec, shard), tree_hash_cache: TreeHashCache::default(), _phantom: PhantomData, } From 46429bf37541314fffd8b28b8d29a263e044d515 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sat, 14 Sep 2019 02:54:23 -0500 Subject: [PATCH 070/151] Update iterators to work properly with shard state/blocks --- shard_node/shard_store/src/impls.rs | 14 ++++ shard_node/shard_store/src/iter.rs | 119 +++++++++++----------------- shard_node/shard_store/src/lib.rs | 1 + 3 files changed, 60 insertions(+), 74 deletions(-) diff --git a/shard_node/shard_store/src/impls.rs b/shard_node/shard_store/src/impls.rs index 8ff8f974ff6..481c98904c1 100644 --- a/shard_node/shard_store/src/impls.rs +++ b/shard_node/shard_store/src/impls.rs @@ -14,3 +14,17 @@ impl StoreItem for ShardBlock { Self::from_ssz_bytes(bytes).map_err(Into::into) } } + +impl StoreItem for ShardState { + fn db_column() -> DBColumn { + DBColumn::ShardState + } + + fn as_store_bytes(&self) -> Vec { + self.as_ssz_bytes() + } + + fn from_store_bytes(bytes: &mut [u8]) -> Result { + Self::from_ssz_bytes(bytes).map_err(Into::into) + } +} diff --git a/shard_node/shard_store/src/iter.rs b/shard_node/shard_store/src/iter.rs index ce524582f5c..0210a5c73c4 100644 --- a/shard_node/shard_store/src/iter.rs +++ b/shard_node/shard_store/src/iter.rs @@ -1,16 +1,16 @@ use crate::Store; use std::borrow::Cow; use std::sync::Arc; -use types::{ShardBlock, ShardState, ShardStateError, EthSpec, Hash256, ShardSlot}; +use types::{ShardBlock, ShardState, ShardStateError, ShardSpec, Hash256, ShardSlot}; #[derive(Clone)] -pub struct StateRootsIterator<'a, T: EthSpec, U> { +pub struct StateRootsIterator<'a, T: ShardSpec, U> { store: Arc, shard_state: Cow<'a, ShardState>, slot: ShardSlot, } -impl<'a, T: EthSpec, U: Store> StateRootsIterator<'a, T, U> { +impl<'a, T: ShardSpec, U: Store> StateRootsIterator<'a, T, U> { pub fn new(store: Arc, shard_state: &'a ShardState, start_slot: ShardSlot) -> Self { Self { store, @@ -28,7 +28,7 @@ impl<'a, T: EthSpec, U: Store> StateRootsIterator<'a, T, U> { } } -impl<'a, T: EthSpec, U: Store> Iterator for StateRootsIterator<'a, T, U> { +impl<'a, T: ShardSpec, U: Store> Iterator for StateRootsIterator<'a, T, U> { type Item = (Hash256, ShardSlot); fn next(&mut self) -> Option { @@ -38,42 +38,34 @@ impl<'a, T: EthSpec, U: Store> Iterator for StateRootsIterator<'a, T, U> { self.slot -= 1; - match self.shard_state.get_state_root(self.slot) { - Ok(root) => Some((*root, self.slot)), - Err(ShardStateError::SlotOutOfBounds) => { - // Read a `BeaconState` from the store that has access to prior historical root. - let shard_state: ShardState = { - let new_state_root = self.shard_state.get_oldest_state_root().ok()?; + // Efficiency gain if using log search via the accumulator instead + while self.slot < self.shard_state.slot { + let next_root = self.shard_state.history_accumulator[0]; + let shard_state: ShardState = self.store.get(&next_root).ok()??; - self.store.get(&new_state_root).ok()? - }?; - - self.shard_state = Cow::Owned(shard_state); - - let root = self.shard_state.get_state_root(self.slot).ok()?; - - Some((*root, self.slot)) + if self.slot > shard_state.slot { + return Some((Hash256::zero(), self.slot)); } - _ => None, + + self.shard_state = Cow::Owned(shard_state); } + + Some((self.shard_state.latest_block_header.state_root, self.slot)) } } #[derive(Clone)] -/// Extends `BlockRootsIterator`, returning `BeaconBlock` instances, instead of their roots. -pub struct BlockIterator<'a, T: EthSpec, U> { +pub struct BlockIterator<'a, T: ShardSpec, U> { roots: BlockRootsIterator<'a, T, U>, } -impl<'a, T: EthSpec, U: Store> BlockIterator<'a, T, U> { - /// Create a new iterator over all blocks in the given `beacon_state` and prior states. +impl<'a, T: ShardSpec, U: Store> BlockIterator<'a, T, U> { pub fn new(store: Arc, shard_state: &'a ShardState, start_slot: ShardSlot) -> Self { Self { roots: BlockRootsIterator::new(store, shard_state, start_slot), } } - /// Create a new iterator over all blocks in the given `beacon_state` and prior states. pub fn owned(store: Arc, shard_state: ShardState, start_slot: ShardSlot) -> Self { Self { roots: BlockRootsIterator::owned(store, shard_state, start_slot), @@ -81,7 +73,7 @@ impl<'a, T: EthSpec, U: Store> BlockIterator<'a, T, U> { } } -impl<'a, T: EthSpec, U: Store> Iterator for BlockIterator<'a, T, U> { +impl<'a, T: ShardSpec, U: Store> Iterator for BlockIterator<'a, T, U> { type Item = ShardBlock; fn next(&mut self) -> Option { @@ -91,13 +83,13 @@ impl<'a, T: EthSpec, U: Store> Iterator for BlockIterator<'a, T, U> { } #[derive(Clone)] -pub struct BlockRootsIterator<'a, T: EthSpec, U> { +pub struct BlockRootsIterator<'a, T: ShardSpec, U> { store: Arc, shard_state: Cow<'a, ShardState>, slot: ShardSlot, } -impl<'a, T: EthSpec, U: Store> BlockRootsIterator<'a, T, U> { +impl<'a, T: ShardSpec, U: Store> BlockRootsIterator<'a, T, U> { /// Create a new iterator over all block roots in the given `shard_state` and prior states. pub fn new(store: Arc, shard_state: &'a ShardState, start_slot: ShardSlot) -> Self { Self { @@ -107,7 +99,7 @@ impl<'a, T: EthSpec, U: Store> BlockRootsIterator<'a, T, U> { } } - /// Create a new iterator over all block roots in the given `beacon_state` and prior states. + /// Create a new iterator over all block roots in the given `shard_state` and prior states. pub fn owned(store: Arc, shard_state: ShardState, start_slot: ShardSlot) -> Self { Self { store, @@ -117,7 +109,7 @@ impl<'a, T: EthSpec, U: Store> BlockRootsIterator<'a, T, U> { } } -impl<'a, T: EthSpec, U: Store> Iterator for BlockRootsIterator<'a, T, U> { +impl<'a, T: ShardSpec, U: Store> Iterator for BlockRootsIterator<'a, T, U> { type Item = (Hash256, ShardSlot); fn next(&mut self) -> Option { @@ -127,44 +119,34 @@ impl<'a, T: EthSpec, U: Store> Iterator for BlockRootsIterator<'a, T, U> { self.slot -= 1; - match self.shard_state.get_block_root(self.slot) { - Ok(root) => Some((*root, self.slot)), - Err(ShardStateError::SlotOutOfBounds) => { - // Read a `ShardState` from the store that has access to prior historical root. - let shard_state: ShardState = { - // Load the earliest state from disk. - let new_state_root = self.shard_state.get_oldest_state_root().ok()?; - - self.store.get(&new_state_root).ok()? - }?; - - self.shard_state = Cow::Owned(shard_state); + // Efficiency gain if using log search via the accumulator instead + while self.slot < self.shard_state.slot { + let next_root = self.shard_state.history_accumulator[0]; + let shard_state: ShardState = self.store.get(&next_root).ok()??; - let root = self.shard_state.get_block_root(self.slot).ok()?; - - Some((*root, self.slot)) + if self.slot > shard_state.slot { + return Some((Hash256::zero(), self.slot)); } - _ => None, + + self.shard_state = Cow::Owned(shard_state); } + + Some((self.shard_state.latest_block_header.canonical_root(), self.slot)) } } #[derive(Clone)] -pub struct BestBlockRootsIterator<'a, T: EthSpec, U> { +pub struct BestBlockRootsIterator<'a, T: ShardSpec, U> { store: Arc, shard_state: Cow<'a, ShardState>, slot: ShardSlot, } -impl<'a, T: EthSpec, U: Store> BestBlockRootsIterator<'a, T, U> { +impl<'a, T: ShardSpec, U: Store> BestBlockRootsIterator<'a, T, U> { pub fn new(store: Arc, shard_state: &'a ShardState, start_slot: ShardSlot) -> Self { let mut slot = start_slot; if slot >= shard_state.slot { - // Slot may be too high. slot = shard_state.slot; - if shard_state.get_block_root(slot).is_err() { - slot -= 1; - } } Self { @@ -174,17 +156,12 @@ impl<'a, T: EthSpec, U: Store> BestBlockRootsIterator<'a, T, U> { } } - /// Create a new iterator over all block roots in the given `beacon_state` and prior states. + /// Create a new iterator over all block roots in the given `shard_state` and prior states. pub fn owned(store: Arc, shard_state: ShardState, start_slot: ShardSlot) -> Self { let mut slot = start_slot; if slot >= shard_state.slot { // Slot may be too high. slot = shard_state.slot; - // TODO: Use a function other than `get_block_root` as this will always return `Err()` - // for slot = state.slot. - if shard_state.get_block_root(slot).is_err() { - slot -= 1; - } } Self { @@ -195,7 +172,7 @@ impl<'a, T: EthSpec, U: Store> BestBlockRootsIterator<'a, T, U> { } } -impl<'a, T: EthSpec, U: Store> Iterator for BestBlockRootsIterator<'a, T, U> { +impl<'a, T: ShardSpec, U: Store> Iterator for BestBlockRootsIterator<'a, T, U> { type Item = (Hash256, ShardSlot); fn next(&mut self) -> Option { @@ -206,24 +183,18 @@ impl<'a, T: EthSpec, U: Store> Iterator for BestBlockRootsIterator<'a, T, U> { self.slot -= 1; - match self.shard_state.get_block_root(self.slot) { - Ok(root) => Some((*root, self.slot)), - Err(ShardStateError::SlotOutOfBounds) => { - // Read a `BeaconState` from the store that has access to prior historical root. - let shard_state: ShardState = { - // Load the earliest state from disk. - let new_state_root = self.shard_state.get_oldest_state_root().ok()?; + // Efficiency gain if using log search via the accumulator instead + while self.slot < self.shard_state.slot { + let next_root = self.shard_state.history_accumulator[0]; + let shard_state: ShardState = self.store.get(&next_root).ok()??; - self.store.get(&new_state_root).ok()? - }?; - - self.shard_state = Cow::Owned(shard_state); - - let root = self.shard_state.get_block_root(self.slot).ok()?; - - Some((*root, self.slot)) + if self.slot > shard_state.slot { + return Some((Hash256::zero(), self.slot)); } - _ => None, + + self.shard_state = Cow::Owned(shard_state); } + + Some((self.shard_state.latest_block_header.canonical_root(), self.slot)) } } \ No newline at end of file diff --git a/shard_node/shard_store/src/lib.rs b/shard_node/shard_store/src/lib.rs index 79ca4c3c20a..2885bb1d0e6 100644 --- a/shard_node/shard_store/src/lib.rs +++ b/shard_node/shard_store/src/lib.rs @@ -10,6 +10,7 @@ mod impls; mod block_at_slot; +mod iter; mod errors; mod memory_store; From cda140ef3440bfa3733eb0dbcecc0e8b3092e90c Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sat, 14 Sep 2019 03:07:30 -0500 Subject: [PATCH 071/151] Type updates for ShardSpec --- eth2/shard_operation_pool/src/lib.rs | 14 +++++++------- .../src/per_epoch_processing/errors.rs | 1 + .../src/per_shard_block_processing.rs | 4 ++-- eth2/types/src/beacon_state.rs | 3 ++- eth2/types/src/lib.rs | 2 +- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/eth2/shard_operation_pool/src/lib.rs b/eth2/shard_operation_pool/src/lib.rs index 7db43ac8619..c9cbfc92257 100644 --- a/eth2/shard_operation_pool/src/lib.rs +++ b/eth2/shard_operation_pool/src/lib.rs @@ -6,26 +6,26 @@ use parking_lot::RwLock; use std::collections::{btree_map::Entry, hash_map, BTreeMap, HashMap, HashSet}; use std::marker::PhantomData; use types::{ - BeaconState, ShardAttestation, ShardSlot, ShardState, ChainSpec, EthSpec, Validator + BeaconState, ShardAttestation, ShardSlot, ShardState, ChainSpec, EthSpec, ShardSpec, Validator }; #[derive(Default, Debug)] -pub struct ShardOperationPool { +pub struct OperationPool { attestations: RwLock>>, _phantom: PhantomData, } -impl ShardOperationPool { +impl OperationPool { /// Create a new operation pool. pub fn new() -> Self { Self::default() } /// Insert an attestation into the pool, aggregating it with existing attestations if possible. - pub fn insert_attestation( + pub fn insert_attestation( &self, attestation: ShardAttestation, - beacon_state: &BeaconState, + beacon_state: &BeaconState, spec: &ChainSpec, ) -> () { let id = AttestationId::from_data(&attestation.data, beacon_state, spec); @@ -64,7 +64,7 @@ impl ShardOperationPool { } /// Get a list of attestations for inclusion in a block. - pub fn get_attestations(&self, state: &ShardState, beacon_state: &BeaconState, spec: &ChainSpec) -> Vec { + pub fn get_attestations(&self, state: &ShardState, beacon_state: &BeaconState, spec: &ChainSpec) -> Vec { // enforce the right beacon state is being passed through let attesting_slot = ShardSlot::from(state.slot - 1); let epoch = attesting_slot.epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot); @@ -104,6 +104,6 @@ where .collect() } -impl PartialEq for ShardOperationPool { +impl PartialEq for OperationPool { fn eq(&self, other: &Self) -> bool { *self.attestations.read() == *other.attestations.read()} } diff --git a/eth2/state_processing/src/per_epoch_processing/errors.rs b/eth2/state_processing/src/per_epoch_processing/errors.rs index 4632e83bb52..947c7e2a77a 100644 --- a/eth2/state_processing/src/per_epoch_processing/errors.rs +++ b/eth2/state_processing/src/per_epoch_processing/errors.rs @@ -16,6 +16,7 @@ pub enum EpochProcessingError { /// (validator_index) InclusionSlotsInconsistent(usize), BeaconStateError(BeaconStateError), + ShardStateError(ShardStateError), InclusionError(InclusionError), } diff --git a/eth2/state_processing/src/per_shard_block_processing.rs b/eth2/state_processing/src/per_shard_block_processing.rs index d983a801648..4e1885a2737 100644 --- a/eth2/state_processing/src/per_shard_block_processing.rs +++ b/eth2/state_processing/src/per_shard_block_processing.rs @@ -1,8 +1,8 @@ use types::*; -pub fn per_shard_block_processing( +pub fn per_shard_block_processing( beacon_state: &BeaconState, - state: &mut ShardState, + state: &mut ShardState, block: &ShardBlock, spec: &ChainSpec, ) -> Result<(), Error> { diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index d2ca20f7744..a160824ae47 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -27,6 +27,7 @@ mod pubkey_cache; mod tests; pub const CACHED_EPOCHS: usize = 3; +pub const CACHED_PERIODS: usize = 3; const MAX_RANDOM_BYTE: u64 = (1 << 8) - 1; #[derive(Debug, PartialEq)] @@ -141,7 +142,7 @@ where #[ssz(skip_deserializing)] #[tree_hash(skip_hashing)] #[test_random(default)] - pub period_caches: [PeriodCommitteeCache; 3], + pub period_caches: [PeriodCommitteeCache; CACHED_PERIODS], #[serde(default)] #[ssz(skip_serializing)] #[ssz(skip_deserializing)] diff --git a/eth2/types/src/lib.rs b/eth2/types/src/lib.rs index 999f1d5eafb..4e9d14c2c5c 100644 --- a/eth2/types/src/lib.rs +++ b/eth2/types/src/lib.rs @@ -77,7 +77,7 @@ pub use crate::shard_attestation_data::ShardAttestationData; pub use crate::shard_block::ShardBlock; pub use crate::shard_block_header::ShardBlockHeader; pub use crate::shard_pending_attestation::ShardPendingAttestation; -pub use crate::shard_state::ShardState; +pub use crate::shard_state::{Error as ShardStateError, *}; pub use crate::slot_epoch::{Epoch, Slot, ShardSlot}; pub use crate::slot_height::{SlotHeight, ShardSlotHeight}; pub use crate::transfer::Transfer; From d6c6b466e9e47960489ea3fa55e2387ffe04087b Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sat, 14 Sep 2019 03:15:07 -0500 Subject: [PATCH 072/151] lmd ghost using right trait for shardspec and iterators --- Cargo.toml | 1 + eth2/shard_lmd_ghost/cargo.toml | 22 +++++++++++++++++++ eth2/shard_lmd_ghost/src/lib.rs | 10 ++++----- eth2/shard_lmd_ghost/src/reduced_tree.rs | 28 ++++++++++++------------ shard_node/shard_store/src/lib.rs | 2 +- 5 files changed, 43 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e5ae7a1455f..36b67ccf2c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "eth2/lmd_ghost", + "eth2/shard_lmd_ghost", "eth2/operation_pool", "eth2/shard_operation_pool", "eth2/state_processing", diff --git a/eth2/shard_lmd_ghost/cargo.toml b/eth2/shard_lmd_ghost/cargo.toml index e69de29bb2d..a8c11726337 100644 --- a/eth2/shard_lmd_ghost/cargo.toml +++ b/eth2/shard_lmd_ghost/cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "shard_lmd_ghost" +version = "0.1.0" +authors = ["Age Manning ", "Paul Hauner "] +edition = "2018" + +[dependencies] +parking_lot = "0.7" +shard_store = { path = "../../shard_node/shard_store" } +eth2_ssz = { path = "../utils/ssz" } +state_processing = { path = "../state_processing" } +types = { path = "../types" } +log = "0.4.6" +bit-vec = "0.5.0" + +[dev-dependencies] +criterion = "0.2" +hex = "0.3.2" +yaml-rust = "0.4.2" +bls = { path = "../utils/bls" } +slot_clock = { path = "../utils/slot_clock" } +env_logger = "0.6.0" diff --git a/eth2/shard_lmd_ghost/src/lib.rs b/eth2/shard_lmd_ghost/src/lib.rs index d3e5104ebee..b5cf55fbcce 100644 --- a/eth2/shard_lmd_ghost/src/lib.rs +++ b/eth2/shard_lmd_ghost/src/lib.rs @@ -1,14 +1,14 @@ mod reduced_tree; use std::sync::Arc; -use store::Store; -use types::{ShardBlock, EthSpec, Hash256, Slot}; +use shard_store::Store; +use types::{ShardBlock, ShardSpec, Hash256, ShardSlot}; pub use reduced_tree::ThreadSafeReducedTree; pub type Result = std::result::Result; -pub trait LmdGhost: Send + Sync { +pub trait LmdGhost: Send + Sync { /// Create a new instance, with the given `store` and `finalized_root`. fn new(store: Arc, finalized_block: &ShardBlock, finalized_root: Hash256) -> Self; @@ -18,7 +18,7 @@ pub trait LmdGhost: Send + Sync { &self, validator_index: usize, block_hash: Hash256, - block_slot: Slot, + block_slot: ShardSlot, ) -> Result<()>; /// Process a block that was seen on the network. @@ -28,7 +28,7 @@ pub trait LmdGhost: Send + Sync { /// (in block height). fn find_head( &self, - start_block_slot: Slot, + start_block_slot: ShardSlot, start_block_root: Hash256, weight: F, ) -> Result diff --git a/eth2/shard_lmd_ghost/src/reduced_tree.rs b/eth2/shard_lmd_ghost/src/reduced_tree.rs index 7e382d6999f..a4a320b5de0 100644 --- a/eth2/shard_lmd_ghost/src/reduced_tree.rs +++ b/eth2/shard_lmd_ghost/src/reduced_tree.rs @@ -8,8 +8,8 @@ use parking_lot::RwLock; use std::collections::HashMap; use std::marker::PhantomData; use std::sync::Arc; -use store::{iter::BestBlockRootsIterator, Error as StoreError, Store}; -use types::{ShardBlock, ShardState, EthSpec, Hash256, Slot}; +use shard_store::{iter::BestBlockRootsIterator, Error as StoreError, Store}; +use types::{ShardBlock, ShardState, ShardSpec, Hash256, ShardSlot}; type Result = std::result::Result; @@ -38,7 +38,7 @@ pub struct ThreadSafeReducedTree { impl LmdGhost for ThreadSafeReducedTree where T: Store, - E: EthSpec, + E: ShardSpec, { fn new(store: Arc, genesis_block: &ShardBlock, genesis_root: Hash256) -> Self { ThreadSafeReducedTree { @@ -50,7 +50,7 @@ where &self, validator_index: usize, block_hash: Hash256, - block_slot: Slot, + block_slot: ShardSlot, ) -> SuperResult<()> { self.core .write() @@ -68,7 +68,7 @@ where fn find_head( &self, - start_block_slot: Slot, + start_block_slot: ShardSlot, start_block_root: Hash256, weight_fn: F, ) -> SuperResult @@ -96,14 +96,14 @@ struct ReducedTree { /// Maps validator indices to their latest votes. latest_votes: ElasticList>, /// Stores the root of the tree, used for pruning. - root: (Hash256, Slot), + root: (Hash256, ShardSlot), _phantom: PhantomData, } impl ReducedTree where T: Store, - E: EthSpec, + E: ShardSpec, { pub fn new(store: Arc, genesis_block: &ShardBlock, genesis_root: Hash256) -> Self { let mut nodes = HashMap::new(); @@ -126,7 +126,7 @@ where } } - pub fn update_root(&mut self, new_slot: Slot, new_root: Hash256) -> Result<()> { + pub fn update_root(&mut self, new_slot: ShardSlot, new_root: Hash256) -> Result<()> { if !self.nodes.contains_key(&new_root) { let node = Node { block_hash: new_root, @@ -169,7 +169,7 @@ where &mut self, validator_index: usize, block_hash: Hash256, - slot: Slot, + slot: ShardSlot, ) -> Result<()> { if slot >= self.root_slot() { if let Some(previous_vote) = self.latest_votes.get(validator_index) { @@ -200,7 +200,7 @@ where pub fn update_weights_and_find_head( &mut self, - start_block_slot: Slot, + start_block_slot: ShardSlot, start_block_root: Hash256, weight_fn: F, ) -> Result @@ -384,7 +384,7 @@ where Ok(()) } - fn add_weightless_node(&mut self, slot: Slot, hash: Hash256) -> Result<()> { + fn add_weightless_node(&mut self, slot: ShardSlot, hash: Hash256) -> Result<()> { if slot >= self.root_slot() && !self.nodes.contains_key(&hash) { let node = Node { block_hash: hash, @@ -486,7 +486,7 @@ where } /// For the given `child` block hash, return the block's ancestor at the given `target` slot. - fn find_ancestor_at_slot(&self, child: Hash256, target: Slot) -> Result { + fn find_ancestor_at_slot(&self, child: Hash256, target: ShardSlot) -> Result { let (root, slot) = self .iter_ancestors(child)? .find(|(_block, slot)| *slot <= target) @@ -565,7 +565,7 @@ where .ok_or_else(|| Error::MissingState(state_root)) } - fn root_slot(&self) -> Slot { + fn root_slot(&self) -> ShardSlot { self.root.1 } } @@ -612,7 +612,7 @@ impl Node { #[derive(Debug, Clone, Copy)] pub struct Vote { hash: Hash256, - slot: Slot, + slot: ShardSlot, } /// A Vec-wrapper which will grow to match any request. diff --git a/shard_node/shard_store/src/lib.rs b/shard_node/shard_store/src/lib.rs index 2885bb1d0e6..ebd8337ff4c 100644 --- a/shard_node/shard_store/src/lib.rs +++ b/shard_node/shard_store/src/lib.rs @@ -8,9 +8,9 @@ //! Provides a simple API for storing/retrieving all types that sometimes needs type-hints. See //! tests for implementation examples. +pub mod iter; mod impls; mod block_at_slot; -mod iter; mod errors; mod memory_store; From 2cded1eaa445a0cff0b2ae79ddf3635b1096a1e1 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sat, 14 Sep 2019 11:45:06 -0500 Subject: [PATCH 073/151] Shard chain dependencies in check and all is compiling --- Cargo.toml | 1 + shard_node/shard_chain/Cargo.toml | 25 + shard_node/shard_chain/src/checkpoint.rs | 10 +- shard_node/shard_chain/src/errors.rs | 22 +- shard_node/shard_chain/src/fork_choice.rs | 189 ++-- shard_node/shard_chain/src/lib.rs | 11 +- shard_node/shard_chain/src/shard_chain.rs | 1064 ++++++++++----------- 7 files changed, 648 insertions(+), 674 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 36b67ccf2c8..1b34748bfee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ members = [ "beacon_node/version", "beacon_node/beacon_chain", "shard_node/shard_store", + "shard_node/shard_chain", "tests/ef_tests", "protos", "validator_client", diff --git a/shard_node/shard_chain/Cargo.toml b/shard_node/shard_chain/Cargo.toml index 41f5df3b8d4..76866dff006 100644 --- a/shard_node/shard_chain/Cargo.toml +++ b/shard_node/shard_chain/Cargo.toml @@ -5,3 +5,28 @@ authors = ["will "] edition = "2018" [dependencies] +beacon_chain = { path = "../../beacon_node/beacon_chain" } +bls = { path = "../../eth2/utils/bls" } +boolean-bitfield = { path = "../../eth2/utils/boolean-bitfield" } +shard_store = { path = "../shard_store" } +failure = "0.1" +failure_derive = "0.1" +hashing = { path = "../../eth2/utils/hashing" } +parking_lot = "0.7" +prometheus = "^0.6" +log = "0.4" +shard_operation_pool = { path = "../../eth2/shard_operation_pool" } +env_logger = "0.6" +serde = "1.0" +serde_derive = "1.0" +serde_json = "1.0" +slot_clock = { path = "../../eth2/utils/slot_clock" } +eth2_ssz = { path = "../../eth2/utils/ssz" } +eth2_ssz_derive = { path = "../../eth2/utils/ssz_derive" } +state_processing = { path = "../../eth2/state_processing" } +tree_hash = { path = "../../eth2/utils/tree_hash" } +types = { path = "../../eth2/types" } +shard_lmd_ghost = { path = "../../eth2/shard_lmd_ghost" } + +[dev-dependencies] +rand = "0.5.5" diff --git a/shard_node/shard_chain/src/checkpoint.rs b/shard_node/shard_chain/src/checkpoint.rs index e39e2dbd1e1..c32b76d6d52 100644 --- a/shard_node/shard_chain/src/checkpoint.rs +++ b/shard_node/shard_chain/src/checkpoint.rs @@ -1,19 +1,16 @@ use serde_derive::Serialize; use ssz_derive::{Decode, Encode}; -use types::{ShardBlock, ShardState, EthSpec, Hash256}; +use types::{ShardBlock, ShardState, ShardSpec, Hash256}; -/// Represents some block and it's associated state. Generally, this will be used for tracking the -/// head, justified head and finalized head. #[derive(Clone, Serialize, PartialEq, Debug, Encode, Decode)] -pub struct CheckPoint { +pub struct CheckPoint { pub shard_block: ShardBlock, pub shard_block_root: Hash256, pub shard_state: ShardState, pub shard_state_root: Hash256, } -impl CheckPoint { - /// Create a new checkpoint. +impl CheckPoint { pub fn new( shard_block: ShardBlock, shard_block_root: Hash256, @@ -28,7 +25,6 @@ impl CheckPoint { } } - /// Update all fields of the checkpoint. pub fn update( &mut self, shard_block: ShardBlock, diff --git a/shard_node/shard_chain/src/errors.rs b/shard_node/shard_chain/src/errors.rs index 926d0bda338..2ecbf45d297 100644 --- a/shard_node/shard_chain/src/errors.rs +++ b/shard_node/shard_chain/src/errors.rs @@ -1,5 +1,5 @@ -use crate::fork_choice::Error as ForkChoiceError; -use crate::metrics::Error as MetricsError; +// use crate::fork_choice::Error as ForkChoiceError; +// use crate::metrics::Error as MetricsError; use state_processing::BlockProcessingError; use state_processing::SlotProcessingError; use types::*; @@ -19,23 +19,23 @@ pub enum ShardChainError { InsufficientValidators, BadRecentBlockRoots, UnableToReadSlot, - ShardStateError(BeaconStateError), + ShardStateError(ShardStateError), DBInconsistent(String), - DBError(store::Error), - ForkChoiceError(ForkChoiceError), + DBError(shard_store::Error), + // ForkChoiceError(ForkChoiceError), MissingShardBlock(Hash256), MissingShardState(Hash256), SlotProcessingError(SlotProcessingError), - MetricsError(String), + // MetricsError(String), } easy_from_to!(SlotProcessingError, ShardChainError); -impl From for ShardChainError { - fn from(e: MetricsError) -> ShardChainError { - ShardChainError::MetricsError(format!("{:?}", e)) - } -} +// impl From for ShardChainError { +// fn from(e: MetricsError) -> ShardChainError { +// ShardChainError::MetricsError(format!("{:?}", e)) +// } +// } #[derive(Debug, PartialEq)] pub enum BlockProductionError { diff --git a/shard_node/shard_chain/src/fork_choice.rs b/shard_node/shard_chain/src/fork_choice.rs index 06e0439d414..3422949ecd7 100644 --- a/shard_node/shard_chain/src/fork_choice.rs +++ b/shard_node/shard_chain/src/fork_choice.rs @@ -1,3 +1,10 @@ +use crate::{ShardChain, ShardChainTypes}; +use shard_lmd_ghost::LmdGhost; +use state_processing::common::get_attesting_indices_unsorted; +use std::sync::Arc; +use shard_store::{Error as StoreError, Store}; +use types::{Attestation, ShardBlock, ShardState, ShardStateError, Epoch, ShardSpec, Hash256}; + type Result = std::result::Result; #[derive(Debug, PartialEq)] @@ -10,116 +17,108 @@ pub enum Error { } pub struct ForkChoice { - backend: T::ShardLmdGhost, - /// Used for resolving the `0x00..00` alias back to genesis. - /// - /// Does not necessarily need to be the _actual_ genesis, it suffices to be the finalized root - /// whenever the struct was instantiated. + backend: T::LmdGhost, genesis_block_root: Hash256, } -impl ForkChoice { - /// Instantiate a new fork chooser. - /// - /// "Genesis" does not necessarily need to be the absolute genesis, it can be some finalized - /// block. +impl ForkChoice { pub fn new( store: Arc, genesis_block: &ShardBlock, genesis_block_root: Hash256, ) -> Self { Self { - backend: T::ShardLmdGhost::new(store, genesis_block, genesis_block_root), + backend: T::LmdGhost::new(store, genesis_block, genesis_block_root), genesis_block_root, } } - // general pseudocode here - pub fn find_head(&self, chain: &ShardChain) -> Result { - let beacon_state = chain.get_beacon_state(); - let finalized_epoch = beacon_state.finalized_epoch; - let start_block_root = chain.get_crosslink(finalized_epoch); - let start_block_slot = chain.get_block(start_block_root).slot; - - // A function that returns the weight for some validator index. - let weight = |validator_index: usize| -> Option { - beacon_state - .validator_registry - .get(validator_index) - .map(|v| v.effective_balance) - }; - - self.backend - .find_head(start_block_slot, start_block_root, weight) - .map_err(Into::into) - } - - /// Process all attestations in the given `block`. - /// - /// Assumes the block (and therefore it's attestations) are valid. It is a logic error to - /// provide an invalid block. - pub fn process_block( - &self, - beacon_state: &BeaconState, - block: &ShardBlock, - block_root: Hash256, - ) -> Result<()> { - // Note: we never count the block as a latest message, only attestations. - // - // I (Paul H) do not have an explicit reference to this, but I derive it from this - // document: - // - // https://github.com/ethereum/eth2.0-specs/blob/v0.7.0/specs/core/0_fork-choice.md - for attestation in &block.body.attestations { - self.process_attestation_from_block(beacon_state, attestation, block)?; - } - - self.backend.process_block(block, block_root)?; - - Ok(()) - } - - fn process_attestation_from_block( - &self, - beacon_state: &BeaconState, - attestation: &Attestation, - block: &ShardBlock - ) -> Result<()> { - // Note: `get_attesting_indices_unsorted` requires that the beacon state caches be built. - let validator_indices = get_shard_attesting_indices_unsorted( - block.slot, - beacon_state, - &attestation.data, - &attestation.aggregation_bitfield, - )?; - - let block_hash = attestation.data.target_root; - - if block_hash != Hash256::zero() { - for validator_index in validator_indices { - self.backend - .process_attestation(validator_index, block_hash, block.slot)?; - } - } - - Ok(()) - } - - /// Inform the fork choice that the given block (and corresponding root) have been finalized so - /// it may prune it's storage. - /// - /// `finalized_block_root` must be the root of `finalized_block`. - pub fn process_finalization( - &self, - finalized_block: &BeaconBlock, - finalized_block_root: Hash256, - ) -> Result<()> { - self.backend - .update_finalized_root(finalized_block, finalized_block_root) - .map_err(Into::into) - } + // pub fn find_head(&self, chain: &ShardChain) -> Result { + // let beacon_state = chain.get_beacon_state(); + // let finalized_epoch = beacon_state.finalized_epoch; + // let start_block_root = chain.get_crosslink(finalized_epoch); + // let start_block_slot = chain.get_block(start_block_root).slot; + + // // A function that returns the weight for some validator index. + // let weight = |validator_index: usize| -> Option { + // beacon_state + // .validator_registry + // .get(validator_index) + // .map(|v| v.effective_balance) + // }; + + // self.backend + // .find_head(start_block_slot, start_block_root, weight) + // .map_err(Into::into) + // } + + // /// Process all attestations in the given `block`. + // /// + // /// Assumes the block (and therefore it's attestations) are valid. It is a logic error to + // /// provide an invalid block. + // pub fn process_block( + // &self, + // beacon_state: &BeaconState, + // block: &ShardBlock, + // block_root: Hash256, + // ) -> Result<()> { + // // Note: we never count the block as a latest message, only attestations. + // // + // // I (Paul H) do not have an explicit reference to this, but I derive it from this + // // document: + // // + // // https://github.com/ethereum/eth2.0-specs/blob/v0.7.0/specs/core/0_fork-choice.md + // for attestation in &block.body.attestations { + // self.process_attestation_from_block(beacon_state, attestation, block)?; + // } + + // self.backend.process_block(block, block_root)?; + + // Ok(()) + // } + + // fn process_attestation_from_block( + // &self, + // beacon_state: &BeaconState, + // attestation: &Attestation, + // block: &ShardBlock + // ) -> Result<()> { + // // Note: `get_attesting_indices_unsorted` requires that the beacon state caches be built. + // let validator_indices = get_shard_attesting_indices_unsorted( + // block.slot, + // beacon_state, + // &attestation.data, + // &attestation.aggregation_bitfield, + // )?; + + // let block_hash = attestation.data.target_root; + + // if block_hash != Hash256::zero() { + // for validator_index in validator_indices { + // self.backend + // .process_attestation(validator_index, block_hash, block.slot)?; + // } + // } + + // Ok(()) + // } + + // /// Inform the fork choice that the given block (and corresponding root) have been finalized so + // /// it may prune it's storage. + // /// + // /// `finalized_block_root` must be the root of `finalized_block`. + // pub fn process_finalization( + // &self, + // finalized_block: &BeaconBlock, + // finalized_block_root: Hash256, + // ) -> Result<()> { + // self.backend + // .update_finalized_root(finalized_block, finalized_block_root) + // .map_err(Into::into) + // } } + impl From for Error { fn from(e: ShardStateError) -> Error { Error::ShardStateError(e) diff --git a/shard_node/shard_chain/src/lib.rs b/shard_node/shard_chain/src/lib.rs index e7a11a969c0..613b3507129 100644 --- a/shard_node/shard_chain/src/lib.rs +++ b/shard_node/shard_chain/src/lib.rs @@ -1,3 +1,8 @@ -fn main() { - println!("Hello, world!"); -} +pub mod shard_chain; +pub mod checkpoint; +pub mod errors; +pub mod fork_choice; + +pub use self::shard_chain::{ShardChain, ShardChainTypes}; +pub use self::checkpoint::CheckPoint; +pub use self::errors::{ShardChainError, BlockProductionError}; diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 7a19b04d564..6aab47907e9 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -1,20 +1,18 @@ use crate::checkpoint::CheckPoint; use crate::errors::{ShardChainError as Error, BlockProductionError}; +use beacon_chain::{BeaconChain, BeaconChainTypes}; use crate::fork_choice::{Error as ForkChoiceError, ForkChoice}; -use lmd_ghost::LmdGhost; -// use operation_pool::{OperationPool}; +use shard_lmd_ghost::LmdGhost; +use shard_operation_pool::{OperationPool}; use parking_lot::{RwLock, RwLockReadGuard}; use slot_clock::SlotClock; -use state_processing::per_block_processing::errors::{ - AttestationValidationError, AttesterSlashingValidationError, -}; use state_processing::{ - per_block_processing, per_block_processing_without_verifying_block_signature, - per_slot_processing, BlockProcessingError, + per_shard_block_processing, + per_shard_slot_processing, BlockProcessingError, }; use std::sync::Arc; // use store::iter::{BestBlockRootsIterator, BlockIterator, BlockRootsIterator, StateRootsIterator}; -use store::{Error as DBError, Store}; +use shard_store::{Error as DBError, Store}; use tree_hash::TreeHash; use types::*; @@ -25,10 +23,10 @@ use types::*; pub const GRAFFITI: &str = "sigp/lighthouse-0.0.0-prerelease"; pub trait ShardChainTypes { - type Store: store::Store; + type Store: shard_store::Store; type SlotClock: slot_clock::SlotClock; - type LmdGhost: LmdGhost; - type EthSpec: types::EthSpec; + type LmdGhost: LmdGhost; + type ShardSpec: types::ShardSpec; } /// Represents the "Shard Chain" component of Ethereum 2.0. It holds a reference to a parent Beacon Chain @@ -36,38 +34,26 @@ pub struct ShardChain { pub parent_beacon: Arc>, pub shard: Shard, pub spec: ChainSpec, - /// Persistent storage for blocks, states, etc. Typically an on-disk store, such as LevelDB. pub store: Arc, - /// Reports the current slot, typically based upon the system clock. pub slot_clock: T::SlotClock, - /// Stores all operations (e.g., transactions) that are candidates for - /// inclusion in a block. - pub op_pool: OperationPool, - /// Stores a "snapshot" of the chain at the time the head-of-the-chain block was recieved. - canonical_head: RwLock>, - /// The same state from `self.canonical_head`, but updated at the start of each slot with a - /// skip slot if no block is recieved. This is effectively a cache that avoids repeating calls - /// to `per_slot_processing`. - state: RwLock>, - /// The root of the genesis block. + pub op_pool: OperationPool, + canonical_head: RwLock>, + state: RwLock>, genesis_block_root: Hash256, - /// A state-machine that is updated with information from the network and chooses a canonical - /// head block. pub fork_choice: ForkChoice, } -impl ShardChain { - /// Instantiate a new Shard Chain, from genesis. +impl ShardChain { pub fn from_genesis( store: Arc, slot_clock: T::SlotClock, - mut genesis_state: ShardState, + mut genesis_state: ShardState, genesis_block: ShardBlock, spec: ChainSpec, shard: Shard, parent_beacon: Arc>, ) -> Result { - genesis_state.build_all_caches(&spec)?; + genesis_state.build_cache(&spec)?; let state_root = genesis_state.canonical_root(); store.put(&state_root, &genesis_state)?; @@ -100,529 +86,491 @@ impl ShardChain Result, Error> { - let bodies: Result, _> = roots - .iter() - .map(|root| match self.get_block(root)? { - Some(block) => Ok(block.body), - None => Err(Error::DBInconsistent(format!("Missing block: {}", root))), - }) - .collect(); - - Ok(bodies?) - } - - /// Returns the beacon block header for each beacon block root in `roots`. - /// - /// Fails if any root in `roots` does not have a corresponding block. - pub fn get_block_headers(&self, roots: &[Hash256]) -> Result, Error> { - let headers: Result, _> = roots - .iter() - .map(|root| match self.get_block(root)? { - Some(block) => Ok(block.block_header()), - None => Err(Error::DBInconsistent("Missing block".into())), - }) - .collect(); - - Ok(headers?) - } - /// Iterate in reverse (highest to lowest slot) through all blocks from the block at `slot` - /// through to the genesis block. - /// - /// Returns `None` for headers prior to genesis or when there is an error reading from `Store`. - /// - /// Contains duplicate headers when skip slots are encountered. - pub fn rev_iter_blocks(&self, slot: Slot) -> BlockIterator { - BlockIterator::owned(self.store.clone(), self.state.read().clone(), slot) - } - - /// Iterates in reverse (highest to lowest slot) through all block roots from `slot` through to - /// genesis. - /// - /// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. - /// - /// Contains duplicate roots when skip slots are encountered. - pub fn rev_iter_block_roots(&self, slot: Slot) -> BlockRootsIterator { - BlockRootsIterator::owned(self.store.clone(), self.state.read().clone(), slot) - } - - /// Iterates in reverse (highest to lowest slot) through all block roots from largest - /// `slot <= beacon_state.slot` through to genesis. - /// - /// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. - /// - /// Contains duplicate roots when skip slots are encountered. - pub fn rev_iter_best_block_roots( - &self, - slot: Slot, - ) -> BestBlockRootsIterator { - BestBlockRootsIterator::owned(self.store.clone(), self.state.read().clone(), slot) - } - - /// Iterates in reverse (highest to lowest slot) through all state roots from `slot` through to - /// genesis. - /// - /// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. - pub fn f(&self, slot: Slot) -> StateRootsIterator { - StateRootsIterator::owned(self.store.clone(), self.state.read().clone(), slot) - } - - /// Returns the block at the given root, if any. - /// - /// ## Errors - /// - /// May return a database error. - pub fn get_block(&self, block_root: &Hash256) -> Result, Error> { - Ok(self.store.get(block_root)?) - } - - /// Returns a read-lock guarded `BeaconState` which is the `canonical_head` that has been - /// updated to match the current slot clock. - pub fn current_state(&self) -> RwLockReadGuard> { - self.state.read() - } - - /// Returns a read-lock guarded `CheckPoint` struct for reading the head (as chosen by the - /// fork-choice rule). - /// - /// It is important to note that the `beacon_state` returned may not match the present slot. It - /// is the state as it was when the head block was received, which could be some slots prior to - /// now. - pub fn head(&self) -> RwLockReadGuard> { - self.canonical_head.read() - } - - /// Returns the slot of the highest block in the canonical chain. - pub fn best_slot(&self) -> Slot { - self.canonical_head.read().shard_block.slot - } - - /// Ensures the current canonical `BeaconState` has been transitioned to match the `slot_clock`. - pub fn catchup_state(&self) -> Result<(), Error> { - let spec = &self.spec; - - let present_slot = match self.slot_clock.present_slot() { - Ok(Some(slot)) => slot, - _ => return Err(Error::UnableToReadSlot), - }; - - if self.state.read().slot < present_slot { - let mut state = self.state.write(); - - // If required, transition the new state to the present slot. - for _ in state.slot.as_u64()..present_slot.as_u64() { - // per_slot_processing(&mut *state, spec)?; - // logic here to manage everything... add this in - } - - state.build_all_caches(spec)?; - } - - Ok(()) - } - - /// Build all of the caches on the current state. - /// - /// Ideally this shouldn't be required, however we leave it here for testing. - pub fn ensure_state_caches_are_built(&self) -> Result<(), Error> { - self.state.write().build_all_caches(&self.spec)?; - - Ok(()) - } - - /// Returns the validator index (if any) for the given public key. - /// - /// Information is retrieved from the present `beacon_state.validator_registry`. - pub fn validator_index(&self, pubkey: &PublicKey) -> Option { - // reference directly to beacon chain parent - // needs to make sure it is part of this particular shard - for (i, validator) in self - .parent_beacon - .current_state() - .validator_registry - .iter() - .enumerate() - { - if validator.pubkey == *pubkey { - if self.parent_beacon.current_state().get_attestation_duties(i).shard = self.shard { - return Some(i); - } - } - } - None - } - - /// Reads the slot clock, returns `None` if the slot is unavailable. - /// - /// The slot might be unavailable due to an error with the system clock, or if the present time - /// is before genesis (i.e., a negative slot). - /// - /// This is distinct to `present_slot`, which simply reads the latest state. If a - /// call to `read_slot_clock` results in a higher slot than a call to `present_slot`, - /// `self.state` should undergo per slot processing. - pub fn read_slot_clock(&self) -> Option { - match self.slot_clock.present_slot() { - Ok(Some(some_slot)) => Some(some_slot), - Ok(None) => None, - _ => None, - } - } - - /// Reads the slot clock (see `self.read_slot_clock()` and returns the number of slots since - /// genesis. - pub fn slots_since_genesis(&self) -> Option { - let now = self.read_slot_clock()?; - let genesis_slot = self.spec.genesis_slot; - - if now < genesis_slot { - None - } else { - Some(SlotHeight::from(now.as_u64() - genesis_slot.as_u64())) - } - } - - /// Returns slot of the present state. - /// - /// This is distinct to `read_slot_clock`, which reads from the actual system clock. If - /// `self.state` has not been transitioned it is possible for the system clock to be on a - /// different slot to what is returned from this call. - pub fn present_slot(&self) -> Slot { - self.state.read().slot - } - - /// Returns the block proposer for a given slot. - /// - /// Information is read from the present `beacon_state` shuffling, only information from the - /// present epoch is available. - pub fn block_proposer(&self, slot: Slot, shard: Shard) -> Result { - // Update to go to beacon chain for this information - // Ensures that the present state has been advanced to the present slot, skipping slots if - // blocks are not present. - // self.catchup_state()?; - - // // TODO: permit lookups of the proposer at any slot. - let index = self.parent_beacon.get_shard_proposer_index( - slot, - shard, - &self.spec, - )?; - - Ok(index) - } - - /// Produce an `AttestationData` that is valid for the present `slot` and given `shard`. - /// - /// Attests to the canonical chain. - pub fn produce_attestation_data(&self) -> Result { - let state = self.state.read(); - let head_block_root = self.head().shard_block_root; - let head_block_slot = self.head().shard_block.slot; - - self.produce_attestation_data_for_block(head_block_root, head_block_slot, &*state) - } - - /// Produce an `AttestationData` that attests to the chain denoted by `block_root` and `state`. - /// - /// Permits attesting to any arbitrary chain. Generally, the `produce_attestation_data` - /// function should be used as it attests to the canonical chain. - pub fn produce_attestation_data_for_block( - &self, - head_block_root: Hash256, - head_block_slot: Slot, - state: &ShardState, - ) -> Result { - - Ok(AttestationData { - shard_block_root: head_block_root, - }) - } - - /// Accept a new attestation from the network. - /// - /// If valid, the attestation is added to the `op_pool` and aggregated with another attestation - /// if possible. - pub fn process_attestation( - &self, - attestation: Attestation, - ) -> Result<(), AttestationValidationError> { - let result = self - .op_pool - .insert_attestation(attestation, &*self.state.read(), &self.spec); - - result - } - - // This needs to be written and implemented - pub fn process_transactions() -> {} - - /// Accept some block and attempt to add it to block DAG. - /// - /// Will accept blocks from prior slots, however it will reject any block from a future slot. - pub fn process_block(&self, block: ShardBlock) -> Result { - // In the future... need some logic here that will actually check to see - // if the slot has been part of a finalized crosslink on the beacon chain - // extra logic needed, but for our testnet/system we won't need this to the full degree - // let finalized_slot = self - // .state - // .read() - // .finalized_epoch - // .start_slot(T::EthSpec::slots_per_epoch()); - - // if block.slot <= finalized_slot { - // return Ok(BlockProcessingOutcome::FinalizedSlot); - // } - - if block.slot == 0 { - return Ok(BlockProcessingOutcome::GenesisBlock); - } - - let block_root = block.block_header().canonical_root(); - - if block_root == self.genesis_block_root { - return Ok(BlockProcessingOutcome::GenesisBlock); - } - - let present_slot = self - .read_slot_clock() - .ok_or_else(|| Error::UnableToReadSlot)?; - - if block.slot > present_slot { - return Ok(BlockProcessingOutcome::FutureSlot { - present_slot, - block_slot: block.slot, - }); - } - - if self.store.exists::(&block_root)? { - return Ok(BlockProcessingOutcome::BlockIsAlreadyKnown); - } - - // Load the blocks parent block from the database, returning invalid if that block is not - // found. - let parent_block_root = block.previous_block_root; - let parent_block: ShardBlock = match self.store.get(&parent_block_root)? { - Some(previous_block_root) => previous_block_root, - None => { - return Ok(BlockProcessingOutcome::ParentUnknown { - parent: parent_block_root, - }); - } - }; - - // Load the parent blocks state from the database, returning an error if it is not found. - // It is an error because if know the parent block we should also know the parent state. - let parent_state_root = parent_block.state_root; - let parent_state = self - .store - .get(&parent_state_root)? - .ok_or_else(|| Error::DBInconsistent(format!("Missing state {}", parent_state_root)))?; - - // Transition the parent state to the block slot. - let mut state: ShardState = parent_state; - for _ in state.slot.as_u64()..block.slot.as_u64() { - per_slot_processing(&mut state, &self.spec)?; - } - - // Apply the received block to its parent state (which has been transitioned into this - // slot). - match per_block_processing(&mut state, &block, &self.spec) { - Err(BlockProcessingError::ShardStateError(e)) => { - return Err(Error::ShardStateError(e)) - } - Err(e) => return Ok(BlockProcessingOutcome::PerBlockProcessingError(e)), - _ => {} - } - - let state_root = state.canonical_root(); - - if block.state_root != state_root { - return Ok(BlockProcessingOutcome::StateRootMismatch); - } - - // Store the block and state. - self.store.put(&block_root, &block)?; - self.store.put(&state_root, &state)?; + // /// Returns the beacon block body for each beacon block root in `roots`. + // /// + // /// Fails if any root in `roots` does not have a corresponding block. + // pub fn get_block_bodies(&self, roots: &[Hash256]) -> Result, Error> { + // let bodies: Result, _> = roots + // .iter() + // .map(|root| match self.get_block(root)? { + // Some(block) => Ok(block.body), + // None => Err(Error::DBInconsistent(format!("Missing block: {}", root))), + // }) + // .collect(); + + // Ok(bodies?) + // } + + // /// Returns the beacon block header for each beacon block root in `roots`. + // /// + // /// Fails if any root in `roots` does not have a corresponding block. + // pub fn get_block_headers(&self, roots: &[Hash256]) -> Result, Error> { + // let headers: Result, _> = roots + // .iter() + // .map(|root| match self.get_block(root)? { + // Some(block) => Ok(block.block_header()), + // None => Err(Error::DBInconsistent("Missing block".into())), + // }) + // .collect(); + + // Ok(headers?) + // } + // /// Iterate in reverse (highest to lowest slot) through all blocks from the block at `slot` + // /// through to the genesis block. + // /// + // /// Returns `None` for headers prior to genesis or when there is an error reading from `Store`. + // /// + // /// Contains duplicate headers when skip slots are encountered. + // pub fn rev_iter_blocks(&self, slot: Slot) -> BlockIterator { + // BlockIterator::owned(self.store.clone(), self.state.read().clone(), slot) + // } + + // /// Iterates in reverse (highest to lowest slot) through all block roots from `slot` through to + // /// genesis. + // /// + // /// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. + // /// + // /// Contains duplicate roots when skip slots are encountered. + // pub fn rev_iter_block_roots(&self, slot: Slot) -> BlockRootsIterator { + // BlockRootsIterator::owned(self.store.clone(), self.state.read().clone(), slot) + // } + + // /// Iterates in reverse (highest to lowest slot) through all block roots from largest + // /// `slot <= beacon_state.slot` through to genesis. + // /// + // /// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. + // /// + // /// Contains duplicate roots when skip slots are encountered. + // pub fn rev_iter_best_block_roots( + // &self, + // slot: Slot, + // ) -> BestBlockRootsIterator { + // BestBlockRootsIterator::owned(self.store.clone(), self.state.read().clone(), slot) + // } + + // /// Iterates in reverse (highest to lowest slot) through all state roots from `slot` through to + // /// genesis. + // /// + // /// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. + // pub fn f(&self, slot: Slot) -> StateRootsIterator { + // StateRootsIterator::owned(self.store.clone(), self.state.read().clone(), slot) + // } + + // /// Returns the block at the given root, if any. + // /// + // /// ## Errors + // /// + // /// May return a database error. + // pub fn get_block(&self, block_root: &Hash256) -> Result, Error> { + // Ok(self.store.get(block_root)?) + // } + + // /// Returns a read-lock guarded `BeaconState` which is the `canonical_head` that has been + // /// updated to match the current slot clock. + // pub fn current_state(&self) -> RwLockReadGuard> { + // self.state.read() + // } + + // /// Returns a read-lock guarded `CheckPoint` struct for reading the head (as chosen by the + // /// fork-choice rule). + // /// + // /// It is important to note that the `beacon_state` returned may not match the present slot. It + // /// is the state as it was when the head block was received, which could be some slots prior to + // /// now. + // pub fn head(&self) -> RwLockReadGuard> { + // self.canonical_head.read() + // } + + // /// Returns the slot of the highest block in the canonical chain. + // pub fn best_slot(&self) -> Slot { + // self.canonical_head.read().shard_block.slot + // } + + // /// Ensures the current canonical `BeaconState` has been transitioned to match the `slot_clock`. + // pub fn catchup_state(&self) -> Result<(), Error> { + // let spec = &self.spec; + + // let present_slot = match self.slot_clock.present_slot() { + // Ok(Some(slot)) => slot, + // _ => return Err(Error::UnableToReadSlot), + // }; + + // if self.state.read().slot < present_slot { + // let mut state = self.state.write(); + + // // If required, transition the new state to the present slot. + // for _ in state.slot.as_u64()..present_slot.as_u64() { + // // per_slot_processing(&mut *state, spec)?; + // // logic here to manage everything... add this in + // } + + // state.build_all_caches(spec)?; + // } + + // Ok(()) + // } + + // /// Build all of the caches on the current state. + // /// + // /// Ideally this shouldn't be required, however we leave it here for testing. + // pub fn ensure_state_caches_are_built(&self) -> Result<(), Error> { + // self.state.write().build_all_caches(&self.spec)?; + + // Ok(()) + // } + + // /// Returns the validator index (if any) for the given public key. + // /// + // /// Information is retrieved from the present `beacon_state.validator_registry`. + // pub fn validator_index(&self, pubkey: &PublicKey) -> Option { + // // reference directly to beacon chain parent + // // needs to make sure it is part of this particular shard + // for (i, validator) in self + // .parent_beacon + // .current_state() + // .validator_registry + // .iter() + // .enumerate() + // { + // if validator.pubkey == *pubkey { + // if self.parent_beacon.current_state().get_attestation_duties(i).shard = self.shard { + // return Some(i); + // } + // } + // } + // None + // } + + // /// Reads the slot clock, returns `None` if the slot is unavailable. + // /// + // /// The slot might be unavailable due to an error with the system clock, or if the present time + // /// is before genesis (i.e., a negative slot). + // /// + // /// This is distinct to `present_slot`, which simply reads the latest state. If a + // /// call to `read_slot_clock` results in a higher slot than a call to `present_slot`, + // /// `self.state` should undergo per slot processing. + // pub fn read_slot_clock(&self) -> Option { + // match self.slot_clock.present_slot() { + // Ok(Some(some_slot)) => Some(some_slot), + // Ok(None) => None, + // _ => None, + // } + // } + + // /// Reads the slot clock (see `self.read_slot_clock()` and returns the number of slots since + // /// genesis. + // pub fn slots_since_genesis(&self) -> Option { + // let now = self.read_slot_clock()?; + // let genesis_slot = self.spec.genesis_slot; + + // if now < genesis_slot { + // None + // } else { + // Some(SlotHeight::from(now.as_u64() - genesis_slot.as_u64())) + // } + // } + + // /// Returns slot of the present state. + // /// + // /// This is distinct to `read_slot_clock`, which reads from the actual system clock. If + // /// `self.state` has not been transitioned it is possible for the system clock to be on a + // /// different slot to what is returned from this call. + // pub fn present_slot(&self) -> Slot { + // self.state.read().slot + // } + + // /// Returns the block proposer for a given slot. + // /// + // /// Information is read from the present `beacon_state` shuffling, only information from the + // /// present epoch is available. + // pub fn block_proposer(&self, slot: Slot, shard: Shard) -> Result { + // // Update to go to beacon chain for this information + // // Ensures that the present state has been advanced to the present slot, skipping slots if + // // blocks are not present. + // // self.catchup_state()?; + + // // // TODO: permit lookups of the proposer at any slot. + // let index = self.parent_beacon.get_shard_proposer_index( + // slot, + // shard, + // &self.spec, + // )?; + + // Ok(index) + // } + + // /// Produce an `AttestationData` that is valid for the present `slot` and given `shard`. + // /// + // /// Attests to the canonical chain. + // pub fn produce_attestation_data(&self) -> Result { + // let state = self.state.read(); + // let head_block_root = self.head().shard_block_root; + // let head_block_slot = self.head().shard_block.slot; + + // self.produce_attestation_data_for_block(head_block_root, head_block_slot, &*state) + // } + + // /// Produce an `AttestationData` that attests to the chain denoted by `block_root` and `state`. + // /// + // /// Permits attesting to any arbitrary chain. Generally, the `produce_attestation_data` + // /// function should be used as it attests to the canonical chain. + // pub fn produce_attestation_data_for_block( + // &self, + // head_block_root: Hash256, + // head_block_slot: Slot, + // state: &ShardState, + // ) -> Result { + + // Ok(AttestationData { + // shard_block_root: head_block_root, + // }) + // } + + // /// Accept a new attestation from the network. + // /// + // /// If valid, the attestation is added to the `op_pool` and aggregated with another attestation + // /// if possible. + // pub fn process_attestation( + // &self, + // attestation: Attestation, + // ) -> Result<(), AttestationValidationError> { + // let result = self + // .op_pool + // .insert_attestation(attestation, &*self.state.read(), &self.spec); + + // result + // } + + // // This needs to be written and implemented + // pub fn process_transactions() -> {} + + // /// Accept some block and attempt to add it to block DAG. + // /// + // /// Will accept blocks from prior slots, however it will reject any block from a future slot. + // pub fn process_block(&self, block: ShardBlock) -> Result { + // // In the future... need some logic here that will actually check to see + // // if the slot has been part of a finalized crosslink on the beacon chain + // // extra logic needed, but for our testnet/system we won't need this to the full degree + // // let finalized_slot = self + // // .state + // // .read() + // // .finalized_epoch + // // .start_slot(T::EthSpec::slots_per_epoch()); + + // // if block.slot <= finalized_slot { + // // return Ok(BlockProcessingOutcome::FinalizedSlot); + // // } + + // if block.slot == 0 { + // return Ok(BlockProcessingOutcome::GenesisBlock); + // } + + // let block_root = block.block_header().canonical_root(); + + // if block_root == self.genesis_block_root { + // return Ok(BlockProcessingOutcome::GenesisBlock); + // } + + // let present_slot = self + // .read_slot_clock() + // .ok_or_else(|| Error::UnableToReadSlot)?; + + // if block.slot > present_slot { + // return Ok(BlockProcessingOutcome::FutureSlot { + // present_slot, + // block_slot: block.slot, + // }); + // } + + // if self.store.exists::(&block_root)? { + // return Ok(BlockProcessingOutcome::BlockIsAlreadyKnown); + // } + + // // Load the blocks parent block from the database, returning invalid if that block is not + // // found. + // let parent_block_root = block.previous_block_root; + // let parent_block: ShardBlock = match self.store.get(&parent_block_root)? { + // Some(previous_block_root) => previous_block_root, + // None => { + // return Ok(BlockProcessingOutcome::ParentUnknown { + // parent: parent_block_root, + // }); + // } + // }; + + // // Load the parent blocks state from the database, returning an error if it is not found. + // // It is an error because if know the parent block we should also know the parent state. + // let parent_state_root = parent_block.state_root; + // let parent_state = self + // .store + // .get(&parent_state_root)? + // .ok_or_else(|| Error::DBInconsistent(format!("Missing state {}", parent_state_root)))?; + + // // Transition the parent state to the block slot. + // let mut state: ShardState = parent_state; + // for _ in state.slot.as_u64()..block.slot.as_u64() { + // per_shard_slot_processing(&mut state, &self.spec)?; + // } + + // // Apply the received block to its parent state (which has been transitioned into this + // // slot). + // match per_shard_block_processing(&mut state, &block, &self.spec) { + // Err(BlockProcessingError::ShardStateError(e)) => { + // return Err(Error::ShardStateError(e)) + // } + // Err(e) => return Ok(BlockProcessingOutcome::PerBlockProcessingError(e)), + // _ => {} + // } + + // let state_root = state.canonical_root(); + + // if block.state_root != state_root { + // return Ok(BlockProcessingOutcome::StateRootMismatch); + // } + + // // Store the block and state. + // self.store.put(&block_root, &block)?; + // self.store.put(&state_root, &state)?; - // Register the new block with the fork choice service. - self.fork_choice.process_block(&state, &block, block_root)?; - - // Execute the fork choice algorithm, enthroning a new head if discovered. - // - // Note: in the future we may choose to run fork-choice less often, potentially based upon - // some heuristic around number of attestations seen for the block. - self.fork_choice()?; - Ok(BlockProcessingOutcome::Processed { block_root }) - } - - /// Produce a new block at the present slot. - /// - /// The produced block will not be inherently valid, it must be signed by a block producer. - /// Block signing is out of the scope of this function and should be done by a separate program. - pub fn produce_block( - &self, - ) -> Result<(ShardBlock, ShardState), BlockProductionError> { - let state = self.state.read().clone(); - let slot = self - .read_slot_clock() - .ok_or_else(|| BlockProductionError::UnableToReadSlot)?; - - self.produce_block_on_state(state, slot) - } - - /// Produce a block for some `slot` upon the given `state`. - /// - /// Typically the `self.produce_block()` function should be used, instead of calling this - /// function directly. This function is useful for purposefully creating forks or blocks at - /// non-current slots. - /// - /// The given state will be advanced to the given `produce_at_slot`, then a block will be - /// produced at that slot height. - pub fn produce_block_on_state( - &self, - mut state: ShardState, - produce_at_slot: Slot, - ) -> Result<(ShardBlock, ShardState), BlockProductionError> { - // If required, transition the new state to the present slot. - while state.slot < produce_at_slot { - per_slot_processing(&mut state, &self.spec)?; - } - - let previous_block_root = if state.slot > 0 { - *state - .get_block_root(state.slot - 1) - .map_err(|_| BlockProductionError::UnableToGetBlockRootFromState)? - } else { - state.latest_block_header.canonical_root() - }; - - let mut graffiti: [u8; 32] = [0; 32]; - graffiti.copy_from_slice(GRAFFITI.as_bytes()); - - let mut block = ShardBlock { - slot: state.slot, - previous_block_root, - state_root: Hash256::zero(), // Updated after the state is calculated. - signature: Signature::empty_signature(), // To be completed by a validator. - // need to add the attestations here - body: ShardBlockBody { - graffiti, - attestations: self.op_pool.get_attestations(&state, &self.spec), - }, - }; - - per_block_processing_without_verifying_block_signature(&mut state, &block, &self.spec)?; - - let state_root = state.canonical_root(); - - block.state_root = state_root; - - Ok((block, state)) - } - - /// Execute the fork choice algorithm and enthrone the result as the canonical head. - pub fn fork_choice(&self) -> Result<(), Error> { - // Determine the root of the block that is the head of the chain. - let shard_block_root = self.fork_choice.find_head(&self)?; - - // If a new head was chosen. - if shard_block_root != self.head().shard_block_root { - let shard_block: ShardBlock = self - .store - .get(&shard_block_root)? - .ok_or_else(|| Error::MissingShardBlock(shard_block_root))?; - - let shard_state_root = shard_block.state_root; - let shard_state: ShardState = self - .store - .get(&shard_state_root)? - .ok_or_else(|| Error::MissingShardState(shard_state_root))?; - - // Never revert back past a finalized epoch. - // NEED logic to make sure the slot is not coming from an older slot - // if new_finalized_epoch < old_finalized_epoch { - // Err(Error::RevertedFinalizedEpoch { - // previous_epoch: old_finalized_epoch, - // new_epoch: new_finalized_epoch, - // }) - // } else { - self.update_canonical_head(CheckPoint { - shard_block: shard_block, - shard_block_root, - shard_state, - shard_state_root, - })?; - - Ok(()) - } else { - Ok(()) - } - } - - /// Update the canonical head to `new_head`. - fn update_canonical_head(&self, new_head: CheckPoint) -> Result<(), Error> { - // Update the checkpoint that stores the head of the chain at the time it received the - // block. - *self.canonical_head.write() = new_head; - - // Update the always-at-the-present-slot state we keep around for performance gains. - *self.state.write() = { - let mut state = self.canonical_head.read().shard_state.clone(); - - let present_slot = match self.slot_clock.present_slot() { - Ok(Some(slot)) => slot, - _ => return Err(Error::UnableToReadSlot), - }; - - // If required, transition the new state to the present slot. - for _ in state.slot.as_u64()..present_slot.as_u64() { - per_slot_processing(&mut state, &self.spec)?; - } - - state.build_all_caches(&self.spec)?; - - state - }; - - Ok(()) - } - - /// Called after `self` has had a new block finalized. - /// - /// Performs pruning and finality-based optimizations. - fn after_finalization( - &self, - old_finalized_epoch: Epoch, - finalized_block_root: Hash256, - ) -> Result<(), Error> { - // Need to build logic here to manage pruning for shard as well - // let finalized_block = self - // .store - // .get::(&finalized_block_root)? - // .ok_or_else(|| Error::MissingBeaconBlock(finalized_block_root))?; - - // let new_finalized_epoch = finalized_block.slot.epoch(T::EthSpec::slots_per_epoch()); - - // if new_finalized_epoch < old_finalized_epoch { - // Err(Error::RevertedFinalizedEpoch { - // previous_epoch: old_finalized_epoch, - // new_epoch: new_finalized_epoch, - // }) - // } else { - // self.fork_choice - // .process_finalization(&finalized_block, finalized_block_root)?; - - // Ok(()) - // } - } - - /// Returns `true` if the given block root has not been processed. - pub fn is_new_block_root(&self, shard_block_root: &Hash256) -> Result { - Ok(!self.store.exists::(shard_block_root)?) - } + // // Register the new block with the fork choice service. + // // self.fork_choice.process_block(&state, &block, block_root)?; + + // // Execute the fork choice algorithm, enthroning a new head if discovered. + // // + // // Note: in the future we may choose to run fork-choice less often, potentially based upon + // // some heuristic around number of attestations seen for the block. + // // self.fork_choice()?; + // Ok(BlockProcessingOutcome::Processed { block_root }) + // } + + // /// Produce a new block at the present slot. + // /// + // /// The produced block will not be inherently valid, it must be signed by a block producer. + // /// Block signing is out of the scope of this function and should be done by a separate program. + // pub fn produce_block( + // &self, + // ) -> Result<(ShardBlock, ShardState), BlockProductionError> { + // let state = self.state.read().clone(); + // let slot = self + // .read_slot_clock() + // .ok_or_else(|| BlockProductionError::UnableToReadSlot)?; + + // self.produce_block_on_state(state, slot) + // } + + // /// Produce a block for some `slot` upon the given `state`. + // /// + // /// Typically the `self.produce_block()` function should be used, instead of calling this + // /// function directly. This function is useful for purposefully creating forks or blocks at + // /// non-current slots. + // /// + // /// The given state will be advanced to the given `produce_at_slot`, then a block will be + // /// produced at that slot height. + // pub fn produce_block_on_state( + // &self, + // mut state: ShardState, + // produce_at_slot: Slot, + // ) -> Result<(ShardBlock, ShardState), BlockProductionError> { + // // If required, transition the new state to the present slot. + // while state.slot < produce_at_slot { + // per_slot_processing(&mut state, &self.spec)?; + // } + + // let previous_block_root = if state.slot > 0 { + // *state + // .get_block_root(state.slot - 1) + // .map_err(|_| BlockProductionError::UnableToGetBlockRootFromState)? + // } else { + // state.latest_block_header.canonical_root() + // }; + + // let mut graffiti: [u8; 32] = [0; 32]; + // graffiti.copy_from_slice(GRAFFITI.as_bytes()); + + // let mut block = ShardBlock { + // slot: state.slot, + // previous_block_root, + // state_root: Hash256::zero(), // Updated after the state is calculated. + // signature: Signature::empty_signature(), // To be completed by a validator. + // // need to add the attestations here + // body: ShardBlockBody { + // graffiti, + // attestations: self.op_pool.get_attestations(&state, &self.spec), + // }, + // }; + + // per_block_processing_without_verifying_block_signature(&mut state, &block, &self.spec)?; + + // let state_root = state.canonical_root(); + + // block.state_root = state_root; + + // Ok((block, state)) + // } + + // /// Execute the fork choice algorithm and enthrone the result as the canonical head. + // /// Update the canonical head to `new_head`. + // fn update_canonical_head(&self, new_head: CheckPoint) -> Result<(), Error> { + // // Update the checkpoint that stores the head of the chain at the time it received the + // // block. + // *self.canonical_head.write() = new_head; + + // // Update the always-at-the-present-slot state we keep around for performance gains. + // *self.state.write() = { + // let mut state = self.canonical_head.read().shard_state.clone(); + + // let present_slot = match self.slot_clock.present_slot() { + // Ok(Some(slot)) => slot, + // _ => return Err(Error::UnableToReadSlot), + // }; + + // // If required, transition the new state to the present slot. + // for _ in state.slot.as_u64()..present_slot.as_u64() { + // per_slot_processing(&mut state, &self.spec)?; + // } + + // state.build_all_caches(&self.spec)?; + + // state + // }; + + // Ok(()) + // } + + // /// Called after `self` has had a new block finalized. + // /// + // /// Performs pruning and finality-based optimizations. + // fn after_finalization( + // &self, + // old_finalized_epoch: Epoch, + // finalized_block_root: Hash256, + // ) -> Result<(), Error> { + // // Need to build logic here to manage pruning for shard as well + // // let finalized_block = self + // // .store + // // .get::(&finalized_block_root)? + // // .ok_or_else(|| Error::MissingBeaconBlock(finalized_block_root))?; + + // // let new_finalized_epoch = finalized_block.slot.epoch(T::EthSpec::slots_per_epoch()); + + // // if new_finalized_epoch < old_finalized_epoch { + // // Err(Error::RevertedFinalizedEpoch { + // // previous_epoch: old_finalized_epoch, + // // new_epoch: new_finalized_epoch, + // // }) + // // } else { + // // self.fork_choice + // // .process_finalization(&finalized_block, finalized_block_root)?; + + // // Ok(()) + // // } + // } + + // /// Returns `true` if the given block root has not been processed. + // pub fn is_new_block_root(&self, shard_block_root: &Hash256) -> Result { + // Ok(!self.store.exists::(shard_block_root)?) + // } } impl From for Error { @@ -631,15 +579,15 @@ impl From for Error { } } -impl From for Error { - fn from(e: ForkChoiceError) -> Error { - Error::ForkChoiceError(e) - } -} +// impl From for Error { +// fn from(e: ForkChoiceError) -> Error { +// Error::ForkChoiceError(e) +// } +// } -impl From for Error { - fn from(e: BeaconStateError) -> Error { - Error::BeaconStateError(e) +impl From for Error { + fn from(e: ShardStateError) -> Error { + Error::ShardStateError(e) } } From c278cd2d296bbaab4ee21e2fdb51a2d9636ab600 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sat, 14 Sep 2019 12:06:50 -0500 Subject: [PATCH 074/151] Initiial shard chain interfaces --- shard_node/shard_chain/src/shard_chain.rs | 165 ++++++++++------------ 1 file changed, 74 insertions(+), 91 deletions(-) diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 6aab47907e9..515be732a66 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -11,7 +11,7 @@ use state_processing::{ per_shard_slot_processing, BlockProcessingError, }; use std::sync::Arc; -// use store::iter::{BestBlockRootsIterator, BlockIterator, BlockRootsIterator, StateRootsIterator}; +use shard_store::iter::{BestBlockRootsIterator, BlockIterator, BlockRootsIterator, StateRootsIterator}; use shard_store::{Error as DBError, Store}; use tree_hash::TreeHash; use types::*; @@ -86,105 +86,88 @@ impl ShardChain { }) } - // /// Returns the beacon block body for each beacon block root in `roots`. - // /// - // /// Fails if any root in `roots` does not have a corresponding block. - // pub fn get_block_bodies(&self, roots: &[Hash256]) -> Result, Error> { - // let bodies: Result, _> = roots - // .iter() - // .map(|root| match self.get_block(root)? { - // Some(block) => Ok(block.body), - // None => Err(Error::DBInconsistent(format!("Missing block: {}", root))), - // }) - // .collect(); + pub fn get_block_headers(&self, roots: &[Hash256]) -> Result, Error> { + let headers: Result, _> = roots + .iter() + .map(|root| match self.get_block(root)? { + Some(block) => Ok(block.block_header()), + None => Err(Error::DBInconsistent("Missing block".into())), + }) + .collect(); - // Ok(bodies?) - // } - - // /// Returns the beacon block header for each beacon block root in `roots`. - // /// - // /// Fails if any root in `roots` does not have a corresponding block. - // pub fn get_block_headers(&self, roots: &[Hash256]) -> Result, Error> { - // let headers: Result, _> = roots - // .iter() - // .map(|root| match self.get_block(root)? { - // Some(block) => Ok(block.block_header()), - // None => Err(Error::DBInconsistent("Missing block".into())), - // }) - // .collect(); + Ok(headers?) + } - // Ok(headers?) - // } - // /// Iterate in reverse (highest to lowest slot) through all blocks from the block at `slot` - // /// through to the genesis block. - // /// - // /// Returns `None` for headers prior to genesis or when there is an error reading from `Store`. - // /// - // /// Contains duplicate headers when skip slots are encountered. - // pub fn rev_iter_blocks(&self, slot: Slot) -> BlockIterator { - // BlockIterator::owned(self.store.clone(), self.state.read().clone(), slot) - // } + /// Iterate in reverse (highest to lowest slot) through all blocks from the block at `slot` + /// through to the genesis block. + /// + /// Returns `None` for headers prior to genesis or when there is an error reading from `Store`. + /// + /// Contains duplicate headers when skip slots are encountered. + pub fn rev_iter_blocks(&self, slot: ShardSlot) -> BlockIterator { + BlockIterator::owned(self.store.clone(), self.state.read().clone(), slot) + } - // /// Iterates in reverse (highest to lowest slot) through all block roots from `slot` through to - // /// genesis. - // /// - // /// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. - // /// - // /// Contains duplicate roots when skip slots are encountered. - // pub fn rev_iter_block_roots(&self, slot: Slot) -> BlockRootsIterator { - // BlockRootsIterator::owned(self.store.clone(), self.state.read().clone(), slot) - // } + /// Iterates in reverse (highest to lowest slot) through all block roots from `slot` through to + /// genesis. + /// + /// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. + /// + /// Contains duplicate roots when skip slots are encountered. + pub fn rev_iter_block_roots(&self, slot: ShardSlot) -> BlockRootsIterator { + BlockRootsIterator::owned(self.store.clone(), self.state.read().clone(), slot) + } - // /// Iterates in reverse (highest to lowest slot) through all block roots from largest - // /// `slot <= beacon_state.slot` through to genesis. - // /// - // /// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. - // /// - // /// Contains duplicate roots when skip slots are encountered. - // pub fn rev_iter_best_block_roots( - // &self, - // slot: Slot, - // ) -> BestBlockRootsIterator { - // BestBlockRootsIterator::owned(self.store.clone(), self.state.read().clone(), slot) - // } + /// Iterates in reverse (highest to lowest slot) through all block roots from largest + /// `slot <= beacon_state.slot` through to genesis. + /// + /// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. + /// + /// Contains duplicate roots when skip slots are encountered. + pub fn rev_iter_best_block_roots( + &self, + slot: ShardSlot, + ) -> BestBlockRootsIterator { + BestBlockRootsIterator::owned(self.store.clone(), self.state.read().clone(), slot) + } - // /// Iterates in reverse (highest to lowest slot) through all state roots from `slot` through to - // /// genesis. - // /// - // /// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. - // pub fn f(&self, slot: Slot) -> StateRootsIterator { - // StateRootsIterator::owned(self.store.clone(), self.state.read().clone(), slot) - // } + /// Iterates in reverse (highest to lowest slot) through all state roots from `slot` through to + /// genesis. + /// + /// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. + pub fn rev_iter_state_roots(&self, slot: ShardSlot) -> StateRootsIterator { + StateRootsIterator::owned(self.store.clone(), self.state.read().clone(), slot) + } - // /// Returns the block at the given root, if any. - // /// - // /// ## Errors - // /// - // /// May return a database error. - // pub fn get_block(&self, block_root: &Hash256) -> Result, Error> { - // Ok(self.store.get(block_root)?) - // } + /// Returns the block at the given root, if any. + /// + /// ## Errors + /// + /// May return a database error. + pub fn get_block(&self, block_root: &Hash256) -> Result, Error> { + Ok(self.store.get(block_root)?) + } - // /// Returns a read-lock guarded `BeaconState` which is the `canonical_head` that has been - // /// updated to match the current slot clock. - // pub fn current_state(&self) -> RwLockReadGuard> { - // self.state.read() - // } + /// Returns a read-lock guarded `ShardState` which is the `canonical_head` that has been + /// updated to match the current slot clock. + pub fn current_state(&self) -> RwLockReadGuard> { + self.state.read() + } - // /// Returns a read-lock guarded `CheckPoint` struct for reading the head (as chosen by the - // /// fork-choice rule). - // /// - // /// It is important to note that the `beacon_state` returned may not match the present slot. It - // /// is the state as it was when the head block was received, which could be some slots prior to - // /// now. - // pub fn head(&self) -> RwLockReadGuard> { - // self.canonical_head.read() - // } + /// Returns a read-lock guarded `CheckPoint` struct for reading the head (as chosen by the + /// fork-choice rule). + /// + /// It is important to note that the `shard_state` returned may not match the present slot. It + /// is the state as it was when the head block was received, which could be some slots prior to + /// now. + pub fn head(&self) -> RwLockReadGuard> { + self.canonical_head.read() + } - // /// Returns the slot of the highest block in the canonical chain. - // pub fn best_slot(&self) -> Slot { - // self.canonical_head.read().shard_block.slot - // } + /// Returns the slot of the highest block in the canonical chain. + pub fn best_slot(&self) -> ShardSlot { + self.canonical_head.read().shard_block.slot + } // /// Ensures the current canonical `BeaconState` has been transitioned to match the `slot_clock`. // pub fn catchup_state(&self) -> Result<(), Error> { From 64e35da59ded028cbc4596a224ac077b941ee7d3 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sat, 14 Sep 2019 12:45:32 -0500 Subject: [PATCH 075/151] Update iterator since skip blocks do have a stored state --- shard_node/shard_store/src/iter.rs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/shard_node/shard_store/src/iter.rs b/shard_node/shard_store/src/iter.rs index 0210a5c73c4..cf7738afa87 100644 --- a/shard_node/shard_store/src/iter.rs +++ b/shard_node/shard_store/src/iter.rs @@ -42,11 +42,6 @@ impl<'a, T: ShardSpec, U: Store> Iterator for StateRootsIterator<'a, T, U> { while self.slot < self.shard_state.slot { let next_root = self.shard_state.history_accumulator[0]; let shard_state: ShardState = self.store.get(&next_root).ok()??; - - if self.slot > shard_state.slot { - return Some((Hash256::zero(), self.slot)); - } - self.shard_state = Cow::Owned(shard_state); } @@ -123,11 +118,6 @@ impl<'a, T: ShardSpec, U: Store> Iterator for BlockRootsIterator<'a, T, U> { while self.slot < self.shard_state.slot { let next_root = self.shard_state.history_accumulator[0]; let shard_state: ShardState = self.store.get(&next_root).ok()??; - - if self.slot > shard_state.slot { - return Some((Hash256::zero(), self.slot)); - } - self.shard_state = Cow::Owned(shard_state); } @@ -187,11 +177,6 @@ impl<'a, T: ShardSpec, U: Store> Iterator for BestBlockRootsIterator<'a, T, U> { while self.slot < self.shard_state.slot { let next_root = self.shard_state.history_accumulator[0]; let shard_state: ShardState = self.store.get(&next_root).ok()??; - - if self.slot > shard_state.slot { - return Some((Hash256::zero(), self.slot)); - } - self.shard_state = Cow::Owned(shard_state); } From 07c3fce9acc11e3a58849bf64b969a109c62c963 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sat, 14 Sep 2019 12:52:09 -0500 Subject: [PATCH 076/151] slot processing has state root update logic for skip slots from genesis --- eth2/state_processing/src/per_shard_slot_processing.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/eth2/state_processing/src/per_shard_slot_processing.rs b/eth2/state_processing/src/per_shard_slot_processing.rs index 052a07b74fe..d6f7d7f7191 100644 --- a/eth2/state_processing/src/per_shard_slot_processing.rs +++ b/eth2/state_processing/src/per_shard_slot_processing.rs @@ -29,8 +29,12 @@ fn process_shard_slot( spec: &ChainSpec, ) -> () { let previous_state_root = Hash256::from_slice(&state.tree_hash_root()[..]); - let mut depth = 0; + if state.latest_block_header.state_root == spec.zero_hash { + state.latest_block_header.state_root = previous_state_root; + } + + let mut depth = 0; while (state.slot.as_u64() % u64::pow(2, depth as u32) == 0 as u64) && (depth < T::history_accumulator_depth() as u64) { state.history_accumulator[depth as usize] = previous_state_root; depth += 1; From 19d977ab0b769fdd2e4547ea420513c7c708488b Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sat, 14 Sep 2019 12:52:40 -0500 Subject: [PATCH 077/151] Continued shard chain interfaces in place --- shard_node/shard_chain/src/shard_chain.rs | 35 +++++++++++------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 515be732a66..d1ff39a033b 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -169,29 +169,28 @@ impl ShardChain { self.canonical_head.read().shard_block.slot } - // /// Ensures the current canonical `BeaconState` has been transitioned to match the `slot_clock`. - // pub fn catchup_state(&self) -> Result<(), Error> { - // let spec = &self.spec; + /// Ensures the current canonical `ShardState` has been transitioned to match the `slot_clock`. + pub fn catchup_state(&self) -> Result<(), Error> { + let spec = &self.spec; - // let present_slot = match self.slot_clock.present_slot() { - // Ok(Some(slot)) => slot, - // _ => return Err(Error::UnableToReadSlot), - // }; + let present_slot = match self.slot_clock.present_slot() { + Ok(Some(slot)) => slot, + _ => return Err(Error::UnableToReadSlot), + }; - // if self.state.read().slot < present_slot { - // let mut state = self.state.write(); + if self.state.read().slot < present_slot { + let mut state = self.state.write(); - // // If required, transition the new state to the present slot. - // for _ in state.slot.as_u64()..present_slot.as_u64() { - // // per_slot_processing(&mut *state, spec)?; - // // logic here to manage everything... add this in - // } + // If required, transition the new state to the present slot. + for _ in state.slot.as_u64()..present_slot.as_u64() { + per_shard_slot_processing(&mut *state, spec)?; + } - // state.build_all_caches(spec)?; - // } + state.build_cache(spec)?; + } - // Ok(()) - // } + Ok(()) + } // /// Build all of the caches on the current state. // /// From 00c3a7b57f9219f07051e537b991f22540c57774 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sat, 14 Sep 2019 13:04:59 -0500 Subject: [PATCH 078/151] Included Slot -> ShardSlot conversion function --- eth2/types/src/slot_epoch.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/eth2/types/src/slot_epoch.rs b/eth2/types/src/slot_epoch.rs index ea652d6d1cb..7e6cafd36a9 100644 --- a/eth2/types/src/slot_epoch.rs +++ b/eth2/types/src/slot_epoch.rs @@ -70,6 +70,10 @@ impl Slot { SlotHeight::from(self.0.saturating_sub(genesis_slot.as_u64())) } + pub fn shard_slot(self, slots_per_epoch: u64, shard_slots_per_epoch: u64) -> ShardSlot { + ShardSlot::from(self.0 * shard_slots_per_epoch / slots_per_epoch) + } + pub fn max_value() -> Slot { Slot(u64::max_value()) } From 0cee3d8fbe51e37a47b54a0fa4f7fed643e48582 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sat, 14 Sep 2019 13:38:55 -0500 Subject: [PATCH 079/151] Update shard chain error conversions --- eth2/state_processing/src/lib.rs | 2 +- .../state_processing/src/per_shard_slot_processing.rs | 11 +++++++++++ eth2/types/src/chain_spec.rs | 3 +++ shard_node/shard_chain/src/errors.rs | 10 +++++----- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/eth2/state_processing/src/lib.rs b/eth2/state_processing/src/lib.rs index 1caa5255268..230649aeaf1 100644 --- a/eth2/state_processing/src/lib.rs +++ b/eth2/state_processing/src/lib.rs @@ -17,4 +17,4 @@ pub use per_block_processing::{ pub use per_epoch_processing::{errors::EpochProcessingError, per_epoch_processing}; pub use per_slot_processing::{per_slot_processing, Error as SlotProcessingError}; pub use per_shard_block_processing::per_shard_block_processing; -pub use per_shard_slot_processing::per_shard_slot_processing; +pub use per_shard_slot_processing::{per_shard_slot_processing, Error as ShardSlotProcessingError}; diff --git a/eth2/state_processing/src/per_shard_slot_processing.rs b/eth2/state_processing/src/per_shard_slot_processing.rs index d6f7d7f7191..e1563a1ed7e 100644 --- a/eth2/state_processing/src/per_shard_slot_processing.rs +++ b/eth2/state_processing/src/per_shard_slot_processing.rs @@ -2,6 +2,11 @@ use crate::*; use types::*; use tree_hash::TreeHash; +#[derive(Debug, PartialEq)] +pub enum Error { + ShardStateError(ShardStateError), +} + pub fn per_shard_slot_processing( state: &mut ShardState, spec: &ChainSpec, @@ -40,3 +45,9 @@ fn process_shard_slot( depth += 1; } } + +impl From for Error { + fn from(e: ShardStateError) -> Error { + Error::ShardStateError(e) + } +} diff --git a/eth2/types/src/chain_spec.rs b/eth2/types/src/chain_spec.rs index 2221f0eaf3c..d24ba1ecff0 100644 --- a/eth2/types/src/chain_spec.rs +++ b/eth2/types/src/chain_spec.rs @@ -79,6 +79,7 @@ pub struct ChainSpec { */ pub slots_per_epoch: u64, pub shard_slots_per_beacon_slot: u64, + pub shard_slots_per_epoch: u64, /* * Phase 1 specific values, fork epoch and slot are hardcoded to values for now @@ -212,6 +213,7 @@ impl ChainSpec { */ slots_per_epoch: 64, shard_slots_per_beacon_slot: 2, + shard_slots_per_epoch: 128, /* * Phase 1 specific values, fork epoch and slot are hardcoded to values for now @@ -280,6 +282,7 @@ impl ChainSpec { chain_id: 2, // lighthouse testnet chain id boot_nodes, slots_per_epoch: 8, + shard_slots_per_epoch: 16, ..ChainSpec::mainnet() } } diff --git a/shard_node/shard_chain/src/errors.rs b/shard_node/shard_chain/src/errors.rs index 2ecbf45d297..c8e434cae4f 100644 --- a/shard_node/shard_chain/src/errors.rs +++ b/shard_node/shard_chain/src/errors.rs @@ -1,7 +1,7 @@ // use crate::fork_choice::Error as ForkChoiceError; // use crate::metrics::Error as MetricsError; use state_processing::BlockProcessingError; -use state_processing::SlotProcessingError; +use state_processing::ShardSlotProcessingError; use types::*; macro_rules! easy_from_to { @@ -25,11 +25,11 @@ pub enum ShardChainError { // ForkChoiceError(ForkChoiceError), MissingShardBlock(Hash256), MissingShardState(Hash256), - SlotProcessingError(SlotProcessingError), + ShardSlotProcessingError(ShardSlotProcessingError), // MetricsError(String), } -easy_from_to!(SlotProcessingError, ShardChainError); +easy_from_to!(ShardSlotProcessingError, ShardChainError); // impl From for ShardChainError { // fn from(e: MetricsError) -> ShardChainError { @@ -41,11 +41,11 @@ easy_from_to!(SlotProcessingError, ShardChainError); pub enum BlockProductionError { UnableToGetBlockRootFromState, UnableToReadSlot, - SlotProcessingError(SlotProcessingError), + ShardSlotProcessingError(ShardSlotProcessingError), BlockProcessingError(BlockProcessingError), ShardStateError(ShardStateError), } easy_from_to!(BlockProcessingError, BlockProductionError); easy_from_to!(ShardStateError, BlockProductionError); -easy_from_to!(SlotProcessingError, BlockProductionError); +easy_from_to!(ShardSlotProcessingError, BlockProductionError); From 5ca4ab0fd102833b48e06a02c82596cb9e861772 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sat, 14 Sep 2019 13:39:33 -0500 Subject: [PATCH 080/151] Build validator index interface + build cache interface --- shard_node/shard_chain/src/shard_chain.rs | 56 +++++++++++------------ 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index d1ff39a033b..25e333da95d 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -174,7 +174,7 @@ impl ShardChain { let spec = &self.spec; let present_slot = match self.slot_clock.present_slot() { - Ok(Some(slot)) => slot, + Ok(Some(slot)) => slot.shard_slot(spec.slots_per_epoch, spec.shard_slots_per_epoch), _ => return Err(Error::UnableToReadSlot), }; @@ -192,36 +192,34 @@ impl ShardChain { Ok(()) } - // /// Build all of the caches on the current state. - // /// - // /// Ideally this shouldn't be required, however we leave it here for testing. - // pub fn ensure_state_caches_are_built(&self) -> Result<(), Error> { - // self.state.write().build_all_caches(&self.spec)?; + /// Build all of the caches on the current state. + /// + /// Ideally this shouldn't be required, however we leave it here for testing. + pub fn ensure_state_caches_are_built(&self) -> Result<(), Error> { + self.state.write().build_cache(&self.spec)?; - // Ok(()) - // } + Ok(()) + } - // /// Returns the validator index (if any) for the given public key. - // /// - // /// Information is retrieved from the present `beacon_state.validator_registry`. - // pub fn validator_index(&self, pubkey: &PublicKey) -> Option { - // // reference directly to beacon chain parent - // // needs to make sure it is part of this particular shard - // for (i, validator) in self - // .parent_beacon - // .current_state() - // .validator_registry - // .iter() - // .enumerate() - // { - // if validator.pubkey == *pubkey { - // if self.parent_beacon.current_state().get_attestation_duties(i).shard = self.shard { - // return Some(i); - // } - // } - // } - // None - // } + /// Returns the validator index (if any) for the given public key. + /// + /// Information is retrieved from the present `beacon_state.validator_registry`. + pub fn validator_index(&self, pubkey: &PublicKey) -> Option { + // reference directly to beacon chain parent + for (i, validator) in self + .parent_beacon + .head() + .beacon_state + .validator_registry + .iter() + .enumerate() + { + if validator.pubkey == *pubkey { + return Some(i); + } + } + None + } // /// Reads the slot clock, returns `None` if the slot is unavailable. // /// From c5b64e860d46418b5cb92b7b58cfc3db781cba26 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sat, 14 Sep 2019 14:37:44 -0500 Subject: [PATCH 081/151] Implemented slot clock functionalities --- shard_node/shard_chain/src/shard_chain.rs | 72 ++++++++++++----------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 25e333da95d..707c671b0fe 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -221,43 +221,47 @@ impl ShardChain { None } - // /// Reads the slot clock, returns `None` if the slot is unavailable. - // /// - // /// The slot might be unavailable due to an error with the system clock, or if the present time - // /// is before genesis (i.e., a negative slot). - // /// - // /// This is distinct to `present_slot`, which simply reads the latest state. If a - // /// call to `read_slot_clock` results in a higher slot than a call to `present_slot`, - // /// `self.state` should undergo per slot processing. - // pub fn read_slot_clock(&self) -> Option { - // match self.slot_clock.present_slot() { - // Ok(Some(some_slot)) => Some(some_slot), - // Ok(None) => None, - // _ => None, - // } - // } + /// Reads the slot clock and returns a ShardSlot, returns `None` if the slot is unavailable. + /// + /// The slot might be unavailable due to an error with the system clock, or if the present time + /// is before genesis (i.e., a negative slot). + /// + /// This is distinct to `present_slot`, which simply reads the latest state. If a + /// call to `read_slot_clock` results in a higher slot than a call to `present_slot`, + /// `self.state` should undergo per slot processing. + pub fn read_slot_clock(&self) -> Option { + let spec = &self.spec; + + match self.slot_clock.present_slot() { + Ok(Some(some_slot)) => Some(some_slot.shard_slot(spec.slots_per_epoch, spec.shard_slots_per_epoch)), + Ok(None) => None, + _ => None, + } + } + + /// Reads the slot clock (see `self.read_slot_clock()` and returns the number of slots since + /// genesis. + pub fn slots_since_genesis(&self) -> Option { + let now = self.read_slot_clock()?; + let spec = &self.spec; + let genesis_slot = spec.phase_1_fork_epoch * spec.shard_slots_per_epoch; - // /// Reads the slot clock (see `self.read_slot_clock()` and returns the number of slots since - // /// genesis. - // pub fn slots_since_genesis(&self) -> Option { - // let now = self.read_slot_clock()?; - // let genesis_slot = self.spec.genesis_slot; - // if now < genesis_slot { - // None - // } else { - // Some(SlotHeight::from(now.as_u64() - genesis_slot.as_u64())) - // } - // } + if now < genesis_slot { + None + } else { + Some(ShardSlotHeight::from(now.as_u64() - genesis_slot)) + } + } - // /// Returns slot of the present state. - // /// - // /// This is distinct to `read_slot_clock`, which reads from the actual system clock. If - // /// `self.state` has not been transitioned it is possible for the system clock to be on a - // /// different slot to what is returned from this call. - // pub fn present_slot(&self) -> Slot { - // self.state.read().slot - // } + /// Returns slot of the present state. + /// + /// This is distinct to `read_slot_clock`, which reads from the actual system clock. If + /// `self.state` has not been transitioned it is possible for the system clock to be on a + /// different slot to what is returned from this call. + pub fn present_slot(&self) -> ShardSlot { + self.state.read().slot + } // /// Returns the block proposer for a given slot. // /// From 96b71a801aa25bef7c05ba18b48d1d5c7f23710e Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sat, 14 Sep 2019 14:55:17 -0500 Subject: [PATCH 082/151] Let beacon state error trickle up to shard chain & fix calcs on shard block producer --- eth2/types/src/beacon_state.rs | 8 +++--- shard_node/shard_chain/src/errors.rs | 4 +++ shard_node/shard_chain/src/shard_chain.rs | 34 ++++++++++------------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index a160824ae47..642cf5cba7e 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -477,21 +477,21 @@ impl BeaconState { pub fn get_shard_proposer_index( &self, - epoch: Epoch, shard: u64, slot: ShardSlot, ) -> Result { let spec = T::default_spec(); let current_epoch = self.current_epoch(); + let target_epoch = slot.epoch(spec.shard_slots_per_epoch, spec.shard_slots_per_beacon_slot); let current_period = current_epoch.period(spec.epochs_per_shard_period); - let target_period = epoch.period(spec.epochs_per_shard_period); + let target_period = target_epoch.period(spec.epochs_per_shard_period); if target_period != current_period { return Err(BeaconStateError::PeriodOutOfBounds); } - let seed = self.generate_seed(epoch, &spec)?; - let committee = self.get_shard_committee(epoch, shard)?.committee; + let seed = self.generate_seed(target_epoch, &spec)?; + let committee = self.get_shard_committee(target_epoch, shard)?.committee; let mut i = 0; Ok(loop { diff --git a/shard_node/shard_chain/src/errors.rs b/shard_node/shard_chain/src/errors.rs index c8e434cae4f..82b57c96a21 100644 --- a/shard_node/shard_chain/src/errors.rs +++ b/shard_node/shard_chain/src/errors.rs @@ -19,6 +19,7 @@ pub enum ShardChainError { InsufficientValidators, BadRecentBlockRoots, UnableToReadSlot, + BeaconStateError(BeaconStateError), ShardStateError(ShardStateError), DBInconsistent(String), DBError(shard_store::Error), @@ -44,8 +45,11 @@ pub enum BlockProductionError { ShardSlotProcessingError(ShardSlotProcessingError), BlockProcessingError(BlockProcessingError), ShardStateError(ShardStateError), + BeaconStateError(BeaconStateError), } easy_from_to!(BlockProcessingError, BlockProductionError); easy_from_to!(ShardStateError, BlockProductionError); +easy_from_to!(BeaconStateError, BlockProductionError); +easy_from_to!(BeaconStateError, ShardChainError); easy_from_to!(ShardSlotProcessingError, BlockProductionError); diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 707c671b0fe..e16e60d90ec 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -263,25 +263,21 @@ impl ShardChain { self.state.read().slot } - // /// Returns the block proposer for a given slot. - // /// - // /// Information is read from the present `beacon_state` shuffling, only information from the - // /// present epoch is available. - // pub fn block_proposer(&self, slot: Slot, shard: Shard) -> Result { - // // Update to go to beacon chain for this information - // // Ensures that the present state has been advanced to the present slot, skipping slots if - // // blocks are not present. - // // self.catchup_state()?; - - // // // TODO: permit lookups of the proposer at any slot. - // let index = self.parent_beacon.get_shard_proposer_index( - // slot, - // shard, - // &self.spec, - // )?; - - // Ok(index) - // } + /// Returns the block proposer for a given slot. + /// + /// Information is read from the present `beacon_state` + pub fn block_proposer(&self, slot: ShardSlot, shard: u64) -> Result { + // Ensures that the present state has been advanced to the present slot, skipping slots if + // blocks are not present. + self.catchup_state()?; + + let index = self.parent_beacon.current_state().get_shard_proposer_index( + shard, + slot, + )?; + + Ok(index) + } // /// Produce an `AttestationData` that is valid for the present `slot` and given `shard`. // /// From b21d6dc9f1e2163455341a6593d2813a31425616 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sat, 14 Sep 2019 16:10:59 -0500 Subject: [PATCH 083/151] Add shard block processing without fork choice for now --- eth2/state_processing/src/lib.rs | 2 +- .../src/per_shard_block_processing.rs | 20 +- shard_node/shard_chain/src/errors.rs | 6 + shard_node/shard_chain/src/shard_chain.rs | 190 ++++++++++-------- 4 files changed, 129 insertions(+), 89 deletions(-) diff --git a/eth2/state_processing/src/lib.rs b/eth2/state_processing/src/lib.rs index 230649aeaf1..0a07d407ea5 100644 --- a/eth2/state_processing/src/lib.rs +++ b/eth2/state_processing/src/lib.rs @@ -16,5 +16,5 @@ pub use per_block_processing::{ }; pub use per_epoch_processing::{errors::EpochProcessingError, per_epoch_processing}; pub use per_slot_processing::{per_slot_processing, Error as SlotProcessingError}; -pub use per_shard_block_processing::per_shard_block_processing; +pub use per_shard_block_processing::{per_shard_block_processing, Error as ShardBlockProcessingError}; pub use per_shard_slot_processing::{per_shard_slot_processing, Error as ShardSlotProcessingError}; diff --git a/eth2/state_processing/src/per_shard_block_processing.rs b/eth2/state_processing/src/per_shard_block_processing.rs index 4e1885a2737..23a99583173 100644 --- a/eth2/state_processing/src/per_shard_block_processing.rs +++ b/eth2/state_processing/src/per_shard_block_processing.rs @@ -1,10 +1,28 @@ use types::*; -pub fn per_shard_block_processing( +#[derive(Debug, PartialEq)] +pub enum Error { + BlockProcessingError, +} + +pub fn per_shard_block_processing( beacon_state: &BeaconState, state: &mut ShardState, block: &ShardBlock, spec: &ChainSpec, ) -> Result<(), Error> { + process_shard_block_header(beacon_state, state, block, spec); + // process_shard_attestations + // process_shard_block_body Ok(()) } + +pub fn process_shard_block_header( + beacon_state: &BeaconState, + state: &mut ShardState, + block: &ShardBlock, + spec: &ChainSpec, +) -> Result<(), Error> { + state.latest_block_header = block.block_header(); + Ok(()) +} \ No newline at end of file diff --git a/shard_node/shard_chain/src/errors.rs b/shard_node/shard_chain/src/errors.rs index 82b57c96a21..b207b2f1745 100644 --- a/shard_node/shard_chain/src/errors.rs +++ b/shard_node/shard_chain/src/errors.rs @@ -2,6 +2,7 @@ // use crate::metrics::Error as MetricsError; use state_processing::BlockProcessingError; use state_processing::ShardSlotProcessingError; +use state_processing::ShardBlockProcessingError; use types::*; macro_rules! easy_from_to { @@ -27,10 +28,12 @@ pub enum ShardChainError { MissingShardBlock(Hash256), MissingShardState(Hash256), ShardSlotProcessingError(ShardSlotProcessingError), + ShardBlockProcessingError(ShardBlockProcessingError), // MetricsError(String), } easy_from_to!(ShardSlotProcessingError, ShardChainError); +easy_from_to!(ShardBlockProcessingError, ShardChainError); // impl From for ShardChainError { // fn from(e: MetricsError) -> ShardChainError { @@ -43,6 +46,7 @@ pub enum BlockProductionError { UnableToGetBlockRootFromState, UnableToReadSlot, ShardSlotProcessingError(ShardSlotProcessingError), + ShardBlockProcessingError(ShardBlockProcessingError), BlockProcessingError(BlockProcessingError), ShardStateError(ShardStateError), BeaconStateError(BeaconStateError), @@ -53,3 +57,5 @@ easy_from_to!(ShardStateError, BlockProductionError); easy_from_to!(BeaconStateError, BlockProductionError); easy_from_to!(BeaconStateError, ShardChainError); easy_from_to!(ShardSlotProcessingError, BlockProductionError); +easy_from_to!(ShardBlockProcessingError, BlockProductionError); + diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index e16e60d90ec..c6b66cb43c4 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -8,7 +8,7 @@ use parking_lot::{RwLock, RwLockReadGuard}; use slot_clock::SlotClock; use state_processing::{ per_shard_block_processing, - per_shard_slot_processing, BlockProcessingError, + per_shard_slot_processing, ShardBlockProcessingError, }; use std::sync::Arc; use shard_store::iter::{BestBlockRootsIterator, BlockIterator, BlockRootsIterator, StateRootsIterator}; @@ -22,6 +22,29 @@ use types::*; // |-------must be this long------| pub const GRAFFITI: &str = "sigp/lighthouse-0.0.0-prerelease"; +#[derive(Debug, PartialEq)] +pub enum BlockProcessingOutcome { + /// Block was valid and imported into the block graph. + Processed { block_root: Hash256 }, + /// The blocks parent_root is unknown. + ParentUnknown { parent: Hash256 }, + /// The block slot is greater than the present slot. + FutureSlot { + present_slot: ShardSlot, + block_slot: ShardSlot, + }, + /// The block state_root does not match the generated state. + StateRootMismatch, + /// The block was a genesis block, these blocks cannot be re-imported. + GenesisBlock, + /// The slot is finalized, no need to import. + FinalizedSlot, + /// Block is already known, no need to re-import. + BlockIsAlreadyKnown, + /// The block could not be applied to the state, it is invalid. + PerBlockProcessingError(ShardBlockProcessingError), +} + pub trait ShardChainTypes { type Store: shard_store::Store; type SlotClock: slot_clock::SlotClock; @@ -321,108 +344,101 @@ impl ShardChain { // result // } - // // This needs to be written and implemented - // pub fn process_transactions() -> {} + /// Accept some block and attempt to add it to block DAG. + /// + /// Will accept blocks from prior slots, however it will reject any block from a future slot. + pub fn process_block(&self, block: ShardBlock) -> Result { + let spec = &self.spec; + let beacon_state = &self.parent_beacon.current_state(); + + let finalized_slot = beacon_state + .finalized_epoch + .start_slot(spec.slots_per_epoch) + .shard_slot(spec.slots_per_epoch, spec.shard_slots_per_epoch); - // /// Accept some block and attempt to add it to block DAG. - // /// - // /// Will accept blocks from prior slots, however it will reject any block from a future slot. - // pub fn process_block(&self, block: ShardBlock) -> Result { - // // In the future... need some logic here that will actually check to see - // // if the slot has been part of a finalized crosslink on the beacon chain - // // extra logic needed, but for our testnet/system we won't need this to the full degree - // // let finalized_slot = self - // // .state - // // .read() - // // .finalized_epoch - // // .start_slot(T::EthSpec::slots_per_epoch()); - - // // if block.slot <= finalized_slot { - // // return Ok(BlockProcessingOutcome::FinalizedSlot); - // // } + if block.slot <= finalized_slot { + return Ok(BlockProcessingOutcome::FinalizedSlot); + } - // if block.slot == 0 { - // return Ok(BlockProcessingOutcome::GenesisBlock); - // } + if block.slot == 0 { + return Ok(BlockProcessingOutcome::GenesisBlock); + } - // let block_root = block.block_header().canonical_root(); + let block_root = block.block_header().canonical_root(); - // if block_root == self.genesis_block_root { - // return Ok(BlockProcessingOutcome::GenesisBlock); - // } + if block_root == self.genesis_block_root { + return Ok(BlockProcessingOutcome::GenesisBlock); + } - // let present_slot = self - // .read_slot_clock() - // .ok_or_else(|| Error::UnableToReadSlot)?; + let present_slot = self + .read_slot_clock() + .ok_or_else(|| Error::UnableToReadSlot)?; - // if block.slot > present_slot { - // return Ok(BlockProcessingOutcome::FutureSlot { - // present_slot, - // block_slot: block.slot, - // }); - // } + if block.slot > present_slot { + return Ok(BlockProcessingOutcome::FutureSlot { + present_slot, + block_slot: block.slot, + }); + } - // if self.store.exists::(&block_root)? { - // return Ok(BlockProcessingOutcome::BlockIsAlreadyKnown); - // } + if self.store.exists::(&block_root)? { + return Ok(BlockProcessingOutcome::BlockIsAlreadyKnown); + } - // // Load the blocks parent block from the database, returning invalid if that block is not - // // found. - // let parent_block_root = block.previous_block_root; - // let parent_block: ShardBlock = match self.store.get(&parent_block_root)? { - // Some(previous_block_root) => previous_block_root, - // None => { - // return Ok(BlockProcessingOutcome::ParentUnknown { - // parent: parent_block_root, - // }); - // } - // }; + // Load the blocks parent block from the database, returning invalid if that block is not + // found. + let parent_block_root = block.parent_root; + let parent_block: ShardBlock = match self.store.get(&parent_block_root)? { + Some(previous_block_root) => previous_block_root, + None => { + return Ok(BlockProcessingOutcome::ParentUnknown { + parent: parent_block_root, + }); + } + }; - // // Load the parent blocks state from the database, returning an error if it is not found. - // // It is an error because if know the parent block we should also know the parent state. - // let parent_state_root = parent_block.state_root; - // let parent_state = self - // .store - // .get(&parent_state_root)? - // .ok_or_else(|| Error::DBInconsistent(format!("Missing state {}", parent_state_root)))?; - - // // Transition the parent state to the block slot. - // let mut state: ShardState = parent_state; - // for _ in state.slot.as_u64()..block.slot.as_u64() { - // per_shard_slot_processing(&mut state, &self.spec)?; - // } + // Load the parent blocks state from the database, returning an error if it is not found. + // It is an error because if know the parent block we should also know the parent state. + let parent_state_root = parent_block.state_root; + let parent_state = self + .store + .get(&parent_state_root)? + .ok_or_else(|| Error::DBInconsistent(format!("Missing state {}", parent_state_root)))?; + + // Transition the parent state to the block slot. + let mut state: ShardState = parent_state; + for _ in state.slot.as_u64()..block.slot.as_u64() { + per_shard_slot_processing(&mut state, &self.spec)?; + } - // // Apply the received block to its parent state (which has been transitioned into this - // // slot). - // match per_shard_block_processing(&mut state, &block, &self.spec) { - // Err(BlockProcessingError::ShardStateError(e)) => { - // return Err(Error::ShardStateError(e)) - // } - // Err(e) => return Ok(BlockProcessingOutcome::PerBlockProcessingError(e)), - // _ => {} - // } + // Apply the received block to its parent state (which has been transitioned into this + // slot). + match per_shard_block_processing(beacon_state, &mut state, &block, &self.spec) { + Err(e) => return Ok(BlockProcessingOutcome::PerBlockProcessingError(e)), + _ => {} + } - // let state_root = state.canonical_root(); + let state_root = state.canonical_root(); - // if block.state_root != state_root { - // return Ok(BlockProcessingOutcome::StateRootMismatch); - // } + if block.state_root != state_root { + return Ok(BlockProcessingOutcome::StateRootMismatch); + } - // // Store the block and state. - // self.store.put(&block_root, &block)?; - // self.store.put(&state_root, &state)?; + // Store the block and state. + self.store.put(&block_root, &block)?; + self.store.put(&state_root, &state)?; - // // Register the new block with the fork choice service. - // // self.fork_choice.process_block(&state, &block, block_root)?; + // Register the new block with the fork choice service. + // self.fork_choice.process_block(&state, &block, block_root)?; - // // Execute the fork choice algorithm, enthroning a new head if discovered. - // // - // // Note: in the future we may choose to run fork-choice less often, potentially based upon - // // some heuristic around number of attestations seen for the block. - // // self.fork_choice()?; - // Ok(BlockProcessingOutcome::Processed { block_root }) - // } + // Execute the fork choice algorithm, enthroning a new head if discovered. + // + // Note: in the future we may choose to run fork-choice less often, potentially based upon + // some heuristic around number of attestations seen for the block. + // self.fork_choice()?; + Ok(BlockProcessingOutcome::Processed { block_root }) + } // /// Produce a new block at the present slot. // /// From 3a6564024fa3c3a7f0522e5a7b0f55ad76c064b2 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sat, 14 Sep 2019 23:21:19 -0500 Subject: [PATCH 084/151] Fork choice errors implemented and find_head() --- shard_node/shard_chain/Cargo.toml | 1 + shard_node/shard_chain/src/errors.rs | 7 +- shard_node/shard_chain/src/fork_choice.rs | 75 ++++++++++++++++------ shard_node/shard_chain/src/shard_chain.rs | 19 ++++-- shard_node/shard_store/src/memory_store.rs | 66 ++++++++++++++++++- 5 files changed, 138 insertions(+), 30 deletions(-) diff --git a/shard_node/shard_chain/Cargo.toml b/shard_node/shard_chain/Cargo.toml index 76866dff006..b4722299d06 100644 --- a/shard_node/shard_chain/Cargo.toml +++ b/shard_node/shard_chain/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" beacon_chain = { path = "../../beacon_node/beacon_chain" } bls = { path = "../../eth2/utils/bls" } boolean-bitfield = { path = "../../eth2/utils/boolean-bitfield" } +store = { path = "../../beacon_node/store" } shard_store = { path = "../shard_store" } failure = "0.1" failure_derive = "0.1" diff --git a/shard_node/shard_chain/src/errors.rs b/shard_node/shard_chain/src/errors.rs index b207b2f1745..eb685241820 100644 --- a/shard_node/shard_chain/src/errors.rs +++ b/shard_node/shard_chain/src/errors.rs @@ -1,8 +1,9 @@ -// use crate::fork_choice::Error as ForkChoiceError; +use crate::fork_choice::Error as ForkChoiceError; // use crate::metrics::Error as MetricsError; use state_processing::BlockProcessingError; use state_processing::ShardSlotProcessingError; use state_processing::ShardBlockProcessingError; +use store::{Error as BeaconDBError}; use types::*; macro_rules! easy_from_to { @@ -24,7 +25,8 @@ pub enum ShardChainError { ShardStateError(ShardStateError), DBInconsistent(String), DBError(shard_store::Error), - // ForkChoiceError(ForkChoiceError), + BeaconDBError(BeaconDBError), + ForkChoiceError(ForkChoiceError), MissingShardBlock(Hash256), MissingShardState(Hash256), ShardSlotProcessingError(ShardSlotProcessingError), @@ -58,4 +60,3 @@ easy_from_to!(BeaconStateError, BlockProductionError); easy_from_to!(BeaconStateError, ShardChainError); easy_from_to!(ShardSlotProcessingError, BlockProductionError); easy_from_to!(ShardBlockProcessingError, BlockProductionError); - diff --git a/shard_node/shard_chain/src/fork_choice.rs b/shard_node/shard_chain/src/fork_choice.rs index 3422949ecd7..025303d6b9b 100644 --- a/shard_node/shard_chain/src/fork_choice.rs +++ b/shard_node/shard_chain/src/fork_choice.rs @@ -1,9 +1,11 @@ -use crate::{ShardChain, ShardChainTypes}; +use crate::{ShardChain, ShardChainTypes, ShardChainError}; +use beacon_chain::BeaconChainTypes; use shard_lmd_ghost::LmdGhost; use state_processing::common::get_attesting_indices_unsorted; use std::sync::Arc; +use store::{Error as BeaconStoreError, Store as BeaconStore}; use shard_store::{Error as StoreError, Store}; -use types::{Attestation, ShardBlock, ShardState, ShardStateError, Epoch, ShardSpec, Hash256}; +use types::{Attestation, BeaconBlock, BeaconState, BeaconStateError, ShardBlock, ShardSlot, ShardState, ShardStateError, Epoch, EthSpec, ShardSpec, Hash256}; type Result = std::result::Result; @@ -11,9 +13,13 @@ type Result = std::result::Result; pub enum Error { MissingBlock(Hash256), MissingState(Hash256), + MissingBeaconState(Hash256), + MissingBeaconBlock(Hash256), BackendError(String), ShardStateError(ShardStateError), + BeaconStateError(BeaconStateError), StoreError(StoreError), + BeaconStoreError(BeaconStoreError), } pub struct ForkChoice { @@ -32,25 +38,42 @@ impl ForkChoice { genesis_block_root, } } - // general pseudocode here - // pub fn find_head(&self, chain: &ShardChain) -> Result { - // let beacon_state = chain.get_beacon_state(); - // let finalized_epoch = beacon_state.finalized_epoch; - // let start_block_root = chain.get_crosslink(finalized_epoch); - // let start_block_slot = chain.get_block(start_block_root).slot; - - // // A function that returns the weight for some validator index. - // let weight = |validator_index: usize| -> Option { - // beacon_state - // .validator_registry - // .get(validator_index) - // .map(|v| v.effective_balance) - // }; - // self.backend - // .find_head(start_block_slot, start_block_root, weight) - // .map_err(Into::into) - // } + pub fn find_head(&self, chain: &ShardChain) -> Result { + let current_state = chain.current_state(); + let beacon_root = current_state.latest_block_header.beacon_block_root; + let beacon_block: BeaconBlock = chain + .parent_beacon + .store + .get(&beacon_root)? + .ok_or_else(|| Error::MissingBeaconBlock(beacon_root))?; + + let beacon_state: BeaconState = chain + .parent_beacon + .store + .get(&beacon_block.state_root)? + .ok_or_else(|| Error::MissingBeaconState(beacon_block.state_root))?; + + let current_crosslink = beacon_state.get_current_crosslink(chain.shard)?; + // Spec needs an update for crosslinks to hold the end shard_block_root + // For now, we will just assume the latest block hash is included and add the + // extra field to the beacon chain + let start_block_root = current_crosslink.crosslink_data_root; + // should be updated to end epoch :) with the new spec todo + let start_block_slot = ShardSlot::from(current_crosslink.epoch.as_u64() * chain.spec.shard_slots_per_epoch); + + // A function that returns the weight for some validator index. + let weight = |validator_index: usize| -> Option { + beacon_state + .validator_registry + .get(validator_index) + .map(|v| v.effective_balance) + }; + + self.backend + .find_head(start_block_slot, start_block_root, weight) + .map_err(Into::into) + } // /// Process all attestations in the given `block`. // /// @@ -125,12 +148,24 @@ impl From for Error { } } +impl From for Error { + fn from(e: BeaconStateError) -> Error { + Error::BeaconStateError(e) + } +} + impl From for Error { fn from(e: StoreError) -> Error { Error::StoreError(e) } } +impl From for Error { + fn from(e: BeaconStoreError) -> Error { + Error::BeaconStoreError(e) + } +} + impl From for Error { fn from(e: String) -> Error { Error::BackendError(e) diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index c6b66cb43c4..bb7ecf9138f 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -11,6 +11,7 @@ use state_processing::{ per_shard_slot_processing, ShardBlockProcessingError, }; use std::sync::Arc; +use store::{Store as BeaconStore, Error as BeaconDBError}; use shard_store::iter::{BestBlockRootsIterator, BlockIterator, BlockRootsIterator, StateRootsIterator}; use shard_store::{Error as DBError, Store}; use tree_hash::TreeHash; @@ -224,6 +225,13 @@ impl ShardChain { Ok(()) } + pub fn get_beacon_state(&self, state_root: Hash256) -> Result>, Error> { + match self.parent_beacon.store.get(&state_root) { + Ok(shard_block) => Ok(shard_block), + Err(e) => Err(Error::BeaconDBError(e)), + } + } + /// Returns the validator index (if any) for the given public key. /// /// Information is retrieved from the present `beacon_state.validator_registry`. @@ -575,15 +583,14 @@ impl From for Error { } } -// impl From for Error { -// fn from(e: ForkChoiceError) -> Error { -// Error::ForkChoiceError(e) -// } -// } +impl From for Error { + fn from(e: ForkChoiceError) -> Error { + Error::ForkChoiceError(e) + } +} impl From for Error { fn from(e: ShardStateError) -> Error { Error::ShardStateError(e) } } - diff --git a/shard_node/shard_store/src/memory_store.rs b/shard_node/shard_store/src/memory_store.rs index 1ed70211181..048c054f52d 100644 --- a/shard_node/shard_store/src/memory_store.rs +++ b/shard_node/shard_store/src/memory_store.rs @@ -1,3 +1,67 @@ +use super::{Error, Store}; +use parking_lot::RwLock; +use std::collections::HashMap; +use std::sync::Arc; + +type DBHashMap = HashMap, Vec>; + +/// A thread-safe `HashMap` wrapper. +#[derive(Clone)] pub struct MemoryStore { - pub hello: u64, + // Note: this `Arc` is only included because of an artificial constraint by gRPC. Hopefully we + // can remove this one day. + db: Arc>, +} + +impl MemoryStore { + /// Create a new, empty database. + pub fn open() -> Self { + Self { + db: Arc::new(RwLock::new(HashMap::new())), + } + } + + fn get_key_for_col(col: &str, key: &[u8]) -> Vec { + let mut col = col.as_bytes().to_vec(); + col.append(&mut key.to_vec()); + col + } +} + +impl Store for MemoryStore { + /// Get the value of some key from the database. Returns `None` if the key does not exist. + fn get_bytes(&self, col: &str, key: &[u8]) -> Result>, Error> { + let column_key = MemoryStore::get_key_for_col(col, key); + + Ok(self + .db + .read() + .get(&column_key) + .and_then(|val| Some(val.clone()))) + } + + /// Puts a key in the database. + fn put_bytes(&self, col: &str, key: &[u8], val: &[u8]) -> Result<(), Error> { + let column_key = MemoryStore::get_key_for_col(col, key); + + self.db.write().insert(column_key, val.to_vec()); + + Ok(()) + } + + /// Return true if some key exists in some column. + fn key_exists(&self, col: &str, key: &[u8]) -> Result { + let column_key = MemoryStore::get_key_for_col(col, key); + + Ok(self.db.read().contains_key(&column_key)) + } + + /// Delete some key from the database. + fn key_delete(&self, col: &str, key: &[u8]) -> Result<(), Error> { + let column_key = MemoryStore::get_key_for_col(col, key); + + self.db.write().remove(&column_key); + + Ok(()) + } } From 138e19bb2cc17554c7a41b96810fd60001ee84d6 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sat, 14 Sep 2019 23:48:12 -0500 Subject: [PATCH 085/151] Include process block and find head for for choice --- eth2/state_processing/src/common/mod.rs | 2 +- shard_node/shard_chain/src/fork_choice.rs | 95 ++++++++++------------- 2 files changed, 44 insertions(+), 53 deletions(-) diff --git a/eth2/state_processing/src/common/mod.rs b/eth2/state_processing/src/common/mod.rs index 26302fed07d..c874d4796e4 100644 --- a/eth2/state_processing/src/common/mod.rs +++ b/eth2/state_processing/src/common/mod.rs @@ -5,7 +5,7 @@ mod slash_validator; mod verify_bitfield; pub use convert_to_indexed::convert_to_indexed; -pub use get_attesting_indices::{get_attesting_indices, get_attesting_indices_unsorted}; +pub use get_attesting_indices::{get_attesting_indices, get_attesting_indices_unsorted, get_shard_attesting_indices, get_shard_attesting_indices_unsorted}; pub use initiate_validator_exit::initiate_validator_exit; pub use slash_validator::slash_validator; pub use verify_bitfield::verify_bitfield_length; diff --git a/shard_node/shard_chain/src/fork_choice.rs b/shard_node/shard_chain/src/fork_choice.rs index 025303d6b9b..10c628c94b5 100644 --- a/shard_node/shard_chain/src/fork_choice.rs +++ b/shard_node/shard_chain/src/fork_choice.rs @@ -1,11 +1,11 @@ use crate::{ShardChain, ShardChainTypes, ShardChainError}; use beacon_chain::BeaconChainTypes; use shard_lmd_ghost::LmdGhost; -use state_processing::common::get_attesting_indices_unsorted; +use state_processing::common::get_shard_attesting_indices_unsorted; use std::sync::Arc; use store::{Error as BeaconStoreError, Store as BeaconStore}; use shard_store::{Error as StoreError, Store}; -use types::{Attestation, BeaconBlock, BeaconState, BeaconStateError, ShardBlock, ShardSlot, ShardState, ShardStateError, Epoch, EthSpec, ShardSpec, Hash256}; +use types::{ShardAttestation, BeaconBlock, BeaconState, BeaconStateError, ShardBlock, ShardSlot, ShardState, ShardStateError, Epoch, EthSpec, ShardSpec, Hash256}; type Result = std::result::Result; @@ -27,7 +27,7 @@ pub struct ForkChoice { genesis_block_root: Hash256, } -impl ForkChoice { +implForkChoice { pub fn new( store: Arc, genesis_block: &ShardBlock, @@ -75,56 +75,47 @@ impl ForkChoice { .map_err(Into::into) } - // /// Process all attestations in the given `block`. - // /// - // /// Assumes the block (and therefore it's attestations) are valid. It is a logic error to - // /// provide an invalid block. - // pub fn process_block( - // &self, - // beacon_state: &BeaconState, - // block: &ShardBlock, - // block_root: Hash256, - // ) -> Result<()> { - // // Note: we never count the block as a latest message, only attestations. - // // - // // I (Paul H) do not have an explicit reference to this, but I derive it from this - // // document: - // // - // // https://github.com/ethereum/eth2.0-specs/blob/v0.7.0/specs/core/0_fork-choice.md - // for attestation in &block.body.attestations { - // self.process_attestation_from_block(beacon_state, attestation, block)?; - // } - - // self.backend.process_block(block, block_root)?; - - // Ok(()) - // } + /// Process all attestations in the given `block`. + /// + /// Assumes the block (and therefore it's attestations) are valid. It is a logic error to + /// provide an invalid block. + pub fn process_block( + &self, + beacon_state: &BeaconState

, + block: &ShardBlock, + block_root: Hash256, + ) -> Result<()> { + self.process_attestation_from_block(beacon_state, block.attestation, block)?; + self.backend.process_block(block, block_root)?; + + Ok(()) + } - // fn process_attestation_from_block( - // &self, - // beacon_state: &BeaconState, - // attestation: &Attestation, - // block: &ShardBlock - // ) -> Result<()> { - // // Note: `get_attesting_indices_unsorted` requires that the beacon state caches be built. - // let validator_indices = get_shard_attesting_indices_unsorted( - // block.slot, - // beacon_state, - // &attestation.data, - // &attestation.aggregation_bitfield, - // )?; - - // let block_hash = attestation.data.target_root; - - // if block_hash != Hash256::zero() { - // for validator_index in validator_indices { - // self.backend - // .process_attestation(validator_index, block_hash, block.slot)?; - // } - // } - - // Ok(()) - // } + fn process_attestation_from_block( + &self, + beacon_state: &BeaconState

, + attestation: &ShardAttestation, + block: &ShardBlock + ) -> Result<()> { + // Note: `get_attesting_indices_unsorted` requires that the beacon state caches be built. + let validator_indices = get_shard_attesting_indices_unsorted( + block.slot, + beacon_state, + &attestation.data, + &attestation.aggregation_bitfield, + )?; + + let block_hash = attestation.data.target_root; + + if block_hash != Hash256::zero() { + for validator_index in validator_indices { + self.backend + .process_attestation(validator_index, block_hash, block.slot)?; + } + } + + Ok(()) + } // /// Inform the fork choice that the given block (and corresponding root) have been finalized so // /// it may prune it's storage. From f9de536a1e04fc97bcf1156d533029dcd12755e3 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sun, 15 Sep 2019 01:50:28 -0500 Subject: [PATCH 086/151] fork choice gadget completed for attestations and block process --- .../src/common/get_attesting_indices.rs | 13 +++++-------- shard_node/shard_chain/src/fork_choice.rs | 6 +++--- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/eth2/state_processing/src/common/get_attesting_indices.rs b/eth2/state_processing/src/common/get_attesting_indices.rs index 6d9c824d7d4..ccbfc3be672 100644 --- a/eth2/state_processing/src/common/get_attesting_indices.rs +++ b/eth2/state_processing/src/common/get_attesting_indices.rs @@ -49,7 +49,7 @@ pub fn get_attesting_indices_unsorted( pub fn get_shard_attesting_indices( shard: Shard, state: &BeaconState, - attestation_data: &AttestationData, + attestation_data: &ShardAttestationData, bitfield: &Bitfield, ) -> Result, BeaconStateError> { get_shard_attesting_indices_unsorted(shard, state, attestation_data, bitfield).map(|mut indices| { @@ -60,19 +60,16 @@ pub fn get_shard_attesting_indices( } /// Returns validator indices which participated in the attestation, unsorted. -/// -/// Spec v0.6.3 pub fn get_shard_attesting_indices_unsorted( shard: Shard, state: &BeaconState, - attestation_data: &AttestationData, + attestation_data: &ShardAttestationData, bitfield: &Bitfield, ) -> Result, BeaconStateError> { - let target_relative_epoch = - RelativeEpoch::from_epoch(state.current_epoch(), attestation_data.target_epoch)?; - + let spec = T::default_spec(); + let target_epoch = attestation_data.target_slot.epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot); let committee = - state.get_crosslink_committee_for_shard(shard, target_relative_epoch)?; + state.get_shard_committee(target_epoch, shard)?; if !verify_bitfield_length(&bitfield, committee.committee.len()) { return Err(BeaconStateError::InvalidBitfield); diff --git a/shard_node/shard_chain/src/fork_choice.rs b/shard_node/shard_chain/src/fork_choice.rs index 10c628c94b5..f9df2a7f5df 100644 --- a/shard_node/shard_chain/src/fork_choice.rs +++ b/shard_node/shard_chain/src/fork_choice.rs @@ -85,7 +85,7 @@ implForkChoice { block: &ShardBlock, block_root: Hash256, ) -> Result<()> { - self.process_attestation_from_block(beacon_state, block.attestation, block)?; + self.process_attestation_from_block(beacon_state, &block.attestation, block)?; self.backend.process_block(block, block_root)?; Ok(()) @@ -99,13 +99,13 @@ implForkChoice { ) -> Result<()> { // Note: `get_attesting_indices_unsorted` requires that the beacon state caches be built. let validator_indices = get_shard_attesting_indices_unsorted( - block.slot, + block.shard, beacon_state, &attestation.data, &attestation.aggregation_bitfield, )?; - let block_hash = attestation.data.target_root; + let block_hash = attestation.data.shard_block_root; if block_hash != Hash256::zero() { for validator_index in validator_indices { From 55d0e0ac48ba2b6dfa31b86b7b6fbc8775d14fa7 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sun, 15 Sep 2019 01:54:57 -0500 Subject: [PATCH 087/151] fork choice finalization --- shard_node/shard_chain/src/fork_choice.rs | 26 +++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/shard_node/shard_chain/src/fork_choice.rs b/shard_node/shard_chain/src/fork_choice.rs index f9df2a7f5df..5e663331394 100644 --- a/shard_node/shard_chain/src/fork_choice.rs +++ b/shard_node/shard_chain/src/fork_choice.rs @@ -117,19 +117,19 @@ implForkChoice { Ok(()) } - // /// Inform the fork choice that the given block (and corresponding root) have been finalized so - // /// it may prune it's storage. - // /// - // /// `finalized_block_root` must be the root of `finalized_block`. - // pub fn process_finalization( - // &self, - // finalized_block: &BeaconBlock, - // finalized_block_root: Hash256, - // ) -> Result<()> { - // self.backend - // .update_finalized_root(finalized_block, finalized_block_root) - // .map_err(Into::into) - // } + /// Inform the fork choice that the given block (and corresponding root) have been finalized so + /// it may prune it's storage. + /// + /// `finalized_block_root` must be the root of `finalized_block`. + pub fn process_finalization( + &self, + finalized_block: &ShardBlock, + finalized_block_root: Hash256, + ) -> Result<()> { + self.backend + .update_finalized_root(finalized_block, finalized_block_root) + .map_err(Into::into) + } } From c8a26e433b23f415f4575955add96ca831508a79 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sun, 15 Sep 2019 02:09:47 -0500 Subject: [PATCH 088/151] Simplified logic and just pass curent beacon state vs. looking up rooted block --- shard_node/shard_chain/src/fork_choice.rs | 15 +-------------- shard_node/shard_chain/src/shard_chain.rs | 2 +- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/shard_node/shard_chain/src/fork_choice.rs b/shard_node/shard_chain/src/fork_choice.rs index 5e663331394..1f56e8fd290 100644 --- a/shard_node/shard_chain/src/fork_choice.rs +++ b/shard_node/shard_chain/src/fork_choice.rs @@ -40,20 +40,7 @@ implForkChoice { } pub fn find_head(&self, chain: &ShardChain) -> Result { - let current_state = chain.current_state(); - let beacon_root = current_state.latest_block_header.beacon_block_root; - let beacon_block: BeaconBlock = chain - .parent_beacon - .store - .get(&beacon_root)? - .ok_or_else(|| Error::MissingBeaconBlock(beacon_root))?; - - let beacon_state: BeaconState = chain - .parent_beacon - .store - .get(&beacon_block.state_root)? - .ok_or_else(|| Error::MissingBeaconState(beacon_block.state_root))?; - + let beacon_state = chain.parent_beacon.current_state(); let current_crosslink = beacon_state.get_current_crosslink(chain.shard)?; // Spec needs an update for crosslinks to hold the end shard_block_root // For now, we will just assume the latest block hash is included and add the diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index bb7ecf9138f..014944bdb0a 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -438,7 +438,7 @@ impl ShardChain { // Register the new block with the fork choice service. - // self.fork_choice.process_block(&state, &block, block_root)?; + self.fork_choice.process_block(&beacon_state, &block, block_root)?; // Execute the fork choice algorithm, enthroning a new head if discovered. // From efae563405034279713e1272ef8f980a396c91e4 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sun, 15 Sep 2019 09:39:08 -0500 Subject: [PATCH 089/151] shard fork choice and updating canonical head completed --- shard_node/shard_chain/src/shard_chain.rs | 82 +++++++++++++++-------- 1 file changed, 53 insertions(+), 29 deletions(-) diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 014944bdb0a..4777892cd27 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -225,13 +225,6 @@ impl ShardChain { Ok(()) } - pub fn get_beacon_state(&self, state_root: Hash256) -> Result>, Error> { - match self.parent_beacon.store.get(&state_root) { - Ok(shard_block) => Ok(shard_block), - Err(e) => Err(Error::BeaconDBError(e)), - } - } - /// Returns the validator index (if any) for the given public key. /// /// Information is retrieved from the present `beacon_state.validator_registry`. @@ -513,34 +506,65 @@ impl ShardChain { // Ok((block, state)) // } - // /// Execute the fork choice algorithm and enthrone the result as the canonical head. - // /// Update the canonical head to `new_head`. - // fn update_canonical_head(&self, new_head: CheckPoint) -> Result<(), Error> { - // // Update the checkpoint that stores the head of the chain at the time it received the - // // block. - // *self.canonical_head.write() = new_head; + /// Execute the fork choice algorithm and enthrone the result as the canonical head. + pub fn fork_choice(&self) -> Result<(), Error> { + // Determine the root of the block that is the head of the chain. + let shard_block_root = self.fork_choice.find_head(&self)?; + + // If a new head was chosen. + if shard_block_root != self.head().shard_block_root { + let shard_block: ShardBlock = self + .store + .get(&shard_block_root)? + .ok_or_else(|| Error::MissingShardBlock(shard_block_root))?; + + let shard_state_root = shard_block.state_root; + let shard_state: ShardState = self + .store + .get(&shard_state_root)? + .ok_or_else(|| Error::MissingShardState(shard_state_root))?; + + self.update_canonical_head(CheckPoint { + shard_block: shard_block, + shard_block_root, + shard_state, + shard_state_root, + })?; + + Ok(()) + } else { + Ok(()) + } + } - // // Update the always-at-the-present-slot state we keep around for performance gains. - // *self.state.write() = { - // let mut state = self.canonical_head.read().shard_state.clone(); + /// Execute the fork choice algorithm and enthrone the result as the canonical head. + /// Update the canonical head to `new_head`. + fn update_canonical_head(&self, new_head: CheckPoint) -> Result<(), Error> { + // Update the checkpoint that stores the head of the chain at the time it received the + // block. + *self.canonical_head.write() = new_head; - // let present_slot = match self.slot_clock.present_slot() { - // Ok(Some(slot)) => slot, - // _ => return Err(Error::UnableToReadSlot), - // }; + // Update the always-at-the-present-slot state we keep around for performance gains. + *self.state.write() = { + let mut state = self.canonical_head.read().shard_state.clone(); - // // If required, transition the new state to the present slot. - // for _ in state.slot.as_u64()..present_slot.as_u64() { - // per_slot_processing(&mut state, &self.spec)?; - // } + let present_slot = match self.slot_clock.present_slot() { + Ok(Some(slot)) => slot, + _ => return Err(Error::UnableToReadSlot), + }; - // state.build_all_caches(&self.spec)?; + // If required, transition the new state to the present slot. + for _ in state.slot.as_u64()..present_slot.as_u64() { + per_shard_slot_processing(&mut state, &self.spec)?; + } - // state - // }; + state.build_cache(&self.spec)?; - // Ok(()) - // } + state + }; + + Ok(()) + } // /// Called after `self` has had a new block finalized. // /// From 29e8e246e9bf0ad06329ff98a5dd4a1ab63cb2d6 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sun, 15 Sep 2019 11:14:24 -0500 Subject: [PATCH 090/151] Attestations added to op pool --- shard_node/shard_chain/src/shard_chain.rs | 25 ++++++++++------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 4777892cd27..e21d90beb97 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -330,20 +330,17 @@ impl ShardChain { // }) // } - // /// Accept a new attestation from the network. - // /// - // /// If valid, the attestation is added to the `op_pool` and aggregated with another attestation - // /// if possible. - // pub fn process_attestation( - // &self, - // attestation: Attestation, - // ) -> Result<(), AttestationValidationError> { - // let result = self - // .op_pool - // .insert_attestation(attestation, &*self.state.read(), &self.spec); - - // result - // } + /// Accept a new attestation from the network. + /// + /// If valid, the attestation is added to the `op_pool` and aggregated with another attestation + /// if possible. + pub fn process_attestation( + &self, + attestation: ShardAttestation, + ) -> () { + self.op_pool + .insert_attestation(attestation, &self.parent_beacon.current_state(), &self.spec); + } /// Accept some block and attempt to add it to block DAG. /// From 6a05560f06f5d0c6b370594a6cc6ba27d6bbc358 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sun, 15 Sep 2019 12:14:54 -0500 Subject: [PATCH 091/151] Implemented "after crosslink" functionality --- shard_node/shard_chain/src/fork_choice.rs | 7 +++ shard_node/shard_chain/src/shard_chain.rs | 57 +++++++++++------------ 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/shard_node/shard_chain/src/fork_choice.rs b/shard_node/shard_chain/src/fork_choice.rs index 1f56e8fd290..86fc3a3638d 100644 --- a/shard_node/shard_chain/src/fork_choice.rs +++ b/shard_node/shard_chain/src/fork_choice.rs @@ -49,6 +49,13 @@ implForkChoice { // should be updated to end epoch :) with the new spec todo let start_block_slot = ShardSlot::from(current_crosslink.epoch.as_u64() * chain.spec.shard_slots_per_epoch); + // Resolve the `0x00.. 00` alias back to genesis + let start_block_root = if start_block_root == Hash256::zero() { + self.genesis_block_root + } else { + start_block_root + }; + // A function that returns the weight for some validator index. let weight = |validator_index: usize| -> Option { beacon_state diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index e21d90beb97..33685545ae8 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -64,6 +64,7 @@ pub struct ShardChain { canonical_head: RwLock>, state: RwLock>, genesis_block_root: Hash256, + pub crosslink_root: Hash256, pub fork_choice: ForkChoice, } @@ -105,6 +106,7 @@ impl ShardChain { state: RwLock::new(genesis_state), canonical_head, genesis_block_root, + crosslink_root: Hash256::default(), fork_choice: ForkChoice::new(store.clone(), &genesis_block, genesis_block_root), store, }) @@ -287,6 +289,17 @@ impl ShardChain { self.state.read().slot } + pub fn check_for_new_crosslink(mut self) -> Result<(), Error> { + let beacon_state = self.parent_beacon.current_state(); + let crosslink_root = beacon_state.get_current_crosslink(self.shard)?.crosslink_data_root; + let current_crossslink_root = self.crosslink_root; + if crosslink_root != current_crossslink_root { + self.crosslink_root = crosslink_root; + self.after_crosslink(crosslink_root); + } + Ok(()) + } + /// Returns the block proposer for a given slot. /// /// Information is read from the present `beacon_state` @@ -434,7 +447,7 @@ impl ShardChain { // // Note: in the future we may choose to run fork-choice less often, potentially based upon // some heuristic around number of attestations seen for the block. - // self.fork_choice()?; + self.fork_choice()?; Ok(BlockProcessingOutcome::Processed { block_root }) } @@ -563,34 +576,20 @@ impl ShardChain { Ok(()) } - // /// Called after `self` has had a new block finalized. - // /// - // /// Performs pruning and finality-based optimizations. - // fn after_finalization( - // &self, - // old_finalized_epoch: Epoch, - // finalized_block_root: Hash256, - // ) -> Result<(), Error> { - // // Need to build logic here to manage pruning for shard as well - // // let finalized_block = self - // // .store - // // .get::(&finalized_block_root)? - // // .ok_or_else(|| Error::MissingBeaconBlock(finalized_block_root))?; - - // // let new_finalized_epoch = finalized_block.slot.epoch(T::EthSpec::slots_per_epoch()); - - // // if new_finalized_epoch < old_finalized_epoch { - // // Err(Error::RevertedFinalizedEpoch { - // // previous_epoch: old_finalized_epoch, - // // new_epoch: new_finalized_epoch, - // // }) - // // } else { - // // self.fork_choice - // // .process_finalization(&finalized_block, finalized_block_root)?; - - // // Ok(()) - // // } - // } + /// Called after `self` has found a new crosslink + /// + /// Performs pruning and fork choice optimizations after recognized crosslinks. + fn after_crosslink(&self, crosslink_root: Hash256) -> Result<(), Error> { + let crosslink_block = self + .store + .get::(&crosslink_root)? + .ok_or_else(|| Error::MissingShardBlock(crosslink_root))?; + + self.fork_choice + .process_finalization(&crosslink_block, crosslink_root)?; + + Ok(()) + } // /// Returns `true` if the given block root has not been processed. // pub fn is_new_block_root(&self, shard_block_root: &Hash256) -> Result { From af19cfcdd8e924eeff04f37f4eca63aaa30be211 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sun, 15 Sep 2019 12:19:11 -0500 Subject: [PATCH 092/151] Add check for crosslink into fork_choice call --- shard_node/shard_chain/src/shard_chain.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 33685545ae8..55574ace551 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -519,6 +519,7 @@ impl ShardChain { /// Execute the fork choice algorithm and enthrone the result as the canonical head. pub fn fork_choice(&self) -> Result<(), Error> { // Determine the root of the block that is the head of the chain. + self.check_for_new_crosslink(); let shard_block_root = self.fork_choice.find_head(&self)?; // If a new head was chosen. From e130271f052a3c3583c79c4ba5c363f7ff037c4e Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sun, 15 Sep 2019 12:43:28 -0500 Subject: [PATCH 093/151] add rwlock for current crosslink --- shard_node/shard_chain/src/shard_chain.rs | 64 +++++++++++------------ 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 55574ace551..ef3265e2a7e 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -64,7 +64,7 @@ pub struct ShardChain { canonical_head: RwLock>, state: RwLock>, genesis_block_root: Hash256, - pub crosslink_root: Hash256, + pub crosslink_root: RwLock, pub fork_choice: ForkChoice, } @@ -106,7 +106,7 @@ impl ShardChain { state: RwLock::new(genesis_state), canonical_head, genesis_block_root, - crosslink_root: Hash256::default(), + crosslink_root: RwLock::new(Hash256::default()), fork_choice: ForkChoice::new(store.clone(), &genesis_block, genesis_block_root), store, }) @@ -289,12 +289,12 @@ impl ShardChain { self.state.read().slot } - pub fn check_for_new_crosslink(mut self) -> Result<(), Error> { + pub fn check_for_new_crosslink(&self) -> Result<(), Error> { let beacon_state = self.parent_beacon.current_state(); let crosslink_root = beacon_state.get_current_crosslink(self.shard)?.crosslink_data_root; - let current_crossslink_root = self.crosslink_root; + let current_crossslink_root = *self.crosslink_root.read(); if crosslink_root != current_crossslink_root { - self.crosslink_root = crosslink_root; + *self.crosslink_root.write() = crosslink_root; self.after_crosslink(crosslink_root); } Ok(()) @@ -316,32 +316,33 @@ impl ShardChain { Ok(index) } - // /// Produce an `AttestationData` that is valid for the present `slot` and given `shard`. - // /// - // /// Attests to the canonical chain. - // pub fn produce_attestation_data(&self) -> Result { - // let state = self.state.read(); - // let head_block_root = self.head().shard_block_root; - // let head_block_slot = self.head().shard_block.slot; + /// Produce an `AttestationData` that is valid for the present `slot` and given `shard`. + /// + /// Attests to the canonical chain. + pub fn produce_attestation_data(&self) -> Result { + let state = self.state.read(); + let head_block_root = self.head().shard_block_root; + let head_block_slot = self.head().shard_block.slot; - // self.produce_attestation_data_for_block(head_block_root, head_block_slot, &*state) - // } + self.produce_attestation_data_for_block(head_block_root, head_block_slot, &*state) + } - // /// Produce an `AttestationData` that attests to the chain denoted by `block_root` and `state`. - // /// - // /// Permits attesting to any arbitrary chain. Generally, the `produce_attestation_data` - // /// function should be used as it attests to the canonical chain. - // pub fn produce_attestation_data_for_block( - // &self, - // head_block_root: Hash256, - // head_block_slot: Slot, - // state: &ShardState, - // ) -> Result { - - // Ok(AttestationData { - // shard_block_root: head_block_root, - // }) - // } + /// Produce an `AttestationData` that attests to the chain denoted by `block_root` and `state`. + /// + /// Permits attesting to any arbitrary chain. Generally, the `produce_attestation_data` + /// function should be used as it attests to the canonical chain. + pub fn produce_attestation_data_for_block( + &self, + head_block_root: Hash256, + head_block_slot: ShardSlot, + state: &ShardState, + ) -> Result { + + Ok(ShardAttestationData { + shard_block_root: head_block_root, + target_slot: head_block_slot, + }) + } /// Accept a new attestation from the network. /// @@ -591,11 +592,6 @@ impl ShardChain { Ok(()) } - - // /// Returns `true` if the given block root has not been processed. - // pub fn is_new_block_root(&self, shard_block_root: &Hash256) -> Result { - // Ok(!self.store.exists::(shard_block_root)?) - // } } impl From for Error { From a065ea27c548d330c9b79278e1516f3edcccfa1a Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sun, 15 Sep 2019 13:45:12 -0500 Subject: [PATCH 094/151] Block production enbled now :) - get_attestation from op pool returns the one with most bitfields --- eth2/shard_operation_pool/src/lib.rs | 24 +---- shard_node/shard_chain/src/shard_chain.rs | 122 ++++++++++------------ 2 files changed, 63 insertions(+), 83 deletions(-) diff --git a/eth2/shard_operation_pool/src/lib.rs b/eth2/shard_operation_pool/src/lib.rs index c9cbfc92257..528e63ffdd9 100644 --- a/eth2/shard_operation_pool/src/lib.rs +++ b/eth2/shard_operation_pool/src/lib.rs @@ -63,22 +63,22 @@ impl OperationPool { self.attestations.read().values().map(Vec::len).sum() } - /// Get a list of attestations for inclusion in a block. - pub fn get_attestations(&self, state: &ShardState, beacon_state: &BeaconState, spec: &ChainSpec) -> Vec { - // enforce the right beacon state is being passed through + /// Get attestation with most attesters for inclusion in a block + pub fn get_attestation(&self, state: &ShardState, beacon_state: &BeaconState, spec: &ChainSpec) -> ShardAttestation { let attesting_slot = ShardSlot::from(state.slot - 1); let epoch = attesting_slot.epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot); let domain_bytes = AttestationId::compute_domain_bytes(epoch, attesting_slot, beacon_state, spec); let reader = self.attestations.read(); - let attestations: Vec = reader + let mut attestations: Vec = reader .iter() .filter(|(key, _)| key.domain_bytes_match(&domain_bytes)) .flat_map(|(_, attestations)| attestations) .cloned() .collect(); - attestations + attestations.sort_by(|a, b| b.aggregation_bitfield.num_set_bits().cmp(&a.aggregation_bitfield.num_set_bits())); + (&attestations[0]).clone() } pub fn prune_attestations(&self, finalized_state: &ShardState) { @@ -90,20 +90,6 @@ impl OperationPool { } } -fn filter_limit_operations<'a, T: 'a, I, F>(operations: I, filter: F, limit: u64) -> Vec -where - I: IntoIterator, - F: Fn(&T) -> bool, - T: Clone, -{ - operations - .into_iter() - .filter(|x| filter(*x)) - .take(limit as usize) - .cloned() - .collect() -} - impl PartialEq for OperationPool { fn eq(&self, other: &Self) -> bool { *self.attestations.read() == *other.attestations.read()} } diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index ef3265e2a7e..7e0da9e9c00 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -452,70 +452,64 @@ impl ShardChain { Ok(BlockProcessingOutcome::Processed { block_root }) } - // /// Produce a new block at the present slot. - // /// - // /// The produced block will not be inherently valid, it must be signed by a block producer. - // /// Block signing is out of the scope of this function and should be done by a separate program. - // pub fn produce_block( - // &self, - // ) -> Result<(ShardBlock, ShardState), BlockProductionError> { - // let state = self.state.read().clone(); - // let slot = self - // .read_slot_clock() - // .ok_or_else(|| BlockProductionError::UnableToReadSlot)?; - - // self.produce_block_on_state(state, slot) - // } - - // /// Produce a block for some `slot` upon the given `state`. - // /// - // /// Typically the `self.produce_block()` function should be used, instead of calling this - // /// function directly. This function is useful for purposefully creating forks or blocks at - // /// non-current slots. - // /// - // /// The given state will be advanced to the given `produce_at_slot`, then a block will be - // /// produced at that slot height. - // pub fn produce_block_on_state( - // &self, - // mut state: ShardState, - // produce_at_slot: Slot, - // ) -> Result<(ShardBlock, ShardState), BlockProductionError> { - // // If required, transition the new state to the present slot. - // while state.slot < produce_at_slot { - // per_slot_processing(&mut state, &self.spec)?; - // } - - // let previous_block_root = if state.slot > 0 { - // *state - // .get_block_root(state.slot - 1) - // .map_err(|_| BlockProductionError::UnableToGetBlockRootFromState)? - // } else { - // state.latest_block_header.canonical_root() - // }; - - // let mut graffiti: [u8; 32] = [0; 32]; - // graffiti.copy_from_slice(GRAFFITI.as_bytes()); - - // let mut block = ShardBlock { - // slot: state.slot, - // previous_block_root, - // state_root: Hash256::zero(), // Updated after the state is calculated. - // signature: Signature::empty_signature(), // To be completed by a validator. - // // need to add the attestations here - // body: ShardBlockBody { - // graffiti, - // attestations: self.op_pool.get_attestations(&state, &self.spec), - // }, - // }; - - // per_block_processing_without_verifying_block_signature(&mut state, &block, &self.spec)?; - - // let state_root = state.canonical_root(); - - // block.state_root = state_root; - - // Ok((block, state)) - // } + /// Produce a new block at the present slot. + /// + /// The produced block will not be inherently valid, it must be signed by a block producer. + /// Block signing is out of the scope of this function and should be done by a separate program. + pub fn produce_block( + &self, + ) -> Result<(ShardBlock, ShardState), BlockProductionError> { + let state = self.state.read().clone(); + let slot = self + .read_slot_clock() + .ok_or_else(|| BlockProductionError::UnableToReadSlot)?; + + self.produce_block_on_state(state, slot) + } + + /// Produce a block for some `slot` upon the given `state`. + /// + /// Typically the `self.produce_block()` function should be used, instead of calling this + /// function directly. This function is useful for purposefully creating forks or blocks at + /// non-current slots. + /// + /// The given state will be advanced to the given `produce_at_slot`, then a block will be + /// produced at that slot height. + pub fn produce_block_on_state( + &self, + mut state: ShardState, + produce_at_slot: ShardSlot, + ) -> Result<(ShardBlock, ShardState), BlockProductionError> { + // If required, transition the new state to the present slot. + while state.slot < produce_at_slot { + per_shard_slot_processing(&mut state, &self.spec)?; + } + + let spec = &self.spec; + let parent_root = state.latest_block_header.canonical_root(); + let beacon_state = self.parent_beacon.current_state(); + let beacon_block_root_epoch = state.latest_block_header.slot.epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot); + let beacon_block_root = beacon_state.get_block_root_at_epoch(beacon_block_root_epoch)?.clone(); + + let mut graffiti: [u8; 32] = [0; 32]; + graffiti.copy_from_slice(GRAFFITI.as_bytes()); + + let mut block = ShardBlock { + shard: state.shard, + slot: state.slot, + beacon_block_root, + parent_root, + state_root: Hash256::zero(), + attestation: self.op_pool.get_attestation(&state, &self.parent_beacon.current_state(), spec), + signature: Signature::empty_signature(), + }; + + per_shard_block_processing(&beacon_state, &mut state, &block, spec); + let state_root = state.canonical_root(); + block.state_root = state_root; + + Ok((block, state)) + } /// Execute the fork choice algorithm and enthrone the result as the canonical head. pub fn fork_choice(&self) -> Result<(), Error> { From ab125c984d848c8adc6f5d5499762582292a40e1 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sun, 15 Sep 2019 13:49:57 -0500 Subject: [PATCH 095/151] remove grafiti --- shard_node/shard_chain/src/shard_chain.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 7e0da9e9c00..dbd9fc38611 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -17,12 +17,6 @@ use shard_store::{Error as DBError, Store}; use tree_hash::TreeHash; use types::*; -// Text included in blocks. -// Must be 32-bytes or panic. -// -// |-------must be this long------| -pub const GRAFFITI: &str = "sigp/lighthouse-0.0.0-prerelease"; - #[derive(Debug, PartialEq)] pub enum BlockProcessingOutcome { /// Block was valid and imported into the block graph. @@ -491,9 +485,6 @@ impl ShardChain { let beacon_block_root_epoch = state.latest_block_header.slot.epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot); let beacon_block_root = beacon_state.get_block_root_at_epoch(beacon_block_root_epoch)?.clone(); - let mut graffiti: [u8; 32] = [0; 32]; - graffiti.copy_from_slice(GRAFFITI.as_bytes()); - let mut block = ShardBlock { shard: state.shard, slot: state.slot, From 53ca0031acf2edce28074b4ff921340e2863f81e Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sun, 15 Sep 2019 15:14:13 -0500 Subject: [PATCH 096/151] Added shard committee function --- shard_node/shard_chain/src/shard_chain.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index dbd9fc38611..750f9a7a585 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -298,10 +298,6 @@ impl ShardChain { /// /// Information is read from the present `beacon_state` pub fn block_proposer(&self, slot: ShardSlot, shard: u64) -> Result { - // Ensures that the present state has been advanced to the present slot, skipping slots if - // blocks are not present. - self.catchup_state()?; - let index = self.parent_beacon.current_state().get_shard_proposer_index( shard, slot, @@ -310,6 +306,11 @@ impl ShardChain { Ok(index) } + pub fn shard_committee(&self, epoch: Epoch, shard: u64) -> Result { + let shard_committee = self.parent_beacon.current_state().get_shard_committee(epoch, shard)?; + Ok(shard_committee) + } + /// Produce an `AttestationData` that is valid for the present `slot` and given `shard`. /// /// Attests to the canonical chain. From d3f1ad19c7691aeeec75e3aa7dcc6adec9a4f9d8 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sun, 15 Sep 2019 21:11:11 -0500 Subject: [PATCH 097/151] Fix runtime issues --- .../src/beacon_state/committee_cache/tests.rs | 1 + .../beacon_state/period_committee_cache.rs | 9 ++- shard_node/shard_store/src/lib.rs | 81 ------------------- 3 files changed, 7 insertions(+), 84 deletions(-) diff --git a/eth2/types/src/beacon_state/committee_cache/tests.rs b/eth2/types/src/beacon_state/committee_cache/tests.rs index f25a4f727a8..41666afca18 100644 --- a/eth2/types/src/beacon_state/committee_cache/tests.rs +++ b/eth2/types/src/beacon_state/committee_cache/tests.rs @@ -155,6 +155,7 @@ pub struct ExcessShardsEthSpec; impl EthSpec for ExcessShardsEthSpec { type ShardCount = U128; type SlotsPerHistoricalRoot = U8192; + type PeriodCommitteeRootsLength = U8192; type LatestRandaoMixesLength = U8192; type LatestActiveIndexRootsLength = U8192; type LatestSlashedExitLength = U8192; diff --git a/eth2/types/src/beacon_state/period_committee_cache.rs b/eth2/types/src/beacon_state/period_committee_cache.rs index 2b1ed6b36d1..0e76dca95b1 100644 --- a/eth2/types/src/beacon_state/period_committee_cache.rs +++ b/eth2/types/src/beacon_state/period_committee_cache.rs @@ -22,9 +22,12 @@ impl PeriodCommitteeCache { let mut committees: Vec = Vec::with_capacity(shard_count); for n in 0..shard_count { - let committee_indices = state - .get_crosslink_committee_for_shard(n as u64, RelativeEpoch::Current)? - .committee[..spec.target_period_committee_size] + let crosslink_committee = state + .get_crosslink_committee_for_shard(n as u64, RelativeEpoch::Current)?; + let crosslink_size = crosslink_committee.committee.len(); + + let committee_indices = crosslink_committee + .committee[..crosslink_size.min(spec.target_period_committee_size)] .to_vec(); let period_committee = PeriodCommittee { shard: n as u64, diff --git a/shard_node/shard_store/src/lib.rs b/shard_node/shard_store/src/lib.rs index ebd8337ff4c..75b71b60cdc 100644 --- a/shard_node/shard_store/src/lib.rs +++ b/shard_node/shard_store/src/lib.rs @@ -139,84 +139,3 @@ pub trait StoreItem: Sized { store.key_delete(column, key) } } - -#[cfg(test)] -mod tests { - use super::*; - use ssz::{Decode, Encode}; - use ssz_derive::{Decode, Encode}; - use tempfile::tempdir; - - #[derive(PartialEq, Debug, Encode, Decode)] - struct StorableThing { - a: u64, - b: u64, - } - - impl StoreItem for StorableThing { - fn db_column() -> DBColumn { - DBColumn::ShardBlock - } - - fn as_store_bytes(&self) -> Vec { - self.as_ssz_bytes() - } - - fn from_store_bytes(bytes: &mut [u8]) -> Result { - Self::from_ssz_bytes(bytes).map_err(Into::into) - } - } - - fn test_impl(store: impl Store) { - let key = Hash256::random(); - let item = StorableThing { a: 1, b: 42 }; - - assert_eq!(store.exists::(&key), Ok(false)); - - store.put(&key, &item).unwrap(); - - assert_eq!(store.exists::(&key), Ok(true)); - - let retrieved = store.get(&key).unwrap().unwrap(); - assert_eq!(item, retrieved); - - store.delete::(&key).unwrap(); - - assert_eq!(store.exists::(&key), Ok(false)); - - assert_eq!(store.get::(&key), Ok(None)); - } - - #[test] - fn diskdb() { - let dir = tempdir().unwrap(); - let path = dir.path(); - let store = DiskStore::open(&path).unwrap(); - - test_impl(store); - } - - #[test] - fn memorydb() { - let store = MemoryStore::open(); - - test_impl(store); - } - - #[test] - fn exists() { - let store = MemoryStore::open(); - let key = Hash256::random(); - let item = StorableThing { a: 1, b: 42 }; - - assert_eq!(store.exists::(&key).unwrap(), false); - - store.put(&key, &item).unwrap(); - - assert_eq!(store.exists::(&key).unwrap(), true); - - store.delete::(&key).unwrap(); - - assert_eq!(store.exists::(&key).unwrap(), false); - } -} From 0ecb00ff2b1007e35fc4ab7a1fa985de5630bef6 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Mon, 16 Sep 2019 00:34:08 -0500 Subject: [PATCH 098/151] Formatting :) --- eth2/shard_lmd_ghost/src/lib.rs | 4 +- eth2/shard_lmd_ghost/src/reduced_tree.rs | 4 +- .../src/attestation_id.rs | 16 +++- eth2/shard_operation_pool/src/lib.rs | 28 ++++-- .../src/common/get_attesting_indices.rs | 20 +++-- eth2/state_processing/src/common/mod.rs | 5 +- eth2/state_processing/src/lib.rs | 8 +- .../process_period_committee.rs | 3 +- .../src/per_shard_block_processing.rs | 2 +- .../src/per_shard_slot_processing.rs | 11 ++- eth2/types/src/beacon_state.rs | 40 ++++----- .../src/beacon_state/beacon_state_types.rs | 2 +- .../beacon_state/period_committee_cache.rs | 8 +- eth2/types/src/lib.rs | 8 +- eth2/types/src/period.rs | 11 ++- eth2/types/src/period_committee.rs | 15 +++- eth2/types/src/relative_period.rs | 1 - eth2/types/src/shard_attestation.rs | 2 +- eth2/types/src/shard_pending_attestation.rs | 2 +- .../src/shard_state/shard_state_types.rs | 2 +- eth2/types/src/slot_epoch.rs | 3 +- eth2/types/src/slot_height.rs | 11 ++- shard_node/shard_chain/src/checkpoint.rs | 2 +- shard_node/shard_chain/src/errors.rs | 4 +- shard_node/shard_chain/src/fork_choice.rs | 17 ++-- shard_node/shard_chain/src/lib.rs | 6 +- shard_node/shard_chain/src/shard_chain.rs | 87 ++++++++++++------- shard_node/shard_store/src/iter.rs | 14 ++- shard_node/shard_store/src/lib.rs | 6 +- 29 files changed, 205 insertions(+), 137 deletions(-) diff --git a/eth2/shard_lmd_ghost/src/lib.rs b/eth2/shard_lmd_ghost/src/lib.rs index b5cf55fbcce..4bc46fcb932 100644 --- a/eth2/shard_lmd_ghost/src/lib.rs +++ b/eth2/shard_lmd_ghost/src/lib.rs @@ -1,8 +1,8 @@ mod reduced_tree; -use std::sync::Arc; use shard_store::Store; -use types::{ShardBlock, ShardSpec, Hash256, ShardSlot}; +use std::sync::Arc; +use types::{Hash256, ShardBlock, ShardSlot, ShardSpec}; pub use reduced_tree::ThreadSafeReducedTree; diff --git a/eth2/shard_lmd_ghost/src/reduced_tree.rs b/eth2/shard_lmd_ghost/src/reduced_tree.rs index a4a320b5de0..87211f3d90d 100644 --- a/eth2/shard_lmd_ghost/src/reduced_tree.rs +++ b/eth2/shard_lmd_ghost/src/reduced_tree.rs @@ -5,11 +5,11 @@ //! This implementation is incomplete and has known bugs. Do not use in production. use super::{LmdGhost, Result as SuperResult}; use parking_lot::RwLock; +use shard_store::{iter::BestBlockRootsIterator, Error as StoreError, Store}; use std::collections::HashMap; use std::marker::PhantomData; use std::sync::Arc; -use shard_store::{iter::BestBlockRootsIterator, Error as StoreError, Store}; -use types::{ShardBlock, ShardState, ShardSpec, Hash256, ShardSlot}; +use types::{Hash256, ShardBlock, ShardSlot, ShardSpec, ShardState}; type Result = std::result::Result; diff --git a/eth2/shard_operation_pool/src/attestation_id.rs b/eth2/shard_operation_pool/src/attestation_id.rs index 3ab40751126..0cb84d4d353 100644 --- a/eth2/shard_operation_pool/src/attestation_id.rs +++ b/eth2/shard_operation_pool/src/attestation_id.rs @@ -1,7 +1,9 @@ use int_to_bytes::int_to_bytes8; use ssz::ssz_encode; use ssz_derive::{Decode, Encode}; -use types::{BeaconState, ShardAttestationData, ShardSlot, ShardState, ChainSpec, Domain, Epoch, EthSpec}; +use types::{ + BeaconState, ChainSpec, Domain, Epoch, EthSpec, ShardAttestationData, ShardSlot, ShardState, +}; /// Serialized `AttestationData` augmented with a domain to encode the fork info. #[derive(PartialEq, Eq, Clone, Hash, Debug, PartialOrd, Ord, Encode, Decode)] @@ -21,7 +23,12 @@ impl AttestationId { let mut bytes = ssz_encode(attestation); let slot = attestation.target_slot; let epoch = slot.epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot); - bytes.extend_from_slice(&AttestationId::compute_domain_bytes(epoch, slot, beacon_state, spec)); + bytes.extend_from_slice(&AttestationId::compute_domain_bytes( + epoch, + slot, + beacon_state, + spec, + )); AttestationId { v: bytes } } @@ -31,9 +38,10 @@ impl AttestationId { beacon_state: &BeaconState, spec: &ChainSpec, ) -> Vec { - let mut domain_bytes = int_to_bytes8(spec.get_domain(epoch, Domain::Attestation, &beacon_state.fork)); + let mut domain_bytes = + int_to_bytes8(spec.get_domain(epoch, Domain::Attestation, &beacon_state.fork)); let mut slot_identifying_bytes = int_to_bytes8(slot.into()); - + domain_bytes.append(&mut slot_identifying_bytes); domain_bytes } diff --git a/eth2/shard_operation_pool/src/lib.rs b/eth2/shard_operation_pool/src/lib.rs index 528e63ffdd9..1f2e8d4e031 100644 --- a/eth2/shard_operation_pool/src/lib.rs +++ b/eth2/shard_operation_pool/src/lib.rs @@ -6,7 +6,7 @@ use parking_lot::RwLock; use std::collections::{btree_map::Entry, hash_map, BTreeMap, HashMap, HashSet}; use std::marker::PhantomData; use types::{ - BeaconState, ShardAttestation, ShardSlot, ShardState, ChainSpec, EthSpec, ShardSpec, Validator + BeaconState, ChainSpec, EthSpec, ShardAttestation, ShardSlot, ShardSpec, ShardState, Validator, }; #[derive(Default, Debug)] @@ -64,10 +64,16 @@ impl OperationPool { } /// Get attestation with most attesters for inclusion in a block - pub fn get_attestation(&self, state: &ShardState, beacon_state: &BeaconState, spec: &ChainSpec) -> ShardAttestation { + pub fn get_attestation( + &self, + state: &ShardState, + beacon_state: &BeaconState, + spec: &ChainSpec, + ) -> ShardAttestation { let attesting_slot = ShardSlot::from(state.slot - 1); let epoch = attesting_slot.epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot); - let domain_bytes = AttestationId::compute_domain_bytes(epoch, attesting_slot, beacon_state, spec); + let domain_bytes = + AttestationId::compute_domain_bytes(epoch, attesting_slot, beacon_state, spec); let reader = self.attestations.read(); let mut attestations: Vec = reader @@ -77,19 +83,25 @@ impl OperationPool { .cloned() .collect(); - attestations.sort_by(|a, b| b.aggregation_bitfield.num_set_bits().cmp(&a.aggregation_bitfield.num_set_bits())); + attestations.sort_by(|a, b| { + b.aggregation_bitfield + .num_set_bits() + .cmp(&a.aggregation_bitfield.num_set_bits()) + }); (&attestations[0]).clone() } pub fn prune_attestations(&self, finalized_state: &ShardState) { self.attestations.write().retain(|_, attestations| { - attestations.first().map_or(false, |att| { - finalized_state.slot <= att.data.target_slot - }) + attestations + .first() + .map_or(false, |att| finalized_state.slot <= att.data.target_slot) }); } } impl PartialEq for OperationPool { - fn eq(&self, other: &Self) -> bool { *self.attestations.read() == *other.attestations.read()} + fn eq(&self, other: &Self) -> bool { + *self.attestations.read() == *other.attestations.read() + } } diff --git a/eth2/state_processing/src/common/get_attesting_indices.rs b/eth2/state_processing/src/common/get_attesting_indices.rs index ccbfc3be672..976daa39179 100644 --- a/eth2/state_processing/src/common/get_attesting_indices.rs +++ b/eth2/state_processing/src/common/get_attesting_indices.rs @@ -45,18 +45,19 @@ pub fn get_attesting_indices_unsorted( .collect()) } - pub fn get_shard_attesting_indices( shard: Shard, state: &BeaconState, attestation_data: &ShardAttestationData, bitfield: &Bitfield, ) -> Result, BeaconStateError> { - get_shard_attesting_indices_unsorted(shard, state, attestation_data, bitfield).map(|mut indices| { - // Fast unstable sort is safe because validator indices are unique - indices.sort_unstable(); - indices - }) + get_shard_attesting_indices_unsorted(shard, state, attestation_data, bitfield).map( + |mut indices| { + // Fast unstable sort is safe because validator indices are unique + indices.sort_unstable(); + indices + }, + ) } /// Returns validator indices which participated in the attestation, unsorted. @@ -67,9 +68,10 @@ pub fn get_shard_attesting_indices_unsorted( bitfield: &Bitfield, ) -> Result, BeaconStateError> { let spec = T::default_spec(); - let target_epoch = attestation_data.target_slot.epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot); - let committee = - state.get_shard_committee(target_epoch, shard)?; + let target_epoch = attestation_data + .target_slot + .epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot); + let committee = state.get_shard_committee(target_epoch, shard)?; if !verify_bitfield_length(&bitfield, committee.committee.len()) { return Err(BeaconStateError::InvalidBitfield); diff --git a/eth2/state_processing/src/common/mod.rs b/eth2/state_processing/src/common/mod.rs index c874d4796e4..486ae9c9ea2 100644 --- a/eth2/state_processing/src/common/mod.rs +++ b/eth2/state_processing/src/common/mod.rs @@ -5,7 +5,10 @@ mod slash_validator; mod verify_bitfield; pub use convert_to_indexed::convert_to_indexed; -pub use get_attesting_indices::{get_attesting_indices, get_attesting_indices_unsorted, get_shard_attesting_indices, get_shard_attesting_indices_unsorted}; +pub use get_attesting_indices::{ + get_attesting_indices, get_attesting_indices_unsorted, get_shard_attesting_indices, + get_shard_attesting_indices_unsorted, +}; pub use initiate_validator_exit::initiate_validator_exit; pub use slash_validator::slash_validator; pub use verify_bitfield::verify_bitfield_length; diff --git a/eth2/state_processing/src/lib.rs b/eth2/state_processing/src/lib.rs index 0a07d407ea5..4e16d7eb451 100644 --- a/eth2/state_processing/src/lib.rs +++ b/eth2/state_processing/src/lib.rs @@ -5,9 +5,9 @@ pub mod common; pub mod get_genesis_state; pub mod per_block_processing; pub mod per_epoch_processing; -pub mod per_slot_processing; pub mod per_shard_block_processing; pub mod per_shard_slot_processing; +pub mod per_slot_processing; pub use get_genesis_state::get_genesis_beacon_state; pub use per_block_processing::{ @@ -15,6 +15,8 @@ pub use per_block_processing::{ per_block_processing, per_block_processing_without_verifying_block_signature, }; pub use per_epoch_processing::{errors::EpochProcessingError, per_epoch_processing}; -pub use per_slot_processing::{per_slot_processing, Error as SlotProcessingError}; -pub use per_shard_block_processing::{per_shard_block_processing, Error as ShardBlockProcessingError}; +pub use per_shard_block_processing::{ + per_shard_block_processing, Error as ShardBlockProcessingError, +}; pub use per_shard_slot_processing::{per_shard_slot_processing, Error as ShardSlotProcessingError}; +pub use per_slot_processing::{per_slot_processing, Error as SlotProcessingError}; diff --git a/eth2/state_processing/src/per_epoch_processing/process_period_committee.rs b/eth2/state_processing/src/per_epoch_processing/process_period_committee.rs index df7d34866ec..88998d3cc9c 100644 --- a/eth2/state_processing/src/per_epoch_processing/process_period_committee.rs +++ b/eth2/state_processing/src/per_epoch_processing/process_period_committee.rs @@ -19,7 +19,8 @@ pub fn process_period_committee( if current_period - shard_fork_period + 2 >= 0 { state.advance_period_cache(spec); - state.period_committee_roots[(current_period.as_u64() % spec.period_committee_root_length) as usize] = + state.period_committee_roots + [(current_period.as_u64() % spec.period_committee_root_length) as usize] = Hash256::from_slice( &state.period_caches[state.period_index(RelativePeriod::Next)] .committees diff --git a/eth2/state_processing/src/per_shard_block_processing.rs b/eth2/state_processing/src/per_shard_block_processing.rs index 23a99583173..2440d053bac 100644 --- a/eth2/state_processing/src/per_shard_block_processing.rs +++ b/eth2/state_processing/src/per_shard_block_processing.rs @@ -25,4 +25,4 @@ pub fn process_shard_block_header( ) -> Result<(), Error> { state.latest_block_header = block.block_header(); Ok(()) -} \ No newline at end of file +} diff --git a/eth2/state_processing/src/per_shard_slot_processing.rs b/eth2/state_processing/src/per_shard_slot_processing.rs index e1563a1ed7e..09cc78f933d 100644 --- a/eth2/state_processing/src/per_shard_slot_processing.rs +++ b/eth2/state_processing/src/per_shard_slot_processing.rs @@ -1,6 +1,6 @@ use crate::*; -use types::*; use tree_hash::TreeHash; +use types::*; #[derive(Debug, PartialEq)] pub enum Error { @@ -29,10 +29,7 @@ pub fn per_shard_slot_processing( } // need to put this in separate directory (process slots) -fn process_shard_slot( - state: &mut ShardState, - spec: &ChainSpec, -) -> () { +fn process_shard_slot(state: &mut ShardState, spec: &ChainSpec) -> () { let previous_state_root = Hash256::from_slice(&state.tree_hash_root()[..]); if state.latest_block_header.state_root == spec.zero_hash { @@ -40,7 +37,9 @@ fn process_shard_slot( } let mut depth = 0; - while (state.slot.as_u64() % u64::pow(2, depth as u32) == 0 as u64) && (depth < T::history_accumulator_depth() as u64) { + while (state.slot.as_u64() % u64::pow(2, depth as u32) == 0 as u64) + && (depth < T::history_accumulator_depth() as u64) + { state.history_accumulator[depth as usize] = previous_state_root; depth += 1; } diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index 642cf5cba7e..6165901636b 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -21,8 +21,8 @@ pub use beacon_state_types::*; mod beacon_state_types; mod committee_cache; -mod period_committee_cache; mod exit_cache; +mod period_committee_cache; mod pubkey_cache; mod tests; @@ -200,7 +200,8 @@ impl BeaconState { // Update to proper variables period_committee_roots: FixedLenVec::from(vec![ spec.zero_hash; - T::PeriodCommitteeRootsLength::to_usize() + T::PeriodCommitteeRootsLength::to_usize( + ) ]), // Finality @@ -416,10 +417,7 @@ impl BeaconState { Ok(committee) } - pub fn period_index( - &self, - relative_period: RelativePeriod, - ) -> usize { + pub fn period_index(&self, relative_period: RelativePeriod) -> usize { match relative_period { RelativePeriod::Previous => 0, RelativePeriod::Current => 1, @@ -435,11 +433,7 @@ impl BeaconState { self.period_caches[self.period_index(relative_period)].get_period_committee(shard) } - pub fn get_shard_committee( - &self, - epoch: Epoch, - shard: u64, - ) -> Result { + pub fn get_shard_committee(&self, epoch: Epoch, shard: u64) -> Result { let spec = T::default_spec(); let current_epoch = self.current_epoch(); let current_period = current_epoch.period(spec.epochs_per_shard_period); @@ -449,37 +443,41 @@ impl BeaconState { return Err(BeaconStateError::PeriodOutOfBounds); } - let earlier_committee = &self.get_period_committee(RelativePeriod::Previous, shard)?.committee; - let later_committee = &self.get_period_committee(RelativePeriod::Current, shard)?.committee; + let earlier_committee = &self + .get_period_committee(RelativePeriod::Previous, shard)? + .committee; + let later_committee = &self + .get_period_committee(RelativePeriod::Current, shard)? + .committee; let mut union = Vec::new(); for &member in earlier_committee { - if member as u64 % spec.epochs_per_shard_period < member as u64 % spec.epochs_per_shard_period { + if member as u64 % spec.epochs_per_shard_period + < member as u64 % spec.epochs_per_shard_period + { union.push(member); } } for &member in later_committee { - if member as u64 % spec.epochs_per_shard_period >= member as u64 % spec.epochs_per_shard_period { + if member as u64 % spec.epochs_per_shard_period + >= member as u64 % spec.epochs_per_shard_period + { union.push(member); } } union.dedup(); - Ok(ShardCommittee{ + Ok(ShardCommittee { epoch: epoch, shard: shard, committee: union, }) } - pub fn get_shard_proposer_index( - &self, - shard: u64, - slot: ShardSlot, - ) -> Result { + pub fn get_shard_proposer_index(&self, shard: u64, slot: ShardSlot) -> Result { let spec = T::default_spec(); let current_epoch = self.current_epoch(); let target_epoch = slot.epoch(spec.shard_slots_per_epoch, spec.shard_slots_per_beacon_slot); diff --git a/eth2/types/src/beacon_state/beacon_state_types.rs b/eth2/types/src/beacon_state/beacon_state_types.rs index 81dadd350a7..c0e6d811c0a 100644 --- a/eth2/types/src/beacon_state/beacon_state_types.rs +++ b/eth2/types/src/beacon_state/beacon_state_types.rs @@ -1,5 +1,5 @@ use crate::*; -use fixed_len_vec::typenum::{Unsigned, U0, U1024, U64, U8, U8192, U256}; +use fixed_len_vec::typenum::{Unsigned, U0, U1024, U256, U64, U8, U8192}; use serde_derive::{Deserialize, Serialize}; use std::fmt::Debug; diff --git a/eth2/types/src/beacon_state/period_committee_cache.rs b/eth2/types/src/beacon_state/period_committee_cache.rs index 0e76dca95b1..0a165977952 100644 --- a/eth2/types/src/beacon_state/period_committee_cache.rs +++ b/eth2/types/src/beacon_state/period_committee_cache.rs @@ -22,12 +22,12 @@ impl PeriodCommitteeCache { let mut committees: Vec = Vec::with_capacity(shard_count); for n in 0..shard_count { - let crosslink_committee = state - .get_crosslink_committee_for_shard(n as u64, RelativeEpoch::Current)?; + let crosslink_committee = + state.get_crosslink_committee_for_shard(n as u64, RelativeEpoch::Current)?; let crosslink_size = crosslink_committee.committee.len(); - let committee_indices = crosslink_committee - .committee[..crosslink_size.min(spec.target_period_committee_size)] + let committee_indices = crosslink_committee.committee + [..crosslink_size.min(spec.target_period_committee_size)] .to_vec(); let period_committee = PeriodCommittee { shard: n as u64, diff --git a/eth2/types/src/lib.rs b/eth2/types/src/lib.rs index 4e9d14c2c5c..60a97025975 100644 --- a/eth2/types/src/lib.rs +++ b/eth2/types/src/lib.rs @@ -25,11 +25,11 @@ pub mod indexed_attestation; pub mod pending_attestation; pub mod period_committee; pub mod proposer_slashing; -pub mod shard_committee; pub mod shard_attestation; pub mod shard_attestation_data; pub mod shard_block; pub mod shard_block_header; +pub mod shard_committee; pub mod shard_pending_attestation; pub mod shard_state; pub mod transfer; @@ -68,7 +68,6 @@ pub use crate::indexed_attestation::IndexedAttestation; pub use crate::pending_attestation::PendingAttestation; pub use crate::period::Period; pub use crate::period_committee::PeriodCommittee; -pub use crate::shard_committee::ShardCommittee; pub use crate::proposer_slashing::ProposerSlashing; pub use crate::relative_epoch::{Error as RelativeEpochError, RelativeEpoch}; pub use crate::relative_period::RelativePeriod; @@ -76,10 +75,11 @@ pub use crate::shard_attestation::ShardAttestation; pub use crate::shard_attestation_data::ShardAttestationData; pub use crate::shard_block::ShardBlock; pub use crate::shard_block_header::ShardBlockHeader; +pub use crate::shard_committee::ShardCommittee; pub use crate::shard_pending_attestation::ShardPendingAttestation; pub use crate::shard_state::{Error as ShardStateError, *}; -pub use crate::slot_epoch::{Epoch, Slot, ShardSlot}; -pub use crate::slot_height::{SlotHeight, ShardSlotHeight}; +pub use crate::slot_epoch::{Epoch, ShardSlot, Slot}; +pub use crate::slot_height::{ShardSlotHeight, SlotHeight}; pub use crate::transfer::Transfer; pub use crate::validator::Validator; pub use crate::voluntary_exit::VoluntaryExit; diff --git a/eth2/types/src/period.rs b/eth2/types/src/period.rs index 3e3fdd51bd9..e2bfcf1916b 100644 --- a/eth2/types/src/period.rs +++ b/eth2/types/src/period.rs @@ -1,15 +1,14 @@ +use crate::test_utils::TestRandom; +use rand::RngCore; use serde_derive::{Deserialize, Serialize}; -use ssz::{ssz_encode, Decode, DecodeError, Encode}; -use std::fmt; -use std::iter::Iterator; use slog; +use ssz::{ssz_encode, Decode, DecodeError, Encode}; use std::cmp::{Ord, Ordering}; +use std::fmt; use std::hash::{Hash, Hasher}; -use rand::RngCore; -use crate::test_utils::TestRandom; +use std::iter::Iterator; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign}; - #[derive(Eq, Debug, Clone, Copy, Default, Serialize, Deserialize)] #[serde(transparent)] pub struct Period(u64); diff --git a/eth2/types/src/period_committee.rs b/eth2/types/src/period_committee.rs index f7139dea43a..2c2a01dbb0a 100644 --- a/eth2/types/src/period_committee.rs +++ b/eth2/types/src/period_committee.rs @@ -1,9 +1,20 @@ use crate::*; -use tree_hash_derive::{CachedTreeHash, TreeHash}; use serde_derive::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; +use tree_hash_derive::{CachedTreeHash, TreeHash}; -#[derive(Default, Clone, Debug, PartialEq, TreeHash, CachedTreeHash, Serialize, Deserialize, Decode, Encode)] +#[derive( + Default, + Clone, + Debug, + PartialEq, + TreeHash, + CachedTreeHash, + Serialize, + Deserialize, + Decode, + Encode, +)] pub struct PeriodCommittee { pub period: Period, pub shard: Shard, diff --git a/eth2/types/src/relative_period.rs b/eth2/types/src/relative_period.rs index 53a2f7ca885..2943cb91f05 100644 --- a/eth2/types/src/relative_period.rs +++ b/eth2/types/src/relative_period.rs @@ -1,6 +1,5 @@ use crate::*; - #[derive(Debug, PartialEq, Clone, Copy)] pub enum Error { PeriodTooLow { base: Period, other: Period }, diff --git a/eth2/types/src/shard_attestation.rs b/eth2/types/src/shard_attestation.rs index a30a7fa4a2d..2658be228f4 100644 --- a/eth2/types/src/shard_attestation.rs +++ b/eth2/types/src/shard_attestation.rs @@ -1,4 +1,4 @@ -use super::{AggregateSignature, ShardAttestationData, Bitfield}; +use super::{AggregateSignature, Bitfield, ShardAttestationData}; use crate::test_utils::TestRandom; use serde_derive::{Deserialize, Serialize}; diff --git a/eth2/types/src/shard_pending_attestation.rs b/eth2/types/src/shard_pending_attestation.rs index 309b23b82bf..9c96c82570d 100644 --- a/eth2/types/src/shard_pending_attestation.rs +++ b/eth2/types/src/shard_pending_attestation.rs @@ -1,5 +1,5 @@ use crate::test_utils::TestRandom; -use crate::{ShardAttestationData, Bitfield}; +use crate::{Bitfield, ShardAttestationData}; use serde_derive::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; diff --git a/eth2/types/src/shard_state/shard_state_types.rs b/eth2/types/src/shard_state/shard_state_types.rs index dfbe6d9236a..9ee624c9049 100644 --- a/eth2/types/src/shard_state/shard_state_types.rs +++ b/eth2/types/src/shard_state/shard_state_types.rs @@ -1,5 +1,5 @@ use crate::*; -use fixed_len_vec::typenum::{U64}; +use fixed_len_vec::typenum::U64; use serde_derive::{Deserialize, Serialize}; use std::fmt::Debug; diff --git a/eth2/types/src/slot_epoch.rs b/eth2/types/src/slot_epoch.rs index 7e6cafd36a9..da46e47ac24 100644 --- a/eth2/types/src/slot_epoch.rs +++ b/eth2/types/src/slot_epoch.rs @@ -10,8 +10,8 @@ //! implement `Into`, however this would allow operations between `Slots` and `Epochs` which //! may lead to programming errors which are not detected by the compiler. -use crate::slot_height::{SlotHeight, ShardSlotHeight}; use crate::period::Period; +use crate::slot_height::{ShardSlotHeight, SlotHeight}; use crate::test_utils::TestRandom; use rand::RngCore; use serde_derive::{Deserialize, Serialize}; @@ -31,7 +31,6 @@ pub struct Slot(u64); #[serde(transparent)] pub struct ShardSlot(u64); - #[derive(Eq, Debug, Clone, Copy, Default, Serialize, Deserialize)] pub struct Epoch(u64); diff --git a/eth2/types/src/slot_height.rs b/eth2/types/src/slot_height.rs index a3b88e31fa8..549c8dd1bf3 100644 --- a/eth2/types/src/slot_height.rs +++ b/eth2/types/src/slot_height.rs @@ -1,4 +1,4 @@ -use crate::slot_epoch::{Epoch, Slot, ShardSlot}; +use crate::slot_epoch::{Epoch, ShardSlot, Slot}; use crate::test_utils::TestRandom; use rand::RngCore; @@ -46,8 +46,13 @@ impl ShardSlotHeight { ShardSlot::from(self.0.saturating_add(genesis_slot.as_u64())) } - pub fn epoch(self, genesis_slot: u64, slots_per_epoch: u64, slots_per_beacon_slot: u64) -> Epoch { - Epoch::from(self.0.saturating_add(genesis_slot) / slots_per_epoch / slots_per_beacon_slot ) + pub fn epoch( + self, + genesis_slot: u64, + slots_per_epoch: u64, + slots_per_beacon_slot: u64, + ) -> Epoch { + Epoch::from(self.0.saturating_add(genesis_slot) / slots_per_epoch / slots_per_beacon_slot) } pub fn max_value() -> ShardSlotHeight { diff --git a/shard_node/shard_chain/src/checkpoint.rs b/shard_node/shard_chain/src/checkpoint.rs index c32b76d6d52..1c445d24d1f 100644 --- a/shard_node/shard_chain/src/checkpoint.rs +++ b/shard_node/shard_chain/src/checkpoint.rs @@ -1,6 +1,6 @@ use serde_derive::Serialize; use ssz_derive::{Decode, Encode}; -use types::{ShardBlock, ShardState, ShardSpec, Hash256}; +use types::{Hash256, ShardBlock, ShardSpec, ShardState}; #[derive(Clone, Serialize, PartialEq, Debug, Encode, Decode)] pub struct CheckPoint { diff --git a/shard_node/shard_chain/src/errors.rs b/shard_node/shard_chain/src/errors.rs index eb685241820..2d92fa789a8 100644 --- a/shard_node/shard_chain/src/errors.rs +++ b/shard_node/shard_chain/src/errors.rs @@ -1,9 +1,9 @@ use crate::fork_choice::Error as ForkChoiceError; // use crate::metrics::Error as MetricsError; use state_processing::BlockProcessingError; -use state_processing::ShardSlotProcessingError; use state_processing::ShardBlockProcessingError; -use store::{Error as BeaconDBError}; +use state_processing::ShardSlotProcessingError; +use store::Error as BeaconDBError; use types::*; macro_rules! easy_from_to { diff --git a/shard_node/shard_chain/src/fork_choice.rs b/shard_node/shard_chain/src/fork_choice.rs index 86fc3a3638d..fca735feeff 100644 --- a/shard_node/shard_chain/src/fork_choice.rs +++ b/shard_node/shard_chain/src/fork_choice.rs @@ -1,11 +1,14 @@ -use crate::{ShardChain, ShardChainTypes, ShardChainError}; +use crate::{ShardChain, ShardChainError, ShardChainTypes}; use beacon_chain::BeaconChainTypes; use shard_lmd_ghost::LmdGhost; +use shard_store::{Error as StoreError, Store}; use state_processing::common::get_shard_attesting_indices_unsorted; use std::sync::Arc; use store::{Error as BeaconStoreError, Store as BeaconStore}; -use shard_store::{Error as StoreError, Store}; -use types::{ShardAttestation, BeaconBlock, BeaconState, BeaconStateError, ShardBlock, ShardSlot, ShardState, ShardStateError, Epoch, EthSpec, ShardSpec, Hash256}; +use types::{ + BeaconBlock, BeaconState, BeaconStateError, Epoch, EthSpec, Hash256, ShardAttestation, + ShardBlock, ShardSlot, ShardSpec, ShardState, ShardStateError, +}; type Result = std::result::Result; @@ -27,7 +30,7 @@ pub struct ForkChoice { genesis_block_root: Hash256, } -implForkChoice { +impl ForkChoice { pub fn new( store: Arc, genesis_block: &ShardBlock, @@ -47,7 +50,8 @@ implForkChoice { // extra field to the beacon chain let start_block_root = current_crosslink.crosslink_data_root; // should be updated to end epoch :) with the new spec todo - let start_block_slot = ShardSlot::from(current_crosslink.epoch.as_u64() * chain.spec.shard_slots_per_epoch); + let start_block_slot = + ShardSlot::from(current_crosslink.epoch.as_u64() * chain.spec.shard_slots_per_epoch); // Resolve the `0x00.. 00` alias back to genesis let start_block_root = if start_block_root == Hash256::zero() { @@ -89,7 +93,7 @@ implForkChoice { &self, beacon_state: &BeaconState

, attestation: &ShardAttestation, - block: &ShardBlock + block: &ShardBlock, ) -> Result<()> { // Note: `get_attesting_indices_unsorted` requires that the beacon state caches be built. let validator_indices = get_shard_attesting_indices_unsorted( @@ -126,7 +130,6 @@ implForkChoice { } } - impl From for Error { fn from(e: ShardStateError) -> Error { Error::ShardStateError(e) diff --git a/shard_node/shard_chain/src/lib.rs b/shard_node/shard_chain/src/lib.rs index 613b3507129..3a394b48ad5 100644 --- a/shard_node/shard_chain/src/lib.rs +++ b/shard_node/shard_chain/src/lib.rs @@ -1,8 +1,8 @@ -pub mod shard_chain; pub mod checkpoint; pub mod errors; pub mod fork_choice; +pub mod shard_chain; -pub use self::shard_chain::{ShardChain, ShardChainTypes}; pub use self::checkpoint::CheckPoint; -pub use self::errors::{ShardChainError, BlockProductionError}; +pub use self::errors::{BlockProductionError, ShardChainError}; +pub use self::shard_chain::{ShardChain, ShardChainTypes}; diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 750f9a7a585..7cd45f7074d 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -1,19 +1,20 @@ use crate::checkpoint::CheckPoint; -use crate::errors::{ShardChainError as Error, BlockProductionError}; -use beacon_chain::{BeaconChain, BeaconChainTypes}; +use crate::errors::{BlockProductionError, ShardChainError as Error}; use crate::fork_choice::{Error as ForkChoiceError, ForkChoice}; -use shard_lmd_ghost::LmdGhost; -use shard_operation_pool::{OperationPool}; +use beacon_chain::{BeaconChain, BeaconChainTypes}; use parking_lot::{RwLock, RwLockReadGuard}; +use shard_lmd_ghost::LmdGhost; +use shard_operation_pool::OperationPool; +use shard_store::iter::{ + BestBlockRootsIterator, BlockIterator, BlockRootsIterator, StateRootsIterator, +}; +use shard_store::{Error as DBError, Store}; use slot_clock::SlotClock; use state_processing::{ - per_shard_block_processing, - per_shard_slot_processing, ShardBlockProcessingError, + per_shard_block_processing, per_shard_slot_processing, ShardBlockProcessingError, }; use std::sync::Arc; -use store::{Store as BeaconStore, Error as BeaconDBError}; -use shard_store::iter::{BestBlockRootsIterator, BlockIterator, BlockRootsIterator, StateRootsIterator}; -use shard_store::{Error as DBError, Store}; +use store::{Error as BeaconDBError, Store as BeaconStore}; use tree_hash::TreeHash; use types::*; @@ -134,7 +135,10 @@ impl ShardChain { /// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. /// /// Contains duplicate roots when skip slots are encountered. - pub fn rev_iter_block_roots(&self, slot: ShardSlot) -> BlockRootsIterator { + pub fn rev_iter_block_roots( + &self, + slot: ShardSlot, + ) -> BlockRootsIterator { BlockRootsIterator::owned(self.store.clone(), self.state.read().clone(), slot) } @@ -155,7 +159,10 @@ impl ShardChain { /// genesis. /// /// Returns `None` for roots prior to genesis or when there is an error reading from `Store`. - pub fn rev_iter_state_roots(&self, slot: ShardSlot) -> StateRootsIterator { + pub fn rev_iter_state_roots( + &self, + slot: ShardSlot, + ) -> StateRootsIterator { StateRootsIterator::owned(self.store.clone(), self.state.read().clone(), slot) } @@ -251,9 +258,11 @@ impl ShardChain { /// `self.state` should undergo per slot processing. pub fn read_slot_clock(&self) -> Option { let spec = &self.spec; - + match self.slot_clock.present_slot() { - Ok(Some(some_slot)) => Some(some_slot.shard_slot(spec.slots_per_epoch, spec.shard_slots_per_epoch)), + Ok(Some(some_slot)) => { + Some(some_slot.shard_slot(spec.slots_per_epoch, spec.shard_slots_per_epoch)) + } Ok(None) => None, _ => None, } @@ -266,7 +275,6 @@ impl ShardChain { let spec = &self.spec; let genesis_slot = spec.phase_1_fork_epoch * spec.shard_slots_per_epoch; - if now < genesis_slot { None } else { @@ -285,7 +293,9 @@ impl ShardChain { pub fn check_for_new_crosslink(&self) -> Result<(), Error> { let beacon_state = self.parent_beacon.current_state(); - let crosslink_root = beacon_state.get_current_crosslink(self.shard)?.crosslink_data_root; + let crosslink_root = beacon_state + .get_current_crosslink(self.shard)? + .crosslink_data_root; let current_crossslink_root = *self.crosslink_root.read(); if crosslink_root != current_crossslink_root { *self.crosslink_root.write() = crosslink_root; @@ -298,16 +308,19 @@ impl ShardChain { /// /// Information is read from the present `beacon_state` pub fn block_proposer(&self, slot: ShardSlot, shard: u64) -> Result { - let index = self.parent_beacon.current_state().get_shard_proposer_index( - shard, - slot, - )?; + let index = self + .parent_beacon + .current_state() + .get_shard_proposer_index(shard, slot)?; Ok(index) } pub fn shard_committee(&self, epoch: Epoch, shard: u64) -> Result { - let shard_committee = self.parent_beacon.current_state().get_shard_committee(epoch, shard)?; + let shard_committee = self + .parent_beacon + .current_state() + .get_shard_committee(epoch, shard)?; Ok(shard_committee) } @@ -332,7 +345,6 @@ impl ShardChain { head_block_slot: ShardSlot, state: &ShardState, ) -> Result { - Ok(ShardAttestationData { shard_block_root: head_block_root, target_slot: head_block_slot, @@ -343,12 +355,12 @@ impl ShardChain { /// /// If valid, the attestation is added to the `op_pool` and aggregated with another attestation /// if possible. - pub fn process_attestation( - &self, - attestation: ShardAttestation, - ) -> () { - self.op_pool - .insert_attestation(attestation, &self.parent_beacon.current_state(), &self.spec); + pub fn process_attestation(&self, attestation: ShardAttestation) -> () { + self.op_pool.insert_attestation( + attestation, + &self.parent_beacon.current_state(), + &self.spec, + ); } /// Accept some block and attempt to add it to block DAG. @@ -357,7 +369,7 @@ impl ShardChain { pub fn process_block(&self, block: ShardBlock) -> Result { let spec = &self.spec; let beacon_state = &self.parent_beacon.current_state(); - + let finalized_slot = beacon_state .finalized_epoch .start_slot(spec.slots_per_epoch) @@ -434,10 +446,10 @@ impl ShardChain { // Store the block and state. self.store.put(&block_root, &block)?; self.store.put(&state_root, &state)?; - // Register the new block with the fork choice service. - self.fork_choice.process_block(&beacon_state, &block, block_root)?; + self.fork_choice + .process_block(&beacon_state, &block, block_root)?; // Execute the fork choice algorithm, enthroning a new head if discovered. // @@ -483,8 +495,13 @@ impl ShardChain { let spec = &self.spec; let parent_root = state.latest_block_header.canonical_root(); let beacon_state = self.parent_beacon.current_state(); - let beacon_block_root_epoch = state.latest_block_header.slot.epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot); - let beacon_block_root = beacon_state.get_block_root_at_epoch(beacon_block_root_epoch)?.clone(); + let beacon_block_root_epoch = state + .latest_block_header + .slot + .epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot); + let beacon_block_root = beacon_state + .get_block_root_at_epoch(beacon_block_root_epoch)? + .clone(); let mut block = ShardBlock { shard: state.shard, @@ -492,7 +509,11 @@ impl ShardChain { beacon_block_root, parent_root, state_root: Hash256::zero(), - attestation: self.op_pool.get_attestation(&state, &self.parent_beacon.current_state(), spec), + attestation: self.op_pool.get_attestation( + &state, + &self.parent_beacon.current_state(), + spec, + ), signature: Signature::empty_signature(), }; diff --git a/shard_node/shard_store/src/iter.rs b/shard_node/shard_store/src/iter.rs index cf7738afa87..7a3bd1ed1ad 100644 --- a/shard_node/shard_store/src/iter.rs +++ b/shard_node/shard_store/src/iter.rs @@ -1,7 +1,7 @@ use crate::Store; use std::borrow::Cow; use std::sync::Arc; -use types::{ShardBlock, ShardState, ShardStateError, ShardSpec, Hash256, ShardSlot}; +use types::{Hash256, ShardBlock, ShardSlot, ShardSpec, ShardState, ShardStateError}; #[derive(Clone)] pub struct StateRootsIterator<'a, T: ShardSpec, U> { @@ -121,7 +121,10 @@ impl<'a, T: ShardSpec, U: Store> Iterator for BlockRootsIterator<'a, T, U> { self.shard_state = Cow::Owned(shard_state); } - Some((self.shard_state.latest_block_header.canonical_root(), self.slot)) + Some(( + self.shard_state.latest_block_header.canonical_root(), + self.slot, + )) } } @@ -180,6 +183,9 @@ impl<'a, T: ShardSpec, U: Store> Iterator for BestBlockRootsIterator<'a, T, U> { self.shard_state = Cow::Owned(shard_state); } - Some((self.shard_state.latest_block_header.canonical_root(), self.slot)) + Some(( + self.shard_state.latest_block_header.canonical_root(), + self.slot, + )) } -} \ No newline at end of file +} diff --git a/shard_node/shard_store/src/lib.rs b/shard_node/shard_store/src/lib.rs index 75b71b60cdc..668ce9100e7 100644 --- a/shard_node/shard_store/src/lib.rs +++ b/shard_node/shard_store/src/lib.rs @@ -8,14 +8,14 @@ //! Provides a simple API for storing/retrieving all types that sometimes needs type-hints. See //! tests for implementation examples. -pub mod iter; -mod impls; mod block_at_slot; mod errors; +mod impls; +pub mod iter; mod memory_store; -pub use memory_store::MemoryStore; pub use errors::Error; +pub use memory_store::MemoryStore; pub use types::*; /// An object capable of storing and retrieving objects implementing `StoreItem`. From 01a456cfa39e9970848d5d5a61210c72721db7e4 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Tue, 17 Sep 2019 15:26:45 -0500 Subject: [PATCH 099/151] Include shard chain harness to begin experimentation --- shard_node/shard_chain/Cargo.toml | 2 + shard_node/shard_chain/src/harness.rs | 321 ++++++++++++++++++++ shard_node/shard_chain/src/harness_tests.rs | 56 ++++ shard_node/shard_chain/src/lib.rs | 2 + 4 files changed, 381 insertions(+) create mode 100644 shard_node/shard_chain/src/harness.rs create mode 100644 shard_node/shard_chain/src/harness_tests.rs diff --git a/shard_node/shard_chain/Cargo.toml b/shard_node/shard_chain/Cargo.toml index b4722299d06..4d0f1002739 100644 --- a/shard_node/shard_chain/Cargo.toml +++ b/shard_node/shard_chain/Cargo.toml @@ -24,10 +24,12 @@ serde_json = "1.0" slot_clock = { path = "../../eth2/utils/slot_clock" } eth2_ssz = { path = "../../eth2/utils/ssz" } eth2_ssz_derive = { path = "../../eth2/utils/ssz_derive" } +rand = "0.5.5" state_processing = { path = "../../eth2/state_processing" } tree_hash = { path = "../../eth2/utils/tree_hash" } types = { path = "../../eth2/types" } shard_lmd_ghost = { path = "../../eth2/shard_lmd_ghost" } +lmd_ghost = { path = "../../eth2/lmd_ghost" } [dev-dependencies] rand = "0.5.5" diff --git a/shard_node/shard_chain/src/harness.rs b/shard_node/shard_chain/src/harness.rs new file mode 100644 index 00000000000..1e60239b33a --- /dev/null +++ b/shard_node/shard_chain/src/harness.rs @@ -0,0 +1,321 @@ +use beacon_chain::{BeaconChain, BeaconChainTypes, BlockProcessingOutcome}; +use crate::shard_chain::{ShardChain, ShardChainTypes, BlockProcessingOutcome as ShardBlockProcessingOutcome}; +use lmd_ghost::LmdGhost; +use shard_lmd_ghost::{LmdGhost as ShardLmdGhost}; +use slot_clock::SlotClock; +use slot_clock::TestingSlotClock; +use state_processing::per_slot_processing; +use std::marker::PhantomData; +use std::sync::Arc; +use store::MemoryStore; +use shard_store::{MemoryStore as ShardMemoryStore}; +use store::Store; +use shard_store::{Store as ShardStore}; +use tree_hash::{SignedRoot, TreeHash}; +use types::*; +use test_utils::TestingBeaconStateBuilder; +use crate::harness_tests; + +// For now only accept 100% honest majority the entire time + +/// Used to make the `Harness` generic over beacon types. +pub struct CommonBeaconTypes +where + L: LmdGhost, + E: EthSpec, +{ + _phantom_l: PhantomData, + _phantom_e: PhantomData, +} + +/// Used to make the `Harness` generic over shard types. +pub struct CommonShardTypes +where + T: ShardLmdGhost, + U: ShardSpec, +{ + _phantom_t: PhantomData, + _phantom_u: PhantomData, +} + +impl BeaconChainTypes for CommonBeaconTypes +where + L: LmdGhost, + E: EthSpec, +{ + type Store = MemoryStore; + type SlotClock = TestingSlotClock; + type LmdGhost = L; + type EthSpec = E; +} + +impl ShardChainTypes for CommonShardTypes +where + T: ShardLmdGhost, + U: ShardSpec, +{ + type Store = ShardMemoryStore; + type SlotClock = TestingSlotClock; + type LmdGhost = T; + type ShardSpec = U; +} + +/// A testing harness which can instantiate a `BeaconChain` and `Shard Chain`, populating it with blocks and +/// attestations. +pub struct ShardChainHarness +where + L: LmdGhost, + E: EthSpec, + T: ShardLmdGhost, + U: ShardSpec, +{ + pub beacon_chain: BeaconChain>, + pub keypairs: Vec, + pub beacon_spec: ChainSpec, + _phantom_t: PhantomData, + _phantom_u: PhantomData, +} + +impl ShardChainHarness +where + L: LmdGhost, + E: EthSpec, + T: ShardLmdGhost, + U: ShardSpec, +{ + /// Instantiate a new harness with `validator_count` initial validators. + pub fn new(validator_count: usize) -> Self { + let beacon_spec = E::default_spec(); + + let beacon_store = Arc::new(MemoryStore::open()); + let shard_store = Arc::new(ShardMemoryStore::open()); + + let beacon_state_builder = + TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(validator_count, &beacon_spec); + let (beacon_genesis_state, keypairs) = beacon_state_builder.build(); + + let mut beacon_genesis_block = BeaconBlock::empty(&beacon_spec); + beacon_genesis_block.state_root = Hash256::from_slice(&beacon_genesis_state.tree_hash_root()); + + // Slot clock + let beacon_slot_clock = TestingSlotClock::new( + beacon_spec.genesis_slot, + beacon_genesis_state.genesis_time, + beacon_spec.seconds_per_slot, + ); + + let beacon_chain = BeaconChain::from_genesis( + beacon_store, + beacon_slot_clock, + beacon_genesis_state, + beacon_genesis_block, + beacon_spec.clone(), + ) + .expect("Terminate if beacon chain generation fails"); + + Self { + beacon_chain, + keypairs, + beacon_spec, + _phantom_t: PhantomData, + _phantom_u: PhantomData, + } + } + + /// Advance slots of `BeaconChain` and `ShardChain` + /// + /// Does not produce blocks or attestations. + pub fn advance_beacon_slot(&self) { + self.beacon_chain.slot_clock.advance_slot(); + self.beacon_chain.catchup_state().expect("should catchup state"); + } + + /// Extend the `BeaconChain` with some blocks and attestations. Returns the root of the + /// last-produced block (the head of the chain). + /// + /// Chain will be extended by `num_blocks` blocks. + /// + /// The `block_strategy` dictates where the new blocks will be placed. + /// + /// The `attestation_strategy` dictates which validators will attest to the newly created + /// blocks. + pub fn extend_beacon_chain( + &self, + num_blocks: usize, + ) -> Hash256 { + let mut current_slot = self.beacon_chain.read_slot_clock().unwrap(); + let mut state = self.get_beacon_state_at_slot(current_slot - 1); + let mut head_block_root = None; + + for _ in 0..num_blocks { + while self.beacon_chain.read_slot_clock().expect("should have a slot") < current_slot { + self.advance_beacon_slot(); + } + + let (block, new_state) = self.build_beacon_block(state.clone(), current_slot); + + let outcome = self + .beacon_chain + .process_block(block) + .expect("should not error during block processing"); + + if let BlockProcessingOutcome::Processed { block_root } = outcome { + head_block_root = Some(block_root); + + self.add_beacon_attestations_to_op_pool( + &new_state, + block_root, + current_slot, + ); + } else { + panic!("block should be successfully processed: {:?}", outcome); + } + + state = new_state; + current_slot += 1; + } + + head_block_root.expect("did not produce any blocks") + } + + fn get_beacon_state_at_slot(&self, state_slot: Slot) -> BeaconState { + let state_root = self + .beacon_chain + .rev_iter_state_roots(self.beacon_chain.current_state().slot - 1) + .find(|(_hash, slot)| *slot == state_slot) + .map(|(hash, _slot)| hash) + .expect("could not find state root"); + + self.beacon_chain + .store + .get(&state_root) + .expect("should read db") + .expect("should find state root") + } + + /// Returns a newly created block, signed by the proposer for the given slot. + fn build_beacon_block( + &self, + mut state: BeaconState, + slot: Slot, + ) -> (BeaconBlock, BeaconState) { + if slot < state.slot { + panic!("produce slot cannot be prior to the state slot"); + } + + while state.slot < slot { + per_slot_processing(&mut state, &self.beacon_spec) + .expect("should be able to advance state to slot"); + } + + state.build_all_caches(&self.beacon_spec).unwrap(); + + let proposer_index = self.beacon_chain + .block_proposer(slot) + .expect("should get block proposer from chain"); + + let sk = &self.keypairs[proposer_index].sk; + let fork = &state.fork.clone(); + + let randao_reveal = { + let epoch = slot.epoch(E::slots_per_epoch()); + let message = epoch.tree_hash_root(); + let domain = self.beacon_spec.get_domain(epoch, Domain::Randao, fork); + Signature::new(&message, domain, sk) + }; + + let (mut block, state) = self + .beacon_chain + .produce_block_on_state(state, slot, randao_reveal) + .expect("should produce block"); + + block.signature = { + let message = block.signed_root(); + let epoch = block.slot.epoch(E::slots_per_epoch()); + let domain = self.beacon_spec.get_domain(epoch, Domain::BeaconProposer, fork); + Signature::new(&message, domain, sk) + }; + + (block, state) + } + + /// Adds attestations to the `BeaconChain` operations pool to be included in future blocks. + /// + /// The `attestation_strategy` dictates which validators should attest. + fn add_beacon_attestations_to_op_pool( + &self, + state: &BeaconState, + head_block_root: Hash256, + head_block_slot: Slot, + ) { + let spec = &self.beacon_spec; + let fork = &state.fork; + + let attesting_validators: Vec = (0..self.keypairs.len()).collect(); + + state + .get_crosslink_committees_at_slot(state.slot) + .expect("should get committees") + .iter() + .for_each(|cc| { + let committee_size = cc.committee.len(); + + for (i, validator_index) in cc.committee.iter().enumerate() { + if attesting_validators.contains(validator_index) { + let data = self + .beacon_chain + .produce_attestation_data_for_block( + cc.shard, + head_block_root, + head_block_slot, + state, + ) + .expect("should produce attestation data"); + + let mut aggregation_bitfield = Bitfield::new(); + aggregation_bitfield.set(i, true); + aggregation_bitfield.set(committee_size, false); + + let mut custody_bitfield = Bitfield::new(); + custody_bitfield.set(committee_size, false); + + let signature = { + let message = AttestationDataAndCustodyBit { + data: data.clone(), + custody_bit: false, + } + .tree_hash_root(); + + let domain = + spec.get_domain(data.target_epoch, Domain::Attestation, fork); + + let mut agg_sig = AggregateSignature::new(); + agg_sig.add(&Signature::new( + &message, + domain, + self.get_sk(*validator_index), + )); + + agg_sig + }; + + let attestation = Attestation { + aggregation_bitfield, + data, + custody_bitfield, + signature, + }; + + self.beacon_chain + .process_attestation(attestation) + .expect("should process attestation"); + } + } + }); + } + + /// Returns the secret key for the given validator index. + fn get_sk(&self, validator_index: usize) -> &SecretKey { + &self.keypairs[validator_index].sk + } +} diff --git a/shard_node/shard_chain/src/harness_tests.rs b/shard_node/shard_chain/src/harness_tests.rs new file mode 100644 index 00000000000..c99b37a4706 --- /dev/null +++ b/shard_node/shard_chain/src/harness_tests.rs @@ -0,0 +1,56 @@ +use crate::harness::{ + ShardChainHarness, CommonBeaconTypes, +}; +use lmd_ghost::ThreadSafeReducedTree; +use shard_lmd_ghost::{ThreadSafeReducedTree as ShardThreadSafeReducedTree}; +use rand::Rng; +use store::{MemoryStore, Store}; +use shard_store::{MemoryStore as ShardMemoryStore, Store as ShardStore}; +use types::test_utils::{SeedableRng, TestRandom, XorShiftRng}; +use types::{Deposit, EthSpec, Hash256, MinimalEthSpec, MinimalShardSpec, Slot}; + +pub const VALIDATOR_COUNT: usize = 24; + +pub type TestBeaconForkChoice = ThreadSafeReducedTree; +pub type TestShardForkChoice = ShardThreadSafeReducedTree; + +fn get_harness(validator_count: usize) -> ShardChainHarness { + let harness = ShardChainHarness::new(validator_count); + + // Move past the zero slot. + harness.advance_beacon_slot(); + + harness +} + +#[test] +fn finalizes_with_full_participation() { + let num_blocks_produced = MinimalEthSpec::slots_per_epoch() * 5; + let harness = get_harness(VALIDATOR_COUNT); + + harness.extend_beacon_chain( + num_blocks_produced as usize, + ); + + let state = &harness.beacon_chain.head().beacon_state; + + assert_eq!( + state.slot, num_blocks_produced, + "head should be at the current slot" + ); + assert_eq!( + state.current_epoch(), + num_blocks_produced / MinimalEthSpec::slots_per_epoch(), + "head should be at the expected epoch" + ); + assert_eq!( + state.current_justified_epoch, + state.current_epoch() - 1, + "the head should be justified one behind the current epoch" + ); + assert_eq!( + state.finalized_epoch, + state.current_epoch() - 2, + "the head should be finalized two behind the current epoch" + ); +} diff --git a/shard_node/shard_chain/src/lib.rs b/shard_node/shard_chain/src/lib.rs index 3a394b48ad5..4c04b2b1821 100644 --- a/shard_node/shard_chain/src/lib.rs +++ b/shard_node/shard_chain/src/lib.rs @@ -2,6 +2,8 @@ pub mod checkpoint; pub mod errors; pub mod fork_choice; pub mod shard_chain; +pub mod harness; +mod harness_tests; pub use self::checkpoint::CheckPoint; pub use self::errors::{BlockProductionError, ShardChainError}; From d7126b9b2650e0eeddb2dfdf82096a60bedfe74d Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Tue, 17 Sep 2019 17:00:41 -0500 Subject: [PATCH 100/151] Shard chain no longer needs intiialization with a genesis block --- eth2/types/src/shard_block_header.rs | 12 ++++++++++++ shard_node/shard_chain/src/shard_chain.rs | 6 +++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/eth2/types/src/shard_block_header.rs b/eth2/types/src/shard_block_header.rs index 6377c489ce4..b87de291969 100644 --- a/eth2/types/src/shard_block_header.rs +++ b/eth2/types/src/shard_block_header.rs @@ -62,6 +62,18 @@ impl ShardBlockHeader { signature: self.signature, } } + + pub fn block(&self) -> ShardBlock { + ShardBlock { + shard: self.shard, + slot: self.slot, + beacon_block_root: self.beacon_block_root, + parent_root: self.parent_root, + state_root: self.state_root, + attestation: self.attestation.clone(), + signature: self.signature.clone(), + } + } } #[cfg(test)] diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 7cd45f7074d..25200b03d7a 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -68,21 +68,21 @@ impl ShardChain { store: Arc, slot_clock: T::SlotClock, mut genesis_state: ShardState, - genesis_block: ShardBlock, spec: ChainSpec, shard: Shard, parent_beacon: Arc>, ) -> Result { genesis_state.build_cache(&spec)?; + let genesis_block_header = &genesis_state.latest_block_header; + let genesis_block = genesis_block_header.block(); let state_root = genesis_state.canonical_root(); store.put(&state_root, &genesis_state)?; - let genesis_block_root = genesis_block.block_header().canonical_root(); + let genesis_block_root = genesis_block_header.canonical_root(); store.put(&genesis_block_root, &genesis_block)?; // Also store the genesis block under the `ZERO_HASH` key. - let genesis_block_root = genesis_block.block_header().canonical_root(); store.put(&spec.zero_hash, &genesis_block)?; let canonical_head = RwLock::new(CheckPoint::new( From acdf501eca87b501fddb3012b4393302e61bccc1 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Tue, 17 Sep 2019 17:02:05 -0500 Subject: [PATCH 101/151] Shard chain initiated in harness --- shard_node/shard_chain/src/harness.rs | 30 +++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/shard_node/shard_chain/src/harness.rs b/shard_node/shard_chain/src/harness.rs index 1e60239b33a..115f1e652af 100644 --- a/shard_node/shard_chain/src/harness.rs +++ b/shard_node/shard_chain/src/harness.rs @@ -69,9 +69,11 @@ where T: ShardLmdGhost, U: ShardSpec, { - pub beacon_chain: BeaconChain>, + pub beacon_chain: Arc>>, pub keypairs: Vec, pub beacon_spec: ChainSpec, + pub shard_chain: ShardChain, CommonBeaconTypes>, + pub shard_spec: ChainSpec, _phantom_t: PhantomData, _phantom_u: PhantomData, } @@ -86,6 +88,7 @@ where /// Instantiate a new harness with `validator_count` initial validators. pub fn new(validator_count: usize) -> Self { let beacon_spec = E::default_spec(); + let shard_spec = U::default_spec(); let beacon_store = Arc::new(MemoryStore::open()); let shard_store = Arc::new(ShardMemoryStore::open()); @@ -93,6 +96,7 @@ where let beacon_state_builder = TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(validator_count, &beacon_spec); let (beacon_genesis_state, keypairs) = beacon_state_builder.build(); + let shard_state = ShardState::genesis(&shard_spec, 0); let mut beacon_genesis_block = BeaconBlock::empty(&beacon_spec); beacon_genesis_block.state_root = Hash256::from_slice(&beacon_genesis_state.tree_hash_root()); @@ -104,19 +108,37 @@ where beacon_spec.seconds_per_slot, ); + let shard_slot_clock = TestingSlotClock::new( + beacon_spec.genesis_slot, + beacon_genesis_state.genesis_time, + beacon_spec.seconds_per_slot, + ); + let beacon_chain = BeaconChain::from_genesis( beacon_store, beacon_slot_clock, beacon_genesis_state, beacon_genesis_block, beacon_spec.clone(), - ) - .expect("Terminate if beacon chain generation fails"); + ).expect("Terminate if beacon chain generation fails"); + let beacon_chain_reference = Arc::new(beacon_chain); + + let shard_chain = ShardChain::from_genesis( + shard_store, + shard_slot_clock, + shard_state, + shard_spec.clone(), + 0, + beacon_chain_reference.clone(), + ).expect("Terminate if beacon chain generation fails"); + Self { - beacon_chain, + beacon_chain: beacon_chain_reference.clone(), keypairs, beacon_spec, + shard_chain: shard_chain, + shard_spec, _phantom_t: PhantomData, _phantom_u: PhantomData, } From d954de1da3d5dd7b82fa527b58b0d009eb87cf09 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Tue, 17 Sep 2019 17:37:07 -0500 Subject: [PATCH 102/151] Temporarily add even more duplication :) ShardSlotClock --- eth2/types/src/chain_spec.rs | 15 ++++--- eth2/utils/slot_clock/src/lib.rs | 17 +++++++- .../slot_clock/src/testing_slot_clock.rs | 40 ++++++++++++++++++- shard_node/shard_chain/src/harness.rs | 12 +++--- shard_node/shard_chain/src/shard_chain.rs | 8 ++-- 5 files changed, 72 insertions(+), 20 deletions(-) diff --git a/eth2/types/src/chain_spec.rs b/eth2/types/src/chain_spec.rs index d24ba1ecff0..c6a83e2f08b 100644 --- a/eth2/types/src/chain_spec.rs +++ b/eth2/types/src/chain_spec.rs @@ -75,15 +75,15 @@ pub struct ChainSpec { pub min_epochs_to_inactivity_penalty: u64, /* - * Additional Time Parameters - */ + * Additional Time Parameters + */ pub slots_per_epoch: u64, pub shard_slots_per_beacon_slot: u64, pub shard_slots_per_epoch: u64, /* - * Phase 1 specific values, fork epoch and slot are hardcoded to values for now - */ + * Phase 1 specific values, fork epoch and slot are hardcoded to values for now + */ pub epochs_per_shard_period: u64, pub period_committee_root_length: u64, pub phase_1_fork_epoch: u64, @@ -209,8 +209,8 @@ impl ChainSpec { min_epochs_to_inactivity_penalty: 4, /* - * Additional Time Parameters - */ + * Additional Time Parameters + */ slots_per_epoch: 64, shard_slots_per_beacon_slot: 2, shard_slots_per_epoch: 128, @@ -279,10 +279,13 @@ impl ChainSpec { min_attestation_inclusion_delay: 2, slots_per_eth1_voting_period: 16, genesis_slot, + epochs_per_shard_period: 8, chain_id: 2, // lighthouse testnet chain id boot_nodes, slots_per_epoch: 8, shard_slots_per_epoch: 16, + phase_1_fork_slot: 96, + phase_1_fork_epoch: 6, ..ChainSpec::mainnet() } } diff --git a/eth2/utils/slot_clock/src/lib.rs b/eth2/utils/slot_clock/src/lib.rs index 7b86684fa41..dea206727a6 100644 --- a/eth2/utils/slot_clock/src/lib.rs +++ b/eth2/utils/slot_clock/src/lib.rs @@ -2,9 +2,9 @@ mod system_time_slot_clock; mod testing_slot_clock; pub use crate::system_time_slot_clock::{Error as SystemTimeSlotClockError, SystemTimeSlotClock}; -pub use crate::testing_slot_clock::{Error as TestingSlotClockError, TestingSlotClock}; +pub use crate::testing_slot_clock::{Error as TestingSlotClockError, TestingSlotClock, ShardTestingSlotClock}; use std::time::Duration; -pub use types::Slot; +pub use types::{Slot, ShardSlot}; pub trait SlotClock: Send + Sync + Sized { type Error; @@ -18,3 +18,16 @@ pub trait SlotClock: Send + Sync + Sized { fn duration_to_next_slot(&self) -> Result, Self::Error>; } + +pub trait ShardSlotClock: Send + Sync + Sized { + type Error; + + /// Create a new `SlotClock`. + /// + /// Returns an Error if `slot_duration_seconds == 0`. + fn new(genesis_slot: ShardSlot, genesis_seconds: u64, slot_duration_seconds: u64) -> Self; + + fn present_slot(&self) -> Result, Self::Error>; + + fn duration_to_next_slot(&self) -> Result, Self::Error>; +} diff --git a/eth2/utils/slot_clock/src/testing_slot_clock.rs b/eth2/utils/slot_clock/src/testing_slot_clock.rs index ab00d2baa7c..346d66ce739 100644 --- a/eth2/utils/slot_clock/src/testing_slot_clock.rs +++ b/eth2/utils/slot_clock/src/testing_slot_clock.rs @@ -1,7 +1,7 @@ -use super::SlotClock; +use super::{SlotClock, ShardSlotClock}; use std::sync::RwLock; use std::time::Duration; -use types::Slot; +use types::{Slot, ShardSlot}; #[derive(Debug, PartialEq)] pub enum Error {} @@ -11,6 +11,10 @@ pub struct TestingSlotClock { slot: RwLock, } +pub struct ShardTestingSlotClock { + slot: RwLock, +} + impl TestingSlotClock { pub fn set_slot(&self, slot: u64) { *self.slot.write().expect("TestingSlotClock poisoned.") = Slot::from(slot); @@ -21,6 +25,17 @@ impl TestingSlotClock { } } + +impl ShardTestingSlotClock { + pub fn set_slot(&self, slot: u64) { + *self.slot.write().expect("TestingSlotClock poisoned.") = ShardSlot::from(slot); + } + + pub fn advance_slot(&self) { + self.set_slot(self.present_slot().unwrap().unwrap().as_u64() + 1) + } +} + impl SlotClock for TestingSlotClock { type Error = Error; @@ -42,6 +57,27 @@ impl SlotClock for TestingSlotClock { } } +impl ShardSlotClock for ShardTestingSlotClock { + type Error = Error; + + /// Create a new `TestingSlotClock` at `genesis_slot`. + fn new(genesis_slot: ShardSlot, _genesis_seconds: u64, _slot_duration_seconds: u64) -> Self { + ShardTestingSlotClock { + slot: RwLock::new(genesis_slot), + } + } + + fn present_slot(&self) -> Result, Error> { + let slot = *self.slot.read().expect("TestingSlotClock poisoned."); + Ok(Some(slot)) + } + + /// Always returns a duration of 1 second. + fn duration_to_next_slot(&self) -> Result, Error> { + Ok(Some(Duration::from_secs(1))) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/shard_node/shard_chain/src/harness.rs b/shard_node/shard_chain/src/harness.rs index 115f1e652af..1be86a3d090 100644 --- a/shard_node/shard_chain/src/harness.rs +++ b/shard_node/shard_chain/src/harness.rs @@ -2,8 +2,8 @@ use beacon_chain::{BeaconChain, BeaconChainTypes, BlockProcessingOutcome}; use crate::shard_chain::{ShardChain, ShardChainTypes, BlockProcessingOutcome as ShardBlockProcessingOutcome}; use lmd_ghost::LmdGhost; use shard_lmd_ghost::{LmdGhost as ShardLmdGhost}; -use slot_clock::SlotClock; -use slot_clock::TestingSlotClock; +use slot_clock::{SlotClock, ShardSlotClock}; +use slot_clock::{TestingSlotClock, ShardTestingSlotClock}; use state_processing::per_slot_processing; use std::marker::PhantomData; use std::sync::Arc; @@ -55,7 +55,7 @@ where U: ShardSpec, { type Store = ShardMemoryStore; - type SlotClock = TestingSlotClock; + type SlotClock = ShardTestingSlotClock; type LmdGhost = T; type ShardSpec = U; } @@ -108,10 +108,10 @@ where beacon_spec.seconds_per_slot, ); - let shard_slot_clock = TestingSlotClock::new( - beacon_spec.genesis_slot, + let shard_slot_clock = ShardTestingSlotClock::new( + ShardSlot::from(shard_spec.phase_1_fork_slot), beacon_genesis_state.genesis_time, - beacon_spec.seconds_per_slot, + shard_spec.shard_seconds_per_slot, ); let beacon_chain = BeaconChain::from_genesis( diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 25200b03d7a..218babb26ff 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -9,7 +9,7 @@ use shard_store::iter::{ BestBlockRootsIterator, BlockIterator, BlockRootsIterator, StateRootsIterator, }; use shard_store::{Error as DBError, Store}; -use slot_clock::SlotClock; +use slot_clock::{SlotClock, ShardSlotClock}; use state_processing::{ per_shard_block_processing, per_shard_slot_processing, ShardBlockProcessingError, }; @@ -43,7 +43,7 @@ pub enum BlockProcessingOutcome { pub trait ShardChainTypes { type Store: shard_store::Store; - type SlotClock: slot_clock::SlotClock; + type SlotClock: slot_clock::ShardSlotClock; type LmdGhost: LmdGhost; type ShardSpec: types::ShardSpec; } @@ -201,7 +201,7 @@ impl ShardChain { let spec = &self.spec; let present_slot = match self.slot_clock.present_slot() { - Ok(Some(slot)) => slot.shard_slot(spec.slots_per_epoch, spec.shard_slots_per_epoch), + Ok(Some(slot)) => slot, _ => return Err(Error::UnableToReadSlot), }; @@ -261,7 +261,7 @@ impl ShardChain { match self.slot_clock.present_slot() { Ok(Some(some_slot)) => { - Some(some_slot.shard_slot(spec.slots_per_epoch, spec.shard_slots_per_epoch)) + Some(some_slot) } Ok(None) => None, _ => None, From f68daea8dcab7673089cde6302cbc8b2caff89b2 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Tue, 17 Sep 2019 17:43:02 -0500 Subject: [PATCH 103/151] Utility functions added to harness --- shard_node/shard_chain/src/harness.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/shard_node/shard_chain/src/harness.rs b/shard_node/shard_chain/src/harness.rs index 1be86a3d090..65b19dddfab 100644 --- a/shard_node/shard_chain/src/harness.rs +++ b/shard_node/shard_chain/src/harness.rs @@ -152,6 +152,11 @@ where self.beacon_chain.catchup_state().expect("should catchup state"); } + pub fn advance_shard_slot(&self) { + self.shard_chain.slot_clock.advance_slot(); + self.shard_chain.catchup_state().expect("should catchup state"); + } + /// Extend the `BeaconChain` with some blocks and attestations. Returns the root of the /// last-produced block (the head of the chain). /// @@ -215,6 +220,21 @@ where .expect("should find state root") } + fn get_shard_state_at_slot(&self, state_slot: ShardSlot) -> ShardState { + let state_root = self + .shard_chain + .rev_iter_state_roots(self.shard_chain.current_state().slot - 1) + .find(|(_hash, slot)| *slot == state_slot) + .map(|(hash, _slot)| hash) + .expect("could not find state root"); + + self.shard_chain + .store + .get(&state_root) + .expect("should read db") + .expect("should find state root") + } + /// Returns a newly created block, signed by the proposer for the given slot. fn build_beacon_block( &self, From 2dddc30930db95e51b7d273b63d91d6b5ac61962 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Tue, 17 Sep 2019 18:31:48 -0500 Subject: [PATCH 104/151] Include attestation aggregation for harness. Make shard chain func less redundant --- eth2/types/src/shard_attestation.rs | 2 - shard_node/shard_chain/src/harness.rs | 97 ++++++++++++++++++++++- shard_node/shard_chain/src/shard_chain.rs | 8 +- 3 files changed, 100 insertions(+), 7 deletions(-) diff --git a/eth2/types/src/shard_attestation.rs b/eth2/types/src/shard_attestation.rs index 2658be228f4..8d2d373e23e 100644 --- a/eth2/types/src/shard_attestation.rs +++ b/eth2/types/src/shard_attestation.rs @@ -27,7 +27,6 @@ use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash}; pub struct ShardAttestation { pub aggregation_bitfield: Bitfield, pub data: ShardAttestationData, - pub custody_bitfield: Bitfield, #[signed_root(skip_hashing)] pub signature: AggregateSignature, } @@ -49,7 +48,6 @@ impl ShardAttestation { self.aggregation_bitfield .union_inplace(&other.aggregation_bitfield); - self.custody_bitfield.union_inplace(&other.custody_bitfield); self.signature.add_aggregate(&other.signature); } } diff --git a/shard_node/shard_chain/src/harness.rs b/shard_node/shard_chain/src/harness.rs index 65b19dddfab..29bb850a784 100644 --- a/shard_node/shard_chain/src/harness.rs +++ b/shard_node/shard_chain/src/harness.rs @@ -4,7 +4,7 @@ use lmd_ghost::LmdGhost; use shard_lmd_ghost::{LmdGhost as ShardLmdGhost}; use slot_clock::{SlotClock, ShardSlotClock}; use slot_clock::{TestingSlotClock, ShardTestingSlotClock}; -use state_processing::per_slot_processing; +use state_processing::{per_slot_processing, per_shard_slot_processing}; use std::marker::PhantomData; use std::sync::Arc; use store::MemoryStore; @@ -281,6 +281,45 @@ where (block, state) } + /// Returns a newly created block, signed by the proposer for the given slot. + fn build_shard_block( + &self, + mut state: ShardState, + slot: ShardSlot, + ) -> (ShardBlock, ShardState) { + let spec = &self.shard_spec; + if slot < state.slot { + panic!("produce slot cannot be prior to the state slot"); + } + + while state.slot < slot { + per_shard_slot_processing(&mut state, &self.shard_spec) + .expect("should be able to advance state to slot"); + } + + state.build_cache(&self.shard_spec).unwrap(); + + let proposer_index = self.shard_chain + .block_proposer(slot) + .expect("should get block proposer from chain"); + + let sk = &self.keypairs[proposer_index].sk; + let (mut block, state) = self + .shard_chain + .produce_block_on_state(state, slot) + .expect("should produce block"); + + block.signature = { + let message = block.signed_root(); + let epoch = block.slot.epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot); + // need to actually handle forks correctly + let domain = self.shard_spec.get_domain(epoch, Domain::ShardProposer, &self.beacon_chain.current_state().fork); + Signature::new(&message, domain, sk) + }; + + (block, state) + } + /// Adds attestations to the `BeaconChain` operations pool to be included in future blocks. /// /// The `attestation_strategy` dictates which validators should attest. @@ -356,6 +395,62 @@ where }); } + fn add_shard_attestations_to_op_pool( + &self, + state: &ShardState, + head_block_root: Hash256, + head_block_slot: ShardSlot, + ) { + let spec = &self.shard_spec; + let fork = &self.beacon_chain.current_state().fork; + + let attesting_validators: Vec = (0..self.keypairs.len()).collect(); + + let shard_committee = self.shard_chain.shard_committee(head_block_slot.epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot)).expect("should get committees"); + let committee_size = shard_committee.committee.len(); + + for (i, validator_index) in shard_committee.committee.iter().enumerate() { + if attesting_validators.contains(validator_index) { + let data = self + .shard_chain + .produce_attestation_data_for_block( + head_block_root, + head_block_slot, + state, + ) + .expect("should produce attestation data"); + + let mut aggregation_bitfield = Bitfield::new(); + aggregation_bitfield.set(i, true); + aggregation_bitfield.set(committee_size, false); + + let signature = { + let message = data.tree_hash_root(); + let domain = + spec.get_domain(data.target_slot.epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot), Domain::ShardAttestation, fork); + + let mut agg_sig = AggregateSignature::new(); + agg_sig.add(&Signature::new( + &message, + domain, + self.get_sk(*validator_index), + )); + + agg_sig + }; + + let attestation = ShardAttestation { + aggregation_bitfield, + data, + signature, + }; + + self.shard_chain + .process_attestation(attestation); + } + } + } + /// Returns the secret key for the given validator index. fn get_sk(&self, validator_index: usize) -> &SecretKey { &self.keypairs[validator_index].sk diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 218babb26ff..f6c1bacbc91 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -307,20 +307,20 @@ impl ShardChain { /// Returns the block proposer for a given slot. /// /// Information is read from the present `beacon_state` - pub fn block_proposer(&self, slot: ShardSlot, shard: u64) -> Result { + pub fn block_proposer(&self, slot: ShardSlot) -> Result { let index = self .parent_beacon .current_state() - .get_shard_proposer_index(shard, slot)?; + .get_shard_proposer_index(self.shard, slot)?; Ok(index) } - pub fn shard_committee(&self, epoch: Epoch, shard: u64) -> Result { + pub fn shard_committee(&self, epoch: Epoch) -> Result { let shard_committee = self .parent_beacon .current_state() - .get_shard_committee(epoch, shard)?; + .get_shard_committee(epoch, self.shard)?; Ok(shard_committee) } From f2a5b41544ab5ff7cebb72e4efe4fc5df6f070a5 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Tue, 17 Sep 2019 18:46:15 -0500 Subject: [PATCH 105/151] included extend shard chain function --- shard_node/shard_chain/src/harness.rs | 48 ++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/shard_node/shard_chain/src/harness.rs b/shard_node/shard_chain/src/harness.rs index 29bb850a784..17803d2f967 100644 --- a/shard_node/shard_chain/src/harness.rs +++ b/shard_node/shard_chain/src/harness.rs @@ -161,11 +161,6 @@ where /// last-produced block (the head of the chain). /// /// Chain will be extended by `num_blocks` blocks. - /// - /// The `block_strategy` dictates where the new blocks will be placed. - /// - /// The `attestation_strategy` dictates which validators will attest to the newly created - /// blocks. pub fn extend_beacon_chain( &self, num_blocks: usize, @@ -205,6 +200,49 @@ where head_block_root.expect("did not produce any blocks") } + /// Extend the `ShardChain` with some blocks and attestations. Returns the root of the + /// last-produced block (the head of the chain). + /// + /// Chain will be extended by `num_blocks` blocks. + pub fn extend_shard_chain( + &self, + num_blocks: usize, + ) -> Hash256 { + let mut current_slot = self.shard_chain.read_slot_clock().unwrap(); + let mut state = self.get_shard_state_at_slot(current_slot - 1); + let mut head_block_root = None; + + for _ in 0..num_blocks { + while self.shard_chain.read_slot_clock().expect("should have a slot") < current_slot { + self.advance_shard_slot(); + } + + let (block, new_state) = self.build_shard_block(state.clone(), current_slot); + + let outcome = self + .shard_chain + .process_block(block) + .expect("should not error during block processing"); + + if let ShardBlockProcessingOutcome::Processed { block_root } = outcome { + head_block_root = Some(block_root); + + self.add_shard_attestations_to_op_pool( + &new_state, + block_root, + current_slot, + ); + } else { + panic!("block should be successfully processed: {:?}", outcome); + } + + state = new_state; + current_slot += 1; + } + + head_block_root.expect("did not produce any blocks") + } + fn get_beacon_state_at_slot(&self, state_slot: Slot) -> BeaconState { let state_root = self .beacon_chain From 0a8f17f8b8fe9befff696bb9947396a40f9b488d Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Tue, 17 Sep 2019 20:37:40 -0500 Subject: [PATCH 106/151] Add get block root at epoch function to shard chain - using iterators --- shard_node/shard_chain/src/shard_chain.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index f6c1bacbc91..7ce5f7d6121 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -175,6 +175,19 @@ impl ShardChain { Ok(self.store.get(block_root)?) } + pub fn get_block_root_at_epoch(&self, epoch: Epoch) -> Result, Error> { + let spec = &self.spec; + let start_slot_at_epoch = epoch.start_slot(self.spec.slots_per_epoch).shard_slot(spec.slots_per_epoch, spec.shard_slots_per_epoch); + let current_slot = self.state.read().slot; + let root = self.rev_iter_block_roots(current_slot) + .find(|(_hash, slot)| slot.as_u64() == start_slot_at_epoch.as_u64()); + + Ok(match root { + Some(root) => Some(root.0), + None => None, + }) + } + /// Returns a read-lock guarded `ShardState` which is the `canonical_head` that has been /// updated to match the current slot clock. pub fn current_state(&self) -> RwLockReadGuard> { From e65f8a78e2cf1e62b51ed63816189d81b1d257d6 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Tue, 17 Sep 2019 20:44:24 -0500 Subject: [PATCH 107/151] Beacon chain requires data root for crosslink now --- beacon_node/beacon_chain/src/beacon_chain.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 96ebe4b41a5..9155d05ab78 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -418,7 +418,7 @@ impl BeaconChain { let head_block_root = self.head().beacon_block_root; let head_block_slot = self.head().beacon_block.slot; - self.produce_attestation_data_for_block(shard, head_block_root, head_block_slot, &*state) + self.produce_attestation_data_for_block(shard, head_block_root, head_block_slot, Hash256::zero(), &*state) } /// Produce an `AttestationData` that attests to the chain denoted by `block_root` and `state`. @@ -430,6 +430,7 @@ impl BeaconChain { shard: u64, head_block_root: Hash256, head_block_slot: Slot, + crosslink_data_root: Hash256, state: &BeaconState, ) -> Result { // Collect some metrics. @@ -478,7 +479,7 @@ impl BeaconChain { target_root, shard, previous_crosslink_root, - crosslink_data_root: Hash256::zero(), + crosslink_data_root, }) } From 0c17b80d7f0fc585d7ef96517bd6ddffb4495b57 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Tue, 17 Sep 2019 20:48:25 -0500 Subject: [PATCH 108/151] Add crosslink logic --- shard_node/shard_chain/src/harness.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/shard_node/shard_chain/src/harness.rs b/shard_node/shard_chain/src/harness.rs index 17803d2f967..ce7ac620a07 100644 --- a/shard_node/shard_chain/src/harness.rs +++ b/shard_node/shard_chain/src/harness.rs @@ -378,6 +378,14 @@ where .iter() .for_each(|cc| { let committee_size = cc.committee.len(); + let shard = cc.shard; + + let crosslink_data_root = match shard { + 0 => self.shard_chain.get_block_root_at_epoch(state.current_epoch()) + .expect("should get crosslink root") + .unwrap_or(Hash256::zero()), + _ => Hash256::zero(), + }; for (i, validator_index) in cc.committee.iter().enumerate() { if attesting_validators.contains(validator_index) { @@ -387,6 +395,7 @@ where cc.shard, head_block_root, head_block_slot, + crosslink_data_root, state, ) .expect("should produce attestation data"); From fea6c6a10f18ee5e3145207d90c04293e220474b Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Wed, 18 Sep 2019 02:21:52 -0500 Subject: [PATCH 109/151] Move attestations into vector before refactor to direct bitfield. further cleanup --- eth2/types/src/beacon_state/beacon_state_types.rs | 2 +- eth2/types/src/chain_spec.rs | 4 ++-- eth2/types/src/shard_block.rs | 4 ++-- eth2/types/src/shard_block_header.rs | 12 ++---------- eth2/types/src/shard_state.rs | 8 -------- shard_node/shard_chain/src/fork_choice.rs | 2 +- shard_node/shard_chain/src/shard_chain.rs | 5 +++-- 7 files changed, 11 insertions(+), 26 deletions(-) diff --git a/eth2/types/src/beacon_state/beacon_state_types.rs b/eth2/types/src/beacon_state/beacon_state_types.rs index c0e6d811c0a..9efdf11e5a1 100644 --- a/eth2/types/src/beacon_state/beacon_state_types.rs +++ b/eth2/types/src/beacon_state/beacon_state_types.rs @@ -136,7 +136,7 @@ pub struct MinimalEthSpec; impl EthSpec for MinimalEthSpec { type ShardCount = U8; type SlotsPerHistoricalRoot = U64; - type PeriodCommitteeRootsLength = U64; + type PeriodCommitteeRootsLength = U256; type LatestRandaoMixesLength = U1024; type LatestActiveIndexRootsLength = U1024; type LatestSlashedExitLength = U64; diff --git a/eth2/types/src/chain_spec.rs b/eth2/types/src/chain_spec.rs index c6a83e2f08b..4f3ba1b044d 100644 --- a/eth2/types/src/chain_spec.rs +++ b/eth2/types/src/chain_spec.rs @@ -279,13 +279,13 @@ impl ChainSpec { min_attestation_inclusion_delay: 2, slots_per_eth1_voting_period: 16, genesis_slot, - epochs_per_shard_period: 8, + epochs_per_shard_period: 4, chain_id: 2, // lighthouse testnet chain id boot_nodes, slots_per_epoch: 8, shard_slots_per_epoch: 16, phase_1_fork_slot: 96, - phase_1_fork_epoch: 6, + phase_1_fork_epoch: 12, ..ChainSpec::mainnet() } } diff --git a/eth2/types/src/shard_block.rs b/eth2/types/src/shard_block.rs index 09276ccf3ae..1b14b3e6c51 100644 --- a/eth2/types/src/shard_block.rs +++ b/eth2/types/src/shard_block.rs @@ -28,7 +28,7 @@ pub struct ShardBlock { pub parent_root: Hash256, pub state_root: Hash256, // add body - pub attestation: ShardAttestation, + pub attestation: Vec, #[signed_root(skip_hashing)] pub signature: Signature, } @@ -41,7 +41,7 @@ impl ShardBlock { beacon_block_root: spec.zero_hash, parent_root: spec.zero_hash, state_root: spec.zero_hash, - attestation: ShardAttestation::default(), + attestation: vec![], signature: Signature::empty_signature(), } } diff --git a/eth2/types/src/shard_block_header.rs b/eth2/types/src/shard_block_header.rs index b87de291969..942836ff4c7 100644 --- a/eth2/types/src/shard_block_header.rs +++ b/eth2/types/src/shard_block_header.rs @@ -28,7 +28,7 @@ pub struct ShardBlockHeader { pub beacon_block_root: Hash256, pub state_root: Hash256, // need to add body - pub attestation: ShardAttestation, + pub attestation: Vec, #[signed_root(skip_hashing)] pub signature: Signature, } @@ -41,7 +41,7 @@ impl ShardBlockHeader { beacon_block_root: spec.zero_hash, parent_root: spec.zero_hash, state_root: spec.zero_hash, - attestation: ShardAttestation::default(), + attestation: vec![], signature: Signature::empty_signature(), } } @@ -75,11 +75,3 @@ impl ShardBlockHeader { } } } - -#[cfg(test)] -mod tests { - use super::*; - - ssz_tests!(ShardBlockHeader); - cached_tree_hash_tests!(ShardBlockHeader); -} diff --git a/eth2/types/src/shard_state.rs b/eth2/types/src/shard_state.rs index 38ebaf2c4cd..c3f28fea101 100644 --- a/eth2/types/src/shard_state.rs +++ b/eth2/types/src/shard_state.rs @@ -49,13 +49,6 @@ where #[tree_hash(skip_hashing)] #[test_random(default)] pub tree_hash_cache: TreeHashCache, - - #[serde(skip_serializing, skip_deserializing)] - #[ssz(skip_serializing)] - #[ssz(skip_deserializing)] - #[tree_hash(skip_hashing)] - #[test_random(default)] - _phantom: PhantomData, } impl ShardState { @@ -69,7 +62,6 @@ impl ShardState { ]), latest_block_header: ShardBlockHeader::empty(spec, shard), tree_hash_cache: TreeHashCache::default(), - _phantom: PhantomData, } } diff --git a/shard_node/shard_chain/src/fork_choice.rs b/shard_node/shard_chain/src/fork_choice.rs index fca735feeff..2b06b08783f 100644 --- a/shard_node/shard_chain/src/fork_choice.rs +++ b/shard_node/shard_chain/src/fork_choice.rs @@ -83,7 +83,7 @@ impl ForkChoice { block: &ShardBlock, block_root: Hash256, ) -> Result<()> { - self.process_attestation_from_block(beacon_state, &block.attestation, block)?; + self.process_attestation_from_block(beacon_state, &block.attestation[0], block)?; self.backend.process_block(block, block_root)?; Ok(()) diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 7ce5f7d6121..ae6dfdfca0b 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -78,6 +78,7 @@ impl ShardChain { let state_root = genesis_state.canonical_root(); store.put(&state_root, &genesis_state)?; + store.put(&spec.zero_hash, &genesis_state)?; let genesis_block_root = genesis_block_header.canonical_root(); store.put(&genesis_block_root, &genesis_block)?; @@ -522,11 +523,11 @@ impl ShardChain { beacon_block_root, parent_root, state_root: Hash256::zero(), - attestation: self.op_pool.get_attestation( + attestation: vec![self.op_pool.get_attestation( &state, &self.parent_beacon.current_state(), spec, - ), + )], signature: Signature::empty_signature(), }; From 5225b97b97164efb5fd95fddd12f66a5bab1444d Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sun, 22 Sep 2019 23:33:48 -0500 Subject: [PATCH 110/151] Update iterators to actually look up proper values in line with stored headers --- shard_node/shard_store/src/iter.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/shard_node/shard_store/src/iter.rs b/shard_node/shard_store/src/iter.rs index 7a3bd1ed1ad..39cf13ed0ac 100644 --- a/shard_node/shard_store/src/iter.rs +++ b/shard_node/shard_store/src/iter.rs @@ -32,20 +32,21 @@ impl<'a, T: ShardSpec, U: Store> Iterator for StateRootsIterator<'a, T, U> { type Item = (Hash256, ShardSlot); fn next(&mut self) -> Option { - if (self.slot == 0) || (self.slot > self.shard_state.slot) { + if (self.slot == T::default_spec().phase_1_fork_slot) || (self.slot > self.shard_state.slot) { return None; } self.slot -= 1; + let mut next_root = self.shard_state.history_accumulator[0]; // Efficiency gain if using log search via the accumulator instead while self.slot < self.shard_state.slot { - let next_root = self.shard_state.history_accumulator[0]; + next_root = self.shard_state.history_accumulator[0]; let shard_state: ShardState = self.store.get(&next_root).ok()??; self.shard_state = Cow::Owned(shard_state); } - Some((self.shard_state.latest_block_header.state_root, self.slot)) + Some((next_root, self.slot)) } } @@ -108,7 +109,7 @@ impl<'a, T: ShardSpec, U: Store> Iterator for BlockRootsIterator<'a, T, U> { type Item = (Hash256, ShardSlot); fn next(&mut self) -> Option { - if (self.slot == 0) || (self.slot > self.shard_state.slot) { + if (self.slot == T::default_spec().phase_1_fork_slot) || (self.slot > self.shard_state.slot) { return None; } @@ -121,8 +122,11 @@ impl<'a, T: ShardSpec, U: Store> Iterator for BlockRootsIterator<'a, T, U> { self.shard_state = Cow::Owned(shard_state); } + let mut latest_block_header = self.shard_state.latest_block_header.clone(); + // zero out the state root to find where it was stored + latest_block_header.state_root = Hash256::zero(); Some(( - self.shard_state.latest_block_header.canonical_root(), + latest_block_header.canonical_root(), self.slot, )) } @@ -169,7 +173,7 @@ impl<'a, T: ShardSpec, U: Store> Iterator for BestBlockRootsIterator<'a, T, U> { type Item = (Hash256, ShardSlot); fn next(&mut self) -> Option { - if self.slot == 0 { + if self.slot == (T::default_spec().phase_1_fork_slot) { // End of Iterator return None; } @@ -183,8 +187,13 @@ impl<'a, T: ShardSpec, U: Store> Iterator for BestBlockRootsIterator<'a, T, U> { self.shard_state = Cow::Owned(shard_state); } + let mut latest_block_header = self.shard_state.latest_block_header.clone(); + if latest_block_header.state_root == Hash256::zero() { + latest_block_header.state_root = self.shard_state.canonical_root(); + } + Some(( - self.shard_state.latest_block_header.canonical_root(), + latest_block_header.canonical_root(), self.slot, )) } From 663e5866bf62acf927b999ed646b31e2cd4efca9 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Mon, 23 Sep 2019 07:41:07 -0500 Subject: [PATCH 111/151] Fork choice operating on the right attestation set. harness tests representing proper slot advancement. --- eth2/shard_operation_pool/src/lib.rs | 10 +++- .../src/per_shard_block_processing.rs | 2 +- eth2/types/src/beacon_state.rs | 2 +- eth2/types/src/chain_spec.rs | 3 +- eth2/types/src/shard_block.rs | 4 +- shard_node/shard_chain/src/fork_choice.rs | 5 +- shard_node/shard_chain/src/harness.rs | 4 +- shard_node/shard_chain/src/harness_tests.rs | 50 ++++++++----------- shard_node/shard_chain/src/shard_chain.rs | 8 +-- 9 files changed, 47 insertions(+), 41 deletions(-) diff --git a/eth2/shard_operation_pool/src/lib.rs b/eth2/shard_operation_pool/src/lib.rs index 1f2e8d4e031..1dc046405ba 100644 --- a/eth2/shard_operation_pool/src/lib.rs +++ b/eth2/shard_operation_pool/src/lib.rs @@ -69,7 +69,7 @@ impl OperationPool { state: &ShardState, beacon_state: &BeaconState, spec: &ChainSpec, - ) -> ShardAttestation { + ) -> Vec { let attesting_slot = ShardSlot::from(state.slot - 1); let epoch = attesting_slot.epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot); let domain_bytes = @@ -88,7 +88,13 @@ impl OperationPool { .num_set_bits() .cmp(&a.aggregation_bitfield.num_set_bits()) }); - (&attestations[0]).clone() + + let mut attestation = vec![]; + if !attestations.is_empty() { + attestation.push((&attestations[0]).clone()); + } + + attestation } pub fn prune_attestations(&self, finalized_state: &ShardState) { diff --git a/eth2/state_processing/src/per_shard_block_processing.rs b/eth2/state_processing/src/per_shard_block_processing.rs index 2440d053bac..e06136f030e 100644 --- a/eth2/state_processing/src/per_shard_block_processing.rs +++ b/eth2/state_processing/src/per_shard_block_processing.rs @@ -23,6 +23,6 @@ pub fn process_shard_block_header( block: &ShardBlock, spec: &ChainSpec, ) -> Result<(), Error> { - state.latest_block_header = block.block_header(); + state.latest_block_header = block.temporary_block_header(spec); Ok(()) } diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index 6165901636b..563d3bb7e1b 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -480,7 +480,7 @@ impl BeaconState { pub fn get_shard_proposer_index(&self, shard: u64, slot: ShardSlot) -> Result { let spec = T::default_spec(); let current_epoch = self.current_epoch(); - let target_epoch = slot.epoch(spec.shard_slots_per_epoch, spec.shard_slots_per_beacon_slot); + let target_epoch = slot.epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot); let current_period = current_epoch.period(spec.epochs_per_shard_period); let target_period = target_epoch.period(spec.epochs_per_shard_period); diff --git a/eth2/types/src/chain_spec.rs b/eth2/types/src/chain_spec.rs index 4f3ba1b044d..acab8c73ab1 100644 --- a/eth2/types/src/chain_spec.rs +++ b/eth2/types/src/chain_spec.rs @@ -284,7 +284,8 @@ impl ChainSpec { boot_nodes, slots_per_epoch: 8, shard_slots_per_epoch: 16, - phase_1_fork_slot: 96, + shard_slots_per_beacon_slot: 2, + phase_1_fork_slot: 192, phase_1_fork_epoch: 12, ..ChainSpec::mainnet() } diff --git a/eth2/types/src/shard_block.rs b/eth2/types/src/shard_block.rs index 1b14b3e6c51..65540a1536c 100644 --- a/eth2/types/src/shard_block.rs +++ b/eth2/types/src/shard_block.rs @@ -22,10 +22,10 @@ use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash}; SignedRoot, )] pub struct ShardBlock { - pub shard: u64, pub slot: ShardSlot, - pub beacon_block_root: Hash256, + pub shard: u64, pub parent_root: Hash256, + pub beacon_block_root: Hash256, pub state_root: Hash256, // add body pub attestation: Vec, diff --git a/shard_node/shard_chain/src/fork_choice.rs b/shard_node/shard_chain/src/fork_choice.rs index 2b06b08783f..941013b35e6 100644 --- a/shard_node/shard_chain/src/fork_choice.rs +++ b/shard_node/shard_chain/src/fork_choice.rs @@ -83,7 +83,10 @@ impl ForkChoice { block: &ShardBlock, block_root: Hash256, ) -> Result<()> { - self.process_attestation_from_block(beacon_state, &block.attestation[0], block)?; + if !&block.attestation.is_empty() { + self.process_attestation_from_block(beacon_state, &block.attestation[0], block)?; + } + self.backend.process_block(block, block_root)?; Ok(()) diff --git a/shard_node/shard_chain/src/harness.rs b/shard_node/shard_chain/src/harness.rs index ce7ac620a07..1c4b34ebb10 100644 --- a/shard_node/shard_chain/src/harness.rs +++ b/shard_node/shard_chain/src/harness.rs @@ -96,7 +96,9 @@ where let beacon_state_builder = TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(validator_count, &beacon_spec); let (beacon_genesis_state, keypairs) = beacon_state_builder.build(); - let shard_state = ShardState::genesis(&shard_spec, 0); + + let mut shard_state = ShardState::genesis(&shard_spec, 0); + shard_state.latest_block_header.state_root = shard_state.canonical_root(); let mut beacon_genesis_block = BeaconBlock::empty(&beacon_spec); beacon_genesis_block.state_root = Hash256::from_slice(&beacon_genesis_state.tree_hash_root()); diff --git a/shard_node/shard_chain/src/harness_tests.rs b/shard_node/shard_chain/src/harness_tests.rs index c99b37a4706..58ef811ec47 100644 --- a/shard_node/shard_chain/src/harness_tests.rs +++ b/shard_node/shard_chain/src/harness_tests.rs @@ -17,40 +17,32 @@ pub type TestShardForkChoice = ShardThreadSafeReducedTree ShardChainHarness { let harness = ShardChainHarness::new(validator_count); - // Move past the zero slot. + // Move past the zero slot harness.advance_beacon_slot(); + harness.advance_shard_slot(); harness } + #[test] -fn finalizes_with_full_participation() { - let num_blocks_produced = MinimalEthSpec::slots_per_epoch() * 5; +fn advance_shard_slot() { let harness = get_harness(VALIDATOR_COUNT); + let num_blocks_produced = MinimalEthSpec::slots_per_epoch() * harness.beacon_spec.phase_1_fork_epoch; - harness.extend_beacon_chain( - num_blocks_produced as usize, - ); - - let state = &harness.beacon_chain.head().beacon_state; - - assert_eq!( - state.slot, num_blocks_produced, - "head should be at the current slot" - ); - assert_eq!( - state.current_epoch(), - num_blocks_produced / MinimalEthSpec::slots_per_epoch(), - "head should be at the expected epoch" - ); - assert_eq!( - state.current_justified_epoch, - state.current_epoch() - 1, - "the head should be justified one behind the current epoch" - ); - assert_eq!( - state.finalized_epoch, - state.current_epoch() - 2, - "the head should be finalized two behind the current epoch" - ); -} + harness.extend_beacon_chain((num_blocks_produced + 1) as usize); + + let beacon_slot = harness.beacon_chain.current_state().slot; + let shard_slot = harness.shard_chain.current_state().slot; + + harness.extend_shard_chain(1); + + for i in 0..30 { + harness.advance_beacon_slot(); + harness.advance_shard_slot(); + harness.extend_beacon_chain(1); + harness.extend_shard_chain(1); + harness.advance_shard_slot(); + harness.extend_shard_chain(1); + } +} \ No newline at end of file diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index ae6dfdfca0b..26c6de65543 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -77,8 +77,10 @@ impl ShardChain { let genesis_block = genesis_block_header.block(); let state_root = genesis_state.canonical_root(); + store.put(&state_root, &genesis_state)?; store.put(&spec.zero_hash, &genesis_state)?; + store.put(&genesis_block_header.state_root, &genesis_state)?; let genesis_block_root = genesis_block_header.canonical_root(); store.put(&genesis_block_root, &genesis_block)?; @@ -461,7 +463,6 @@ impl ShardChain { self.store.put(&block_root, &block)?; self.store.put(&state_root, &state)?; - // Register the new block with the fork choice service. self.fork_choice .process_block(&beacon_state, &block, block_root)?; @@ -523,15 +524,16 @@ impl ShardChain { beacon_block_root, parent_root, state_root: Hash256::zero(), - attestation: vec![self.op_pool.get_attestation( + attestation: self.op_pool.get_attestation( &state, &self.parent_beacon.current_state(), spec, - )], + ), signature: Signature::empty_signature(), }; per_shard_block_processing(&beacon_state, &mut state, &block, spec); + let state_root = state.canonical_root(); block.state_root = state_root; From fba0ecf2c8fd0ae1e1221e67f919568aaf6ea848 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Tue, 24 Sep 2019 19:15:33 -0500 Subject: [PATCH 112/151] Include data and exec_env_states as part of phase 2 --- beacon_node/beacon_chain/src/test_utils.rs | 1 + eth2/types/src/shard_state.rs | 9 ++++++++- eth2/utils/bls/Cargo.toml | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index 991d29418e9..b3cf5120a79 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -291,6 +291,7 @@ where cc.shard, head_block_root, head_block_slot, + Hash256::zero(), state, ) .expect("should produce attestation data"); diff --git a/eth2/types/src/shard_state.rs b/eth2/types/src/shard_state.rs index c3f28fea101..a6341f7f3a7 100644 --- a/eth2/types/src/shard_state.rs +++ b/eth2/types/src/shard_state.rs @@ -27,11 +27,11 @@ pub enum Error { Clone, Serialize, Deserialize, - TestRandom, Encode, Decode, TreeHash, CachedTreeHash, + TestRandom, CompareFields, )] pub struct ShardState @@ -43,6 +43,11 @@ where pub history_accumulator: FixedLenVec, pub latest_block_header: ShardBlockHeader, + #[test_random(default)] + pub data: Vec, + + pub exec_env_states: Vec, + #[serde(skip_serializing, skip_deserializing)] #[ssz(skip_serializing)] #[ssz(skip_deserializing)] @@ -60,6 +65,8 @@ impl ShardState { spec.zero_hash; T::HistoryAccumulatorDepth::to_usize() ]), + data: vec![], + exec_env_states: vec![], latest_block_header: ShardBlockHeader::empty(spec, shard), tree_hash_cache: TreeHashCache::default(), } diff --git a/eth2/utils/bls/Cargo.toml b/eth2/utils/bls/Cargo.toml index 12758946371..7b9d8f8c3cb 100644 --- a/eth2/utils/bls/Cargo.toml +++ b/eth2/utils/bls/Cargo.toml @@ -18,3 +18,4 @@ tree_hash = { path = "../tree_hash" } [features] fake_crypto = [] +default = ["fake_crypto"] From 2717233d79dafee215f1fa4fe9ccff99b5293185 Mon Sep 17 00:00:00 2001 From: wilbarnes Date: Wed, 18 Sep 2019 08:33:36 -0400 Subject: [PATCH 113/151] split shard_state_processing into own directory --- eth2/shard_state_processing/Cargo.toml | 35 +++++++++++++++++++ eth2/shard_state_processing/src/lib.rs | 22 ++++++++++++ eth2/shard_state_processing/src/macros.rs | 24 +++++++++++++ .../src/per_shard_block_processing.rs | 0 .../src/per_shard_slot_processing.rs | 0 5 files changed, 81 insertions(+) create mode 100644 eth2/shard_state_processing/Cargo.toml create mode 100644 eth2/shard_state_processing/src/lib.rs create mode 100644 eth2/shard_state_processing/src/macros.rs rename eth2/{state_processing => shard_state_processing}/src/per_shard_block_processing.rs (100%) rename eth2/{state_processing => shard_state_processing}/src/per_shard_slot_processing.rs (100%) diff --git a/eth2/shard_state_processing/Cargo.toml b/eth2/shard_state_processing/Cargo.toml new file mode 100644 index 00000000000..e1f98260b9d --- /dev/null +++ b/eth2/shard_state_processing/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "state_processing" +version = "0.1.0" +authors = ["Paul Hauner "] +edition = "2018" + +[[bench]] +name = "benches" +harness = false + +[dev-dependencies] +criterion = "0.2" +env_logger = "0.6.0" +serde = "1.0" +serde_derive = "1.0" +serde_yaml = "0.8" + +[dependencies] +bls = { path = "../utils/bls" } +fnv = "1.0" +hashing = { path = "../utils/hashing" } +int_to_bytes = { path = "../utils/int_to_bytes" } +integer-sqrt = "0.1" +itertools = "0.8" +log = "0.4" +merkle_proof = { path = "../utils/merkle_proof" } +eth2_ssz = { path = "../utils/ssz" } +eth2_ssz_derive = { path = "../utils/ssz_derive" } +tree_hash = { path = "../utils/tree_hash" } +tree_hash_derive = { path = "../utils/tree_hash_derive" } +types = { path = "../types" } +rayon = "1.0" + +[features] +fake_crypto = ["bls/fake_crypto"] diff --git a/eth2/shard_state_processing/src/lib.rs b/eth2/shard_state_processing/src/lib.rs new file mode 100644 index 00000000000..4e16d7eb451 --- /dev/null +++ b/eth2/shard_state_processing/src/lib.rs @@ -0,0 +1,22 @@ +#[macro_use] +mod macros; + +pub mod common; +pub mod get_genesis_state; +pub mod per_block_processing; +pub mod per_epoch_processing; +pub mod per_shard_block_processing; +pub mod per_shard_slot_processing; +pub mod per_slot_processing; + +pub use get_genesis_state::get_genesis_beacon_state; +pub use per_block_processing::{ + errors::{BlockInvalid, BlockProcessingError}, + per_block_processing, per_block_processing_without_verifying_block_signature, +}; +pub use per_epoch_processing::{errors::EpochProcessingError, per_epoch_processing}; +pub use per_shard_block_processing::{ + per_shard_block_processing, Error as ShardBlockProcessingError, +}; +pub use per_shard_slot_processing::{per_shard_slot_processing, Error as ShardSlotProcessingError}; +pub use per_slot_processing::{per_slot_processing, Error as SlotProcessingError}; diff --git a/eth2/shard_state_processing/src/macros.rs b/eth2/shard_state_processing/src/macros.rs new file mode 100644 index 00000000000..93a42764b87 --- /dev/null +++ b/eth2/shard_state_processing/src/macros.rs @@ -0,0 +1,24 @@ +macro_rules! verify { + ($condition: expr, $result: expr) => { + if !$condition { + return Err(Error::Invalid($result)); + } + }; +} + +macro_rules! invalid { + ($result: expr) => { + return Err(Error::Invalid($result)); + }; +} + +macro_rules! safe_add_assign { + ($a: expr, $b: expr) => { + $a = $a.saturating_add($b); + }; +} +macro_rules! safe_sub_assign { + ($a: expr, $b: expr) => { + $a = $a.saturating_sub($b); + }; +} diff --git a/eth2/state_processing/src/per_shard_block_processing.rs b/eth2/shard_state_processing/src/per_shard_block_processing.rs similarity index 100% rename from eth2/state_processing/src/per_shard_block_processing.rs rename to eth2/shard_state_processing/src/per_shard_block_processing.rs diff --git a/eth2/state_processing/src/per_shard_slot_processing.rs b/eth2/shard_state_processing/src/per_shard_slot_processing.rs similarity index 100% rename from eth2/state_processing/src/per_shard_slot_processing.rs rename to eth2/shard_state_processing/src/per_shard_slot_processing.rs From 6977fcefb212480e6df4991b6ef860949a2e0f59 Mon Sep 17 00:00:00 2001 From: wilbarnes Date: Wed, 18 Sep 2019 08:46:10 -0400 Subject: [PATCH 114/151] moving shard block errors into own file in subdir --- .../src/per_shard_block_processing.rs | 7 +++---- .../src/per_shard_block_processing/errors.rs | 6 ++++++ 2 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 eth2/shard_state_processing/src/per_shard_block_processing/errors.rs diff --git a/eth2/shard_state_processing/src/per_shard_block_processing.rs b/eth2/shard_state_processing/src/per_shard_block_processing.rs index e06136f030e..b27ec31f63d 100644 --- a/eth2/shard_state_processing/src/per_shard_block_processing.rs +++ b/eth2/shard_state_processing/src/per_shard_block_processing.rs @@ -1,9 +1,8 @@ +use crate::*; use types::*; +use errors::Error; -#[derive(Debug, PartialEq)] -pub enum Error { - BlockProcessingError, -} +pub mod errors; pub fn per_shard_block_processing( beacon_state: &BeaconState, diff --git a/eth2/shard_state_processing/src/per_shard_block_processing/errors.rs b/eth2/shard_state_processing/src/per_shard_block_processing/errors.rs new file mode 100644 index 00000000000..b4aaa9944ae --- /dev/null +++ b/eth2/shard_state_processing/src/per_shard_block_processing/errors.rs @@ -0,0 +1,6 @@ +use types::*; + +#[derive(Debug, PartialEq)] +pub enum Error { + BlockProcessingError, +} From 0e96e6c0b108e4ae1e9947f79c2e04b12b59421d Mon Sep 17 00:00:00 2001 From: wilbarnes Date: Wed, 18 Sep 2019 09:01:44 -0400 Subject: [PATCH 115/151] moved shard slot errors and process_shard_slot into own file --- .../src/per_shard_slot_processing.rs | 35 +++++-------------- .../src/per_shard_slot_processing/errors.rs | 6 ++++ .../per_shard_slot_processing.rs | 20 +++++++++++ 3 files changed, 35 insertions(+), 26 deletions(-) create mode 100644 eth2/shard_state_processing/src/per_shard_slot_processing/errors.rs create mode 100644 eth2/shard_state_processing/src/per_shard_slot_processing/per_shard_slot_processing.rs diff --git a/eth2/shard_state_processing/src/per_shard_slot_processing.rs b/eth2/shard_state_processing/src/per_shard_slot_processing.rs index 09cc78f933d..274067089b7 100644 --- a/eth2/shard_state_processing/src/per_shard_slot_processing.rs +++ b/eth2/shard_state_processing/src/per_shard_slot_processing.rs @@ -2,10 +2,10 @@ use crate::*; use tree_hash::TreeHash; use types::*; -#[derive(Debug, PartialEq)] -pub enum Error { - ShardStateError(ShardStateError), -} +use process_shard_slot::process_shard_slot; + +pub mod errors; +pub mod process_shard_slot; pub fn per_shard_slot_processing( state: &mut ShardState, @@ -28,25 +28,8 @@ pub fn per_shard_slot_processing( Ok(()) } -// need to put this in separate directory (process slots) -fn process_shard_slot(state: &mut ShardState, spec: &ChainSpec) -> () { - let previous_state_root = Hash256::from_slice(&state.tree_hash_root()[..]); - - if state.latest_block_header.state_root == spec.zero_hash { - state.latest_block_header.state_root = previous_state_root; - } - - let mut depth = 0; - while (state.slot.as_u64() % u64::pow(2, depth as u32) == 0 as u64) - && (depth < T::history_accumulator_depth() as u64) - { - state.history_accumulator[depth as usize] = previous_state_root; - depth += 1; - } -} - -impl From for Error { - fn from(e: ShardStateError) -> Error { - Error::ShardStateError(e) - } -} +// impl From for Error { +// fn from(e: ShardStateError) -> Error { +// Error::ShardStateError(e) +// } +// } diff --git a/eth2/shard_state_processing/src/per_shard_slot_processing/errors.rs b/eth2/shard_state_processing/src/per_shard_slot_processing/errors.rs new file mode 100644 index 00000000000..8bd89abbfa8 --- /dev/null +++ b/eth2/shard_state_processing/src/per_shard_slot_processing/errors.rs @@ -0,0 +1,6 @@ +use types::*; + +#[derive(Debug, PartialEq)] +pub enum Error { + ShardStateError, +} diff --git a/eth2/shard_state_processing/src/per_shard_slot_processing/per_shard_slot_processing.rs b/eth2/shard_state_processing/src/per_shard_slot_processing/per_shard_slot_processing.rs new file mode 100644 index 00000000000..eae62da9035 --- /dev/null +++ b/eth2/shard_state_processing/src/per_shard_slot_processing/per_shard_slot_processing.rs @@ -0,0 +1,20 @@ +use crate::*; +use tree_hash::TreeHash; +use types::*; + +pub fn process_shard_slot(state: &mut ShardState, spec: &ChainSpec) -> () { + let previous_state_root = Hash256::from_slice(&state.tree_hash_root()[..]); + + if state.latest_block_header.state_root == spec.zero_hash { + state.latest_block_header.state_root = previous_state_root; + } + + let mut depth = 0; + + while (state.slot.as_u64() % u64::pow(2, depth as u32) == 0 as u64) + && (depth < T::history_accumulator_depth() as u64) + { + state.history_accumulator[depth as usize] = previous_state_root; + depth += 1; + } +} From 8c2d6b0e9a71758380581a885995e3bda6ae84c1 Mon Sep 17 00:00:00 2001 From: wilbarnes Date: Wed, 18 Sep 2019 09:04:32 -0400 Subject: [PATCH 116/151] Cargo.toml needs to be capitalized --- eth2/shard_lmd_ghost/{cargo.toml => Cargo.toml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename eth2/shard_lmd_ghost/{cargo.toml => Cargo.toml} (100%) diff --git a/eth2/shard_lmd_ghost/cargo.toml b/eth2/shard_lmd_ghost/Cargo.toml similarity index 100% rename from eth2/shard_lmd_ghost/cargo.toml rename to eth2/shard_lmd_ghost/Cargo.toml From fa3e8528ba6c0d78629a5d262811014451439e6a Mon Sep 17 00:00:00 2001 From: wilbarnes Date: Wed, 18 Sep 2019 09:22:50 -0400 Subject: [PATCH 117/151] rm shard dependences from state_processing & updated shard_chain errors --- eth2/shard_state_processing/lib.rs | 17 +++++++++++++++++ eth2/state_processing/src/lib.rs | 6 ------ shard_node/shard_chain/src/errors.rs | 7 +++---- 3 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 eth2/shard_state_processing/lib.rs diff --git a/eth2/shard_state_processing/lib.rs b/eth2/shard_state_processing/lib.rs new file mode 100644 index 00000000000..9f26b3319b0 --- /dev/null +++ b/eth2/shard_state_processing/lib.rs @@ -0,0 +1,17 @@ +#[macro_use] +mod macros; + +pub mod common; + +pub mod per_shard_block_processing; +pub mod per_shard_slot_processing; + +pub use per_shard_block_processing::{ + errors::{Error as ShardBlockProcessingError}, + per_shard_block_processing, process_shard_block_header, +}; + +pub use per_shard_slot_processing::{ + errors::{Error as ShardSlotProcessingError}, + per_shard_slot_processing, +}; diff --git a/eth2/state_processing/src/lib.rs b/eth2/state_processing/src/lib.rs index 4e16d7eb451..e040c152579 100644 --- a/eth2/state_processing/src/lib.rs +++ b/eth2/state_processing/src/lib.rs @@ -5,8 +5,6 @@ pub mod common; pub mod get_genesis_state; pub mod per_block_processing; pub mod per_epoch_processing; -pub mod per_shard_block_processing; -pub mod per_shard_slot_processing; pub mod per_slot_processing; pub use get_genesis_state::get_genesis_beacon_state; @@ -15,8 +13,4 @@ pub use per_block_processing::{ per_block_processing, per_block_processing_without_verifying_block_signature, }; pub use per_epoch_processing::{errors::EpochProcessingError, per_epoch_processing}; -pub use per_shard_block_processing::{ - per_shard_block_processing, Error as ShardBlockProcessingError, -}; -pub use per_shard_slot_processing::{per_shard_slot_processing, Error as ShardSlotProcessingError}; pub use per_slot_processing::{per_slot_processing, Error as SlotProcessingError}; diff --git a/shard_node/shard_chain/src/errors.rs b/shard_node/shard_chain/src/errors.rs index 2d92fa789a8..4651f2372cd 100644 --- a/shard_node/shard_chain/src/errors.rs +++ b/shard_node/shard_chain/src/errors.rs @@ -1,8 +1,7 @@ use crate::fork_choice::Error as ForkChoiceError; // use crate::metrics::Error as MetricsError; -use state_processing::BlockProcessingError; -use state_processing::ShardBlockProcessingError; -use state_processing::ShardSlotProcessingError; +use shard_state_processing::ShardBlockProcessingError; +use shard_state_processing::ShardSlotProcessingError; use store::Error as BeaconDBError; use types::*; @@ -54,7 +53,7 @@ pub enum BlockProductionError { BeaconStateError(BeaconStateError), } -easy_from_to!(BlockProcessingError, BlockProductionError); +easy_from_to!(ShardBlockProcessingError, BlockProductionError); easy_from_to!(ShardStateError, BlockProductionError); easy_from_to!(BeaconStateError, BlockProductionError); easy_from_to!(BeaconStateError, ShardChainError); From 0d679bfb311d059b627360ac2f11497268f3fc70 Mon Sep 17 00:00:00 2001 From: wilbarnes Date: Wed, 18 Sep 2019 09:31:00 -0400 Subject: [PATCH 118/151] updated shard_state_processing dependency path in shard_chain Cargo.toml --- shard_node/shard_chain/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shard_node/shard_chain/Cargo.toml b/shard_node/shard_chain/Cargo.toml index 4d0f1002739..5505a033094 100644 --- a/shard_node/shard_chain/Cargo.toml +++ b/shard_node/shard_chain/Cargo.toml @@ -24,8 +24,8 @@ serde_json = "1.0" slot_clock = { path = "../../eth2/utils/slot_clock" } eth2_ssz = { path = "../../eth2/utils/ssz" } eth2_ssz_derive = { path = "../../eth2/utils/ssz_derive" } -rand = "0.5.5" state_processing = { path = "../../eth2/state_processing" } +shard_state_processing = { path = "../../eth2/shard_state_processing" } tree_hash = { path = "../../eth2/utils/tree_hash" } types = { path = "../../eth2/types" } shard_lmd_ghost = { path = "../../eth2/shard_lmd_ghost" } From d02ac113b8ec559ca0a906702568e43a2905d64f Mon Sep 17 00:00:00 2001 From: wilbarnes Date: Wed, 18 Sep 2019 09:33:15 -0400 Subject: [PATCH 119/151] shard_state_processing Cargo.toml package name correctly updated --- eth2/shard_state_processing/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth2/shard_state_processing/Cargo.toml b/eth2/shard_state_processing/Cargo.toml index e1f98260b9d..871536d1c04 100644 --- a/eth2/shard_state_processing/Cargo.toml +++ b/eth2/shard_state_processing/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "state_processing" +name = "shard_state_processing" version = "0.1.0" authors = ["Paul Hauner "] edition = "2018" From 3e0e890fc6e30c07742f3b680de040ac2678ea3f Mon Sep 17 00:00:00 2001 From: wilbarnes Date: Wed, 18 Sep 2019 08:33:36 -0400 Subject: [PATCH 120/151] split shard_state_processing into own directory --- .../src/per_shard_slot_processing.rs | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/eth2/shard_state_processing/src/per_shard_slot_processing.rs b/eth2/shard_state_processing/src/per_shard_slot_processing.rs index 274067089b7..09cc78f933d 100644 --- a/eth2/shard_state_processing/src/per_shard_slot_processing.rs +++ b/eth2/shard_state_processing/src/per_shard_slot_processing.rs @@ -2,10 +2,10 @@ use crate::*; use tree_hash::TreeHash; use types::*; -use process_shard_slot::process_shard_slot; - -pub mod errors; -pub mod process_shard_slot; +#[derive(Debug, PartialEq)] +pub enum Error { + ShardStateError(ShardStateError), +} pub fn per_shard_slot_processing( state: &mut ShardState, @@ -28,8 +28,25 @@ pub fn per_shard_slot_processing( Ok(()) } -// impl From for Error { -// fn from(e: ShardStateError) -> Error { -// Error::ShardStateError(e) -// } -// } +// need to put this in separate directory (process slots) +fn process_shard_slot(state: &mut ShardState, spec: &ChainSpec) -> () { + let previous_state_root = Hash256::from_slice(&state.tree_hash_root()[..]); + + if state.latest_block_header.state_root == spec.zero_hash { + state.latest_block_header.state_root = previous_state_root; + } + + let mut depth = 0; + while (state.slot.as_u64() % u64::pow(2, depth as u32) == 0 as u64) + && (depth < T::history_accumulator_depth() as u64) + { + state.history_accumulator[depth as usize] = previous_state_root; + depth += 1; + } +} + +impl From for Error { + fn from(e: ShardStateError) -> Error { + Error::ShardStateError(e) + } +} From 4ff94b7263d2fdfe7fc913ee1947f23ccf5ed19d Mon Sep 17 00:00:00 2001 From: wilbarnes Date: Wed, 18 Sep 2019 09:01:44 -0400 Subject: [PATCH 121/151] moved shard slot errors and process_shard_slot into own file --- .../src/per_shard_slot_processing.rs | 35 +++++-------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/eth2/shard_state_processing/src/per_shard_slot_processing.rs b/eth2/shard_state_processing/src/per_shard_slot_processing.rs index 09cc78f933d..274067089b7 100644 --- a/eth2/shard_state_processing/src/per_shard_slot_processing.rs +++ b/eth2/shard_state_processing/src/per_shard_slot_processing.rs @@ -2,10 +2,10 @@ use crate::*; use tree_hash::TreeHash; use types::*; -#[derive(Debug, PartialEq)] -pub enum Error { - ShardStateError(ShardStateError), -} +use process_shard_slot::process_shard_slot; + +pub mod errors; +pub mod process_shard_slot; pub fn per_shard_slot_processing( state: &mut ShardState, @@ -28,25 +28,8 @@ pub fn per_shard_slot_processing( Ok(()) } -// need to put this in separate directory (process slots) -fn process_shard_slot(state: &mut ShardState, spec: &ChainSpec) -> () { - let previous_state_root = Hash256::from_slice(&state.tree_hash_root()[..]); - - if state.latest_block_header.state_root == spec.zero_hash { - state.latest_block_header.state_root = previous_state_root; - } - - let mut depth = 0; - while (state.slot.as_u64() % u64::pow(2, depth as u32) == 0 as u64) - && (depth < T::history_accumulator_depth() as u64) - { - state.history_accumulator[depth as usize] = previous_state_root; - depth += 1; - } -} - -impl From for Error { - fn from(e: ShardStateError) -> Error { - Error::ShardStateError(e) - } -} +// impl From for Error { +// fn from(e: ShardStateError) -> Error { +// Error::ShardStateError(e) +// } +// } From 227fe75c723bc3ca480d47fd6fff39a5c160b2bb Mon Sep 17 00:00:00 2001 From: wilbarnes Date: Wed, 18 Sep 2019 09:33:15 -0400 Subject: [PATCH 122/151] update shard_state_processing Cargo.toml & remove benches remnant --- eth2/shard_state_processing/Cargo.toml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/eth2/shard_state_processing/Cargo.toml b/eth2/shard_state_processing/Cargo.toml index 871536d1c04..2aae5055095 100644 --- a/eth2/shard_state_processing/Cargo.toml +++ b/eth2/shard_state_processing/Cargo.toml @@ -4,10 +4,6 @@ version = "0.1.0" authors = ["Paul Hauner "] edition = "2018" -[[bench]] -name = "benches" -harness = false - [dev-dependencies] criterion = "0.2" env_logger = "0.6.0" From f8be39707eb56cae51df13cc6507dd923cd74668 Mon Sep 17 00:00:00 2001 From: wilbarnes Date: Wed, 18 Sep 2019 09:59:04 -0400 Subject: [PATCH 123/151] shard_state_processing lib.rs was in wrong dir --- eth2/shard_state_processing/lib.rs | 17 ----------------- eth2/shard_state_processing/src/lib.rs | 21 +++++++-------------- 2 files changed, 7 insertions(+), 31 deletions(-) delete mode 100644 eth2/shard_state_processing/lib.rs diff --git a/eth2/shard_state_processing/lib.rs b/eth2/shard_state_processing/lib.rs deleted file mode 100644 index 9f26b3319b0..00000000000 --- a/eth2/shard_state_processing/lib.rs +++ /dev/null @@ -1,17 +0,0 @@ -#[macro_use] -mod macros; - -pub mod common; - -pub mod per_shard_block_processing; -pub mod per_shard_slot_processing; - -pub use per_shard_block_processing::{ - errors::{Error as ShardBlockProcessingError}, - per_shard_block_processing, process_shard_block_header, -}; - -pub use per_shard_slot_processing::{ - errors::{Error as ShardSlotProcessingError}, - per_shard_slot_processing, -}; diff --git a/eth2/shard_state_processing/src/lib.rs b/eth2/shard_state_processing/src/lib.rs index 4e16d7eb451..c76ce584daf 100644 --- a/eth2/shard_state_processing/src/lib.rs +++ b/eth2/shard_state_processing/src/lib.rs @@ -1,22 +1,15 @@ #[macro_use] mod macros; -pub mod common; -pub mod get_genesis_state; -pub mod per_block_processing; -pub mod per_epoch_processing; pub mod per_shard_block_processing; pub mod per_shard_slot_processing; -pub mod per_slot_processing; -pub use get_genesis_state::get_genesis_beacon_state; -pub use per_block_processing::{ - errors::{BlockInvalid, BlockProcessingError}, - per_block_processing, per_block_processing_without_verifying_block_signature, -}; -pub use per_epoch_processing::{errors::EpochProcessingError, per_epoch_processing}; pub use per_shard_block_processing::{ - per_shard_block_processing, Error as ShardBlockProcessingError, + errors::{Error as ShardBlockProcessingError}, + per_shard_block_processing, process_shard_block_header, +}; + +pub use per_shard_slot_processing::{ + errors::{Error as ShardSlotProcessingError}, + per_shard_slot_processing, }; -pub use per_shard_slot_processing::{per_shard_slot_processing, Error as ShardSlotProcessingError}; -pub use per_slot_processing::{per_slot_processing, Error as SlotProcessingError}; From 275dcb9f033bf844dcb6c1885467c1e9a454915d Mon Sep 17 00:00:00 2001 From: wilbarnes Date: Wed, 18 Sep 2019 10:13:39 -0400 Subject: [PATCH 124/151] renaming to process_shard_slot.rs --- .../{per_shard_slot_processing.rs => process_shard_slot.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename eth2/shard_state_processing/src/per_shard_slot_processing/{per_shard_slot_processing.rs => process_shard_slot.rs} (100%) diff --git a/eth2/shard_state_processing/src/per_shard_slot_processing/per_shard_slot_processing.rs b/eth2/shard_state_processing/src/per_shard_slot_processing/process_shard_slot.rs similarity index 100% rename from eth2/shard_state_processing/src/per_shard_slot_processing/per_shard_slot_processing.rs rename to eth2/shard_state_processing/src/per_shard_slot_processing/process_shard_slot.rs From 5c577c12d15011c5f8f164cc4d6872f121d69fa5 Mon Sep 17 00:00:00 2001 From: wilbarnes Date: Wed, 18 Sep 2019 12:47:58 -0400 Subject: [PATCH 125/151] fixing dependencies, errors, and renaming functions --- shard_node/shard_chain/src/errors.rs | 2 +- shard_node/shard_chain/src/fork_choice.rs | 1 - shard_node/shard_chain/src/harness.rs | 4 ++-- shard_node/shard_chain/src/shard_chain.rs | 6 ++++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/shard_node/shard_chain/src/errors.rs b/shard_node/shard_chain/src/errors.rs index 4651f2372cd..d373a3d9e84 100644 --- a/shard_node/shard_chain/src/errors.rs +++ b/shard_node/shard_chain/src/errors.rs @@ -48,7 +48,7 @@ pub enum BlockProductionError { UnableToReadSlot, ShardSlotProcessingError(ShardSlotProcessingError), ShardBlockProcessingError(ShardBlockProcessingError), - BlockProcessingError(BlockProcessingError), + BlockProcessingError(ShardBlockProcessingError), ShardStateError(ShardStateError), BeaconStateError(BeaconStateError), } diff --git a/shard_node/shard_chain/src/fork_choice.rs b/shard_node/shard_chain/src/fork_choice.rs index 941013b35e6..381486710c1 100644 --- a/shard_node/shard_chain/src/fork_choice.rs +++ b/shard_node/shard_chain/src/fork_choice.rs @@ -2,7 +2,6 @@ use crate::{ShardChain, ShardChainError, ShardChainTypes}; use beacon_chain::BeaconChainTypes; use shard_lmd_ghost::LmdGhost; use shard_store::{Error as StoreError, Store}; -use state_processing::common::get_shard_attesting_indices_unsorted; use std::sync::Arc; use store::{Error as BeaconStoreError, Store as BeaconStore}; use types::{ diff --git a/shard_node/shard_chain/src/harness.rs b/shard_node/shard_chain/src/harness.rs index 1c4b34ebb10..59f8dfbc20a 100644 --- a/shard_node/shard_chain/src/harness.rs +++ b/shard_node/shard_chain/src/harness.rs @@ -4,7 +4,7 @@ use lmd_ghost::LmdGhost; use shard_lmd_ghost::{LmdGhost as ShardLmdGhost}; use slot_clock::{SlotClock, ShardSlotClock}; use slot_clock::{TestingSlotClock, ShardTestingSlotClock}; -use state_processing::{per_slot_processing, per_shard_slot_processing}; +use shard_state_processing::{per_shard_block_processing, per_shard_slot_processing}; use std::marker::PhantomData; use std::sync::Arc; use store::MemoryStore; @@ -286,7 +286,7 @@ where } while state.slot < slot { - per_slot_processing(&mut state, &self.beacon_spec) + per_shard_slot_processing(&mut state, &self.beacon_spec) .expect("should be able to advance state to slot"); } diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 26c6de65543..890cbf917ee 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -10,8 +10,10 @@ use shard_store::iter::{ }; use shard_store::{Error as DBError, Store}; use slot_clock::{SlotClock, ShardSlotClock}; -use state_processing::{ - per_shard_block_processing, per_shard_slot_processing, ShardBlockProcessingError, +use shard_state_processing::{ + ShardBlockProcessingError, + ShardSlotProcessingError, + per_shard_block_processing, per_shard_slot_processing, }; use std::sync::Arc; use store::{Error as BeaconDBError, Store as BeaconStore}; From 3e0a347ece700057a0d5db5db06b55ce41698103 Mon Sep 17 00:00:00 2001 From: wilbarnes Date: Wed, 18 Sep 2019 19:43:30 -0400 Subject: [PATCH 126/151] removing unused libraries and commented out code --- eth2/shard_state_processing/Cargo.toml | 21 ------------------- .../src/per_shard_slot_processing.rs | 6 ------ 2 files changed, 27 deletions(-) diff --git a/eth2/shard_state_processing/Cargo.toml b/eth2/shard_state_processing/Cargo.toml index 2aae5055095..5bfa01da5a8 100644 --- a/eth2/shard_state_processing/Cargo.toml +++ b/eth2/shard_state_processing/Cargo.toml @@ -4,28 +4,7 @@ version = "0.1.0" authors = ["Paul Hauner "] edition = "2018" -[dev-dependencies] -criterion = "0.2" -env_logger = "0.6.0" -serde = "1.0" -serde_derive = "1.0" -serde_yaml = "0.8" - [dependencies] -bls = { path = "../utils/bls" } -fnv = "1.0" -hashing = { path = "../utils/hashing" } -int_to_bytes = { path = "../utils/int_to_bytes" } -integer-sqrt = "0.1" -itertools = "0.8" -log = "0.4" -merkle_proof = { path = "../utils/merkle_proof" } -eth2_ssz = { path = "../utils/ssz" } -eth2_ssz_derive = { path = "../utils/ssz_derive" } tree_hash = { path = "../utils/tree_hash" } -tree_hash_derive = { path = "../utils/tree_hash_derive" } types = { path = "../types" } -rayon = "1.0" -[features] -fake_crypto = ["bls/fake_crypto"] diff --git a/eth2/shard_state_processing/src/per_shard_slot_processing.rs b/eth2/shard_state_processing/src/per_shard_slot_processing.rs index 274067089b7..42466c1c4c7 100644 --- a/eth2/shard_state_processing/src/per_shard_slot_processing.rs +++ b/eth2/shard_state_processing/src/per_shard_slot_processing.rs @@ -27,9 +27,3 @@ pub fn per_shard_slot_processing( Ok(()) } - -// impl From for Error { -// fn from(e: ShardStateError) -> Error { -// Error::ShardStateError(e) -// } -// } From cb2db2f6caec5bcc911bc36d933ee4483b1380f7 Mon Sep 17 00:00:00 2001 From: wilbarnes Date: Thu, 19 Sep 2019 18:24:43 -0400 Subject: [PATCH 127/151] adding shard_state_processing functions to library --- .../src/per_shard_block_processing.rs | 79 ++++++++++++++++--- 1 file changed, 69 insertions(+), 10 deletions(-) diff --git a/eth2/shard_state_processing/src/per_shard_block_processing.rs b/eth2/shard_state_processing/src/per_shard_block_processing.rs index b27ec31f63d..a72e1fd65de 100644 --- a/eth2/shard_state_processing/src/per_shard_block_processing.rs +++ b/eth2/shard_state_processing/src/per_shard_block_processing.rs @@ -4,24 +4,83 @@ use errors::Error; pub mod errors; -pub fn per_shard_block_processing( - beacon_state: &BeaconState, - state: &mut ShardState, +pub fn per_shard_block_processing( + state: &mut ShardState, + beacon_state: &BeaconState, block: &ShardBlock, spec: &ChainSpec, ) -> Result<(), Error> { - process_shard_block_header(beacon_state, state, block, spec); - // process_shard_attestations - // process_shard_block_body + process_shard_block_header(state, beacon_state, block, spec); + process_shard_attestations(state, beacon_state, block); + process_shard_block_data_fees(state, beacon_state, block); Ok(()) } -pub fn process_shard_block_header( - beacon_state: &BeaconState, - state: &mut ShardState, +pub fn process_shard_block_header( + state: &mut ShardState, + beacon_state: &BeaconState, block: &ShardBlock, spec: &ChainSpec, ) -> Result<(), Error> { - state.latest_block_header = block.temporary_block_header(spec); + verify!(block.slot == state.slot, ShardBlockProcessingError); + verify!(block.parent_root == signing_root(state.latest_block_header), ShardBlockProcessingError); + + state.latest_block_header = block.block_header(); + + let proposer_idx = get_shard_proposer_index(beacon_state, state.shard, block.slot); + let pubkey = beacon_state.validator_registry[proposer_idx].pubkey; + + // perhaps the compute_epoch_of_shard_slot() function here is not correct, find the correct one + let domain = get_domain(beacon_state, spec.domain_shard_proposer, compute_epoch_of_shard_slot(block.slot)); + let proposer = &state.validator_registry[proposer_idx]; + + // update the error here at some point in the near future + verify!(!proposer.slashed, ShardBlockProcessingError); + + verify_block_signature(&state, &beacon_state, &block, &spec); + Ok(()) } + +pub fn verify_block_signature( + state: &ShardState, + beacon_state: &BeaconState, + block: &ShardBlock, + spec: &ChainSpec, +) -> Result<(), Error> { + let block_proposer = &state.validator_registry + [beacon_state.get_shard_proposer_index(block.slot, RelativeEpoch::Current, spec)?]; + + let domain = spec.get_domain( + block.slot.epoch(T::slots_per_epoch()), + Domain::ShardProposer, + &beacon_state.fork, + ); + + verify!( + block + .signature + .verify(&block.signed_root()[..], domain, &block_proposer.pubkey) + ); + + Ok(()) +} + +pub fn process_shard_attestations( + state: &mut ShardState, + beacon_state: &BeaconState, + attestations: &[Attestation], + spec: &ChainSpec, +) -> Result<(), Error> { + verify!( + attestations.len() as u64 <= spec.max_attestations, + BlockProcessingError + ); + + let shard_committee = beacon_state.get_shard_committee(state.current_epoch(), state.shard); + for (i, validator_idx) in shard_committee.iter().enumerate() { + verify_block_signature(&state, &beacon_state, ) + } + + +} From 28da2de57a355e0610e9ff66e7efebbfa12e4000 Mon Sep 17 00:00:00 2001 From: wilbarnes Date: Mon, 23 Sep 2019 10:40:01 -0400 Subject: [PATCH 128/151] adding additional phase 1 chain_spec values --- eth2/types/src/chain_spec.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/eth2/types/src/chain_spec.rs b/eth2/types/src/chain_spec.rs index acab8c73ab1..ce0279d5724 100644 --- a/eth2/types/src/chain_spec.rs +++ b/eth2/types/src/chain_spec.rs @@ -88,6 +88,10 @@ pub struct ChainSpec { pub period_committee_root_length: u64, pub phase_1_fork_epoch: u64, pub phase_1_fork_slot: u64, + pub target_persistent_committee_size: u64, + pub shard_header_size: u64, + pub shard_block_size_target: u64, + pub shard_block_size_limit: u64, /* * Reward and penalty quotients From 5018a87305424c58a46fbe71847c878b8c26ed4e Mon Sep 17 00:00:00 2001 From: wilbarnes Date: Mon, 23 Sep 2019 10:56:00 -0400 Subject: [PATCH 129/151] adding basic fee logic for shard state transition --- .../src/per_shard_block_processing.rs | 28 ++++++++++++++++++- .../src/per_shard_block_processing/add_fee.rs | 26 +++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 eth2/shard_state_processing/src/per_shard_block_processing/add_fee.rs diff --git a/eth2/shard_state_processing/src/per_shard_block_processing.rs b/eth2/shard_state_processing/src/per_shard_block_processing.rs index a72e1fd65de..3ced0401938 100644 --- a/eth2/shard_state_processing/src/per_shard_block_processing.rs +++ b/eth2/shard_state_processing/src/per_shard_block_processing.rs @@ -81,6 +81,32 @@ pub fn process_shard_attestations( for (i, validator_idx) in shard_committee.iter().enumerate() { verify_block_signature(&state, &beacon_state, ) } +} + +pub fn process_shard_block_data_fees( + state: &mut ShardState, + beacon_state: &BeaconState, + block: &ShardBlock, + spec: &ChainSpec, +) -> Result<(), Error> { + let base_reward = get_shard_base_reward(beacon_state); + + add_fee(state, beacon_state, proposer_index); + + // NOTE: incorrect spec value + let quotient = spec.base_reward_quotient; - + if block.body.len < spec.shard_block_size { + state.basefee += Gwei(cmp::max(1, state.basefee * block.body.len - spec.shard_block_size_target) / quotient) + } else { + state.basefee -= Gwei(cmp::min((1, spec.effective_balance_increment + / spec.epochs_per_shard_period + / spec.shard_slots_per_epoch) + ) + ); + }; + + state.basefee = Gwei(); + + Ok(()) } diff --git a/eth2/shard_state_processing/src/per_shard_block_processing/add_fee.rs b/eth2/shard_state_processing/src/per_shard_block_processing/add_fee.rs new file mode 100644 index 00000000000..e965a0b6806 --- /dev/null +++ b/eth2/shard_state_processing/src/per_shard_block_processing/add_fee.rs @@ -0,0 +1,26 @@ +use crate::*; +use types::*; + +pub fn add_fee( + state: &ShardState, + beacon_state: &BeaconState, + index: u64, +) -> Result<(), Error> { + let epoch = self.current_epoch(); + + let earlier_committee = &self + .get_period_committee(RelativePeriod::Previous, shard)? + .committee; + + let later_committee = &self + .get_period_committee(RelativePeriod::Current, shard)? + .committee; + + if index in earlier_committee { + state.earlier_committee_fees[earlier_committee.index(index)] += delta + } else { + state.later_committee_fees[later_committee.index(index)] += delta + }; + + Ok(()) +} From 3537e684790e7fad17bf769d14c79f72749b7257 Mon Sep 17 00:00:00 2001 From: wilbarnes Date: Tue, 24 Sep 2019 23:29:03 -0400 Subject: [PATCH 130/151] commenting out function logic, replacing process_shard_state_header logic --- .../src/per_shard_block_processing.rs | 120 ++++++++++-------- .../src/per_shard_block_processing/add_fee.rs | 28 ++-- .../process_shard_block_header.rs | 19 +++ .../validate_shard_attestation.rs | 24 ++++ 4 files changed, 127 insertions(+), 64 deletions(-) create mode 100644 eth2/shard_state_processing/src/per_shard_block_processing/process_shard_block_header.rs create mode 100644 eth2/shard_state_processing/src/per_shard_block_processing/validate_shard_attestation.rs diff --git a/eth2/shard_state_processing/src/per_shard_block_processing.rs b/eth2/shard_state_processing/src/per_shard_block_processing.rs index 3ced0401938..810d65ebde9 100644 --- a/eth2/shard_state_processing/src/per_shard_block_processing.rs +++ b/eth2/shard_state_processing/src/per_shard_block_processing.rs @@ -11,8 +11,8 @@ pub fn per_shard_block_processing( spec: &ChainSpec, ) -> Result<(), Error> { process_shard_block_header(state, beacon_state, block, spec); - process_shard_attestations(state, beacon_state, block); - process_shard_block_data_fees(state, beacon_state, block); + // process_shard_attestations(state, beacon_state, block); + // process_shard_block_data_fees(state, beacon_state, block); Ok(()) } @@ -22,24 +22,30 @@ pub fn process_shard_block_header( block: &ShardBlock, spec: &ChainSpec, ) -> Result<(), Error> { - verify!(block.slot == state.slot, ShardBlockProcessingError); - verify!(block.parent_root == signing_root(state.latest_block_header), ShardBlockProcessingError); + state.latest_block_header = block.temporary_block_header(spec); - state.latest_block_header = block.block_header(); + Ok(()) - let proposer_idx = get_shard_proposer_index(beacon_state, state.shard, block.slot); - let pubkey = beacon_state.validator_registry[proposer_idx].pubkey; + // below in progress logic that follows actual spec: + // + // verify!(block.slot == state.slot, ShardBlockProcessingError); + // verify!(block.parent_root == signing_root(state.latest_block_header), ShardBlockProcessingError); - // perhaps the compute_epoch_of_shard_slot() function here is not correct, find the correct one - let domain = get_domain(beacon_state, spec.domain_shard_proposer, compute_epoch_of_shard_slot(block.slot)); - let proposer = &state.validator_registry[proposer_idx]; + // state.latest_block_header = block.block_header(); - // update the error here at some point in the near future - verify!(!proposer.slashed, ShardBlockProcessingError); + // let proposer_idx = get_shard_proposer_index(beacon_state, state.shard, block.slot); + // let pubkey = beacon_state.validator_registry[proposer_idx].pubkey; - verify_block_signature(&state, &beacon_state, &block, &spec); + // // perhaps the compute_epoch_of_shard_slot() function here is not correct, find the correct one + // let domain = get_domain(beacon_state, spec.domain_shard_proposer, compute_epoch_of_shard_slot(block.slot)); + // let proposer = &state.validator_registry[proposer_idx]; - Ok(()) + // // update the error here at some point in the near future + // verify!(!proposer.slashed, ShardBlockProcessingError); + + // verify_block_signature(&state, &beacon_state, &block, &spec); + + // Ok(()) } pub fn verify_block_signature( @@ -48,20 +54,21 @@ pub fn verify_block_signature( block: &ShardBlock, spec: &ChainSpec, ) -> Result<(), Error> { - let block_proposer = &state.validator_registry - [beacon_state.get_shard_proposer_index(block.slot, RelativeEpoch::Current, spec)?]; - - let domain = spec.get_domain( - block.slot.epoch(T::slots_per_epoch()), - Domain::ShardProposer, - &beacon_state.fork, - ); - - verify!( - block - .signature - .verify(&block.signed_root()[..], domain, &block_proposer.pubkey) - ); + // below in progress to follow actual spec + // let block_proposer = &state.validator_registry + // [beacon_state.get_shard_proposer_index(block.slot, RelativeEpoch::Current, spec)?]; + + // let domain = spec.get_domain( + // block.slot.epoch(T::slots_per_epoch()), + // Domain::ShardProposer, + // &beacon_state.fork, + // ); + + // verify!( + // block + // .signature + // .verify(&block.signed_root()[..], domain, &block_proposer.pubkey) + // ); Ok(()) } @@ -72,15 +79,25 @@ pub fn process_shard_attestations( attestations: &[Attestation], spec: &ChainSpec, ) -> Result<(), Error> { - verify!( - attestations.len() as u64 <= spec.max_attestations, - BlockProcessingError - ); - - let shard_committee = beacon_state.get_shard_committee(state.current_epoch(), state.shard); - for (i, validator_idx) in shard_committee.iter().enumerate() { - verify_block_signature(&state, &beacon_state, ) - } + // below in progress to follow actual spec + // verify!( + // attestations.len() as u64 <= spec.max_attestations, + // BlockProcessingError + // ); + + // attestations + // .par_iter() + // .enumerate() + // .try_for_each(|(i, attestation)| { + // validate_shard_attestation(state, attestation, spec).map_err(|e| e.into_with_index(i)) + // })?; + + // let shard_committee = beacon_state.get_shard_committee(state.current_epoch(), state.shard); + // for (i, validator_idx) in shard_committee.iter().enumerate() { + // verify_block_signature(&state, &beacon_state, ) + // } + + Ok(()) } pub fn process_shard_block_data_fees( @@ -89,24 +106,25 @@ pub fn process_shard_block_data_fees( block: &ShardBlock, spec: &ChainSpec, ) -> Result<(), Error> { - let base_reward = get_shard_base_reward(beacon_state); + // below in progress to follow actual spec + // let base_reward = get_shard_base_reward(beacon_state); - add_fee(state, beacon_state, proposer_index); + // add_fee(state, beacon_state, proposer_index); - // NOTE: incorrect spec value - let quotient = spec.base_reward_quotient; + // // NOTE: incorrect spec value + // let quotient = spec.base_reward_quotient; - if block.body.len < spec.shard_block_size { - state.basefee += Gwei(cmp::max(1, state.basefee * block.body.len - spec.shard_block_size_target) / quotient) - } else { - state.basefee -= Gwei(cmp::min((1, spec.effective_balance_increment - / spec.epochs_per_shard_period - / spec.shard_slots_per_epoch) - ) - ); - }; + // if block.body.len < spec.shard_block_size { + // state.basefee += Gwei(cmp::max(1, state.basefee * block.body.len - spec.shard_block_size_target) / quotient) + // } else { + // state.basefee -= Gwei(cmp::min((1, spec.effective_balance_increment + // / spec.epochs_per_shard_period + // / spec.shard_slots_per_epoch) + // ) + // ); + // }; - state.basefee = Gwei(); + // state.basefee = Gwei(); Ok(()) } diff --git a/eth2/shard_state_processing/src/per_shard_block_processing/add_fee.rs b/eth2/shard_state_processing/src/per_shard_block_processing/add_fee.rs index e965a0b6806..3692dfe2e25 100644 --- a/eth2/shard_state_processing/src/per_shard_block_processing/add_fee.rs +++ b/eth2/shard_state_processing/src/per_shard_block_processing/add_fee.rs @@ -6,21 +6,23 @@ pub fn add_fee( beacon_state: &BeaconState, index: u64, ) -> Result<(), Error> { - let epoch = self.current_epoch(); - - let earlier_committee = &self - .get_period_committee(RelativePeriod::Previous, shard)? - .committee; + // let epoch = self.current_epoch(); + // + // let earlier_committee = &self + // .get_period_committee(RelativePeriod::Previous, shard)? + // .committee; - let later_committee = &self - .get_period_committee(RelativePeriod::Current, shard)? - .committee; + // let later_committee = &self + // .get_period_committee(RelativePeriod::Current, shard)? + // .committee; - if index in earlier_committee { - state.earlier_committee_fees[earlier_committee.index(index)] += delta - } else { - state.later_committee_fees[later_committee.index(index)] += delta - }; + // if index in earlier_committee { + // state.earlier_committee_fees[earlier_committee.index(index)] += delta + // } else { + // state.later_committee_fees[later_committee.index(index)] += delta + // }; Ok(()) } + + diff --git a/eth2/shard_state_processing/src/per_shard_block_processing/process_shard_block_header.rs b/eth2/shard_state_processing/src/per_shard_block_processing/process_shard_block_header.rs new file mode 100644 index 00000000000..2726eeef7e1 --- /dev/null +++ b/eth2/shard_state_processing/src/per_shard_block_processing/process_shard_block_header.rs @@ -0,0 +1,19 @@ +use crate::*; +use types::*; + +pub fn get_shard_proposer_index( + beacon_state: &BeaconState, + shard: u64, + epoch: Epoch, +) -> Result { + // let epoch = get_current_epoch(beacon_state); + // let persistent_committee = get_period_committee() +} + +pub fn get_persistent_committee( + beacon_state: &BeaconSTate, + shard: u64, + epoch: Epoch, +) -> Result<(), Error> { + Ok(()) +} diff --git a/eth2/shard_state_processing/src/per_shard_block_processing/validate_shard_attestation.rs b/eth2/shard_state_processing/src/per_shard_block_processing/validate_shard_attestation.rs new file mode 100644 index 00000000000..1a3da0922cd --- /dev/null +++ b/eth2/shard_state_processing/src/per_shard_block_processing/validate_shard_attestation.rs @@ -0,0 +1,24 @@ +use crate::common::convert_to_indexed; +use types::*; + +pub fn validate_attestation( + state: &ShardState, + attestation: &Attestation, + spec: &ChainSpec, +) -> Result<(), Error> { + // validate_attestation_parametric(state, attestation, spec, true, false); + + Ok(()) +} + +pub fn validate_attestation_parametric( + state: &ShardState, + attestation: &Attestation, + spec: &ChainSpec, + verify_signature: bool, + time_independent_only: bool, +) -> Result<(), Error> { + // let attestation_slot = state.get_attestation_slot(&attestation.data)?; + + Ok(()) +} From 06a5e3d25c3c53a5e3a3551ae4f5aa505d05d9b8 Mon Sep 17 00:00:00 2001 From: wilbarnes Date: Wed, 25 Sep 2019 12:24:38 -0400 Subject: [PATCH 131/151] commenting out chain_spec variables and other toml file fixes --- .../src/per_shard_block_processing.rs | 1 - eth2/types/src/chain_spec.rs | 8 ++++---- shard_node/shard_chain/Cargo.toml | 1 + 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/eth2/shard_state_processing/src/per_shard_block_processing.rs b/eth2/shard_state_processing/src/per_shard_block_processing.rs index 810d65ebde9..21a33c9f87d 100644 --- a/eth2/shard_state_processing/src/per_shard_block_processing.rs +++ b/eth2/shard_state_processing/src/per_shard_block_processing.rs @@ -50,7 +50,6 @@ pub fn process_shard_block_header( pub fn verify_block_signature( state: &ShardState, - beacon_state: &BeaconState, block: &ShardBlock, spec: &ChainSpec, ) -> Result<(), Error> { diff --git a/eth2/types/src/chain_spec.rs b/eth2/types/src/chain_spec.rs index ce0279d5724..435c8e2cdaf 100644 --- a/eth2/types/src/chain_spec.rs +++ b/eth2/types/src/chain_spec.rs @@ -88,10 +88,10 @@ pub struct ChainSpec { pub period_committee_root_length: u64, pub phase_1_fork_epoch: u64, pub phase_1_fork_slot: u64, - pub target_persistent_committee_size: u64, - pub shard_header_size: u64, - pub shard_block_size_target: u64, - pub shard_block_size_limit: u64, + // pub target_persistent_committee_size: u64, + // pub shard_header_size: u64, + // pub shard_block_size_target: u64, + // pub shard_block_size_limit: u64, /* * Reward and penalty quotients diff --git a/shard_node/shard_chain/Cargo.toml b/shard_node/shard_chain/Cargo.toml index 5505a033094..c920102d668 100644 --- a/shard_node/shard_chain/Cargo.toml +++ b/shard_node/shard_chain/Cargo.toml @@ -30,6 +30,7 @@ tree_hash = { path = "../../eth2/utils/tree_hash" } types = { path = "../../eth2/types" } shard_lmd_ghost = { path = "../../eth2/shard_lmd_ghost" } lmd_ghost = { path = "../../eth2/lmd_ghost" } +rand = "0.5.5" [dev-dependencies] rand = "0.5.5" From 8faa553c2c778ed8038a72016a7459b5ac0b8e5c Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Wed, 25 Sep 2019 11:58:15 -0500 Subject: [PATCH 132/151] Resolved error handling, previous rebase error, and state references. --- .../src/per_shard_block_processing.rs | 6 +++--- shard_node/shard_chain/src/errors.rs | 2 +- shard_node/shard_chain/src/fork_choice.rs | 1 + shard_node/shard_chain/src/harness.rs | 3 ++- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/eth2/shard_state_processing/src/per_shard_block_processing.rs b/eth2/shard_state_processing/src/per_shard_block_processing.rs index 21a33c9f87d..1a237058b8f 100644 --- a/eth2/shard_state_processing/src/per_shard_block_processing.rs +++ b/eth2/shard_state_processing/src/per_shard_block_processing.rs @@ -5,20 +5,20 @@ use errors::Error; pub mod errors; pub fn per_shard_block_processing( - state: &mut ShardState, beacon_state: &BeaconState, + state: &mut ShardState, block: &ShardBlock, spec: &ChainSpec, ) -> Result<(), Error> { - process_shard_block_header(state, beacon_state, block, spec); + process_shard_block_header(beacon_state, state, block, spec); // process_shard_attestations(state, beacon_state, block); // process_shard_block_data_fees(state, beacon_state, block); Ok(()) } pub fn process_shard_block_header( - state: &mut ShardState, beacon_state: &BeaconState, + state: &mut ShardState, block: &ShardBlock, spec: &ChainSpec, ) -> Result<(), Error> { diff --git a/shard_node/shard_chain/src/errors.rs b/shard_node/shard_chain/src/errors.rs index d373a3d9e84..702e0bb3010 100644 --- a/shard_node/shard_chain/src/errors.rs +++ b/shard_node/shard_chain/src/errors.rs @@ -58,4 +58,4 @@ easy_from_to!(ShardStateError, BlockProductionError); easy_from_to!(BeaconStateError, BlockProductionError); easy_from_to!(BeaconStateError, ShardChainError); easy_from_to!(ShardSlotProcessingError, BlockProductionError); -easy_from_to!(ShardBlockProcessingError, BlockProductionError); + diff --git a/shard_node/shard_chain/src/fork_choice.rs b/shard_node/shard_chain/src/fork_choice.rs index 381486710c1..941013b35e6 100644 --- a/shard_node/shard_chain/src/fork_choice.rs +++ b/shard_node/shard_chain/src/fork_choice.rs @@ -2,6 +2,7 @@ use crate::{ShardChain, ShardChainError, ShardChainTypes}; use beacon_chain::BeaconChainTypes; use shard_lmd_ghost::LmdGhost; use shard_store::{Error as StoreError, Store}; +use state_processing::common::get_shard_attesting_indices_unsorted; use std::sync::Arc; use store::{Error as BeaconStoreError, Store as BeaconStore}; use types::{ diff --git a/shard_node/shard_chain/src/harness.rs b/shard_node/shard_chain/src/harness.rs index 59f8dfbc20a..31aa5adcb06 100644 --- a/shard_node/shard_chain/src/harness.rs +++ b/shard_node/shard_chain/src/harness.rs @@ -5,6 +5,7 @@ use shard_lmd_ghost::{LmdGhost as ShardLmdGhost}; use slot_clock::{SlotClock, ShardSlotClock}; use slot_clock::{TestingSlotClock, ShardTestingSlotClock}; use shard_state_processing::{per_shard_block_processing, per_shard_slot_processing}; +use state_processing::{per_slot_processing, per_block_processing}; use std::marker::PhantomData; use std::sync::Arc; use store::MemoryStore; @@ -286,7 +287,7 @@ where } while state.slot < slot { - per_shard_slot_processing(&mut state, &self.beacon_spec) + per_slot_processing(&mut state, &self.beacon_spec) .expect("should be able to advance state to slot"); } From b856a4e34af55daba0c277068a20747688f6905f Mon Sep 17 00:00:00 2001 From: wilbarnes Date: Wed, 25 Sep 2019 13:04:56 -0400 Subject: [PATCH 133/151] author update, remove chain_spec variables --- eth2/shard_state_processing/Cargo.toml | 2 +- eth2/types/src/chain_spec.rs | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/eth2/shard_state_processing/Cargo.toml b/eth2/shard_state_processing/Cargo.toml index 5bfa01da5a8..7c94a8182e8 100644 --- a/eth2/shard_state_processing/Cargo.toml +++ b/eth2/shard_state_processing/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "shard_state_processing" version = "0.1.0" -authors = ["Paul Hauner "] +authors = ["Will Villanueva", "Wil Barnes"] edition = "2018" [dependencies] diff --git a/eth2/types/src/chain_spec.rs b/eth2/types/src/chain_spec.rs index 435c8e2cdaf..acab8c73ab1 100644 --- a/eth2/types/src/chain_spec.rs +++ b/eth2/types/src/chain_spec.rs @@ -88,10 +88,6 @@ pub struct ChainSpec { pub period_committee_root_length: u64, pub phase_1_fork_epoch: u64, pub phase_1_fork_slot: u64, - // pub target_persistent_committee_size: u64, - // pub shard_header_size: u64, - // pub shard_block_size_target: u64, - // pub shard_block_size_limit: u64, /* * Reward and penalty quotients From 72f8ea19f4a2fc637fb096ae3b3d87d23fbd4879 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Wed, 25 Sep 2019 12:17:59 -0500 Subject: [PATCH 134/151] Make rust beautiful again --- beacon_node/beacon_chain/src/beacon_chain.rs | 8 +- eth2/shard_state_processing/src/lib.rs | 7 +- .../src/per_shard_block_processing.rs | 16 +-- .../process_shard_slot.rs | 2 +- eth2/utils/slot_clock/src/lib.rs | 6 +- .../slot_clock/src/testing_slot_clock.rs | 5 +- shard_node/shard_chain/src/errors.rs | 1 - shard_node/shard_chain/src/harness.rs | 136 ++++++++++-------- shard_node/shard_chain/src/harness_tests.rs | 19 +-- shard_node/shard_chain/src/lib.rs | 2 +- shard_node/shard_chain/src/shard_chain.rs | 22 +-- shard_node/shard_store/src/iter.rs | 16 +-- 12 files changed, 132 insertions(+), 108 deletions(-) diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 9155d05ab78..a6d35fd9914 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -418,7 +418,13 @@ impl BeaconChain { let head_block_root = self.head().beacon_block_root; let head_block_slot = self.head().beacon_block.slot; - self.produce_attestation_data_for_block(shard, head_block_root, head_block_slot, Hash256::zero(), &*state) + self.produce_attestation_data_for_block( + shard, + head_block_root, + head_block_slot, + Hash256::zero(), + &*state, + ) } /// Produce an `AttestationData` that attests to the chain denoted by `block_root` and `state`. diff --git a/eth2/shard_state_processing/src/lib.rs b/eth2/shard_state_processing/src/lib.rs index c76ce584daf..e8dd32ad53f 100644 --- a/eth2/shard_state_processing/src/lib.rs +++ b/eth2/shard_state_processing/src/lib.rs @@ -5,11 +5,10 @@ pub mod per_shard_block_processing; pub mod per_shard_slot_processing; pub use per_shard_block_processing::{ - errors::{Error as ShardBlockProcessingError}, - per_shard_block_processing, process_shard_block_header, + errors::Error as ShardBlockProcessingError, per_shard_block_processing, + process_shard_block_header, }; pub use per_shard_slot_processing::{ - errors::{Error as ShardSlotProcessingError}, - per_shard_slot_processing, + errors::Error as ShardSlotProcessingError, per_shard_slot_processing, }; diff --git a/eth2/shard_state_processing/src/per_shard_block_processing.rs b/eth2/shard_state_processing/src/per_shard_block_processing.rs index 1a237058b8f..d6f14f0da4b 100644 --- a/eth2/shard_state_processing/src/per_shard_block_processing.rs +++ b/eth2/shard_state_processing/src/per_shard_block_processing.rs @@ -1,6 +1,6 @@ use crate::*; -use types::*; use errors::Error; +use types::*; pub mod errors; @@ -64,8 +64,8 @@ pub fn verify_block_signature( // ); // verify!( - // block - // .signature + // block + // .signature // .verify(&block.signed_root()[..], domain, &block_proposer.pubkey) // ); @@ -84,16 +84,16 @@ pub fn process_shard_attestations( // BlockProcessingError // ); - // attestations + // attestations // .par_iter() // .enumerate() // .try_for_each(|(i, attestation)| { // validate_shard_attestation(state, attestation, spec).map_err(|e| e.into_with_index(i)) // })?; - // let shard_committee = beacon_state.get_shard_committee(state.current_epoch(), state.shard); + // let shard_committee = beacon_state.get_shard_committee(state.current_epoch(), state.shard); // for (i, validator_idx) in shard_committee.iter().enumerate() { - // verify_block_signature(&state, &beacon_state, ) + // verify_block_signature(&state, &beacon_state, ) // } Ok(()) @@ -116,8 +116,8 @@ pub fn process_shard_block_data_fees( // if block.body.len < spec.shard_block_size { // state.basefee += Gwei(cmp::max(1, state.basefee * block.body.len - spec.shard_block_size_target) / quotient) // } else { - // state.basefee -= Gwei(cmp::min((1, spec.effective_balance_increment - // / spec.epochs_per_shard_period + // state.basefee -= Gwei(cmp::min((1, spec.effective_balance_increment + // / spec.epochs_per_shard_period // / spec.shard_slots_per_epoch) // ) // ); diff --git a/eth2/shard_state_processing/src/per_shard_slot_processing/process_shard_slot.rs b/eth2/shard_state_processing/src/per_shard_slot_processing/process_shard_slot.rs index eae62da9035..2eb492bdfee 100644 --- a/eth2/shard_state_processing/src/per_shard_slot_processing/process_shard_slot.rs +++ b/eth2/shard_state_processing/src/per_shard_slot_processing/process_shard_slot.rs @@ -12,7 +12,7 @@ pub fn process_shard_slot(state: &mut ShardState, spec: &ChainS let mut depth = 0; while (state.slot.as_u64() % u64::pow(2, depth as u32) == 0 as u64) - && (depth < T::history_accumulator_depth() as u64) + && (depth < T::history_accumulator_depth() as u64) { state.history_accumulator[depth as usize] = previous_state_root; depth += 1; diff --git a/eth2/utils/slot_clock/src/lib.rs b/eth2/utils/slot_clock/src/lib.rs index dea206727a6..e674e035b77 100644 --- a/eth2/utils/slot_clock/src/lib.rs +++ b/eth2/utils/slot_clock/src/lib.rs @@ -2,9 +2,11 @@ mod system_time_slot_clock; mod testing_slot_clock; pub use crate::system_time_slot_clock::{Error as SystemTimeSlotClockError, SystemTimeSlotClock}; -pub use crate::testing_slot_clock::{Error as TestingSlotClockError, TestingSlotClock, ShardTestingSlotClock}; +pub use crate::testing_slot_clock::{ + Error as TestingSlotClockError, ShardTestingSlotClock, TestingSlotClock, +}; use std::time::Duration; -pub use types::{Slot, ShardSlot}; +pub use types::{ShardSlot, Slot}; pub trait SlotClock: Send + Sync + Sized { type Error; diff --git a/eth2/utils/slot_clock/src/testing_slot_clock.rs b/eth2/utils/slot_clock/src/testing_slot_clock.rs index 346d66ce739..149b7ac33a6 100644 --- a/eth2/utils/slot_clock/src/testing_slot_clock.rs +++ b/eth2/utils/slot_clock/src/testing_slot_clock.rs @@ -1,7 +1,7 @@ -use super::{SlotClock, ShardSlotClock}; +use super::{ShardSlotClock, SlotClock}; use std::sync::RwLock; use std::time::Duration; -use types::{Slot, ShardSlot}; +use types::{ShardSlot, Slot}; #[derive(Debug, PartialEq)] pub enum Error {} @@ -25,7 +25,6 @@ impl TestingSlotClock { } } - impl ShardTestingSlotClock { pub fn set_slot(&self, slot: u64) { *self.slot.write().expect("TestingSlotClock poisoned.") = ShardSlot::from(slot); diff --git a/shard_node/shard_chain/src/errors.rs b/shard_node/shard_chain/src/errors.rs index 702e0bb3010..edc7017bec9 100644 --- a/shard_node/shard_chain/src/errors.rs +++ b/shard_node/shard_chain/src/errors.rs @@ -58,4 +58,3 @@ easy_from_to!(ShardStateError, BlockProductionError); easy_from_to!(BeaconStateError, BlockProductionError); easy_from_to!(BeaconStateError, ShardChainError); easy_from_to!(ShardSlotProcessingError, BlockProductionError); - diff --git a/shard_node/shard_chain/src/harness.rs b/shard_node/shard_chain/src/harness.rs index 31aa5adcb06..8073f975140 100644 --- a/shard_node/shard_chain/src/harness.rs +++ b/shard_node/shard_chain/src/harness.rs @@ -1,21 +1,23 @@ +use crate::harness_tests; +use crate::shard_chain::{ + BlockProcessingOutcome as ShardBlockProcessingOutcome, ShardChain, ShardChainTypes, +}; use beacon_chain::{BeaconChain, BeaconChainTypes, BlockProcessingOutcome}; -use crate::shard_chain::{ShardChain, ShardChainTypes, BlockProcessingOutcome as ShardBlockProcessingOutcome}; use lmd_ghost::LmdGhost; -use shard_lmd_ghost::{LmdGhost as ShardLmdGhost}; -use slot_clock::{SlotClock, ShardSlotClock}; -use slot_clock::{TestingSlotClock, ShardTestingSlotClock}; +use shard_lmd_ghost::LmdGhost as ShardLmdGhost; use shard_state_processing::{per_shard_block_processing, per_shard_slot_processing}; -use state_processing::{per_slot_processing, per_block_processing}; +use shard_store::MemoryStore as ShardMemoryStore; +use shard_store::Store as ShardStore; +use slot_clock::{ShardSlotClock, SlotClock}; +use slot_clock::{ShardTestingSlotClock, TestingSlotClock}; +use state_processing::{per_block_processing, per_slot_processing}; use std::marker::PhantomData; use std::sync::Arc; use store::MemoryStore; -use shard_store::{MemoryStore as ShardMemoryStore}; use store::Store; -use shard_store::{Store as ShardStore}; +use test_utils::TestingBeaconStateBuilder; use tree_hash::{SignedRoot, TreeHash}; use types::*; -use test_utils::TestingBeaconStateBuilder; -use crate::harness_tests; // For now only accept 100% honest majority the entire time @@ -94,15 +96,18 @@ where let beacon_store = Arc::new(MemoryStore::open()); let shard_store = Arc::new(ShardMemoryStore::open()); - let beacon_state_builder = - TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(validator_count, &beacon_spec); + let beacon_state_builder = TestingBeaconStateBuilder::from_default_keypairs_file_if_exists( + validator_count, + &beacon_spec, + ); let (beacon_genesis_state, keypairs) = beacon_state_builder.build(); let mut shard_state = ShardState::genesis(&shard_spec, 0); shard_state.latest_block_header.state_root = shard_state.canonical_root(); let mut beacon_genesis_block = BeaconBlock::empty(&beacon_spec); - beacon_genesis_block.state_root = Hash256::from_slice(&beacon_genesis_state.tree_hash_root()); + beacon_genesis_block.state_root = + Hash256::from_slice(&beacon_genesis_state.tree_hash_root()); // Slot clock let beacon_slot_clock = TestingSlotClock::new( @@ -123,7 +128,8 @@ where beacon_genesis_state, beacon_genesis_block, beacon_spec.clone(), - ).expect("Terminate if beacon chain generation fails"); + ) + .expect("Terminate if beacon chain generation fails"); let beacon_chain_reference = Arc::new(beacon_chain); let shard_chain = ShardChain::from_genesis( @@ -133,8 +139,8 @@ where shard_spec.clone(), 0, beacon_chain_reference.clone(), - ).expect("Terminate if beacon chain generation fails"); - + ) + .expect("Terminate if beacon chain generation fails"); Self { beacon_chain: beacon_chain_reference.clone(), @@ -152,28 +158,34 @@ where /// Does not produce blocks or attestations. pub fn advance_beacon_slot(&self) { self.beacon_chain.slot_clock.advance_slot(); - self.beacon_chain.catchup_state().expect("should catchup state"); + self.beacon_chain + .catchup_state() + .expect("should catchup state"); } pub fn advance_shard_slot(&self) { self.shard_chain.slot_clock.advance_slot(); - self.shard_chain.catchup_state().expect("should catchup state"); + self.shard_chain + .catchup_state() + .expect("should catchup state"); } /// Extend the `BeaconChain` with some blocks and attestations. Returns the root of the /// last-produced block (the head of the chain). /// /// Chain will be extended by `num_blocks` blocks. - pub fn extend_beacon_chain( - &self, - num_blocks: usize, - ) -> Hash256 { + pub fn extend_beacon_chain(&self, num_blocks: usize) -> Hash256 { let mut current_slot = self.beacon_chain.read_slot_clock().unwrap(); let mut state = self.get_beacon_state_at_slot(current_slot - 1); let mut head_block_root = None; for _ in 0..num_blocks { - while self.beacon_chain.read_slot_clock().expect("should have a slot") < current_slot { + while self + .beacon_chain + .read_slot_clock() + .expect("should have a slot") + < current_slot + { self.advance_beacon_slot(); } @@ -187,11 +199,7 @@ where if let BlockProcessingOutcome::Processed { block_root } = outcome { head_block_root = Some(block_root); - self.add_beacon_attestations_to_op_pool( - &new_state, - block_root, - current_slot, - ); + self.add_beacon_attestations_to_op_pool(&new_state, block_root, current_slot); } else { panic!("block should be successfully processed: {:?}", outcome); } @@ -207,16 +215,18 @@ where /// last-produced block (the head of the chain). /// /// Chain will be extended by `num_blocks` blocks. - pub fn extend_shard_chain( - &self, - num_blocks: usize, - ) -> Hash256 { + pub fn extend_shard_chain(&self, num_blocks: usize) -> Hash256 { let mut current_slot = self.shard_chain.read_slot_clock().unwrap(); let mut state = self.get_shard_state_at_slot(current_slot - 1); let mut head_block_root = None; for _ in 0..num_blocks { - while self.shard_chain.read_slot_clock().expect("should have a slot") < current_slot { + while self + .shard_chain + .read_slot_clock() + .expect("should have a slot") + < current_slot + { self.advance_shard_slot(); } @@ -230,11 +240,7 @@ where if let ShardBlockProcessingOutcome::Processed { block_root } = outcome { head_block_root = Some(block_root); - self.add_shard_attestations_to_op_pool( - &new_state, - block_root, - current_slot, - ); + self.add_shard_attestations_to_op_pool(&new_state, block_root, current_slot); } else { panic!("block should be successfully processed: {:?}", outcome); } @@ -293,7 +299,8 @@ where state.build_all_caches(&self.beacon_spec).unwrap(); - let proposer_index = self.beacon_chain + let proposer_index = self + .beacon_chain .block_proposer(slot) .expect("should get block proposer from chain"); @@ -315,7 +322,9 @@ where block.signature = { let message = block.signed_root(); let epoch = block.slot.epoch(E::slots_per_epoch()); - let domain = self.beacon_spec.get_domain(epoch, Domain::BeaconProposer, fork); + let domain = self + .beacon_spec + .get_domain(epoch, Domain::BeaconProposer, fork); Signature::new(&message, domain, sk) }; @@ -340,7 +349,8 @@ where state.build_cache(&self.shard_spec).unwrap(); - let proposer_index = self.shard_chain + let proposer_index = self + .shard_chain .block_proposer(slot) .expect("should get block proposer from chain"); @@ -352,9 +362,15 @@ where block.signature = { let message = block.signed_root(); - let epoch = block.slot.epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot); + let epoch = block + .slot + .epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot); // need to actually handle forks correctly - let domain = self.shard_spec.get_domain(epoch, Domain::ShardProposer, &self.beacon_chain.current_state().fork); + let domain = self.shard_spec.get_domain( + epoch, + Domain::ShardProposer, + &self.beacon_chain.current_state().fork, + ); Signature::new(&message, domain, sk) }; @@ -384,14 +400,16 @@ where let shard = cc.shard; let crosslink_data_root = match shard { - 0 => self.shard_chain.get_block_root_at_epoch(state.current_epoch()) - .expect("should get crosslink root") - .unwrap_or(Hash256::zero()), + 0 => self + .shard_chain + .get_block_root_at_epoch(state.current_epoch()) + .expect("should get crosslink root") + .unwrap_or(Hash256::zero()), _ => Hash256::zero(), }; for (i, validator_index) in cc.committee.iter().enumerate() { - if attesting_validators.contains(validator_index) { + if attesting_validators.contains(validator_index) { let data = self .beacon_chain .produce_attestation_data_for_block( @@ -456,18 +474,19 @@ where let attesting_validators: Vec = (0..self.keypairs.len()).collect(); - let shard_committee = self.shard_chain.shard_committee(head_block_slot.epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot)).expect("should get committees"); + let shard_committee = self + .shard_chain + .shard_committee( + head_block_slot.epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot), + ) + .expect("should get committees"); let committee_size = shard_committee.committee.len(); for (i, validator_index) in shard_committee.committee.iter().enumerate() { - if attesting_validators.contains(validator_index) { + if attesting_validators.contains(validator_index) { let data = self .shard_chain - .produce_attestation_data_for_block( - head_block_root, - head_block_slot, - state, - ) + .produce_attestation_data_for_block(head_block_root, head_block_slot, state) .expect("should produce attestation data"); let mut aggregation_bitfield = Bitfield::new(); @@ -476,8 +495,12 @@ where let signature = { let message = data.tree_hash_root(); - let domain = - spec.get_domain(data.target_slot.epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot), Domain::ShardAttestation, fork); + let domain = spec.get_domain( + data.target_slot + .epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot), + Domain::ShardAttestation, + fork, + ); let mut agg_sig = AggregateSignature::new(); agg_sig.add(&Signature::new( @@ -495,8 +518,7 @@ where signature, }; - self.shard_chain - .process_attestation(attestation); + self.shard_chain.process_attestation(attestation); } } } diff --git a/shard_node/shard_chain/src/harness_tests.rs b/shard_node/shard_chain/src/harness_tests.rs index 58ef811ec47..ea1990c139a 100644 --- a/shard_node/shard_chain/src/harness_tests.rs +++ b/shard_node/shard_chain/src/harness_tests.rs @@ -1,11 +1,9 @@ -use crate::harness::{ - ShardChainHarness, CommonBeaconTypes, -}; +use crate::harness::{CommonBeaconTypes, ShardChainHarness}; use lmd_ghost::ThreadSafeReducedTree; -use shard_lmd_ghost::{ThreadSafeReducedTree as ShardThreadSafeReducedTree}; use rand::Rng; -use store::{MemoryStore, Store}; +use shard_lmd_ghost::ThreadSafeReducedTree as ShardThreadSafeReducedTree; use shard_store::{MemoryStore as ShardMemoryStore, Store as ShardStore}; +use store::{MemoryStore, Store}; use types::test_utils::{SeedableRng, TestRandom, XorShiftRng}; use types::{Deposit, EthSpec, Hash256, MinimalEthSpec, MinimalShardSpec, Slot}; @@ -14,7 +12,10 @@ pub const VALIDATOR_COUNT: usize = 24; pub type TestBeaconForkChoice = ThreadSafeReducedTree; pub type TestShardForkChoice = ShardThreadSafeReducedTree; -fn get_harness(validator_count: usize) -> ShardChainHarness { +fn get_harness( + validator_count: usize, +) -> ShardChainHarness +{ let harness = ShardChainHarness::new(validator_count); // Move past the zero slot @@ -24,11 +25,11 @@ fn get_harness(validator_count: usize) -> ShardChainHarness ShardChain { pub fn get_block_root_at_epoch(&self, epoch: Epoch) -> Result, Error> { let spec = &self.spec; - let start_slot_at_epoch = epoch.start_slot(self.spec.slots_per_epoch).shard_slot(spec.slots_per_epoch, spec.shard_slots_per_epoch); + let start_slot_at_epoch = epoch + .start_slot(self.spec.slots_per_epoch) + .shard_slot(spec.slots_per_epoch, spec.shard_slots_per_epoch); let current_slot = self.state.read().slot; - let root = self.rev_iter_block_roots(current_slot) + let root = self + .rev_iter_block_roots(current_slot) .find(|(_hash, slot)| slot.as_u64() == start_slot_at_epoch.as_u64()); Ok(match root { @@ -278,9 +280,7 @@ impl ShardChain { let spec = &self.spec; match self.slot_clock.present_slot() { - Ok(Some(some_slot)) => { - Some(some_slot) - } + Ok(Some(some_slot)) => Some(some_slot), Ok(None) => None, _ => None, } diff --git a/shard_node/shard_store/src/iter.rs b/shard_node/shard_store/src/iter.rs index 39cf13ed0ac..18814ded36a 100644 --- a/shard_node/shard_store/src/iter.rs +++ b/shard_node/shard_store/src/iter.rs @@ -32,7 +32,8 @@ impl<'a, T: ShardSpec, U: Store> Iterator for StateRootsIterator<'a, T, U> { type Item = (Hash256, ShardSlot); fn next(&mut self) -> Option { - if (self.slot == T::default_spec().phase_1_fork_slot) || (self.slot > self.shard_state.slot) { + if (self.slot == T::default_spec().phase_1_fork_slot) || (self.slot > self.shard_state.slot) + { return None; } @@ -109,7 +110,8 @@ impl<'a, T: ShardSpec, U: Store> Iterator for BlockRootsIterator<'a, T, U> { type Item = (Hash256, ShardSlot); fn next(&mut self) -> Option { - if (self.slot == T::default_spec().phase_1_fork_slot) || (self.slot > self.shard_state.slot) { + if (self.slot == T::default_spec().phase_1_fork_slot) || (self.slot > self.shard_state.slot) + { return None; } @@ -125,10 +127,7 @@ impl<'a, T: ShardSpec, U: Store> Iterator for BlockRootsIterator<'a, T, U> { let mut latest_block_header = self.shard_state.latest_block_header.clone(); // zero out the state root to find where it was stored latest_block_header.state_root = Hash256::zero(); - Some(( - latest_block_header.canonical_root(), - self.slot, - )) + Some((latest_block_header.canonical_root(), self.slot)) } } @@ -192,9 +191,6 @@ impl<'a, T: ShardSpec, U: Store> Iterator for BestBlockRootsIterator<'a, T, U> { latest_block_header.state_root = self.shard_state.canonical_root(); } - Some(( - latest_block_header.canonical_root(), - self.slot, - )) + Some((latest_block_header.canonical_root(), self.slot)) } } From b89d6500f66a2d627e84702640ee62bbc9d7c164 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Wed, 25 Sep 2019 14:29:03 -0500 Subject: [PATCH 135/151] Provide way to pass a block body through --- eth2/types/src/shard_block.rs | 5 ++++- eth2/types/src/shard_block_header.rs | 6 +++++- eth2/types/src/shard_state.rs | 5 ----- shard_node/shard_chain/src/harness.rs | 7 ++++--- shard_node/shard_chain/src/harness_tests.rs | 6 +++--- shard_node/shard_chain/src/shard_chain.rs | 5 ++++- 6 files changed, 20 insertions(+), 14 deletions(-) diff --git a/eth2/types/src/shard_block.rs b/eth2/types/src/shard_block.rs index 65540a1536c..c7285856d55 100644 --- a/eth2/types/src/shard_block.rs +++ b/eth2/types/src/shard_block.rs @@ -27,7 +27,8 @@ pub struct ShardBlock { pub parent_root: Hash256, pub beacon_block_root: Hash256, pub state_root: Hash256, - // add body + #[test_random(default)] + pub body: Vec, pub attestation: Vec, #[signed_root(skip_hashing)] pub signature: Signature, @@ -42,6 +43,7 @@ impl ShardBlock { parent_root: spec.zero_hash, state_root: spec.zero_hash, attestation: vec![], + body: vec![], signature: Signature::empty_signature(), } } @@ -57,6 +59,7 @@ impl ShardBlock { beacon_block_root: self.beacon_block_root, parent_root: self.parent_root, state_root: self.state_root, + body: self.body.clone(), attestation: self.attestation.clone(), signature: self.signature.clone(), } diff --git a/eth2/types/src/shard_block_header.rs b/eth2/types/src/shard_block_header.rs index 942836ff4c7..6959dfb4214 100644 --- a/eth2/types/src/shard_block_header.rs +++ b/eth2/types/src/shard_block_header.rs @@ -27,7 +27,8 @@ pub struct ShardBlockHeader { pub parent_root: Hash256, pub beacon_block_root: Hash256, pub state_root: Hash256, - // need to add body + #[test_random(default)] + pub body: Vec, pub attestation: Vec, #[signed_root(skip_hashing)] pub signature: Signature, @@ -41,6 +42,7 @@ impl ShardBlockHeader { beacon_block_root: spec.zero_hash, parent_root: spec.zero_hash, state_root: spec.zero_hash, + body: vec![], attestation: vec![], signature: Signature::empty_signature(), } @@ -58,6 +60,7 @@ impl ShardBlockHeader { beacon_block_root: self.beacon_block_root, parent_root: self.parent_root, state_root: self.state_root, + body: self.body, attestation: self.attestation, signature: self.signature, } @@ -70,6 +73,7 @@ impl ShardBlockHeader { beacon_block_root: self.beacon_block_root, parent_root: self.parent_root, state_root: self.state_root, + body: self.body.clone(), attestation: self.attestation.clone(), signature: self.signature.clone(), } diff --git a/eth2/types/src/shard_state.rs b/eth2/types/src/shard_state.rs index a6341f7f3a7..94ec7fa60b3 100644 --- a/eth2/types/src/shard_state.rs +++ b/eth2/types/src/shard_state.rs @@ -42,10 +42,6 @@ where pub slot: ShardSlot, pub history_accumulator: FixedLenVec, pub latest_block_header: ShardBlockHeader, - - #[test_random(default)] - pub data: Vec, - pub exec_env_states: Vec, #[serde(skip_serializing, skip_deserializing)] @@ -65,7 +61,6 @@ impl ShardState { spec.zero_hash; T::HistoryAccumulatorDepth::to_usize() ]), - data: vec![], exec_env_states: vec![], latest_block_header: ShardBlockHeader::empty(spec, shard), tree_hash_cache: TreeHashCache::default(), diff --git a/shard_node/shard_chain/src/harness.rs b/shard_node/shard_chain/src/harness.rs index 8073f975140..0e8146e362e 100644 --- a/shard_node/shard_chain/src/harness.rs +++ b/shard_node/shard_chain/src/harness.rs @@ -215,7 +215,7 @@ where /// last-produced block (the head of the chain). /// /// Chain will be extended by `num_blocks` blocks. - pub fn extend_shard_chain(&self, num_blocks: usize) -> Hash256 { + pub fn extend_shard_chain(&self, num_blocks: usize, body: Vec) -> Hash256 { let mut current_slot = self.shard_chain.read_slot_clock().unwrap(); let mut state = self.get_shard_state_at_slot(current_slot - 1); let mut head_block_root = None; @@ -230,7 +230,7 @@ where self.advance_shard_slot(); } - let (block, new_state) = self.build_shard_block(state.clone(), current_slot); + let (block, new_state) = self.build_shard_block(state.clone(), current_slot, &body); let outcome = self .shard_chain @@ -336,6 +336,7 @@ where &self, mut state: ShardState, slot: ShardSlot, + body: &Vec, ) -> (ShardBlock, ShardState) { let spec = &self.shard_spec; if slot < state.slot { @@ -357,7 +358,7 @@ where let sk = &self.keypairs[proposer_index].sk; let (mut block, state) = self .shard_chain - .produce_block_on_state(state, slot) + .produce_block_on_state(state, slot, body.clone()) .expect("should produce block"); block.signature = { diff --git a/shard_node/shard_chain/src/harness_tests.rs b/shard_node/shard_chain/src/harness_tests.rs index ea1990c139a..4338aa9a285 100644 --- a/shard_node/shard_chain/src/harness_tests.rs +++ b/shard_node/shard_chain/src/harness_tests.rs @@ -36,14 +36,14 @@ fn advance_shard_slot() { let beacon_slot = harness.beacon_chain.current_state().slot; let shard_slot = harness.shard_chain.current_state().slot; - harness.extend_shard_chain(1); + harness.extend_shard_chain(1, vec![]); for i in 0..30 { harness.advance_beacon_slot(); harness.advance_shard_slot(); harness.extend_beacon_chain(1); - harness.extend_shard_chain(1); + harness.extend_shard_chain(1, vec![]); harness.advance_shard_slot(); - harness.extend_shard_chain(1); + harness.extend_shard_chain(1, vec![]); } } diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 26f65ddceee..fe84bf8e563 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -482,13 +482,14 @@ impl ShardChain { /// Block signing is out of the scope of this function and should be done by a separate program. pub fn produce_block( &self, + body: Vec, ) -> Result<(ShardBlock, ShardState), BlockProductionError> { let state = self.state.read().clone(); let slot = self .read_slot_clock() .ok_or_else(|| BlockProductionError::UnableToReadSlot)?; - self.produce_block_on_state(state, slot) + self.produce_block_on_state(state, slot, body) } /// Produce a block for some `slot` upon the given `state`. @@ -503,6 +504,7 @@ impl ShardChain { &self, mut state: ShardState, produce_at_slot: ShardSlot, + body: Vec, ) -> Result<(ShardBlock, ShardState), BlockProductionError> { // If required, transition the new state to the present slot. while state.slot < produce_at_slot { @@ -525,6 +527,7 @@ impl ShardChain { slot: state.slot, beacon_block_root, parent_root, + body, state_root: Hash256::zero(), attestation: self.op_pool.get_attestation( &state, From d4292b84585c46c0f76de0919e5acc5e810bcd65 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Wed, 25 Sep 2019 16:23:00 -0500 Subject: [PATCH 136/151] Basic shard node running via clap tools --- Cargo.toml | 1 + shard_node/cargo.toml | 30 ++++++++++++++++++++++++++ shard_node/shard_chain/src/lib.rs | 1 + shard_node/src/main.rs | 25 +++++++++++++++++++++ shard_node/src/run.rs | 36 +++++++++++++++++++++++++++++++ 5 files changed, 93 insertions(+) create mode 100644 shard_node/cargo.toml create mode 100644 shard_node/src/main.rs create mode 100644 shard_node/src/run.rs diff --git a/Cargo.toml b/Cargo.toml index 1b34748bfee..a4ed73adba6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ members = [ "beacon_node/rpc", "beacon_node/version", "beacon_node/beacon_chain", + "shard_node", "shard_node/shard_store", "shard_node/shard_chain", "tests/ef_tests", diff --git a/shard_node/cargo.toml b/shard_node/cargo.toml new file mode 100644 index 00000000000..efa473d9428 --- /dev/null +++ b/shard_node/cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "shard_node" +version = "0.1.0" +authors = ["Will Villanueva"] +edition = "2018" + +[dependencies] +shard_chain = { path = "./shard_chain" } +types = { path = "../eth2/types" } +toml = "^0.5" +store = { path = "../beacon_node/store" } +shard_store = { path = "./shard_store" } +clap = "2.32.0" +serde = "1.0" +shard_operation_pool = { path = "../eth2/shard_operation_pool" } +slog = { version = "^2.2.3" , features = ["max_level_trace"] } +slog-term = "^2.4.0" +slog-async = "^2.3.0" +slot_clock = { path = "../eth2/utils/slot_clock" } +ctrlc = { version = "3.1.1", features = ["termination"] } +tokio = "0.1.15" +tokio-timer = "0.2.10" +futures = "0.1.25" +exit-future = "0.1.3" +shard_state_processing = { path = "../eth2/shard_state_processing" } +env_logger = "0.6.1" +dirs = "2.0.1" +shard_lmd_ghost = { path = "../eth2/shard_lmd_ghost" } +lmd_ghost = { path = "../eth2/lmd_ghost" } +rand = "0.5.5" diff --git a/shard_node/shard_chain/src/lib.rs b/shard_node/shard_chain/src/lib.rs index 4a67965a331..0d401b191bf 100644 --- a/shard_node/shard_chain/src/lib.rs +++ b/shard_node/shard_chain/src/lib.rs @@ -8,3 +8,4 @@ pub mod shard_chain; pub use self::checkpoint::CheckPoint; pub use self::errors::{BlockProductionError, ShardChainError}; pub use self::shard_chain::{ShardChain, ShardChainTypes}; +pub use self::harness::ShardChainHarness; diff --git a/shard_node/src/main.rs b/shard_node/src/main.rs new file mode 100644 index 00000000000..c1f42d5b829 --- /dev/null +++ b/shard_node/src/main.rs @@ -0,0 +1,25 @@ +mod run; + +extern crate clap; +use shard_chain::ShardChainHarness; +use clap::{Arg, App, SubCommand}; + +fn main() { + let matches = App::new("My Super Program") + .version("0.1.0") + .author("Will Villanueva") + .about("Simulates Shard Chains") + .arg(Arg::with_name("shards") + .short("s") + .long("shards") + .value_name("FILE") + .help("Sets a custom config file") + .takes_value(true)) + .get_matches(); + + // Matches number of shards to run + let shards = matches.value_of("shards").unwrap_or("1"); + run::run_harness(); +} + + diff --git a/shard_node/src/run.rs b/shard_node/src/run.rs new file mode 100644 index 00000000000..cc675100cb7 --- /dev/null +++ b/shard_node/src/run.rs @@ -0,0 +1,36 @@ +use shard_chain::ShardChainHarness; +use lmd_ghost::ThreadSafeReducedTree; +use rand::Rng; +use shard_lmd_ghost::ThreadSafeReducedTree as ShardThreadSafeReducedTree; +use shard_store::{MemoryStore as ShardMemoryStore, Store as ShardStore}; +use store::{MemoryStore, Store}; +use types::test_utils::{SeedableRng, TestRandom, XorShiftRng}; +use types::{EthSpec, MinimalEthSpec, MinimalShardSpec, Slot}; + +pub const VALIDATOR_COUNT: usize = 24; + +pub type TestBeaconForkChoice = ThreadSafeReducedTree; +pub type TestShardForkChoice = ShardThreadSafeReducedTree; + +fn get_harness( + validator_count: usize, +) -> ShardChainHarness +{ + let harness = ShardChainHarness::new(validator_count); + + // Move past the zero slot + harness.advance_beacon_slot(); + harness.advance_shard_slot(); + + harness +} + +pub fn run_harness() -> () { + let harness = get_harness(VALIDATOR_COUNT); + let num_blocks_produced = + MinimalEthSpec::slots_per_epoch() * harness.beacon_spec.phase_1_fork_epoch; + + harness.extend_beacon_chain((num_blocks_produced + 1) as usize); + harness.extend_shard_chain(1, vec![]); + println!("{:?}", harness.shard_chain.current_state().clone()); +} From 0c9a7854a152138a82ec436d177d12f0d8e45b8b Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Thu, 26 Sep 2019 13:47:45 -0500 Subject: [PATCH 137/151] Update shard chain to be a reference within the harness (so it can be passed around) --- shard_node/shard_chain/src/harness.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/shard_node/shard_chain/src/harness.rs b/shard_node/shard_chain/src/harness.rs index 0e8146e362e..1cffc409185 100644 --- a/shard_node/shard_chain/src/harness.rs +++ b/shard_node/shard_chain/src/harness.rs @@ -75,7 +75,7 @@ where pub beacon_chain: Arc>>, pub keypairs: Vec, pub beacon_spec: ChainSpec, - pub shard_chain: ShardChain, CommonBeaconTypes>, + pub shard_chain: Arc, CommonBeaconTypes>>, pub shard_spec: ChainSpec, _phantom_t: PhantomData, _phantom_u: PhantomData, @@ -141,12 +141,13 @@ where beacon_chain_reference.clone(), ) .expect("Terminate if beacon chain generation fails"); + let shard_chain_reference = Arc::new(shard_chain); Self { beacon_chain: beacon_chain_reference.clone(), keypairs, beacon_spec, - shard_chain: shard_chain, + shard_chain: shard_chain_reference.clone(), shard_spec, _phantom_t: PhantomData, _phantom_u: PhantomData, From 17a476f5e2f956344af0f19bfeeb4909a19ee602 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Thu, 26 Sep 2019 16:05:47 -0500 Subject: [PATCH 138/151] Included rest api - it is a mirror of what is on "interop" branch upstream. --- Cargo.toml | 1 + shard_node/cargo.toml | 1 + shard_node/rest_api/Cargo.toml | 27 +++++ shard_node/rest_api/src/config.rs | 23 ++++ shard_node/rest_api/src/error.rs | 78 +++++++++++++ shard_node/rest_api/src/helpers.rs | 41 +++++++ shard_node/rest_api/src/lib.rs | 122 ++++++++++++++++++++ shard_node/rest_api/src/macros.rs | 13 +++ shard_node/rest_api/src/response_builder.rs | 99 ++++++++++++++++ shard_node/rest_api/src/shard.rs | 9 ++ shard_node/rest_api/src/url_query.rs | 80 +++++++++++++ shard_node/shard_chain/Cargo.toml | 2 +- 12 files changed, 495 insertions(+), 1 deletion(-) create mode 100644 shard_node/rest_api/Cargo.toml create mode 100644 shard_node/rest_api/src/config.rs create mode 100644 shard_node/rest_api/src/error.rs create mode 100644 shard_node/rest_api/src/helpers.rs create mode 100644 shard_node/rest_api/src/lib.rs create mode 100644 shard_node/rest_api/src/macros.rs create mode 100644 shard_node/rest_api/src/response_builder.rs create mode 100644 shard_node/rest_api/src/shard.rs create mode 100644 shard_node/rest_api/src/url_query.rs diff --git a/Cargo.toml b/Cargo.toml index a4ed73adba6..0df7bc42da2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ members = [ "shard_node", "shard_node/shard_store", "shard_node/shard_chain", + "shard_node/rest_api", "tests/ef_tests", "protos", "validator_client", diff --git a/shard_node/cargo.toml b/shard_node/cargo.toml index efa473d9428..f29521c65a0 100644 --- a/shard_node/cargo.toml +++ b/shard_node/cargo.toml @@ -10,6 +10,7 @@ types = { path = "../eth2/types" } toml = "^0.5" store = { path = "../beacon_node/store" } shard_store = { path = "./shard_store" } +rest_api = { path = "./rest_api" } clap = "2.32.0" serde = "1.0" shard_operation_pool = { path = "../eth2/shard_operation_pool" } diff --git a/shard_node/rest_api/Cargo.toml b/shard_node/rest_api/Cargo.toml new file mode 100644 index 00000000000..63843de282b --- /dev/null +++ b/shard_node/rest_api/Cargo.toml @@ -0,0 +1,27 @@ + +[package] +name = "rest_api" +version = "0.1.0" +authors = ["Luke Anderson ", "Will Villanueva"] +edition = "2018" + +[dependencies] +beacon_chain = { path = "../../beacon_node/beacon_chain" } +shard_chain = { path = "../shard_chain" } +shard_store = { path = "../shard_store" } +serde = { version = "1.0", features = ["derive"] } +serde_json = "^1.0" +serde_yaml = "0.8" +slog = "^2.2.3" +slog-term = "^2.4.0" +slog-async = "^2.3.0" +eth2_ssz = { path = "../../eth2/utils/ssz" } +eth2_ssz_derive = { path = "../../eth2/utils/ssz_derive" } +types = { path = "../../eth2/types" } +http = "^0.1.17" +hyper = "0.12.34" +exit-future = "0.1.3" +tokio = "0.1.17" +url = "2.0" +lazy_static = "1.3.0" +futures = "0.1.25" diff --git a/shard_node/rest_api/src/config.rs b/shard_node/rest_api/src/config.rs new file mode 100644 index 00000000000..791f37abb71 --- /dev/null +++ b/shard_node/rest_api/src/config.rs @@ -0,0 +1,23 @@ +use serde::{Deserialize, Serialize}; +use std::net::Ipv4Addr; + +/// HTTP REST API Configuration +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Config { + /// Enable the REST API server. + pub enabled: bool, + /// The IPv4 address the REST API HTTP server will listen on. + pub listen_address: Ipv4Addr, + /// The port the REST API HTTP server will listen on. + pub port: u16, +} + +impl Default for Config { + fn default() -> Self { + Config { + enabled: true, + listen_address: Ipv4Addr::new(127, 0, 0, 1), + port: 5052, + } + } +} diff --git a/shard_node/rest_api/src/error.rs b/shard_node/rest_api/src/error.rs new file mode 100644 index 00000000000..47f6b12dedf --- /dev/null +++ b/shard_node/rest_api/src/error.rs @@ -0,0 +1,78 @@ +use crate::BoxFut; +use hyper::{Body, Response, StatusCode}; +use std::error::Error as StdError; + +#[derive(PartialEq, Debug, Clone)] +pub enum ApiError { + MethodNotAllowed(String), + ServerError(String), + NotImplemented(String), + BadRequest(String), + NotFound(String), + UnsupportedType(String), + ImATeapot(String), +} + +pub type ApiResult = Result, ApiError>; + +impl ApiError { + pub fn status_code(self) -> (StatusCode, String) { + match self { + ApiError::MethodNotAllowed(desc) => (StatusCode::METHOD_NOT_ALLOWED, desc), + ApiError::ServerError(desc) => (StatusCode::INTERNAL_SERVER_ERROR, desc), + ApiError::NotImplemented(desc) => (StatusCode::NOT_IMPLEMENTED, desc), + ApiError::BadRequest(desc) => (StatusCode::BAD_REQUEST, desc), + ApiError::NotFound(desc) => (StatusCode::NOT_FOUND, desc), + ApiError::UnsupportedType(desc) => (StatusCode::UNSUPPORTED_MEDIA_TYPE, desc), + ApiError::ImATeapot(desc) => (StatusCode::IM_A_TEAPOT, desc), + } + } +} + +impl Into> for ApiError { + fn into(self) -> Response { + let status_code = self.status_code(); + Response::builder() + .status(status_code.0) + .header("content-type", "text/plain; charset=utf-8") + .body(Body::from(status_code.1)) + .expect("Response should always be created.") + } +} + +impl Into for ApiError { + fn into(self) -> BoxFut { + Box::new(futures::future::err(self)) + } +} + +impl From for ApiError { + fn from(e: shard_store::Error) -> ApiError { + ApiError::ServerError(format!("Database error: {:?}", e)) + } +} + +impl From for ApiError { + fn from(e: types::ShardStateError) -> ApiError { + ApiError::ServerError(format!("BeaconState error: {:?}", e)) + } +} + +impl From for ApiError { + fn from(e: hyper::error::Error) -> ApiError { + ApiError::ServerError(format!("Networking error: {:?}", e)) + } +} + +impl StdError for ApiError { + fn cause(&self) -> Option<&dyn StdError> { + None + } +} + +impl std::fmt::Display for ApiError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let status = self.clone().status_code(); + write!(f, "{:?}: {:?}", status.0, status.1) + } +} diff --git a/shard_node/rest_api/src/helpers.rs b/shard_node/rest_api/src/helpers.rs new file mode 100644 index 00000000000..8490e547d40 --- /dev/null +++ b/shard_node/rest_api/src/helpers.rs @@ -0,0 +1,41 @@ +use crate::{ApiError, ApiResult}; +use beacon_chain::BeaconChainTypes; +use shard_chain::{ShardChain, ShardChainTypes}; +use http::header; +use hyper::{Body, Request}; +use std::sync::Arc; + +/// Checks the provided request to ensure that the `content-type` header. +/// +/// The content-type header should either be omitted, in which case JSON is assumed, or it should +/// explicity specify `application/json`. If anything else is provided, an error is returned. +pub fn check_content_type_for_json(req: &Request) -> Result<(), ApiError> { + match req.headers().get(header::CONTENT_TYPE) { + Some(h) if h == "application/json" => Ok(()), + Some(h) => Err(ApiError::BadRequest(format!( + "The provided content-type {:?} is not available, this endpoint only supports json.", + h + ))), + _ => Ok(()), + } +} + +pub fn get_shard_chain_from_request ( + req: &Request, +) -> Result<(Arc>), ApiError> { + // Get shard chain + let shard_chain = req + .extensions() + .get::>>() + .ok_or_else(|| ApiError::ServerError("Beacon chain extension missing".into()))?; + + Ok(shard_chain.clone()) +} + +pub fn get_logger_from_request(req: &Request) -> slog::Logger { + let log = req + .extensions() + .get::() + .expect("Should always get the logger from the request, since we put it in there."); + log.to_owned() +} diff --git a/shard_node/rest_api/src/lib.rs b/shard_node/rest_api/src/lib.rs new file mode 100644 index 00000000000..943a084edfa --- /dev/null +++ b/shard_node/rest_api/src/lib.rs @@ -0,0 +1,122 @@ +#[macro_use] +mod macros; +#[macro_use] +extern crate lazy_static; + +mod config; +mod error; +mod helpers; +mod response_builder; +mod url_query; +mod shard; + +use beacon_chain::BeaconChainTypes; +use shard_chain::{ShardChain, ShardChainTypes}; +use error::{ApiError, ApiResult}; +use futures::future::IntoFuture; +use hyper::rt::Future; +use hyper::service::Service; +use hyper::{Body, Method, Request, Response, Server}; +use slog::{info, o, warn}; +use std::sync::Arc; +use url_query::UrlQuery; +use tokio::runtime::TaskExecutor; + +pub use config::Config as ApiConfig; + +type BoxFut = Box, Error = ApiError> + Send>; + +pub struct ApiService { + log: slog::Logger, + shard_chain: Arc>, +} + +fn into_boxfut(item: F) -> BoxFut +where + F: IntoFuture, Error = ApiError>, + F::Future: Send, +{ + Box::new(item.into_future()) +} + +impl Service for ApiService { + type ReqBody = Body; + type ResBody = Body; + type Error = ApiError; + type Future = BoxFut; + + fn call(&mut self, mut req: Request) -> Self::Future { + req.extensions_mut() + .insert::(self.log.clone()); + req.extensions_mut() + .insert::>>(self.shard_chain.clone()); + + let path = req.uri().path().to_string(); + + let result = match (req.method(), path.as_ref()) { + (&Method::GET, "/hello") => into_boxfut(shard::hello(req)), + _ => Box::new(futures::future::err(ApiError::NotFound( + "Request path and/or method not found.".to_owned(), + ))), + }; + + let response = match result.wait() { + // Return the `hyper::Response`. + Ok(response) => { + slog::debug!(self.log, "Request successful: {:?}", path); + response + } + // Map the `ApiError` into `hyper::Response`. + Err(e) => { + slog::debug!(self.log, "Request failure: {:?}", path); + e.into() + } + }; + + Box::new(futures::future::ok(response)) + } +} + +pub fn start_server( + config: &ApiConfig, + executor: &TaskExecutor, + shard_chain: Arc>, + log: &slog::Logger, +) -> Result<(), hyper::Error> { + let log = log.new(o!("Service" => "Api")); + + // Get the address to bind to + let bind_addr = (config.listen_address, config.port).into(); + + // Clone our stateful objects, for use in service closure. + let server_log = log.clone(); + let server_bc = shard_chain.clone(); + + let service = move || -> futures::future::FutureResult, String> { + futures::future::ok(ApiService { + log: server_log.clone(), + shard_chain: server_bc.clone(), + }) + }; + + let log_clone = log.clone(); + let server = Server::bind(&bind_addr) + .serve(service) + .map_err(move |e| { + warn!( + log_clone, + "API failed to start, Unable to bind"; "address" => format!("{:?}", e) + ) + }); + + info!( + log, + "REST API started"; + "address" => format!("{}", config.listen_address), + "port" => config.port, + ); + + executor.spawn(server); + + Ok(()) +} diff --git a/shard_node/rest_api/src/macros.rs b/shard_node/rest_api/src/macros.rs new file mode 100644 index 00000000000..e95cfb8aedd --- /dev/null +++ b/shard_node/rest_api/src/macros.rs @@ -0,0 +1,13 @@ +macro_rules! try_future { + ($expr:expr) => { + match $expr { + core::result::Result::Ok(val) => val, + core::result::Result::Err(err) => { + return Box::new(futures::future::err(std::convert::From::from(err))) + } + } + }; + ($expr:expr,) => { + $crate::try_future!($expr) + }; +} diff --git a/shard_node/rest_api/src/response_builder.rs b/shard_node/rest_api/src/response_builder.rs new file mode 100644 index 00000000000..d5b530f8ab6 --- /dev/null +++ b/shard_node/rest_api/src/response_builder.rs @@ -0,0 +1,99 @@ +use super::{ApiError, ApiResult}; +use http::header; +use hyper::{Body, Request, Response, StatusCode}; +use serde::Serialize; +use ssz::Encode; + +pub enum Encoding { + JSON, + SSZ, + YAML, + TEXT, +} + +pub struct ResponseBuilder { + encoding: Encoding, +} + +impl ResponseBuilder { + pub fn new(req: &Request) -> Result { + let content_header: String = req + .headers() + .get(header::CONTENT_TYPE) + .map_or(Ok(""), |h| h.to_str()) + .map_err(|e| { + ApiError::BadRequest(format!( + "The content-type header contains invalid characters: {:?}", + e + )) + }) + .map(|h| String::from(h))?; + + // JSON is our default encoding, unless something else is requested. + let encoding = match content_header { + ref h if h.starts_with("application/ssz") => Encoding::SSZ, + ref h if h.starts_with("application/yaml") => Encoding::YAML, + ref h if h.starts_with("text/") => Encoding::TEXT, + _ => Encoding::JSON, + }; + Ok(Self { encoding }) + } + + pub fn body(self, item: &T) -> ApiResult { + match self.encoding { + Encoding::SSZ => Response::builder() + .status(StatusCode::OK) + .header("content-type", "application/ssz") + .body(Body::from(item.as_ssz_bytes())) + .map_err(|e| ApiError::ServerError(format!("Failed to build response: {:?}", e))), + _ => self.body_no_ssz(item), + } + } + + pub fn body_no_ssz(self, item: &T) -> ApiResult { + let (body, content_type) = match self.encoding { + Encoding::JSON => ( + Body::from(serde_json::to_string(&item).map_err(|e| { + ApiError::ServerError(format!( + "Unable to serialize response body as JSON: {:?}", + e + )) + })?), + "application/json", + ), + Encoding::SSZ => { + return Err(ApiError::UnsupportedType( + "Response cannot be encoded as SSZ.".into(), + )); + } + Encoding::YAML => ( + Body::from(serde_yaml::to_string(&item).map_err(|e| { + ApiError::ServerError(format!( + "Unable to serialize response body as YAML: {:?}", + e + )) + })?), + "application/yaml", + ), + Encoding::TEXT => { + return Err(ApiError::UnsupportedType( + "Response cannot be encoded as plain text.".into(), + )); + } + }; + + Response::builder() + .status(StatusCode::OK) + .header("content-type", content_type) + .body(Body::from(body)) + .map_err(|e| ApiError::ServerError(format!("Failed to build response: {:?}", e))) + } + + pub fn body_text(self, text: String) -> ApiResult { + Response::builder() + .status(StatusCode::OK) + .header("content-type", "text/plain; charset=utf-8") + .body(Body::from(text)) + .map_err(|e| ApiError::ServerError(format!("Failed to build response: {:?}", e))) + } +} diff --git a/shard_node/rest_api/src/shard.rs b/shard_node/rest_api/src/shard.rs new file mode 100644 index 00000000000..fb97322045a --- /dev/null +++ b/shard_node/rest_api/src/shard.rs @@ -0,0 +1,9 @@ +use crate::helpers::*; +use crate::response_builder::ResponseBuilder; +use crate::{ApiError, ApiResult, UrlQuery}; +use hyper::{Body, Request}; + +/// HTTP handler to return a `BeaconState` at the genesis block. +pub fn hello(req: Request) -> ApiResult { + ResponseBuilder::new(&req)?.body_text("hello".to_string()) +} diff --git a/shard_node/rest_api/src/url_query.rs b/shard_node/rest_api/src/url_query.rs new file mode 100644 index 00000000000..5fbb1ed673d --- /dev/null +++ b/shard_node/rest_api/src/url_query.rs @@ -0,0 +1,80 @@ +use crate::ApiError; +use hyper::Request; + +/// Provides handy functions for parsing the query parameters of a URL. + +#[derive(Clone, Copy)] +pub struct UrlQuery<'a>(url::form_urlencoded::Parse<'a>); + +impl<'a> UrlQuery<'a> { + /// Instantiate from an existing `Request`. + /// + /// Returns `Err` if `req` does not contain any query parameters. + pub fn from_request(req: &'a Request) -> Result { + let query_str = req.uri().query().ok_or_else(|| { + ApiError::BadRequest( + "URL query must be valid and contain at least one key.".to_string(), + ) + })?; + + Ok(UrlQuery(url::form_urlencoded::parse(query_str.as_bytes()))) + } + + /// Returns the first `(key, value)` pair found where the `key` is in `keys`. + /// + /// If no match is found, an `InvalidQueryParams` error is returned. + pub fn first_of(mut self, keys: &[&str]) -> Result<(String, String), ApiError> { + self.0 + .find(|(key, _value)| keys.contains(&&**key)) + .map(|(key, value)| (key.into_owned(), value.into_owned())) + .ok_or_else(|| { + ApiError::BadRequest(format!( + "URL query must contain at least one of the following keys: {:?}", + keys + )) + }) + } + + /// Returns the value for `key`, if and only if `key` is the only key present in the query + /// parameters. + pub fn only_one(self, key: &str) -> Result { + let queries: Vec<_> = self + .0 + .map(|(k, v)| (k.into_owned(), v.into_owned())) + .collect(); + + if queries.len() == 1 { + let (first_key, first_value) = &queries[0]; // Must have 0 index if len is 1. + if first_key == key { + Ok(first_value.to_string()) + } else { + Err(ApiError::BadRequest(format!( + "Only the {} query parameter is supported", + key + ))) + } + } else { + Err(ApiError::BadRequest(format!( + "Only one query parameter is allowed, {} supplied", + queries.len() + ))) + } + } + + /// Returns a vector of all values present where `key` is in `keys + /// + /// If no match is found, an `InvalidQueryParams` error is returned. + pub fn all_of(self, key: &str) -> Result, ApiError> { + let queries: Vec<_> = self + .0 + .filter_map(|(k, v)| { + if k.eq(key) { + Some(v.into_owned()) + } else { + None + } + }) + .collect(); + Ok(queries) + } +} diff --git a/shard_node/shard_chain/Cargo.toml b/shard_node/shard_chain/Cargo.toml index c920102d668..88000fb3cb7 100644 --- a/shard_node/shard_chain/Cargo.toml +++ b/shard_node/shard_chain/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "shard_chain" version = "0.1.0" -authors = ["will "] +authors = ["will Villanueva"] edition = "2018" [dependencies] From 30ee3934b297dd15267679fbbf17fa64e0673c19 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Thu, 26 Sep 2019 16:16:07 -0500 Subject: [PATCH 139/151] Include a beginning - functional simulation/harness. Need to spawn server, interate logger --- shard_node/rest_api/src/helpers.rs | 4 +-- shard_node/rest_api/src/lib.rs | 20 ++++++------ shard_node/shard_chain/src/lib.rs | 2 +- shard_node/src/main.rs | 49 ++++++++++++++++++++++-------- shard_node/src/run.rs | 49 +++++++++++++++++++++++++++--- 5 files changed, 93 insertions(+), 31 deletions(-) diff --git a/shard_node/rest_api/src/helpers.rs b/shard_node/rest_api/src/helpers.rs index 8490e547d40..961f837307a 100644 --- a/shard_node/rest_api/src/helpers.rs +++ b/shard_node/rest_api/src/helpers.rs @@ -1,8 +1,8 @@ use crate::{ApiError, ApiResult}; use beacon_chain::BeaconChainTypes; -use shard_chain::{ShardChain, ShardChainTypes}; use http::header; use hyper::{Body, Request}; +use shard_chain::{ShardChain, ShardChainTypes}; use std::sync::Arc; /// Checks the provided request to ensure that the `content-type` header. @@ -20,7 +20,7 @@ pub fn check_content_type_for_json(req: &Request) -> Result<(), ApiError> } } -pub fn get_shard_chain_from_request ( +pub fn get_shard_chain_from_request( req: &Request, ) -> Result<(Arc>), ApiError> { // Get shard chain diff --git a/shard_node/rest_api/src/lib.rs b/shard_node/rest_api/src/lib.rs index 943a084edfa..7928bc954e6 100644 --- a/shard_node/rest_api/src/lib.rs +++ b/shard_node/rest_api/src/lib.rs @@ -7,20 +7,20 @@ mod config; mod error; mod helpers; mod response_builder; -mod url_query; mod shard; +mod url_query; use beacon_chain::BeaconChainTypes; -use shard_chain::{ShardChain, ShardChainTypes}; use error::{ApiError, ApiResult}; use futures::future::IntoFuture; use hyper::rt::Future; use hyper::service::Service; use hyper::{Body, Method, Request, Response, Server}; +use shard_chain::{ShardChain, ShardChainTypes}; use slog::{info, o, warn}; use std::sync::Arc; -use url_query::UrlQuery; use tokio::runtime::TaskExecutor; +use url_query::UrlQuery; pub use config::Config as ApiConfig; @@ -100,14 +100,12 @@ pub fn start_server }; let log_clone = log.clone(); - let server = Server::bind(&bind_addr) - .serve(service) - .map_err(move |e| { - warn!( - log_clone, - "API failed to start, Unable to bind"; "address" => format!("{:?}", e) - ) - }); + let server = Server::bind(&bind_addr).serve(service).map_err(move |e| { + warn!( + log_clone, + "API failed to start, Unable to bind"; "address" => format!("{:?}", e) + ) + }); info!( log, diff --git a/shard_node/shard_chain/src/lib.rs b/shard_node/shard_chain/src/lib.rs index 0d401b191bf..485f4e3c662 100644 --- a/shard_node/shard_chain/src/lib.rs +++ b/shard_node/shard_chain/src/lib.rs @@ -7,5 +7,5 @@ pub mod shard_chain; pub use self::checkpoint::CheckPoint; pub use self::errors::{BlockProductionError, ShardChainError}; -pub use self::shard_chain::{ShardChain, ShardChainTypes}; pub use self::harness::ShardChainHarness; +pub use self::shard_chain::{ShardChain, ShardChainTypes}; diff --git a/shard_node/src/main.rs b/shard_node/src/main.rs index c1f42d5b829..0cb9d0a642f 100644 --- a/shard_node/src/main.rs +++ b/shard_node/src/main.rs @@ -1,25 +1,48 @@ mod run; extern crate clap; +use clap::{App, Arg, SubCommand}; use shard_chain::ShardChainHarness; -use clap::{Arg, App, SubCommand}; +use slog::{crit, o, Drain, Level}; fn main() { let matches = App::new("My Super Program") - .version("0.1.0") - .author("Will Villanueva") - .about("Simulates Shard Chains") - .arg(Arg::with_name("shards") - .short("s") - .long("shards") - .value_name("FILE") - .help("Sets a custom config file") - .takes_value(true)) - .get_matches(); + .version("0.1.0") + .author("Will Villanueva") + .about("Simulates Shard Chains") + .arg( + Arg::with_name("shards") + .short("s") + .long("shards") + .value_name("FILE") + .help("Sets a custom config file") + .takes_value(true), + ) + .arg( + Arg::with_name("verbosity") + .short("v") + .multiple(true) + .help("Sets the verbosity level") + .takes_value(true), + ) + .get_matches(); // Matches number of shards to run let shards = matches.value_of("shards").unwrap_or("1"); - run::run_harness(); -} + // build the initial logger + let decorator = slog_term::TermDecorator::new().build(); + let drain = slog_term::CompactFormat::new(decorator).build().fuse(); + let drain = slog_async::Async::new(drain).build(); + + let drain = match matches.occurrences_of("verbosity") { + 0 => drain.filter_level(Level::Info), + 1 => drain.filter_level(Level::Debug), + 2 => drain.filter_level(Level::Trace), + _ => drain.filter_level(Level::Trace), + }; + let mut log = slog::Logger::root(drain.fuse(), o!()); + + run::run_harness(&log); +} diff --git a/shard_node/src/run.rs b/shard_node/src/run.rs index cc675100cb7..6bc1fc10f96 100644 --- a/shard_node/src/run.rs +++ b/shard_node/src/run.rs @@ -1,12 +1,20 @@ -use shard_chain::ShardChainHarness; use lmd_ghost::ThreadSafeReducedTree; use rand::Rng; +use shard_chain::ShardChainHarness; use shard_lmd_ghost::ThreadSafeReducedTree as ShardThreadSafeReducedTree; use shard_store::{MemoryStore as ShardMemoryStore, Store as ShardStore}; use store::{MemoryStore, Store}; +use tokio::prelude::*; +use tokio::runtime::Builder; +use tokio::runtime::Runtime; +use tokio::runtime::TaskExecutor; +use tokio::timer::Interval; +use tokio_timer::clock::Clock; use types::test_utils::{SeedableRng, TestRandom, XorShiftRng}; use types::{EthSpec, MinimalEthSpec, MinimalShardSpec, Slot}; +use std::time::{Duration, Instant}; + pub const VALIDATOR_COUNT: usize = 24; pub type TestBeaconForkChoice = ThreadSafeReducedTree; @@ -25,12 +33,45 @@ fn get_harness( harness } -pub fn run_harness() -> () { +pub fn run_harness(log: &slog::Logger) -> () { + // handle tokio result or error + let runtime = Builder::new() + .name_prefix("shard-") + .clock(Clock::system()) + .build() + .map_err(|e| format!("{:?}", e)) + .unwrap(); + + let executor = runtime.executor(); + let harness = get_harness(VALIDATOR_COUNT); let num_blocks_produced = MinimalEthSpec::slots_per_epoch() * harness.beacon_spec.phase_1_fork_epoch; harness.extend_beacon_chain((num_blocks_produced + 1) as usize); - harness.extend_shard_chain(1, vec![]); - println!("{:?}", harness.shard_chain.current_state().clone()); + harness.extend_shard_chain(1, vec![]); + + let interval = Interval::new(Instant::now(), Duration::from_millis(3000)); + let mut test = 0; + executor.spawn( + interval + .for_each(move |_| { + harness.advance_shard_slot(); + println!("Shard Slot Advanced"); + if test % 2 == 0 { + harness.advance_beacon_slot(); + println!("Beacon Slot Advanced"); + harness.extend_beacon_chain(1); + println!("Beacon Slot Published"); + } + harness.extend_shard_chain(1, vec![]); + println!("Shard Slot Published"); + test = test + 1; + Ok(()) + }) + .map_err(|e| panic!("interval errored; err={:?}", e)), + ); + + // manage proper messages, etc + runtime.shutdown_on_idle().wait().unwrap(); } From 4691cd70e571d268dc0f61585eb314d01bd05f71 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Thu, 26 Sep 2019 16:48:07 -0500 Subject: [PATCH 140/151] Proper logging, API server functioning --- shard_node/src/run.rs | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/shard_node/src/run.rs b/shard_node/src/run.rs index 6bc1fc10f96..960d470c827 100644 --- a/shard_node/src/run.rs +++ b/shard_node/src/run.rs @@ -1,8 +1,10 @@ use lmd_ghost::ThreadSafeReducedTree; use rand::Rng; +use rest_api::{start_server, ApiConfig}; use shard_chain::ShardChainHarness; use shard_lmd_ghost::ThreadSafeReducedTree as ShardThreadSafeReducedTree; use shard_store::{MemoryStore as ShardMemoryStore, Store as ShardStore}; +use slog::{error, info, warn}; use store::{MemoryStore, Store}; use tokio::prelude::*; use tokio::runtime::Builder; @@ -44,34 +46,65 @@ pub fn run_harness(log: &slog::Logger) -> () { let executor = runtime.executor(); + warn!( + log, + "This is a SIMULATION and is not safe to be used in any production setting." + ); + + info!( + log, + "Initializing beacon node"; + "validator count" => format!("{:?}", VALIDATOR_COUNT), + "db_type" => "memory store", + ); + + info!( + log, + "Initializing shard node"; + "db_type" => "memory store", + "shard_node_id" => "0", + ); + let harness = get_harness(VALIDATOR_COUNT); + let fork_epoch = harness.beacon_spec.phase_1_fork_epoch; let num_blocks_produced = - MinimalEthSpec::slots_per_epoch() * harness.beacon_spec.phase_1_fork_epoch; + MinimalEthSpec::slots_per_epoch() * fork_epoch; + + info!( + log, + "Fast forwarding beacon node to phase 1 fork epoch"; + "fork_epoch" => format!("{:?}", fork_epoch), + ); harness.extend_beacon_chain((num_blocks_produced + 1) as usize); harness.extend_shard_chain(1, vec![]); let interval = Interval::new(Instant::now(), Duration::from_millis(3000)); let mut test = 0; + let mut active_logger = log.clone(); + + let shard_chain = harness.shard_chain.clone(); executor.spawn( interval .for_each(move |_| { harness.advance_shard_slot(); - println!("Shard Slot Advanced"); + info!(active_logger, "Shard Slot Advanced";); if test % 2 == 0 { harness.advance_beacon_slot(); - println!("Beacon Slot Advanced"); + info!(active_logger, "Beacon Slot Advanced";); harness.extend_beacon_chain(1); - println!("Beacon Slot Published"); + info!(active_logger, "Beacon Block Produced";); } harness.extend_shard_chain(1, vec![]); - println!("Shard Slot Published"); + info!(active_logger, "Shard Block Produced";); test = test + 1; Ok(()) }) .map_err(|e| panic!("interval errored; err={:?}", e)), ); + start_server(&ApiConfig::default(), &executor, shard_chain, &log); + // manage proper messages, etc runtime.shutdown_on_idle().wait().unwrap(); } From afa1a5b1db4ee99c69df226b3d6a55900aa23379 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Thu, 26 Sep 2019 17:34:59 -0500 Subject: [PATCH 141/151] Support current block and state endpoints for shard 0 --- shard_node/rest_api/src/lib.rs | 2 ++ shard_node/rest_api/src/shard.rs | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/shard_node/rest_api/src/lib.rs b/shard_node/rest_api/src/lib.rs index 7928bc954e6..22761704f09 100644 --- a/shard_node/rest_api/src/lib.rs +++ b/shard_node/rest_api/src/lib.rs @@ -55,6 +55,8 @@ impl Service for ApiService { let result = match (req.method(), path.as_ref()) { (&Method::GET, "/hello") => into_boxfut(shard::hello(req)), + (&Method::GET, "/shard/0/state") => into_boxfut(shard::get_state::(req)), + (&Method::GET, "/shard/0/block") => into_boxfut(shard::get_block::(req)), _ => Box::new(futures::future::err(ApiError::NotFound( "Request path and/or method not found.".to_owned(), ))), diff --git a/shard_node/rest_api/src/shard.rs b/shard_node/rest_api/src/shard.rs index fb97322045a..325b4ea0ee2 100644 --- a/shard_node/rest_api/src/shard.rs +++ b/shard_node/rest_api/src/shard.rs @@ -1,9 +1,25 @@ use crate::helpers::*; use crate::response_builder::ResponseBuilder; use crate::{ApiError, ApiResult, UrlQuery}; +use beacon_chain::BeaconChainTypes; +use shard_chain::{ShardChain, ShardChainTypes}; use hyper::{Body, Request}; /// HTTP handler to return a `BeaconState` at the genesis block. pub fn hello(req: Request) -> ApiResult { ResponseBuilder::new(&req)?.body_text("hello".to_string()) } + +pub fn get_state(req: Request) -> ApiResult { + let shard_chain = get_shard_chain_from_request::(&req)?; + let current_state = shard_chain.current_state(); + + ResponseBuilder::new(&req)?.body(¤t_state.clone()) +} + +pub fn get_block(req: Request) -> ApiResult { + let shard_chain = get_shard_chain_from_request::(&req)?; + let current_block = &shard_chain.head().shard_block; + + ResponseBuilder::new(&req)?.body(¤t_block.clone()) +} From e8ede475b77d04214be520aa233bfe67de97b5a9 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Thu, 26 Sep 2019 17:42:03 -0500 Subject: [PATCH 142/151] Remove fake crypto, cleanup --- eth2/utils/bls/Cargo.toml | 1 - shard_node/rest_api/src/lib.rs | 1 - shard_node/rest_api/src/shard.rs | 5 ----- 3 files changed, 7 deletions(-) diff --git a/eth2/utils/bls/Cargo.toml b/eth2/utils/bls/Cargo.toml index 7b9d8f8c3cb..12758946371 100644 --- a/eth2/utils/bls/Cargo.toml +++ b/eth2/utils/bls/Cargo.toml @@ -18,4 +18,3 @@ tree_hash = { path = "../tree_hash" } [features] fake_crypto = [] -default = ["fake_crypto"] diff --git a/shard_node/rest_api/src/lib.rs b/shard_node/rest_api/src/lib.rs index 22761704f09..2ef3226b981 100644 --- a/shard_node/rest_api/src/lib.rs +++ b/shard_node/rest_api/src/lib.rs @@ -54,7 +54,6 @@ impl Service for ApiService { let path = req.uri().path().to_string(); let result = match (req.method(), path.as_ref()) { - (&Method::GET, "/hello") => into_boxfut(shard::hello(req)), (&Method::GET, "/shard/0/state") => into_boxfut(shard::get_state::(req)), (&Method::GET, "/shard/0/block") => into_boxfut(shard::get_block::(req)), _ => Box::new(futures::future::err(ApiError::NotFound( diff --git a/shard_node/rest_api/src/shard.rs b/shard_node/rest_api/src/shard.rs index 325b4ea0ee2..170a8a9dc0d 100644 --- a/shard_node/rest_api/src/shard.rs +++ b/shard_node/rest_api/src/shard.rs @@ -5,11 +5,6 @@ use beacon_chain::BeaconChainTypes; use shard_chain::{ShardChain, ShardChainTypes}; use hyper::{Body, Request}; -/// HTTP handler to return a `BeaconState` at the genesis block. -pub fn hello(req: Request) -> ApiResult { - ResponseBuilder::new(&req)?.body_text("hello".to_string()) -} - pub fn get_state(req: Request) -> ApiResult { let shard_chain = get_shard_chain_from_request::(&req)?; let current_state = shard_chain.current_state(); From 20d7070958b340fd5caa34e16a83c73f751c45f6 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Thu, 26 Sep 2019 18:10:11 -0500 Subject: [PATCH 143/151] Body data for op pool --- eth2/shard_operation_pool/src/lib.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/eth2/shard_operation_pool/src/lib.rs b/eth2/shard_operation_pool/src/lib.rs index 1dc046405ba..df4f6cdce02 100644 --- a/eth2/shard_operation_pool/src/lib.rs +++ b/eth2/shard_operation_pool/src/lib.rs @@ -12,6 +12,7 @@ use types::{ #[derive(Default, Debug)] pub struct OperationPool { attestations: RwLock>>, + body: RwLock>, _phantom: PhantomData, } @@ -104,6 +105,20 @@ impl OperationPool { .map_or(false, |att| finalized_state.slot <= att.data.target_slot) }); } + + // This is temporary and should not be here at all - this would actually be defined within + // the validator client and its own communication with the relay network. We will put it here for now + // as it is the most simple. As the simulation advances, this should be removed + pub fn insert_body( + &self, + body: Vec, + ) -> () { + *self.body.write() = body; + } + + pub fn get_body(&self) -> Vec { + self.body.read().clone() + } } impl PartialEq for OperationPool { From d8428323414080f1e425825e4e56d3ef107c64d8 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Thu, 26 Sep 2019 18:18:46 -0500 Subject: [PATCH 144/151] Boy data for each block is submitted via op-pool for now. --- eth2/shard_operation_pool/src/lib.rs | 5 ++++- shard_node/shard_chain/src/harness.rs | 7 +++---- shard_node/shard_chain/src/harness_tests.rs | 6 +++--- shard_node/shard_chain/src/shard_chain.rs | 14 ++++++++++---- shard_node/src/run.rs | 4 ++-- 5 files changed, 22 insertions(+), 14 deletions(-) diff --git a/eth2/shard_operation_pool/src/lib.rs b/eth2/shard_operation_pool/src/lib.rs index df4f6cdce02..0127bdfdd27 100644 --- a/eth2/shard_operation_pool/src/lib.rs +++ b/eth2/shard_operation_pool/src/lib.rs @@ -117,7 +117,10 @@ impl OperationPool { } pub fn get_body(&self) -> Vec { - self.body.read().clone() + let body = self.body.read().clone(); + // quite hacky to reset it - but this does not belong here in the first place + *self.body.write() = vec![]; + body } } diff --git a/shard_node/shard_chain/src/harness.rs b/shard_node/shard_chain/src/harness.rs index 1cffc409185..962c7a918ff 100644 --- a/shard_node/shard_chain/src/harness.rs +++ b/shard_node/shard_chain/src/harness.rs @@ -216,7 +216,7 @@ where /// last-produced block (the head of the chain). /// /// Chain will be extended by `num_blocks` blocks. - pub fn extend_shard_chain(&self, num_blocks: usize, body: Vec) -> Hash256 { + pub fn extend_shard_chain(&self, num_blocks: usize) -> Hash256 { let mut current_slot = self.shard_chain.read_slot_clock().unwrap(); let mut state = self.get_shard_state_at_slot(current_slot - 1); let mut head_block_root = None; @@ -231,7 +231,7 @@ where self.advance_shard_slot(); } - let (block, new_state) = self.build_shard_block(state.clone(), current_slot, &body); + let (block, new_state) = self.build_shard_block(state.clone(), current_slot); let outcome = self .shard_chain @@ -337,7 +337,6 @@ where &self, mut state: ShardState, slot: ShardSlot, - body: &Vec, ) -> (ShardBlock, ShardState) { let spec = &self.shard_spec; if slot < state.slot { @@ -359,7 +358,7 @@ where let sk = &self.keypairs[proposer_index].sk; let (mut block, state) = self .shard_chain - .produce_block_on_state(state, slot, body.clone()) + .produce_block_on_state(state, slot) .expect("should produce block"); block.signature = { diff --git a/shard_node/shard_chain/src/harness_tests.rs b/shard_node/shard_chain/src/harness_tests.rs index 4338aa9a285..ea1990c139a 100644 --- a/shard_node/shard_chain/src/harness_tests.rs +++ b/shard_node/shard_chain/src/harness_tests.rs @@ -36,14 +36,14 @@ fn advance_shard_slot() { let beacon_slot = harness.beacon_chain.current_state().slot; let shard_slot = harness.shard_chain.current_state().slot; - harness.extend_shard_chain(1, vec![]); + harness.extend_shard_chain(1); for i in 0..30 { harness.advance_beacon_slot(); harness.advance_shard_slot(); harness.extend_beacon_chain(1); - harness.extend_shard_chain(1, vec![]); + harness.extend_shard_chain(1); harness.advance_shard_slot(); - harness.extend_shard_chain(1, vec![]); + harness.extend_shard_chain(1); } } diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index fe84bf8e563..efff4fc2f88 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -381,6 +381,14 @@ impl ShardChain { ); } + /// Accept a new body + /// + /// This is a temporary solution until relay markets are situated and we have a way + /// for the body to be properly given to the node + pub fn process_body(&self, body: Vec) -> () { + self.op_pool.insert_body(body); + } + /// Accept some block and attempt to add it to block DAG. /// /// Will accept blocks from prior slots, however it will reject any block from a future slot. @@ -482,14 +490,13 @@ impl ShardChain { /// Block signing is out of the scope of this function and should be done by a separate program. pub fn produce_block( &self, - body: Vec, ) -> Result<(ShardBlock, ShardState), BlockProductionError> { let state = self.state.read().clone(); let slot = self .read_slot_clock() .ok_or_else(|| BlockProductionError::UnableToReadSlot)?; - self.produce_block_on_state(state, slot, body) + self.produce_block_on_state(state, slot) } /// Produce a block for some `slot` upon the given `state`. @@ -504,7 +511,6 @@ impl ShardChain { &self, mut state: ShardState, produce_at_slot: ShardSlot, - body: Vec, ) -> Result<(ShardBlock, ShardState), BlockProductionError> { // If required, transition the new state to the present slot. while state.slot < produce_at_slot { @@ -527,7 +533,7 @@ impl ShardChain { slot: state.slot, beacon_block_root, parent_root, - body, + body: self.op_pool.get_body(), state_root: Hash256::zero(), attestation: self.op_pool.get_attestation( &state, diff --git a/shard_node/src/run.rs b/shard_node/src/run.rs index 960d470c827..23cfc352054 100644 --- a/shard_node/src/run.rs +++ b/shard_node/src/run.rs @@ -77,7 +77,7 @@ pub fn run_harness(log: &slog::Logger) -> () { ); harness.extend_beacon_chain((num_blocks_produced + 1) as usize); - harness.extend_shard_chain(1, vec![]); + harness.extend_shard_chain(1); let interval = Interval::new(Instant::now(), Duration::from_millis(3000)); let mut test = 0; @@ -95,7 +95,7 @@ pub fn run_harness(log: &slog::Logger) -> () { harness.extend_beacon_chain(1); info!(active_logger, "Beacon Block Produced";); } - harness.extend_shard_chain(1, vec![]); + harness.extend_shard_chain(1); info!(active_logger, "Shard Block Produced";); test = test + 1; Ok(()) From 0d51f54d9acf735d88d35bc8f48612572ce9b293 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Fri, 27 Sep 2019 00:54:52 -0500 Subject: [PATCH 145/151] api service set - endpoints set for getting blocks, state and writing the block body --- eth2/shard_operation_pool/src/lib.rs | 5 +- shard_node/rest_api/Cargo.toml | 1 + shard_node/rest_api/src/error.rs | 6 +++ shard_node/rest_api/src/lib.rs | 35 +++++------- shard_node/rest_api/src/shard.rs | 60 +++++++++++++++++++-- shard_node/shard_chain/Cargo.toml | 1 + shard_node/shard_chain/src/harness_tests.rs | 4 ++ shard_node/src/run.rs | 3 +- 8 files changed, 82 insertions(+), 33 deletions(-) diff --git a/eth2/shard_operation_pool/src/lib.rs b/eth2/shard_operation_pool/src/lib.rs index 0127bdfdd27..b69ec93a329 100644 --- a/eth2/shard_operation_pool/src/lib.rs +++ b/eth2/shard_operation_pool/src/lib.rs @@ -109,10 +109,7 @@ impl OperationPool { // This is temporary and should not be here at all - this would actually be defined within // the validator client and its own communication with the relay network. We will put it here for now // as it is the most simple. As the simulation advances, this should be removed - pub fn insert_body( - &self, - body: Vec, - ) -> () { + pub fn insert_body(&self, body: Vec) -> () { *self.body.write() = body; } diff --git a/shard_node/rest_api/Cargo.toml b/shard_node/rest_api/Cargo.toml index 63843de282b..3d0060f1ac3 100644 --- a/shard_node/rest_api/Cargo.toml +++ b/shard_node/rest_api/Cargo.toml @@ -17,6 +17,7 @@ slog-term = "^2.4.0" slog-async = "^2.3.0" eth2_ssz = { path = "../../eth2/utils/ssz" } eth2_ssz_derive = { path = "../../eth2/utils/ssz_derive" } +hex = "^0.4.0" types = { path = "../../eth2/types" } http = "^0.1.17" hyper = "0.12.34" diff --git a/shard_node/rest_api/src/error.rs b/shard_node/rest_api/src/error.rs index 47f6b12dedf..a271360bda4 100644 --- a/shard_node/rest_api/src/error.rs +++ b/shard_node/rest_api/src/error.rs @@ -58,6 +58,12 @@ impl From for ApiError { } } +impl From for ApiError { + fn from(e: hex::FromHexError) -> ApiError { + ApiError::BadRequest(format!("Could not decode hex: {:?}", e)) + } +} + impl From for ApiError { fn from(e: hyper::error::Error) -> ApiError { ApiError::ServerError(format!("Networking error: {:?}", e)) diff --git a/shard_node/rest_api/src/lib.rs b/shard_node/rest_api/src/lib.rs index 2ef3226b981..21e9ea182fe 100644 --- a/shard_node/rest_api/src/lib.rs +++ b/shard_node/rest_api/src/lib.rs @@ -53,28 +53,17 @@ impl Service for ApiService { let path = req.uri().path().to_string(); - let result = match (req.method(), path.as_ref()) { + // errors are not being converted at the moment - so any validation error + // will take down the server. There is a PR in progress to fix this issue: + // https://github.com/sigp/lighthouse/pull/537 + match (req.method(), path.as_ref()) { (&Method::GET, "/shard/0/state") => into_boxfut(shard::get_state::(req)), (&Method::GET, "/shard/0/block") => into_boxfut(shard::get_block::(req)), + (&Method::POST, "/shard/0/block_body") => shard::process_block_body::(req), _ => Box::new(futures::future::err(ApiError::NotFound( "Request path and/or method not found.".to_owned(), ))), - }; - - let response = match result.wait() { - // Return the `hyper::Response`. - Ok(response) => { - slog::debug!(self.log, "Request successful: {:?}", path); - response - } - // Map the `ApiError` into `hyper::Response`. - Err(e) => { - slog::debug!(self.log, "Request failure: {:?}", path); - e.into() - } - }; - - Box::new(futures::future::ok(response)) + } } } @@ -91,12 +80,12 @@ pub fn start_server // Clone our stateful objects, for use in service closure. let server_log = log.clone(); - let server_bc = shard_chain.clone(); + let server_sc = shard_chain.clone(); let service = move || -> futures::future::FutureResult, String> { futures::future::ok(ApiService { log: server_log.clone(), - shard_chain: server_bc.clone(), + shard_chain: server_sc.clone(), }) }; @@ -109,10 +98,10 @@ pub fn start_server }); info!( - log, - "REST API started"; - "address" => format!("{}", config.listen_address), - "port" => config.port, + log, + "REST API started"; + "address" => format!("{}", config.listen_address), + "port" => config.port, ); executor.spawn(server); diff --git a/shard_node/rest_api/src/shard.rs b/shard_node/rest_api/src/shard.rs index 170a8a9dc0d..8113786aeb0 100644 --- a/shard_node/rest_api/src/shard.rs +++ b/shard_node/rest_api/src/shard.rs @@ -1,20 +1,72 @@ use crate::helpers::*; use crate::response_builder::ResponseBuilder; -use crate::{ApiError, ApiResult, UrlQuery}; +use crate::{ApiError, ApiResult, BoxFut, UrlQuery}; use beacon_chain::BeaconChainTypes; +use futures::future::Future; +use futures::future::IntoFuture; +use futures::stream::Stream; +use hex; +use http::header; +use hyper::{Body, Request, Response, StatusCode}; +use serde::Deserialize; use shard_chain::{ShardChain, ShardChainTypes}; -use hyper::{Body, Request}; +use slog::{info, trace, warn}; +use ssz_derive::Encode; -pub fn get_state(req: Request) -> ApiResult { +pub fn get_state( + req: Request, +) -> ApiResult { let shard_chain = get_shard_chain_from_request::(&req)?; let current_state = shard_chain.current_state(); ResponseBuilder::new(&req)?.body(¤t_state.clone()) } -pub fn get_block(req: Request) -> ApiResult { +pub fn get_block( + req: Request, +) -> ApiResult { let shard_chain = get_shard_chain_from_request::(&req)?; let current_block = &shard_chain.head().shard_block; ResponseBuilder::new(&req)?.body(¤t_block.clone()) } + +#[derive(Deserialize, Debug)] +struct BlockBodyRequest { + block_body: String, +} + +pub fn process_block_body( + req: Request, +) -> BoxFut { + let log = get_logger_from_request(&req); + info!( + log, + "A block body has been submitted, adding it to current pool." + ); + + let _ = try_future!(check_content_type_for_json(&req)); + let shard_chain = try_future!(get_shard_chain_from_request::(&req)); + let response_builder = ResponseBuilder::new(&req); + let body = req.into_body(); + + Box::new( + body.concat2() + .map_err(|e| ApiError::ServerError(format!("Unable to get request body: {:?}", e))) + .map(|chunk| chunk.iter().cloned().collect::>()) + .and_then(move |chunks| { + serde_json::from_slice(&chunks.as_slice()).map_err(|e| { + ApiError::BadRequest(format!( + "Unable to deserialize JSON into a BeaconBlock: {:?}", + e + )) + }) + }) + .and_then(move |block_body_request: BlockBodyRequest| { + let body = hex::decode(block_body_request.block_body)?; + shard_chain.process_body(body); + Ok(()) + }) + .and_then(|_| response_builder?.body_text("success".to_string())), + ) +} diff --git a/shard_node/shard_chain/Cargo.toml b/shard_node/shard_chain/Cargo.toml index 88000fb3cb7..8356459699c 100644 --- a/shard_node/shard_chain/Cargo.toml +++ b/shard_node/shard_chain/Cargo.toml @@ -13,6 +13,7 @@ shard_store = { path = "../shard_store" } failure = "0.1" failure_derive = "0.1" hashing = { path = "../../eth2/utils/hashing" } +hex = "^0.4.0" parking_lot = "0.7" prometheus = "^0.6" log = "0.4" diff --git a/shard_node/shard_chain/src/harness_tests.rs b/shard_node/shard_chain/src/harness_tests.rs index ea1990c139a..0e0c6219ff0 100644 --- a/shard_node/shard_chain/src/harness_tests.rs +++ b/shard_node/shard_chain/src/harness_tests.rs @@ -1,4 +1,5 @@ use crate::harness::{CommonBeaconTypes, ShardChainHarness}; +use hex; use lmd_ghost::ThreadSafeReducedTree; use rand::Rng; use shard_lmd_ghost::ThreadSafeReducedTree as ShardThreadSafeReducedTree; @@ -36,6 +37,9 @@ fn advance_shard_slot() { let beacon_slot = harness.beacon_chain.current_state().slot; let shard_slot = harness.shard_chain.current_state().slot; + harness + .shard_chain + .process_body(hex::decode("48656c6c6f20776f726c6421").unwrap()); harness.extend_shard_chain(1); for i in 0..30 { diff --git a/shard_node/src/run.rs b/shard_node/src/run.rs index 23cfc352054..ea12ab749cc 100644 --- a/shard_node/src/run.rs +++ b/shard_node/src/run.rs @@ -67,8 +67,7 @@ pub fn run_harness(log: &slog::Logger) -> () { let harness = get_harness(VALIDATOR_COUNT); let fork_epoch = harness.beacon_spec.phase_1_fork_epoch; - let num_blocks_produced = - MinimalEthSpec::slots_per_epoch() * fork_epoch; + let num_blocks_produced = MinimalEthSpec::slots_per_epoch() * fork_epoch; info!( log, From 78c555dc2243f0792313c888e19556e0b60e22f7 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Fri, 27 Sep 2019 02:43:04 -0500 Subject: [PATCH 146/151] Refactored to use client modely --- Cargo.toml | 1 + shard_node/cargo.toml | 1 + shard_node/shard_client/Cargo.toml | 27 +++++ shard_node/shard_client/src/lib.rs | 184 +++++++++++++++++++++++++++++ shard_node/src/main.rs | 2 +- shard_node/src/run.rs | 91 +------------- 6 files changed, 216 insertions(+), 90 deletions(-) create mode 100644 shard_node/shard_client/Cargo.toml create mode 100644 shard_node/shard_client/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 0df7bc42da2..850cd69733b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ members = [ "shard_node", "shard_node/shard_store", "shard_node/shard_chain", + "shard_node/shard_client", "shard_node/rest_api", "tests/ef_tests", "protos", diff --git a/shard_node/cargo.toml b/shard_node/cargo.toml index f29521c65a0..ad2bc3d5ca6 100644 --- a/shard_node/cargo.toml +++ b/shard_node/cargo.toml @@ -6,6 +6,7 @@ edition = "2018" [dependencies] shard_chain = { path = "./shard_chain" } +shard_client = { path = "./shard_client" } types = { path = "../eth2/types" } toml = "^0.5" store = { path = "../beacon_node/store" } diff --git a/shard_node/shard_client/Cargo.toml b/shard_node/shard_client/Cargo.toml new file mode 100644 index 00000000000..6189362be69 --- /dev/null +++ b/shard_node/shard_client/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "shard_client" +version = "0.1.0" +authors = ["Will Villanueva"] +edition = "2018" + +[dependencies] +shard_chain = { path = "../shard_chain" } +types = { path = "../../eth2/types" } +toml = "^0.5" +store = { path = "../../beacon_node/store" } +shard_store = { path = "../shard_store" } +rest_api = { path = "../rest_api" } +serde = "1.0" +shard_operation_pool = { path = "../../eth2/shard_operation_pool" } +slog = { version = "^2.2.3" , features = ["max_level_trace"] } +slog-term = "^2.4.0" +slog-async = "^2.3.0" +tokio = "0.1.15" +tokio-timer = "0.2.10" +futures = "0.1.25" +exit-future = "0.1.3" +shard_state_processing = { path = "../../eth2/shard_state_processing" } +env_logger = "0.6.1" +shard_lmd_ghost = { path = "../../eth2/shard_lmd_ghost" } +lmd_ghost = { path = "../../eth2/lmd_ghost" } +rand = "0.5.5" diff --git a/shard_node/shard_client/src/lib.rs b/shard_node/shard_client/src/lib.rs new file mode 100644 index 00000000000..2af77a5b6fb --- /dev/null +++ b/shard_node/shard_client/src/lib.rs @@ -0,0 +1,184 @@ +use lmd_ghost::ThreadSafeReducedTree; +use rand::Rng; +use rest_api::{start_server, ApiConfig}; +use shard_chain::ShardChainHarness; +use shard_lmd_ghost::ThreadSafeReducedTree as ShardThreadSafeReducedTree; +use shard_store::{MemoryStore as ShardMemoryStore, Store as ShardStore}; +use slog::{error, info, o, warn}; +use store::{MemoryStore, Store}; +use tokio::prelude::*; +use tokio::runtime::Builder; +use tokio::runtime::Runtime; +use tokio::runtime::TaskExecutor; +use tokio::timer::Interval; +use tokio_timer::clock::Clock; +use types::test_utils::{SeedableRng, TestRandom, XorShiftRng}; +use types::{EthSpec, MinimalEthSpec, MinimalShardSpec, Slot}; + +use std::time::{Duration, Instant}; + +pub const VALIDATOR_COUNT: usize = 24; + +pub type TestBeaconForkChoice = ThreadSafeReducedTree; +pub type TestShardForkChoice = ShardThreadSafeReducedTree; + +pub fn run_shard_chain(log: &slog::Logger, executor: &TaskExecutor) -> () { + info!( + log, + "Initializing beacon node"; + "validator count" => format!("{:?}", VALIDATOR_COUNT), + "db_type" => "memory store", + ); + + info!( + log, + "Initializing shard node"; + "db_type" => "memory store", + "shard_node_id" => "0", + ); + + let harness = get_harness(VALIDATOR_COUNT); + let fork_epoch = harness.beacon_spec.phase_1_fork_epoch; + let num_blocks_produced = MinimalEthSpec::slots_per_epoch() * fork_epoch; + + info!( + log, + "Fast forwarding beacon node to phase 1 fork epoch"; + "fork_epoch" => format!("{:?}", fork_epoch), + ); + + harness.extend_beacon_chain((num_blocks_produced + 1) as usize); + + info!( + log, + "Beacon chain successfully progressed to phase 1 fork epoch"; + ); + + extend_shard_chain(log, &harness); + + let interval = Interval::new(Instant::now(), Duration::from_millis(3000)); + let shard_chain = harness.shard_chain.clone(); + let harness_logger = log.clone(); + let mut round = 0; + + executor.spawn( + interval + .for_each(move |_| { + advance_shard_slot(&harness_logger, &harness); + if round % 2 == 0 { + advance_beacon_slot(&harness_logger, &harness); + extend_beacon_chain(&harness_logger, &harness); + } + extend_shard_chain(&harness_logger, &harness); + round = round + 1; + Ok(()) + }) + .map_err(|e| panic!("interval errored; err={:?}", e)), + ); + + start_server(&ApiConfig::default(), &executor, shard_chain, &log); +} + +fn get_harness( + validator_count: usize, +) -> ShardChainHarness +{ + let harness = ShardChainHarness::new(validator_count); + + // Move past the zero slot + harness.advance_beacon_slot(); + harness.advance_shard_slot(); + + harness +} + +fn advance_shard_slot( + log: &slog::Logger, + harness: &ShardChainHarness< + TestBeaconForkChoice, + MinimalEthSpec, + TestShardForkChoice, + MinimalShardSpec, + >, +) -> () { + harness.advance_shard_slot(); + info!( + log, + "Shard slot advanced"; + "slot" => format!("{:?}", harness.shard_chain.present_slot()) + ); +} + +fn advance_beacon_slot( + log: &slog::Logger, + harness: &ShardChainHarness< + TestBeaconForkChoice, + MinimalEthSpec, + TestShardForkChoice, + MinimalShardSpec, + >, +) -> () { + harness.advance_beacon_slot(); + let present_slot = harness.beacon_chain.present_slot(); + info!( + log, + "Beacon slot advanced"; + "slot" => format!("{:?}", present_slot) + ); + + if present_slot % MinimalEthSpec::slots_per_epoch() == 0 { + info!( + log, + "Epoch Boundary"; + "Epoch" => format!("{:?}", present_slot.epoch(MinimalEthSpec::slots_per_epoch())) + ) + } +} + +fn extend_shard_chain( + log: &slog::Logger, + harness: &ShardChainHarness< + TestBeaconForkChoice, + MinimalEthSpec, + TestShardForkChoice, + MinimalShardSpec, + >, +) -> () { + harness.extend_shard_chain(1); + + if let Some(genesis_height) = harness.shard_chain.slots_since_genesis() { + info!( + log, + "Shard Block Published"; + "best_slot" => harness.shard_chain.head().shard_block.slot, + "latest_block_root" => format!("{}", harness.shard_chain.head().shard_block_root), + "wall_clock_slot" => harness.shard_chain.read_slot_clock().unwrap(), + "state_slot" => harness.shard_chain.head().shard_state.slot, + "slots_since_genesis" => genesis_height, + ); + } +} + +fn extend_beacon_chain( + log: &slog::Logger, + harness: &ShardChainHarness< + TestBeaconForkChoice, + MinimalEthSpec, + TestShardForkChoice, + MinimalShardSpec, + >, +) -> () { + harness.extend_beacon_chain(1); + + if let Some(genesis_height) = harness.beacon_chain.slots_since_genesis() { + info!( + log, + "Beacon Block Published"; + "best_slot" => harness.beacon_chain.head().beacon_block.slot, + "latest_block_root" => format!("{}", harness.beacon_chain.head().beacon_block_root), + "wall_clock_slot" => harness.beacon_chain.read_slot_clock().unwrap(), + "state_slot" => harness.beacon_chain.head().beacon_state.slot, + "slots_since_genesis" => genesis_height, + ); + } +} diff --git a/shard_node/src/main.rs b/shard_node/src/main.rs index 0cb9d0a642f..8382f107d86 100644 --- a/shard_node/src/main.rs +++ b/shard_node/src/main.rs @@ -44,5 +44,5 @@ fn main() { let mut log = slog::Logger::root(drain.fuse(), o!()); - run::run_harness(&log); + run::run_simulation(&log); } diff --git a/shard_node/src/run.rs b/shard_node/src/run.rs index ea12ab749cc..7e2efa212a0 100644 --- a/shard_node/src/run.rs +++ b/shard_node/src/run.rs @@ -1,41 +1,11 @@ -use lmd_ghost::ThreadSafeReducedTree; -use rand::Rng; -use rest_api::{start_server, ApiConfig}; -use shard_chain::ShardChainHarness; -use shard_lmd_ghost::ThreadSafeReducedTree as ShardThreadSafeReducedTree; -use shard_store::{MemoryStore as ShardMemoryStore, Store as ShardStore}; use slog::{error, info, warn}; -use store::{MemoryStore, Store}; use tokio::prelude::*; use tokio::runtime::Builder; use tokio::runtime::Runtime; use tokio::runtime::TaskExecutor; -use tokio::timer::Interval; use tokio_timer::clock::Clock; -use types::test_utils::{SeedableRng, TestRandom, XorShiftRng}; -use types::{EthSpec, MinimalEthSpec, MinimalShardSpec, Slot}; -use std::time::{Duration, Instant}; - -pub const VALIDATOR_COUNT: usize = 24; - -pub type TestBeaconForkChoice = ThreadSafeReducedTree; -pub type TestShardForkChoice = ShardThreadSafeReducedTree; - -fn get_harness( - validator_count: usize, -) -> ShardChainHarness -{ - let harness = ShardChainHarness::new(validator_count); - - // Move past the zero slot - harness.advance_beacon_slot(); - harness.advance_shard_slot(); - - harness -} - -pub fn run_harness(log: &slog::Logger) -> () { +pub fn run_simulation(log: &slog::Logger) -> () { // handle tokio result or error let runtime = Builder::new() .name_prefix("shard-") @@ -46,64 +16,7 @@ pub fn run_harness(log: &slog::Logger) -> () { let executor = runtime.executor(); - warn!( - log, - "This is a SIMULATION and is not safe to be used in any production setting." - ); - - info!( - log, - "Initializing beacon node"; - "validator count" => format!("{:?}", VALIDATOR_COUNT), - "db_type" => "memory store", - ); - - info!( - log, - "Initializing shard node"; - "db_type" => "memory store", - "shard_node_id" => "0", - ); - - let harness = get_harness(VALIDATOR_COUNT); - let fork_epoch = harness.beacon_spec.phase_1_fork_epoch; - let num_blocks_produced = MinimalEthSpec::slots_per_epoch() * fork_epoch; - - info!( - log, - "Fast forwarding beacon node to phase 1 fork epoch"; - "fork_epoch" => format!("{:?}", fork_epoch), - ); - - harness.extend_beacon_chain((num_blocks_produced + 1) as usize); - harness.extend_shard_chain(1); - - let interval = Interval::new(Instant::now(), Duration::from_millis(3000)); - let mut test = 0; - let mut active_logger = log.clone(); - - let shard_chain = harness.shard_chain.clone(); - executor.spawn( - interval - .for_each(move |_| { - harness.advance_shard_slot(); - info!(active_logger, "Shard Slot Advanced";); - if test % 2 == 0 { - harness.advance_beacon_slot(); - info!(active_logger, "Beacon Slot Advanced";); - harness.extend_beacon_chain(1); - info!(active_logger, "Beacon Block Produced";); - } - harness.extend_shard_chain(1); - info!(active_logger, "Shard Block Produced";); - test = test + 1; - Ok(()) - }) - .map_err(|e| panic!("interval errored; err={:?}", e)), - ); - - start_server(&ApiConfig::default(), &executor, shard_chain, &log); + shard_client::run_shard_chain(log, &executor); - // manage proper messages, etc runtime.shutdown_on_idle().wait().unwrap(); } From bf19ef9d4fcac2f3b19329d22a29ff6c62064a2f Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Fri, 27 Sep 2019 02:43:34 -0500 Subject: [PATCH 147/151] Included logging on rest api endpoints --- shard_node/rest_api/src/shard.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/shard_node/rest_api/src/shard.rs b/shard_node/rest_api/src/shard.rs index 8113786aeb0..6cf2228c634 100644 --- a/shard_node/rest_api/src/shard.rs +++ b/shard_node/rest_api/src/shard.rs @@ -16,6 +16,9 @@ use ssz_derive::Encode; pub fn get_state( req: Request, ) -> ApiResult { + let log = get_logger_from_request(&req); + info!(log, "Latest state requested"); + let shard_chain = get_shard_chain_from_request::(&req)?; let current_state = shard_chain.current_state(); @@ -25,6 +28,9 @@ pub fn get_state( pub fn get_block( req: Request, ) -> ApiResult { + let log = get_logger_from_request(&req); + info!(log, "Latest block requested"); + let shard_chain = get_shard_chain_from_request::(&req)?; let current_block = &shard_chain.head().shard_block; From cd91b02406f67411f0bcea3649e60390480dae21 Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Fri, 27 Sep 2019 03:20:58 -0500 Subject: [PATCH 148/151] Clean up unused imports --- eth2/shard_operation_pool/src/attestation_id.rs | 4 +--- eth2/shard_operation_pool/src/lib.rs | 7 ++----- .../src/per_shard_slot_processing.rs | 1 - eth2/types/src/period.rs | 1 - eth2/types/src/shard_state.rs | 3 --- shard_node/rest_api/src/helpers.rs | 2 +- shard_node/rest_api/src/lib.rs | 3 +-- shard_node/rest_api/src/shard.rs | 11 ++++------- shard_node/shard_chain/src/fork_choice.rs | 10 +++++----- shard_node/shard_chain/src/harness.rs | 4 ++-- shard_node/shard_chain/src/harness_tests.rs | 11 ++++------- shard_node/shard_chain/src/shard_chain.rs | 7 +------ shard_node/shard_client/src/lib.rs | 13 ++++--------- shard_node/shard_store/src/iter.rs | 2 +- shard_node/src/main.rs | 9 ++++----- shard_node/src/run.rs | 3 --- 16 files changed, 30 insertions(+), 61 deletions(-) diff --git a/eth2/shard_operation_pool/src/attestation_id.rs b/eth2/shard_operation_pool/src/attestation_id.rs index 0cb84d4d353..569118f086b 100644 --- a/eth2/shard_operation_pool/src/attestation_id.rs +++ b/eth2/shard_operation_pool/src/attestation_id.rs @@ -1,9 +1,7 @@ use int_to_bytes::int_to_bytes8; use ssz::ssz_encode; use ssz_derive::{Decode, Encode}; -use types::{ - BeaconState, ChainSpec, Domain, Epoch, EthSpec, ShardAttestationData, ShardSlot, ShardState, -}; +use types::{BeaconState, ChainSpec, Domain, Epoch, EthSpec, ShardAttestationData, ShardSlot}; /// Serialized `AttestationData` augmented with a domain to encode the fork info. #[derive(PartialEq, Eq, Clone, Hash, Debug, PartialOrd, Ord, Encode, Decode)] diff --git a/eth2/shard_operation_pool/src/lib.rs b/eth2/shard_operation_pool/src/lib.rs index b69ec93a329..071efce4eba 100644 --- a/eth2/shard_operation_pool/src/lib.rs +++ b/eth2/shard_operation_pool/src/lib.rs @@ -1,13 +1,10 @@ mod attestation_id; use attestation_id::AttestationId; -use itertools::Itertools; use parking_lot::RwLock; -use std::collections::{btree_map::Entry, hash_map, BTreeMap, HashMap, HashSet}; +use std::collections::{hash_map, HashMap}; use std::marker::PhantomData; -use types::{ - BeaconState, ChainSpec, EthSpec, ShardAttestation, ShardSlot, ShardSpec, ShardState, Validator, -}; +use types::{BeaconState, ChainSpec, EthSpec, ShardAttestation, ShardSlot, ShardSpec, ShardState}; #[derive(Default, Debug)] pub struct OperationPool { diff --git a/eth2/shard_state_processing/src/per_shard_slot_processing.rs b/eth2/shard_state_processing/src/per_shard_slot_processing.rs index 42466c1c4c7..a7c29f98f4b 100644 --- a/eth2/shard_state_processing/src/per_shard_slot_processing.rs +++ b/eth2/shard_state_processing/src/per_shard_slot_processing.rs @@ -1,5 +1,4 @@ use crate::*; -use tree_hash::TreeHash; use types::*; use process_shard_slot::process_shard_slot; diff --git a/eth2/types/src/period.rs b/eth2/types/src/period.rs index e2bfcf1916b..7c3c3cf1a73 100644 --- a/eth2/types/src/period.rs +++ b/eth2/types/src/period.rs @@ -6,7 +6,6 @@ use ssz::{ssz_encode, Decode, DecodeError, Encode}; use std::cmp::{Ord, Ordering}; use std::fmt; use std::hash::{Hash, Hasher}; -use std::iter::Iterator; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign}; #[derive(Eq, Debug, Clone, Copy, Default, Serialize, Deserialize)] diff --git a/eth2/types/src/shard_state.rs b/eth2/types/src/shard_state.rs index 94ec7fa60b3..117c6ef202b 100644 --- a/eth2/types/src/shard_state.rs +++ b/eth2/types/src/shard_state.rs @@ -3,11 +3,8 @@ use crate::*; use cached_tree_hash::{Error as TreeHashCacheError, TreeHashCache}; use compare_fields_derive::CompareFields; use fixed_len_vec::FixedLenVec; -use hashing::hash; use serde_derive::{Deserialize, Serialize}; -use ssz::ssz_encode; use ssz_derive::{Decode, Encode}; -use std::marker::PhantomData; use test_random_derive::TestRandom; use tree_hash::TreeHash; use tree_hash_derive::{CachedTreeHash, TreeHash}; diff --git a/shard_node/rest_api/src/helpers.rs b/shard_node/rest_api/src/helpers.rs index 961f837307a..bbb26616177 100644 --- a/shard_node/rest_api/src/helpers.rs +++ b/shard_node/rest_api/src/helpers.rs @@ -1,4 +1,4 @@ -use crate::{ApiError, ApiResult}; +use crate::ApiError; use beacon_chain::BeaconChainTypes; use http::header; use hyper::{Body, Request}; diff --git a/shard_node/rest_api/src/lib.rs b/shard_node/rest_api/src/lib.rs index 21e9ea182fe..041c513caaa 100644 --- a/shard_node/rest_api/src/lib.rs +++ b/shard_node/rest_api/src/lib.rs @@ -1,6 +1,6 @@ #[macro_use] mod macros; -#[macro_use] + extern crate lazy_static; mod config; @@ -20,7 +20,6 @@ use shard_chain::{ShardChain, ShardChainTypes}; use slog::{info, o, warn}; use std::sync::Arc; use tokio::runtime::TaskExecutor; -use url_query::UrlQuery; pub use config::Config as ApiConfig; diff --git a/shard_node/rest_api/src/shard.rs b/shard_node/rest_api/src/shard.rs index 6cf2228c634..6d16a2b133c 100644 --- a/shard_node/rest_api/src/shard.rs +++ b/shard_node/rest_api/src/shard.rs @@ -1,17 +1,14 @@ use crate::helpers::*; use crate::response_builder::ResponseBuilder; -use crate::{ApiError, ApiResult, BoxFut, UrlQuery}; +use crate::{ApiError, ApiResult, BoxFut}; use beacon_chain::BeaconChainTypes; use futures::future::Future; -use futures::future::IntoFuture; use futures::stream::Stream; use hex; -use http::header; -use hyper::{Body, Request, Response, StatusCode}; +use hyper::{Body, Request}; use serde::Deserialize; -use shard_chain::{ShardChain, ShardChainTypes}; -use slog::{info, trace, warn}; -use ssz_derive::Encode; +use shard_chain::ShardChainTypes; +use slog::info; pub fn get_state( req: Request, diff --git a/shard_node/shard_chain/src/fork_choice.rs b/shard_node/shard_chain/src/fork_choice.rs index 941013b35e6..3bf0965ce76 100644 --- a/shard_node/shard_chain/src/fork_choice.rs +++ b/shard_node/shard_chain/src/fork_choice.rs @@ -1,13 +1,13 @@ -use crate::{ShardChain, ShardChainError, ShardChainTypes}; +use crate::{ShardChain, ShardChainTypes}; use beacon_chain::BeaconChainTypes; use shard_lmd_ghost::LmdGhost; -use shard_store::{Error as StoreError, Store}; +use shard_store::Error as StoreError; use state_processing::common::get_shard_attesting_indices_unsorted; use std::sync::Arc; -use store::{Error as BeaconStoreError, Store as BeaconStore}; +use store::Error as BeaconStoreError; use types::{ - BeaconBlock, BeaconState, BeaconStateError, Epoch, EthSpec, Hash256, ShardAttestation, - ShardBlock, ShardSlot, ShardSpec, ShardState, ShardStateError, + BeaconState, BeaconStateError, EthSpec, Hash256, ShardAttestation, ShardBlock, ShardSlot, + ShardStateError, }; type Result = std::result::Result; diff --git a/shard_node/shard_chain/src/harness.rs b/shard_node/shard_chain/src/harness.rs index 962c7a918ff..20c62daba8f 100644 --- a/shard_node/shard_chain/src/harness.rs +++ b/shard_node/shard_chain/src/harness.rs @@ -5,12 +5,12 @@ use crate::shard_chain::{ use beacon_chain::{BeaconChain, BeaconChainTypes, BlockProcessingOutcome}; use lmd_ghost::LmdGhost; use shard_lmd_ghost::LmdGhost as ShardLmdGhost; -use shard_state_processing::{per_shard_block_processing, per_shard_slot_processing}; +use shard_state_processing::per_shard_slot_processing; use shard_store::MemoryStore as ShardMemoryStore; use shard_store::Store as ShardStore; use slot_clock::{ShardSlotClock, SlotClock}; use slot_clock::{ShardTestingSlotClock, TestingSlotClock}; -use state_processing::{per_block_processing, per_slot_processing}; +use state_processing::per_slot_processing; use std::marker::PhantomData; use std::sync::Arc; use store::MemoryStore; diff --git a/shard_node/shard_chain/src/harness_tests.rs b/shard_node/shard_chain/src/harness_tests.rs index 0e0c6219ff0..3e449f44497 100644 --- a/shard_node/shard_chain/src/harness_tests.rs +++ b/shard_node/shard_chain/src/harness_tests.rs @@ -1,12 +1,9 @@ -use crate::harness::{CommonBeaconTypes, ShardChainHarness}; -use hex; +use crate::harness::ShardChainHarness; use lmd_ghost::ThreadSafeReducedTree; -use rand::Rng; use shard_lmd_ghost::ThreadSafeReducedTree as ShardThreadSafeReducedTree; -use shard_store::{MemoryStore as ShardMemoryStore, Store as ShardStore}; -use store::{MemoryStore, Store}; -use types::test_utils::{SeedableRng, TestRandom, XorShiftRng}; -use types::{Deposit, EthSpec, Hash256, MinimalEthSpec, MinimalShardSpec, Slot}; +use shard_store::MemoryStore as ShardMemoryStore; +use store::MemoryStore; +use types::{MinimalEthSpec, MinimalShardSpec}; pub const VALIDATOR_COUNT: usize = 24; diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index efff4fc2f88..72814b66d55 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -7,16 +7,13 @@ use shard_lmd_ghost::LmdGhost; use shard_operation_pool::OperationPool; use shard_state_processing::{ per_shard_block_processing, per_shard_slot_processing, ShardBlockProcessingError, - ShardSlotProcessingError, }; use shard_store::iter::{ BestBlockRootsIterator, BlockIterator, BlockRootsIterator, StateRootsIterator, }; use shard_store::{Error as DBError, Store}; -use slot_clock::{ShardSlotClock, SlotClock}; +use slot_clock::ShardSlotClock; use std::sync::Arc; -use store::{Error as BeaconDBError, Store as BeaconStore}; -use tree_hash::TreeHash; use types::*; #[derive(Debug, PartialEq)] @@ -277,8 +274,6 @@ impl ShardChain { /// call to `read_slot_clock` results in a higher slot than a call to `present_slot`, /// `self.state` should undergo per slot processing. pub fn read_slot_clock(&self) -> Option { - let spec = &self.spec; - match self.slot_clock.present_slot() { Ok(Some(some_slot)) => Some(some_slot), Ok(None) => None, diff --git a/shard_node/shard_client/src/lib.rs b/shard_node/shard_client/src/lib.rs index 2af77a5b6fb..3396878663d 100644 --- a/shard_node/shard_client/src/lib.rs +++ b/shard_node/shard_client/src/lib.rs @@ -1,19 +1,14 @@ use lmd_ghost::ThreadSafeReducedTree; -use rand::Rng; use rest_api::{start_server, ApiConfig}; use shard_chain::ShardChainHarness; use shard_lmd_ghost::ThreadSafeReducedTree as ShardThreadSafeReducedTree; -use shard_store::{MemoryStore as ShardMemoryStore, Store as ShardStore}; -use slog::{error, info, o, warn}; -use store::{MemoryStore, Store}; +use shard_store::MemoryStore as ShardMemoryStore; +use slog::info; +use store::MemoryStore; use tokio::prelude::*; -use tokio::runtime::Builder; -use tokio::runtime::Runtime; use tokio::runtime::TaskExecutor; use tokio::timer::Interval; -use tokio_timer::clock::Clock; -use types::test_utils::{SeedableRng, TestRandom, XorShiftRng}; -use types::{EthSpec, MinimalEthSpec, MinimalShardSpec, Slot}; +use types::{EthSpec, MinimalEthSpec, MinimalShardSpec}; use std::time::{Duration, Instant}; diff --git a/shard_node/shard_store/src/iter.rs b/shard_node/shard_store/src/iter.rs index 18814ded36a..87068434da8 100644 --- a/shard_node/shard_store/src/iter.rs +++ b/shard_node/shard_store/src/iter.rs @@ -1,7 +1,7 @@ use crate::Store; use std::borrow::Cow; use std::sync::Arc; -use types::{Hash256, ShardBlock, ShardSlot, ShardSpec, ShardState, ShardStateError}; +use types::{Hash256, ShardBlock, ShardSlot, ShardSpec, ShardState}; #[derive(Clone)] pub struct StateRootsIterator<'a, T: ShardSpec, U> { diff --git a/shard_node/src/main.rs b/shard_node/src/main.rs index 8382f107d86..00599b898fb 100644 --- a/shard_node/src/main.rs +++ b/shard_node/src/main.rs @@ -1,9 +1,8 @@ mod run; extern crate clap; -use clap::{App, Arg, SubCommand}; -use shard_chain::ShardChainHarness; -use slog::{crit, o, Drain, Level}; +use clap::{App, Arg}; +use slog::{o, Drain, Level}; fn main() { let matches = App::new("My Super Program") @@ -28,7 +27,7 @@ fn main() { .get_matches(); // Matches number of shards to run - let shards = matches.value_of("shards").unwrap_or("1"); + // let shards = matches.value_of("shards").unwrap_or("1"); // build the initial logger let decorator = slog_term::TermDecorator::new().build(); @@ -42,7 +41,7 @@ fn main() { _ => drain.filter_level(Level::Trace), }; - let mut log = slog::Logger::root(drain.fuse(), o!()); + let log = slog::Logger::root(drain.fuse(), o!()); run::run_simulation(&log); } diff --git a/shard_node/src/run.rs b/shard_node/src/run.rs index 7e2efa212a0..83b7e97b225 100644 --- a/shard_node/src/run.rs +++ b/shard_node/src/run.rs @@ -1,8 +1,5 @@ -use slog::{error, info, warn}; use tokio::prelude::*; use tokio::runtime::Builder; -use tokio::runtime::Runtime; -use tokio::runtime::TaskExecutor; use tokio_timer::clock::Clock; pub fn run_simulation(log: &slog::Logger) -> () { From 158f5a62658a6b3a1b401ac44a5fc220511a732a Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Fri, 27 Sep 2019 12:43:09 -0500 Subject: [PATCH 149/151] Added further logging to beacon chain and shard chain --- beacon_node/beacon_chain/Cargo.toml | 4 +++ beacon_node/beacon_chain/src/beacon_chain.rs | 29 ++++++++++++++++---- beacon_node/beacon_chain/src/test_utils.rs | 4 ++- beacon_node/client/src/beacon_chain_types.rs | 15 ++++++++-- shard_node/shard_chain/Cargo.toml | 4 +++ shard_node/shard_chain/src/harness.rs | 5 +++- shard_node/shard_chain/src/harness_tests.rs | 9 +++++- shard_node/shard_chain/src/shard_chain.rs | 24 ++++++++++++++++ shard_node/shard_client/src/lib.rs | 5 ++-- 9 files changed, 86 insertions(+), 13 deletions(-) diff --git a/beacon_node/beacon_chain/Cargo.toml b/beacon_node/beacon_chain/Cargo.toml index 793ce79cdd0..95186fde73c 100644 --- a/beacon_node/beacon_chain/Cargo.toml +++ b/beacon_node/beacon_chain/Cargo.toml @@ -20,6 +20,10 @@ serde = "1.0" serde_derive = "1.0" serde_json = "1.0" slot_clock = { path = "../../eth2/utils/slot_clock" } +slog = { version = "^2.2.3" , features = ["max_level_trace"] } +sloggers = { version = "^0.3" } +slog-term = "^2.4.0" +slog-async = "^2.3.0" eth2_ssz = { path = "../../eth2/utils/ssz" } eth2_ssz_derive = { path = "../../eth2/utils/ssz_derive" } state_processing = { path = "../../eth2/state_processing" } diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index a6d35fd9914..0ce08a4b6cc 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -4,10 +4,10 @@ use crate::fork_choice::{Error as ForkChoiceError, ForkChoice}; use crate::metrics::Metrics; use crate::persisted_beacon_chain::{PersistedBeaconChain, BEACON_CHAIN_DB_KEY}; use lmd_ghost::LmdGhost; -use log::trace; use operation_pool::DepositInsertStatus; use operation_pool::{OperationPool, PersistedOperationPool}; use parking_lot::{RwLock, RwLockReadGuard}; +use slog::{info, Logger}; use slot_clock::SlotClock; use state_processing::per_block_processing::errors::{ AttestationValidationError, AttesterSlashingValidationError, DepositValidationError, @@ -83,6 +83,8 @@ pub struct BeaconChain { pub fork_choice: ForkChoice, /// Stores metrics about this `BeaconChain`. pub metrics: Metrics, + + pub log: Logger, } impl BeaconChain { @@ -93,6 +95,7 @@ impl BeaconChain { mut genesis_state: BeaconState, genesis_block: BeaconBlock, spec: ChainSpec, + log: Logger, ) -> Result { genesis_state.build_all_caches(&spec)?; @@ -106,6 +109,12 @@ impl BeaconChain { let genesis_block_root = genesis_block.block_header().canonical_root(); store.put(&spec.zero_hash, &genesis_block)?; + info!(log, "Beacon chain initialized from genesis"; + "gensis_slot" => genesis_state.slot, + "state_root" => format!("{}", state_root), + "block_root" => format!("{}", genesis_block_root), + ); + let canonical_head = RwLock::new(CheckPoint::new( genesis_block.clone(), genesis_block_root, @@ -123,6 +132,7 @@ impl BeaconChain { fork_choice: ForkChoice::new(store.clone(), &genesis_block, genesis_block_root), metrics: Metrics::new()?, store, + log, }) } @@ -130,6 +140,7 @@ impl BeaconChain { pub fn from_store( store: Arc, spec: ChainSpec, + log: Logger, ) -> Result>, Error> { let key = Hash256::from_slice(&BEACON_CHAIN_DB_KEY.as_bytes()); let p: PersistedBeaconChain = match store.get(&key) { @@ -159,6 +170,7 @@ impl BeaconChain { genesis_block_root: p.genesis_block_root, metrics: Metrics::new()?, store, + log, })) } @@ -395,10 +407,6 @@ impl BeaconChain { &self, validator_index: usize, ) -> Result, BeaconStateError> { - trace!( - "BeaconChain::validator_attestion_slot_and_shard: validator_index: {}", - validator_index - ); if let Some(attestation_duty) = self .state .read() @@ -803,6 +811,12 @@ impl BeaconChain { new_epoch: new_finalized_epoch, }) } else { + info!(self.log, "Beracon Fork choice produces new head"; + "block_root" => format!("{}", &beacon_block_root), + "state_root" => format!("{}", &beacon_state_root), + "slot" => format!("{}", &beacon_block.slot), + ); + self.update_canonical_head(CheckPoint { beacon_block: beacon_block, beacon_block_root, @@ -873,6 +887,11 @@ impl BeaconChain { new_epoch: new_finalized_epoch, }) } else { + info!(self.log, "Beacon Finalization Detected"; + "root" => format!("{}", finalized_block_root), + "pruning fork choice from slot" => format!("{}", finalized_block.slot), + ); + self.fork_choice .process_finalization(&finalized_block, finalized_block_root)?; diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index b3cf5120a79..a3fe2d3290d 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -1,5 +1,6 @@ use crate::{BeaconChain, BeaconChainTypes, BlockProcessingOutcome}; use lmd_ghost::LmdGhost; +use slog::Logger; use slot_clock::SlotClock; use slot_clock::TestingSlotClock; use state_processing::per_slot_processing; @@ -80,7 +81,7 @@ where E: EthSpec, { /// Instantiate a new harness with `validator_count` initial validators. - pub fn new(validator_count: usize) -> Self { + pub fn new(validator_count: usize, log: Logger) -> Self { let spec = E::default_spec(); let store = Arc::new(MemoryStore::open()); @@ -105,6 +106,7 @@ where genesis_state, genesis_block, spec.clone(), + log, ) .expect("Terminate if beacon chain generation fails"); diff --git a/beacon_node/client/src/beacon_chain_types.rs b/beacon_node/client/src/beacon_chain_types.rs index c923f724c84..4e334f823b6 100644 --- a/beacon_node/client/src/beacon_chain_types.rs +++ b/beacon_node/client/src/beacon_chain_types.rs @@ -49,7 +49,9 @@ where T: BeaconChainTypes, T::LmdGhost: LmdGhost, { - if let Ok(Some(beacon_chain)) = BeaconChain::from_store(store.clone(), spec.clone()) { + if let Ok(Some(beacon_chain)) = + BeaconChain::from_store(store.clone(), spec.clone(), log.clone()) + { info!( log, "Loaded BeaconChain from store"; @@ -78,7 +80,14 @@ where // Genesis chain //TODO: Handle error correctly - BeaconChain::from_genesis(store, slot_clock, genesis_state, genesis_block, spec) - .expect("Terminate if beacon chain generation fails") + BeaconChain::from_genesis( + store, + slot_clock, + genesis_state, + genesis_block, + spec, + log.clone(), + ) + .expect("Terminate if beacon chain generation fails") } } diff --git a/shard_node/shard_chain/Cargo.toml b/shard_node/shard_chain/Cargo.toml index 8356459699c..65046ba9143 100644 --- a/shard_node/shard_chain/Cargo.toml +++ b/shard_node/shard_chain/Cargo.toml @@ -22,6 +22,10 @@ env_logger = "0.6" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" +slog = { version = "^2.2.3" , features = ["max_level_trace"] } +sloggers = { version = "^0.3" } +slog-term = "^2.4.0" +slog-async = "^2.3.0" slot_clock = { path = "../../eth2/utils/slot_clock" } eth2_ssz = { path = "../../eth2/utils/ssz" } eth2_ssz_derive = { path = "../../eth2/utils/ssz_derive" } diff --git a/shard_node/shard_chain/src/harness.rs b/shard_node/shard_chain/src/harness.rs index 20c62daba8f..082d77451ed 100644 --- a/shard_node/shard_chain/src/harness.rs +++ b/shard_node/shard_chain/src/harness.rs @@ -8,6 +8,7 @@ use shard_lmd_ghost::LmdGhost as ShardLmdGhost; use shard_state_processing::per_shard_slot_processing; use shard_store::MemoryStore as ShardMemoryStore; use shard_store::Store as ShardStore; +use slog::Logger; use slot_clock::{ShardSlotClock, SlotClock}; use slot_clock::{ShardTestingSlotClock, TestingSlotClock}; use state_processing::per_slot_processing; @@ -89,7 +90,7 @@ where U: ShardSpec, { /// Instantiate a new harness with `validator_count` initial validators. - pub fn new(validator_count: usize) -> Self { + pub fn new(validator_count: usize, log: Logger) -> Self { let beacon_spec = E::default_spec(); let shard_spec = U::default_spec(); @@ -128,6 +129,7 @@ where beacon_genesis_state, beacon_genesis_block, beacon_spec.clone(), + log.clone(), ) .expect("Terminate if beacon chain generation fails"); let beacon_chain_reference = Arc::new(beacon_chain); @@ -139,6 +141,7 @@ where shard_spec.clone(), 0, beacon_chain_reference.clone(), + log, ) .expect("Terminate if beacon chain generation fails"); let shard_chain_reference = Arc::new(shard_chain); diff --git a/shard_node/shard_chain/src/harness_tests.rs b/shard_node/shard_chain/src/harness_tests.rs index 3e449f44497..2941f8987f1 100644 --- a/shard_node/shard_chain/src/harness_tests.rs +++ b/shard_node/shard_chain/src/harness_tests.rs @@ -2,6 +2,8 @@ use crate::harness::ShardChainHarness; use lmd_ghost::ThreadSafeReducedTree; use shard_lmd_ghost::ThreadSafeReducedTree as ShardThreadSafeReducedTree; use shard_store::MemoryStore as ShardMemoryStore; +use slog::Logger; +use sloggers::{terminal::TerminalLoggerBuilder, types::Severity, Build}; use store::MemoryStore; use types::{MinimalEthSpec, MinimalShardSpec}; @@ -14,7 +16,12 @@ fn get_harness( validator_count: usize, ) -> ShardChainHarness { - let harness = ShardChainHarness::new(validator_count); + let log = TerminalLoggerBuilder::new() + .level(Severity::Warning) + .build() + .expect("logger should build"); + + let harness = ShardChainHarness::new(validator_count, log); // Move past the zero slot harness.advance_beacon_slot(); diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 72814b66d55..1aa5b383cb6 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -12,6 +12,7 @@ use shard_store::iter::{ BestBlockRootsIterator, BlockIterator, BlockRootsIterator, StateRootsIterator, }; use shard_store::{Error as DBError, Store}; +use slog::{info, Logger}; use slot_clock::ShardSlotClock; use std::sync::Arc; use types::*; @@ -59,6 +60,7 @@ pub struct ShardChain { genesis_block_root: Hash256, pub crosslink_root: RwLock, pub fork_choice: ForkChoice, + pub log: Logger, } impl ShardChain { @@ -69,6 +71,7 @@ impl ShardChain { spec: ChainSpec, shard: Shard, parent_beacon: Arc>, + log: Logger, ) -> Result { genesis_state.build_cache(&spec)?; let genesis_block_header = &genesis_state.latest_block_header; @@ -83,6 +86,13 @@ impl ShardChain { let genesis_block_root = genesis_block_header.canonical_root(); store.put(&genesis_block_root, &genesis_block)?; + info!(log, "Shard chain initialized from genesis"; + "shard" => shard, + "gensis_slot" => genesis_state.slot, + "state_root" => format!("{}", state_root), + "block_root" => format!("{}", genesis_block_root), + ); + // Also store the genesis block under the `ZERO_HASH` key. store.put(&spec.zero_hash, &genesis_block)?; @@ -105,6 +115,7 @@ impl ShardChain { crosslink_root: RwLock::new(Hash256::default()), fork_choice: ForkChoice::new(store.clone(), &genesis_block, genesis_block_root), store, + log, }) } @@ -565,6 +576,13 @@ impl ShardChain { .get(&shard_state_root)? .ok_or_else(|| Error::MissingShardState(shard_state_root))?; + info!(self.log, "Shard Fork choice produces new head"; + "shard" => self.shard, + "block_root" => format!("{}", &shard_block_root), + "state_root" => format!("{}", &shard_state_root), + "slot" => format!("{}", &shard_block.slot), + ); + self.update_canonical_head(CheckPoint { shard_block: shard_block, shard_block_root, @@ -619,6 +637,12 @@ impl ShardChain { self.fork_choice .process_finalization(&crosslink_block, crosslink_root)?; + info!(self.log, "New crosslink detected from beacon chain"; + "shard" => self.shard, + "root" => format!("{}", crosslink_root), + "pruning fork choice from slot" => format!("{}", crosslink_block.slot), + ); + Ok(()) } } diff --git a/shard_node/shard_client/src/lib.rs b/shard_node/shard_client/src/lib.rs index 3396878663d..137cab6761d 100644 --- a/shard_node/shard_client/src/lib.rs +++ b/shard_node/shard_client/src/lib.rs @@ -32,7 +32,7 @@ pub fn run_shard_chain(log: &slog::Logger, executor: &TaskExecutor) -> () { "shard_node_id" => "0", ); - let harness = get_harness(VALIDATOR_COUNT); + let harness = get_harness(VALIDATOR_COUNT, log.clone()); let fork_epoch = harness.beacon_spec.phase_1_fork_epoch; let num_blocks_produced = MinimalEthSpec::slots_per_epoch() * fork_epoch; @@ -76,9 +76,10 @@ pub fn run_shard_chain(log: &slog::Logger, executor: &TaskExecutor) -> () { fn get_harness( validator_count: usize, + log: slog::Logger, ) -> ShardChainHarness { - let harness = ShardChainHarness::new(validator_count); + let harness = ShardChainHarness::new(validator_count, log); // Move past the zero slot harness.advance_beacon_slot(); From d299205893e06b94c7334f3109cc7368d220de3b Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Fri, 27 Sep 2019 19:28:45 -0500 Subject: [PATCH 150/151] Fix some off by one errors :) improve logging --- beacon_node/beacon_chain/src/beacon_chain.rs | 2 +- .../per_block_processing/validate_attestation.rs | 6 ------ shard_node/rest_api/src/shard.rs | 6 +++--- shard_node/shard_chain/src/harness.rs | 7 +++++-- shard_node/shard_chain/src/harness_tests.rs | 13 +++++-------- shard_node/shard_chain/src/shard_chain.rs | 15 +++++++++++---- shard_node/shard_client/src/lib.rs | 6 ++++-- 7 files changed, 29 insertions(+), 26 deletions(-) diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 0ce08a4b6cc..b077c351ea9 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -811,7 +811,7 @@ impl BeaconChain { new_epoch: new_finalized_epoch, }) } else { - info!(self.log, "Beracon Fork choice produces new head"; + info!(self.log, "Beacon Fork choice produces new head"; "block_root" => format!("{}", &beacon_block_root), "state_root" => format!("{}", &beacon_state_root), "slot" => format!("{}", &beacon_block.slot), diff --git a/eth2/state_processing/src/per_block_processing/validate_attestation.rs b/eth2/state_processing/src/per_block_processing/validate_attestation.rs index a2ee268bbab..e2722a4357f 100644 --- a/eth2/state_processing/src/per_block_processing/validate_attestation.rs +++ b/eth2/state_processing/src/per_block_processing/validate_attestation.rs @@ -80,12 +80,6 @@ fn validate_attestation_parametric( verify_casper_ffg_vote(attestation, state)?; } - // Crosslink data root is zero (to be removed in phase 1). - verify!( - attestation.data.crosslink_data_root == spec.zero_hash, - Invalid::ShardBlockRootNotZero - ); - // Check signature and bitfields let indexed_attestation = convert_to_indexed(state, attestation)?; if verify_signature { diff --git a/shard_node/rest_api/src/shard.rs b/shard_node/rest_api/src/shard.rs index 6d16a2b133c..86ec1631135 100644 --- a/shard_node/rest_api/src/shard.rs +++ b/shard_node/rest_api/src/shard.rs @@ -14,7 +14,7 @@ pub fn get_state( req: Request, ) -> ApiResult { let log = get_logger_from_request(&req); - info!(log, "Latest state requested"); + info!(log, "REST_API: Latest state requested"); let shard_chain = get_shard_chain_from_request::(&req)?; let current_state = shard_chain.current_state(); @@ -26,7 +26,7 @@ pub fn get_block( req: Request, ) -> ApiResult { let log = get_logger_from_request(&req); - info!(log, "Latest block requested"); + info!(log, "REST_API: Latest block requested"); let shard_chain = get_shard_chain_from_request::(&req)?; let current_block = &shard_chain.head().shard_block; @@ -45,7 +45,7 @@ pub fn process_block_body Hash256::zero(), }; + let check_root = self + .shard_chain + .get_block_root_at_epoch(state.current_epoch()); for (i, validator_index) in cc.committee.iter().enumerate() { if attesting_validators.contains(validator_index) { let data = self @@ -481,7 +484,7 @@ where let shard_committee = self .shard_chain .shard_committee( - head_block_slot.epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot), + (head_block_slot + 1).epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot), ) .expect("should get committees"); let committee_size = shard_committee.committee.len(); diff --git a/shard_node/shard_chain/src/harness_tests.rs b/shard_node/shard_chain/src/harness_tests.rs index 2941f8987f1..96b739a4459 100644 --- a/shard_node/shard_chain/src/harness_tests.rs +++ b/shard_node/shard_chain/src/harness_tests.rs @@ -34,23 +34,20 @@ fn get_harness( fn advance_shard_slot() { let harness = get_harness(VALIDATOR_COUNT); let num_blocks_produced = - MinimalEthSpec::slots_per_epoch() * harness.beacon_spec.phase_1_fork_epoch; + harness.beacon_spec.slots_per_epoch * harness.beacon_spec.phase_1_fork_epoch; - harness.extend_beacon_chain((num_blocks_produced + 1) as usize); - - let beacon_slot = harness.beacon_chain.current_state().slot; - let shard_slot = harness.shard_chain.current_state().slot; + harness.extend_beacon_chain((num_blocks_produced) as usize); harness .shard_chain .process_body(hex::decode("48656c6c6f20776f726c6421").unwrap()); harness.extend_shard_chain(1); - for i in 0..30 { - harness.advance_beacon_slot(); + for i in 0..100 { harness.advance_shard_slot(); - harness.extend_beacon_chain(1); + harness.advance_beacon_slot(); harness.extend_shard_chain(1); + harness.extend_beacon_chain(1); harness.advance_shard_slot(); harness.extend_shard_chain(1); } diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 1aa5b383cb6..3283ceec87c 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -190,9 +190,10 @@ impl ShardChain { pub fn get_block_root_at_epoch(&self, epoch: Epoch) -> Result, Error> { let spec = &self.spec; let start_slot_at_epoch = epoch - .start_slot(self.spec.slots_per_epoch) + .start_slot(spec.slots_per_epoch) .shard_slot(spec.slots_per_epoch, spec.shard_slots_per_epoch); let current_slot = self.state.read().slot; + let root = self .rev_iter_block_roots(current_slot) .find(|(_hash, slot)| slot.as_u64() == start_slot_at_epoch.as_u64()); @@ -320,6 +321,7 @@ impl ShardChain { let crosslink_root = beacon_state .get_current_crosslink(self.shard)? .crosslink_data_root; + let current_crossslink_root = *self.crosslink_root.read(); if crosslink_root != current_crossslink_root { *self.crosslink_root.write() = crosslink_root; @@ -345,6 +347,7 @@ impl ShardChain { .parent_beacon .current_state() .get_shard_committee(epoch, self.shard)?; + Ok(shard_committee) } @@ -530,9 +533,13 @@ impl ShardChain { .latest_block_header .slot .epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot); - let beacon_block_root = beacon_state - .get_block_root_at_epoch(beacon_block_root_epoch)? - .clone(); + + let beacon_block_root = match beacon_state.get_block_root_at_epoch(beacon_block_root_epoch) + { + Ok(block_root) => block_root, + Err(e) => beacon_state.get_block_root_at_epoch(beacon_block_root_epoch - 1)?, + } + .clone(); let mut block = ShardBlock { shard: state.shard, diff --git a/shard_node/shard_client/src/lib.rs b/shard_node/shard_client/src/lib.rs index 137cab6761d..b16cb3f1736 100644 --- a/shard_node/shard_client/src/lib.rs +++ b/shard_node/shard_client/src/lib.rs @@ -42,7 +42,7 @@ pub fn run_shard_chain(log: &slog::Logger, executor: &TaskExecutor) -> () { "fork_epoch" => format!("{:?}", fork_epoch), ); - harness.extend_beacon_chain((num_blocks_produced + 1) as usize); + harness.extend_beacon_chain((num_blocks_produced) as usize); info!( log, @@ -62,9 +62,11 @@ pub fn run_shard_chain(log: &slog::Logger, executor: &TaskExecutor) -> () { advance_shard_slot(&harness_logger, &harness); if round % 2 == 0 { advance_beacon_slot(&harness_logger, &harness); - extend_beacon_chain(&harness_logger, &harness); } extend_shard_chain(&harness_logger, &harness); + if round % 2 == 0 { + extend_beacon_chain(&harness_logger, &harness); + } round = round + 1; Ok(()) }) From 2fc9064d1113091fdfd049805cdfd734e92d0fac Mon Sep 17 00:00:00 2001 From: Will Villanueva Date: Sun, 29 Sep 2019 17:01:24 -0500 Subject: [PATCH 151/151] Fixed last set of off by one errors :) major todo to have utility function that always grabs beacon state at epoch boundary --- shard_node/shard_chain/src/harness.rs | 7 ++--- shard_node/shard_chain/src/shard_chain.rs | 37 ++++++++++++++++++++--- shard_node/shard_store/src/iter.rs | 6 ++-- 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/shard_node/shard_chain/src/harness.rs b/shard_node/shard_chain/src/harness.rs index e0400d92217..c56029ffd6e 100644 --- a/shard_node/shard_chain/src/harness.rs +++ b/shard_node/shard_chain/src/harness.rs @@ -244,7 +244,7 @@ where if let ShardBlockProcessingOutcome::Processed { block_root } = outcome { head_block_root = Some(block_root); - self.add_shard_attestations_to_op_pool(&new_state, block_root, current_slot - 1); + self.add_shard_attestations_to_op_pool(&new_state, block_root, current_slot); } else { panic!("block should be successfully processed: {:?}", outcome); } @@ -412,9 +412,6 @@ where _ => Hash256::zero(), }; - let check_root = self - .shard_chain - .get_block_root_at_epoch(state.current_epoch()); for (i, validator_index) in cc.committee.iter().enumerate() { if attesting_validators.contains(validator_index) { let data = self @@ -484,7 +481,7 @@ where let shard_committee = self .shard_chain .shard_committee( - (head_block_slot + 1).epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot), + (head_block_slot).epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot), ) .expect("should get committees"); let committee_size = shard_committee.committee.len(); diff --git a/shard_node/shard_chain/src/shard_chain.rs b/shard_node/shard_chain/src/shard_chain.rs index 3283ceec87c..9d0c56a7f18 100644 --- a/shard_node/shard_chain/src/shard_chain.rs +++ b/shard_node/shard_chain/src/shard_chain.rs @@ -15,6 +15,7 @@ use shard_store::{Error as DBError, Store}; use slog::{info, Logger}; use slot_clock::ShardSlotClock; use std::sync::Arc; +use store::{Error as BeaconDBError, Store as BeaconStore}; use types::*; #[derive(Debug, PartialEq)] @@ -189,14 +190,22 @@ impl ShardChain { pub fn get_block_root_at_epoch(&self, epoch: Epoch) -> Result, Error> { let spec = &self.spec; + let start_slot_at_epoch = epoch .start_slot(spec.slots_per_epoch) .shard_slot(spec.slots_per_epoch, spec.shard_slots_per_epoch); + + if start_slot_at_epoch <= spec.phase_1_fork_slot { + return Ok(Some(Hash256::zero())); + } + let current_slot = self.state.read().slot; let root = self - .rev_iter_block_roots(current_slot) - .find(|(_hash, slot)| slot.as_u64() == start_slot_at_epoch.as_u64()); + .rev_iter_block_roots(current_slot - 1) + .find(|(hash, slot)| { + slot.as_u64() <= start_slot_at_epoch.as_u64() && hash != &Hash256::zero() + }); Ok(match root { Some(root) => Some(root.0), @@ -482,8 +491,22 @@ impl ShardChain { self.store.put(&block_root, &block)?; self.store.put(&state_root, &state)?; - self.fork_choice - .process_block(&beacon_state, &block, block_root)?; + // temp - need to update all logic to grab beacon state at epoch boundary + let attestation_epoch = + (block.slot - 1).epoch(spec.slots_per_epoch, spec.shard_slots_per_beacon_slot); + if attestation_epoch == beacon_state.current_epoch() { + self.fork_choice + .process_block(&beacon_state, &block, block_root)?; + } else { + let parent_beacon_state: BeaconState = self + .parent_beacon + .store + .get(beacon_state.get_state_root(beacon_state.slot - 1)?)? + .ok_or_else(|| Error::DBInconsistent(format!("Missing state")))?; + + self.fork_choice + .process_block(&parent_beacon_state, &block, block_root)?; + } // Execute the fork choice algorithm, enthroning a new head if discovered. // @@ -660,6 +683,12 @@ impl From for Error { } } +impl From for Error { + fn from(e: BeaconDBError) -> Error { + Error::BeaconDBError(e) + } +} + impl From for Error { fn from(e: ForkChoiceError) -> Error { Error::ForkChoiceError(e) diff --git a/shard_node/shard_store/src/iter.rs b/shard_node/shard_store/src/iter.rs index 87068434da8..21c5e0f1dfb 100644 --- a/shard_node/shard_store/src/iter.rs +++ b/shard_node/shard_store/src/iter.rs @@ -125,8 +125,10 @@ impl<'a, T: ShardSpec, U: Store> Iterator for BlockRootsIterator<'a, T, U> { } let mut latest_block_header = self.shard_state.latest_block_header.clone(); - // zero out the state root to find where it was stored - latest_block_header.state_root = Hash256::zero(); + if latest_block_header.state_root == Hash256::zero() { + latest_block_header.state_root = self.shard_state.canonical_root(); + } + Some((latest_block_header.canonical_root(), self.slot)) } }