Merge pull request #233 from sigp/implement-shuffle

Swap BeaconState to v0.2.0 shuffling algorithm
This commit is contained in:
Age Manning 2019-02-16 11:48:32 +11:00 committed by GitHub
commit 98c33a7d06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 129 additions and 117 deletions

View File

@ -18,7 +18,7 @@ serde_derive = "1.0"
serde_json = "1.0" serde_json = "1.0"
slog = "^2.2.3" slog = "^2.2.3"
ssz = { path = "../utils/ssz" } ssz = { path = "../utils/ssz" }
fisher_yates_shuffle = { path = "../utils/fisher_yates_shuffle" } swap_or_not_shuffle = { path = "../utils/swap_or_not_shuffle" }
[dev-dependencies] [dev-dependencies]
env_logger = "0.6.0" env_logger = "0.6.0"

View File

@ -5,15 +5,18 @@ use crate::{
PendingAttestation, PublicKey, Signature, Slot, Validator, PendingAttestation, PublicKey, Signature, Slot, Validator,
}; };
use bls::verify_proof_of_possession; use bls::verify_proof_of_possession;
use fisher_yates_shuffle::shuffle;
use honey_badger_split::SplitExt; use honey_badger_split::SplitExt;
use rand::RngCore; use rand::RngCore;
use serde_derive::Serialize; use serde_derive::Serialize;
use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash}; use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash};
use swap_or_not_shuffle::get_permutated_index;
mod tests;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum BeaconStateError { pub enum BeaconStateError {
EpochOutOfBounds, EpochOutOfBounds,
UnableToShuffle,
InsufficientRandaoMixes, InsufficientRandaoMixes,
InsufficientValidators, InsufficientValidators,
InsufficientBlockRoots, InsufficientBlockRoots,
@ -249,23 +252,36 @@ impl BeaconState {
/// committee is itself a list of validator indices. /// committee is itself a list of validator indices.
/// ///
/// Spec v0.1 /// Spec v0.1
pub fn get_shuffling(&self, seed: Hash256, epoch: Epoch, spec: &ChainSpec) -> Vec<Vec<usize>> { pub fn get_shuffling(
&self,
seed: Hash256,
epoch: Epoch,
spec: &ChainSpec,
) -> Option<Vec<Vec<usize>>> {
let active_validator_indices = let active_validator_indices =
get_active_validator_indices(&self.validator_registry, epoch); get_active_validator_indices(&self.validator_registry, epoch);
let committees_per_epoch = let committees_per_epoch =
self.get_epoch_committee_count(active_validator_indices.len(), spec); self.get_epoch_committee_count(active_validator_indices.len(), spec);
// TODO: check that Hash256::from(u64) matches 'int_to_bytes32'. let mut shuffled_active_validator_indices =
let seed = seed ^ Hash256::from(epoch.as_u64()); Vec::with_capacity(active_validator_indices.len());
// TODO: fix `expect` assert. for &i in &active_validator_indices {
let shuffled_active_validator_indices = let shuffled_i = get_permutated_index(
shuffle(&seed, active_validator_indices).expect("Max validator count exceed!"); i,
active_validator_indices.len(),
&seed[..],
spec.shuffle_round_count,
)?;
shuffled_active_validator_indices[i] = active_validator_indices[shuffled_i]
}
Some(
shuffled_active_validator_indices shuffled_active_validator_indices
.honey_badger_split(committees_per_epoch as usize) .honey_badger_split(committees_per_epoch as usize)
.map(|slice: &[usize]| slice.to_vec()) .map(|slice: &[usize]| slice.to_vec())
.collect() .collect(),
)
} }
/// Return the number of committees in the previous epoch. /// Return the number of committees in the previous epoch.
@ -401,7 +417,9 @@ impl BeaconState {
return Err(BeaconStateError::EpochOutOfBounds); return Err(BeaconStateError::EpochOutOfBounds);
}; };
let shuffling = self.get_shuffling(seed, shuffling_epoch, spec); let shuffling = self
.get_shuffling(seed, shuffling_epoch, spec)
.ok_or_else(|| BeaconStateError::UnableToShuffle)?;
let offset = slot.as_u64() % spec.epoch_length; let offset = slot.as_u64() % spec.epoch_length;
let committees_per_slot = committees_per_epoch / spec.epoch_length; let committees_per_slot = committees_per_epoch / spec.epoch_length;
let slot_start_shard = let slot_start_shard =
@ -1064,33 +1082,3 @@ impl<T: RngCore> TestRandom<T> for BeaconState {
} }
} }
} }
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
use ssz::ssz_encode;
#[test]
pub fn test_ssz_round_trip() {
let mut rng = XorShiftRng::from_seed([42; 16]);
let original = BeaconState::random_for_test(&mut rng);
let bytes = ssz_encode(&original);
let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap();
assert_eq!(original, decoded);
}
#[test]
pub fn test_hash_tree_root() {
let mut rng = XorShiftRng::from_seed([42; 16]);
let original = BeaconState::random_for_test(&mut rng);
let result = original.hash_tree_root();
assert_eq!(result.len(), 32);
// TODO: Add further tests
// https://github.com/sigp/lighthouse/issues/170
}
}

View File

@ -0,0 +1,97 @@
#![cfg(test)]
use super::*;
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
use crate::{
beacon_state::BeaconStateError, BeaconState, ChainSpec, Deposit, DepositData, DepositInput,
Eth1Data, Hash256, Keypair,
};
use bls::create_proof_of_possession;
use ssz::ssz_encode;
struct BeaconStateTestBuilder {
pub genesis_time: u64,
pub initial_validator_deposits: Vec<Deposit>,
pub latest_eth1_data: Eth1Data,
pub spec: ChainSpec,
pub keypairs: Vec<Keypair>,
}
impl BeaconStateTestBuilder {
pub fn with_random_validators(validator_count: usize) -> Self {
let genesis_time = 10_000_000;
let keypairs: Vec<Keypair> = (0..validator_count)
.collect::<Vec<usize>>()
.iter()
.map(|_| Keypair::random())
.collect();
let initial_validator_deposits = keypairs
.iter()
.map(|keypair| Deposit {
branch: vec![], // branch verification is not specified.
index: 0, // index verification is not specified.
deposit_data: DepositData {
amount: 32_000_000_000, // 32 ETH (in Gwei)
timestamp: genesis_time - 1,
deposit_input: DepositInput {
pubkey: keypair.pk.clone(),
withdrawal_credentials: Hash256::zero(), // Withdrawal not possible.
proof_of_possession: create_proof_of_possession(&keypair),
},
},
})
.collect();
let latest_eth1_data = Eth1Data {
deposit_root: Hash256::zero(),
block_hash: Hash256::zero(),
};
let spec = ChainSpec::foundation();
Self {
genesis_time,
initial_validator_deposits,
latest_eth1_data,
spec,
keypairs,
}
}
pub fn build(&self) -> Result<BeaconState, BeaconStateError> {
BeaconState::genesis(
self.genesis_time,
self.initial_validator_deposits.clone(),
self.latest_eth1_data.clone(),
&self.spec,
)
}
}
#[test]
pub fn can_produce_genesis_block() {
let builder = BeaconStateTestBuilder::with_random_validators(2);
builder.build().unwrap();
}
#[test]
pub fn test_ssz_round_trip() {
let mut rng = XorShiftRng::from_seed([42; 16]);
let original = BeaconState::random_for_test(&mut rng);
let bytes = ssz_encode(&original);
let (decoded, _) = <_>::ssz_decode(&bytes, 0).unwrap();
assert_eq!(original, decoded);
}
#[test]
pub fn test_hash_tree_root() {
let mut rng = XorShiftRng::from_seed([42; 16]);
let original = BeaconState::random_for_test(&mut rng);
let result = original.hash_tree_root();
assert_eq!(result.len(), 32);
// TODO: Add further tests
// https://github.com/sigp/lighthouse/issues/170
}

View File

@ -1,72 +0,0 @@
#[cfg(test)]
mod tests {
use crate::{
beacon_state::BeaconStateError, BeaconState, ChainSpec, Deposit, DepositData, DepositInput,
Eth1Data, Hash256, Keypair,
};
use bls::create_proof_of_possession;
struct BeaconStateTestBuilder {
pub genesis_time: u64,
pub initial_validator_deposits: Vec<Deposit>,
pub latest_eth1_data: Eth1Data,
pub spec: ChainSpec,
pub keypairs: Vec<Keypair>,
}
impl BeaconStateTestBuilder {
pub fn with_random_validators(validator_count: usize) -> Self {
let genesis_time = 10_000_000;
let keypairs: Vec<Keypair> = (0..validator_count)
.collect::<Vec<usize>>()
.iter()
.map(|_| Keypair::random())
.collect();
let initial_validator_deposits = keypairs
.iter()
.map(|keypair| Deposit {
branch: vec![], // branch verification is not specified.
index: 0, // index verification is not specified.
deposit_data: DepositData {
amount: 32_000_000_000, // 32 ETH (in Gwei)
timestamp: genesis_time - 1,
deposit_input: DepositInput {
pubkey: keypair.pk.clone(),
withdrawal_credentials: Hash256::zero(), // Withdrawal not possible.
proof_of_possession: create_proof_of_possession(&keypair),
},
},
})
.collect();
let latest_eth1_data = Eth1Data {
deposit_root: Hash256::zero(),
block_hash: Hash256::zero(),
};
let spec = ChainSpec::foundation();
Self {
genesis_time,
initial_validator_deposits,
latest_eth1_data,
spec,
keypairs,
}
}
pub fn build(&self) -> Result<BeaconState, BeaconStateError> {
BeaconState::genesis(
self.genesis_time,
self.initial_validator_deposits.clone(),
self.latest_eth1_data.clone(),
&self.spec,
)
}
}
#[test]
pub fn can_produce_genesis_block() {
let builder = BeaconStateTestBuilder::with_random_validators(2);
builder.build().unwrap();
}
}

View File

@ -7,7 +7,6 @@ pub mod attester_slashing;
pub mod beacon_block; pub mod beacon_block;
pub mod beacon_block_body; pub mod beacon_block_body;
pub mod beacon_state; pub mod beacon_state;
pub mod beacon_state_tests;
pub mod casper_slashing; pub mod casper_slashing;
pub mod crosslink; pub mod crosslink;
pub mod deposit; pub mod deposit;

View File

@ -17,7 +17,7 @@ pub struct ChainSpec {
pub beacon_chain_shard_number: u64, pub beacon_chain_shard_number: u64,
pub max_indices_per_slashable_vote: u64, pub max_indices_per_slashable_vote: u64,
pub max_withdrawals_per_epoch: u64, pub max_withdrawals_per_epoch: u64,
pub shuffle_round_count: u64, pub shuffle_round_count: u8,
/* /*
* Deposit contract * Deposit contract