Merge branch 'master' into v0.4.0-types

This commit is contained in:
Paul Hauner
2019-03-07 12:03:27 +11:00
35 changed files with 1842 additions and 94 deletions

View File

@@ -65,6 +65,7 @@ pub struct BeaconChain<T: ClientDB + Sized, U: SlotClock, F: ForkChoice> {
pub slot_clock: U,
pub attestation_aggregator: RwLock<AttestationAggregator>,
pub deposits_for_inclusion: RwLock<Vec<Deposit>>,
pub exits_for_inclusion: RwLock<Vec<Exit>>,
pub proposer_slashings_for_inclusion: RwLock<Vec<ProposerSlashing>>,
pub attester_slashings_for_inclusion: RwLock<Vec<AttesterSlashing>>,
canonical_head: RwLock<CheckPoint>,
@@ -134,6 +135,7 @@ where
slot_clock,
attestation_aggregator,
deposits_for_inclusion: RwLock::new(vec![]),
exits_for_inclusion: RwLock::new(vec![]),
proposer_slashings_for_inclusion: RwLock::new(vec![]),
attester_slashings_for_inclusion: RwLock::new(vec![]),
state: RwLock::new(genesis_state),
@@ -370,13 +372,17 @@ where
/// Accept some deposit and queue it for inclusion in an appropriate block.
pub fn receive_deposit_for_inclusion(&self, deposit: Deposit) {
// TODO: deposits are not check for validity; check them.
// TODO: deposits are not checked for validity; check them.
//
// https://github.com/sigp/lighthouse/issues/276
self.deposits_for_inclusion.write().push(deposit);
}
/// Return a vec of deposits suitable for inclusion in some block.
pub fn get_deposits_for_block(&self) -> Vec<Deposit> {
// TODO: deposits are indiscriminately included; check them for validity.
//
// https://github.com/sigp/lighthouse/issues/275
self.deposits_for_inclusion.read().clone()
}
@@ -386,6 +392,8 @@ where
/// This ensures that `Deposits` are not included twice in successive blocks.
pub fn set_deposits_as_included(&self, included_deposits: &[Deposit]) {
// TODO: method does not take forks into account; consider this.
//
// https://github.com/sigp/lighthouse/issues/275
let mut indices_to_delete = vec![];
for included in included_deposits {
@@ -402,9 +410,49 @@ where
}
}
/// Accept some exit and queue it for inclusion in an appropriate block.
pub fn receive_exit_for_inclusion(&self, exit: Exit) {
// TODO: exits are not checked for validity; check them.
//
// https://github.com/sigp/lighthouse/issues/276
self.exits_for_inclusion.write().push(exit);
}
/// Return a vec of exits suitable for inclusion in some block.
pub fn get_exits_for_block(&self) -> Vec<Exit> {
// TODO: exits are indiscriminately included; check them for validity.
//
// https://github.com/sigp/lighthouse/issues/275
self.exits_for_inclusion.read().clone()
}
/// Takes a list of `Deposits` that were included in recent blocks and removes them from the
/// inclusion queue.
///
/// This ensures that `Deposits` are not included twice in successive blocks.
pub fn set_exits_as_included(&self, included_exits: &[Exit]) {
// TODO: method does not take forks into account; consider this.
let mut indices_to_delete = vec![];
for included in included_exits {
for (i, for_inclusion) in self.exits_for_inclusion.read().iter().enumerate() {
if included == for_inclusion {
indices_to_delete.push(i);
}
}
}
let exits_for_inclusion = &mut self.exits_for_inclusion.write();
for i in indices_to_delete {
exits_for_inclusion.remove(i);
}
}
/// Accept some proposer slashing and queue it for inclusion in an appropriate block.
pub fn receive_proposer_slashing_for_inclusion(&self, proposer_slashing: ProposerSlashing) {
// TODO: proposer_slashings are not check for validity; check them.
// TODO: proposer_slashings are not checked for validity; check them.
//
// https://github.com/sigp/lighthouse/issues/276
self.proposer_slashings_for_inclusion
.write()
.push(proposer_slashing);
@@ -413,6 +461,8 @@ where
/// Return a vec of proposer slashings suitable for inclusion in some block.
pub fn get_proposer_slashings_for_block(&self) -> Vec<ProposerSlashing> {
// TODO: proposer_slashings are indiscriminately included; check them for validity.
//
// https://github.com/sigp/lighthouse/issues/275
self.proposer_slashings_for_inclusion.read().clone()
}
@@ -425,6 +475,8 @@ where
included_proposer_slashings: &[ProposerSlashing],
) {
// TODO: method does not take forks into account; consider this.
//
// https://github.com/sigp/lighthouse/issues/275
let mut indices_to_delete = vec![];
for included in included_proposer_slashings {
@@ -448,7 +500,9 @@ where
/// Accept some attester slashing and queue it for inclusion in an appropriate block.
pub fn receive_attester_slashing_for_inclusion(&self, attester_slashing: AttesterSlashing) {
// TODO: attester_slashings are not check for validity; check them.
// TODO: attester_slashings are not checked for validity; check them.
//
// https://github.com/sigp/lighthouse/issues/276
self.attester_slashings_for_inclusion
.write()
.push(attester_slashing);
@@ -457,6 +511,8 @@ where
/// Return a vec of attester slashings suitable for inclusion in some block.
pub fn get_attester_slashings_for_block(&self) -> Vec<AttesterSlashing> {
// TODO: attester_slashings are indiscriminately included; check them for validity.
//
// https://github.com/sigp/lighthouse/issues/275
self.attester_slashings_for_inclusion.read().clone()
}
@@ -469,6 +525,8 @@ where
included_attester_slashings: &[AttesterSlashing],
) {
// TODO: method does not take forks into account; consider this.
//
// https://github.com/sigp/lighthouse/issues/275
let mut indices_to_delete = vec![];
for included in included_attester_slashings {
@@ -678,7 +736,7 @@ where
attester_slashings: self.get_attester_slashings_for_block(),
attestations,
deposits: self.get_deposits_for_block(),
exits: vec![],
exits: self.get_exits_for_block(),
},
};

View File

@@ -18,6 +18,10 @@ test_cases:
amount: 32
- slot: 5
amount: 32
exits:
# At slot 10, submit an exit for validator #50.
- slot: 10
validator_index: 50
proposer_slashings:
# At slot 2, trigger a proposer slashing for validator #42.
- slot: 2
@@ -39,4 +43,5 @@ test_cases:
num_validators: 1003
slashed_validators: [11, 12, 13, 14, 42]
exited_validators: []
exit_initiated_validators: [50]

View File

@@ -280,6 +280,15 @@ impl BeaconChainHarness {
}
}
/// Submit an exit to the `BeaconChain` for inclusion in some block.
///
/// Note: the `ValidatorHarness` for this validator continues to exist. Once it is exited it
/// will stop receiving duties from the beacon chain and just do nothing when prompted to
/// produce/attest.
pub fn add_exit(&mut self, exit: Exit) {
self.beacon_chain.receive_exit_for_inclusion(exit);
}
/// Submit a proposer slashing to the `BeaconChain` for inclusion in some block.
pub fn add_proposer_slashing(&mut self, proposer_slashing: ProposerSlashing) {
self.beacon_chain

View File

@@ -4,6 +4,7 @@
use crate::beacon_chain_harness::BeaconChainHarness;
use beacon_chain::CheckPoint;
use log::{info, warn};
use ssz::TreeHash;
use types::*;
use types::{
attester_slashing::AttesterSlashingBuilder, proposer_slashing::ProposerSlashingBuilder,
@@ -121,6 +122,20 @@ impl TestCase {
}
}
// Feed exits to the BeaconChain.
if let Some(ref exits) = self.config.exits {
for (slot, validator_index) in exits {
if *slot == slot_height {
info!(
"Including exit at slot height {} for validator {}.",
slot_height, validator_index
);
let exit = build_exit(&harness, *validator_index);
harness.add_exit(exit);
}
}
}
// Build a block or skip a slot.
match self.config.skip_slots {
Some(ref skip_slots) if skip_slots.contains(&slot_height) => {
@@ -185,6 +200,33 @@ impl TestCase {
}
}
fn build_exit(harness: &BeaconChainHarness, validator_index: u64) -> Exit {
let epoch = harness
.beacon_chain
.state
.read()
.current_epoch(&harness.spec);
let mut exit = Exit {
epoch,
validator_index,
signature: Signature::empty_signature(),
};
let message = exit.hash_tree_root();
exit.signature = harness
.validator_sign(
validator_index as usize,
&message[..],
epoch,
harness.spec.domain_exit,
)
.expect("Unable to sign Exit");
exit
}
/// Builds an `AttesterSlashing` for some `validator_indices`.
///
/// Signs the message using a `BeaconChainHarness`.

View File

@@ -3,9 +3,13 @@ use bls::create_proof_of_possession;
use types::*;
use yaml_rust::Yaml;
pub type DepositTuple = (u64, Deposit, Keypair);
pub type ProposerSlashingTuple = (u64, u64);
pub type AttesterSlashingTuple = (u64, Vec<u64>);
pub type ValidatorIndex = u64;
pub type ValidatorIndices = Vec<u64>;
pub type DepositTuple = (SlotHeight, Deposit, Keypair);
pub type ExitTuple = (SlotHeight, ValidatorIndex);
pub type ProposerSlashingTuple = (SlotHeight, ValidatorIndex);
pub type AttesterSlashingTuple = (SlotHeight, ValidatorIndices);
/// Defines the execution of a `BeaconStateHarness` across a series of slots.
#[derive(Debug)]
@@ -24,6 +28,8 @@ pub struct Config {
pub proposer_slashings: Option<Vec<ProposerSlashingTuple>>,
/// Attester slashings to be including during execution.
pub attester_slashings: Option<Vec<AttesterSlashingTuple>>,
/// Exits to be including during execution.
pub exits: Option<Vec<ExitTuple>>,
}
impl Config {
@@ -40,10 +46,26 @@ impl Config {
deposits: parse_deposits(&yaml),
proposer_slashings: parse_proposer_slashings(&yaml),
attester_slashings: parse_attester_slashings(&yaml),
exits: parse_exits(&yaml),
}
}
}
/// Parse the `attester_slashings` section of the YAML document.
fn parse_exits(yaml: &Yaml) -> Option<Vec<ExitTuple>> {
let mut tuples = vec![];
for exit in yaml["exits"].as_vec()? {
let slot = as_u64(exit, "slot").expect("Incomplete exit (slot)");
let validator_index =
as_u64(exit, "validator_index").expect("Incomplete exit (validator_index)");
tuples.push((SlotHeight::from(slot), validator_index));
}
Some(tuples)
}
/// Parse the `attester_slashings` section of the YAML document.
fn parse_attester_slashings(yaml: &Yaml) -> Option<Vec<AttesterSlashingTuple>> {
let mut slashings = vec![];
@@ -53,7 +75,7 @@ fn parse_attester_slashings(yaml: &Yaml) -> Option<Vec<AttesterSlashingTuple>> {
let validator_indices = as_vec_u64(slashing, "validator_indices")
.expect("Incomplete attester_slashing (validator_indices)");
slashings.push((slot, validator_indices));
slashings.push((SlotHeight::from(slot), validator_indices));
}
Some(slashings)
@@ -68,7 +90,7 @@ fn parse_proposer_slashings(yaml: &Yaml) -> Option<Vec<ProposerSlashingTuple>> {
let validator_index = as_u64(slashing, "validator_index")
.expect("Incomplete proposer slashing (validator_index)");
slashings.push((slot, validator_index));
slashings.push((SlotHeight::from(slot), validator_index));
}
Some(slashings)
@@ -102,7 +124,7 @@ fn parse_deposits(yaml: &Yaml) -> Option<Vec<DepositTuple>> {
},
};
deposits.push((slot, deposit, keypair));
deposits.push((SlotHeight::from(slot), deposit, keypair));
}
Some(deposits)

View File

@@ -13,8 +13,10 @@ pub struct StateCheck {
pub num_validators: Option<usize>,
/// A list of validator indices which have been penalized. Must be in ascending order.
pub slashed_validators: Option<Vec<u64>>,
/// A list of validator indices which have been exited. Must be in ascending order.
/// A list of validator indices which have been fully exited. Must be in ascending order.
pub exited_validators: Option<Vec<u64>>,
/// A list of validator indices which have had an exit initiated. Must be in ascending order.
pub exit_initiated_validators: Option<Vec<u64>>,
}
impl StateCheck {
@@ -27,6 +29,7 @@ impl StateCheck {
num_validators: as_usize(&yaml, "num_validators"),
slashed_validators: as_vec_u64(&yaml, "slashed_validators"),
exited_validators: as_vec_u64(&yaml, "exited_validators"),
exit_initiated_validators: as_vec_u64(&yaml, "exit_initiated_validators"),
}
}
@@ -40,6 +43,7 @@ impl StateCheck {
info!("Running state check for slot height {}.", self.slot);
// Check the state slot.
assert_eq!(
self.slot,
state.slot - spec.genesis_epoch.start_slot(spec.slots_per_epoch),
@@ -55,6 +59,7 @@ impl StateCheck {
info!("OK: num_validators = {}.", num_validators);
}
// Check for slashed validators.
if let Some(ref slashed_validators) = self.slashed_validators {
let actually_slashed_validators: Vec<u64> = state
.validator_registry
@@ -75,6 +80,7 @@ impl StateCheck {
info!("OK: slashed_validators = {:?}.", slashed_validators);
}
// Check for exited validators.
if let Some(ref exited_validators) = self.exited_validators {
let actually_exited_validators: Vec<u64> = state
.validator_registry
@@ -94,5 +100,29 @@ impl StateCheck {
);
info!("OK: exited_validators = {:?}.", exited_validators);
}
// Check for validators that have initiated exit.
if let Some(ref exit_initiated_validators) = self.exit_initiated_validators {
let actual: Vec<u64> = state
.validator_registry
.iter()
.enumerate()
.filter_map(|(i, validator)| {
if validator.has_initiated_exit() {
Some(i as u64)
} else {
None
}
})
.collect();
assert_eq!(
actual, *exit_initiated_validators,
"Exit initiated validators != expected."
);
info!(
"OK: exit_initiated_validators = {:?}.",
exit_initiated_validators
);
}
}
}

View File

@@ -134,9 +134,9 @@ mod tests {
let store = BeaconBlockStore::new(db.clone());
let ssz = "definitly not a valid block".as_bytes();
let hash = &Hash256::from("some hash".as_bytes());
let hash = &Hash256::from([0xAA; 32]);
db.put(DB_COLUMN, hash, ssz).unwrap();
db.put(DB_COLUMN, hash.as_bytes(), ssz).unwrap();
assert_eq!(
store.block_at_slot(hash, Slot::from(42_u64)),
Err(BeaconBlockAtSlotError::DBError(
@@ -151,10 +151,10 @@ mod tests {
let store = BeaconBlockStore::new(db.clone());
let ssz = "some bytes".as_bytes();
let hash = &Hash256::from("some hash".as_bytes());
let other_hash = &Hash256::from("another hash".as_bytes());
let hash = &Hash256::from([0xAA; 32]);
let other_hash = &Hash256::from([0xBB; 32]);
db.put(DB_COLUMN, hash, ssz).unwrap();
db.put(DB_COLUMN, hash.as_bytes(), ssz).unwrap();
assert_eq!(
store.block_at_slot(other_hash, Slot::from(42_u64)),
Err(BeaconBlockAtSlotError::UnknownBeaconBlock(*other_hash))
@@ -169,18 +169,15 @@ mod tests {
let thread_count = 10;
let write_count = 10;
// We're expecting the product of these numbers to fit in one byte.
assert!(thread_count * write_count <= 255);
let mut handles = vec![];
for t in 0..thread_count {
let wc = write_count;
let bs = bs.clone();
let handle = thread::spawn(move || {
for w in 0..wc {
let key = (t * w) as u8;
let key = t * w;
let val = 42;
bs.put(&[key][..].into(), &vec![val]).unwrap();
bs.put(&Hash256::from_low_u64_le(key), &vec![val]).unwrap();
}
});
handles.push(handle);
@@ -192,9 +189,9 @@ mod tests {
for t in 0..thread_count {
for w in 0..write_count {
let key = (t * w) as u8;
assert!(bs.exists(&[key][..].into()).unwrap());
let val = bs.get(&[key][..].into()).unwrap().unwrap();
let key = t * w;
assert!(bs.exists(&Hash256::from_low_u64_le(key)).unwrap());
let val = bs.get(&Hash256::from_low_u64_le(key)).unwrap().unwrap();
assert_eq!(vec![42], val);
}
}
@@ -208,19 +205,20 @@ mod tests {
// Specify test block parameters.
let hashes = [
Hash256::from(&[0; 32][..]),
Hash256::from(&[1; 32][..]),
Hash256::from(&[2; 32][..]),
Hash256::from(&[3; 32][..]),
Hash256::from(&[4; 32][..]),
Hash256::from([0; 32]),
Hash256::from([1; 32]),
Hash256::from([2; 32]),
Hash256::from([3; 32]),
Hash256::from([4; 32]),
];
let parent_hashes = [
Hash256::from(&[255; 32][..]), // Genesis block.
Hash256::from(&[0; 32][..]),
Hash256::from(&[1; 32][..]),
Hash256::from(&[2; 32][..]),
Hash256::from(&[3; 32][..]),
Hash256::from([255; 32]), // Genesis block.
Hash256::from([0; 32]),
Hash256::from([1; 32]),
Hash256::from([2; 32]),
Hash256::from([3; 32]),
];
let unknown_hash = Hash256::from([101; 32]); // different from all above
let slots: Vec<Slot> = vec![0, 1, 3, 4, 5].iter().map(|x| Slot::new(*x)).collect();
// Generate a vec of random blocks and store them in the DB.
@@ -233,7 +231,7 @@ mod tests {
block.slot = slots[i];
let ssz = ssz_encode(&block);
db.put(DB_COLUMN, &hashes[i], &ssz).unwrap();
db.put(DB_COLUMN, hashes[i].as_bytes(), &ssz).unwrap();
blocks.push(block);
}
@@ -255,11 +253,10 @@ mod tests {
let ssz = bs.block_at_slot(&hashes[4], Slot::new(6)).unwrap();
assert_eq!(ssz, None);
let bad_hash = &Hash256::from("unknown".as_bytes());
let ssz = bs.block_at_slot(bad_hash, Slot::new(2));
let ssz = bs.block_at_slot(&unknown_hash, Slot::new(2));
assert_eq!(
ssz,
Err(BeaconBlockAtSlotError::UnknownBeaconBlock(*bad_hash))
Err(BeaconBlockAtSlotError::UnknownBeaconBlock(unknown_hash))
);
}
}

View File

@@ -2,25 +2,25 @@ macro_rules! impl_crud_for_store {
($store: ident, $db_column: expr) => {
impl<T: ClientDB> $store<T> {
pub fn put(&self, hash: &Hash256, ssz: &[u8]) -> Result<(), DBError> {
self.db.put($db_column, hash, ssz)
self.db.put($db_column, hash.as_bytes(), ssz)
}
pub fn get(&self, hash: &Hash256) -> Result<Option<Vec<u8>>, DBError> {
self.db.get($db_column, hash)
self.db.get($db_column, hash.as_bytes())
}
pub fn exists(&self, hash: &Hash256) -> Result<bool, DBError> {
self.db.exists($db_column, hash)
self.db.exists($db_column, hash.as_bytes())
}
pub fn delete(&self, hash: &Hash256) -> Result<(), DBError> {
self.db.delete($db_column, hash)
self.db.delete($db_column, hash.as_bytes())
}
}
};
}
#[allow(unused_macros)]
#[cfg(test)]
macro_rules! test_crud_for_store {
($store: ident, $db_column: expr) => {
#[test]
@@ -29,10 +29,10 @@ macro_rules! test_crud_for_store {
let store = $store::new(db.clone());
let ssz = "some bytes".as_bytes();
let hash = &Hash256::from("some hash".as_bytes());
let hash = &Hash256::from([0xAA; 32]);
store.put(hash, ssz).unwrap();
assert_eq!(db.get(DB_COLUMN, hash).unwrap().unwrap(), ssz);
assert_eq!(db.get(DB_COLUMN, hash.as_bytes()).unwrap().unwrap(), ssz);
}
#[test]
@@ -41,9 +41,9 @@ macro_rules! test_crud_for_store {
let store = $store::new(db.clone());
let ssz = "some bytes".as_bytes();
let hash = &Hash256::from("some hash".as_bytes());
let hash = &Hash256::from([0xAA; 32]);
db.put(DB_COLUMN, hash, ssz).unwrap();
db.put(DB_COLUMN, hash.as_bytes(), ssz).unwrap();
assert_eq!(store.get(hash).unwrap().unwrap(), ssz);
}
@@ -53,10 +53,10 @@ macro_rules! test_crud_for_store {
let store = $store::new(db.clone());
let ssz = "some bytes".as_bytes();
let hash = &Hash256::from("some hash".as_bytes());
let other_hash = &Hash256::from("another hash".as_bytes());
let hash = &Hash256::from([0xAA; 32]);
let other_hash = &Hash256::from([0xBB; 32]);
db.put(DB_COLUMN, other_hash, ssz).unwrap();
db.put(DB_COLUMN, other_hash.as_bytes(), ssz).unwrap();
assert_eq!(store.get(hash).unwrap(), None);
}
@@ -66,9 +66,9 @@ macro_rules! test_crud_for_store {
let store = $store::new(db.clone());
let ssz = "some bytes".as_bytes();
let hash = &Hash256::from("some hash".as_bytes());
let hash = &Hash256::from([0xAA; 32]);
db.put(DB_COLUMN, hash, ssz).unwrap();
db.put(DB_COLUMN, hash.as_bytes(), ssz).unwrap();
assert!(store.exists(hash).unwrap());
}
@@ -78,10 +78,10 @@ macro_rules! test_crud_for_store {
let store = $store::new(db.clone());
let ssz = "some bytes".as_bytes();
let hash = &Hash256::from("some hash".as_bytes());
let other_hash = &Hash256::from("another hash".as_bytes());
let hash = &Hash256::from([0xAA; 32]);
let other_hash = &Hash256::from([0xBB; 32]);
db.put(DB_COLUMN, hash, ssz).unwrap();
db.put(DB_COLUMN, hash.as_bytes(), ssz).unwrap();
assert!(!store.exists(other_hash).unwrap());
}
@@ -91,13 +91,13 @@ macro_rules! test_crud_for_store {
let store = $store::new(db.clone());
let ssz = "some bytes".as_bytes();
let hash = &Hash256::from("some hash".as_bytes());
let hash = &Hash256::from([0xAA; 32]);
db.put(DB_COLUMN, hash, ssz).unwrap();
assert!(db.exists(DB_COLUMN, hash).unwrap());
db.put(DB_COLUMN, hash.as_bytes(), ssz).unwrap();
assert!(db.exists(DB_COLUMN, hash.as_bytes()).unwrap());
store.delete(hash).unwrap();
assert!(!db.exists(DB_COLUMN, hash).unwrap());
assert!(!db.exists(DB_COLUMN, hash.as_bytes()).unwrap());
}
};
}

View File

@@ -37,7 +37,7 @@ mod tests {
let db = Arc::new(MemoryDB::open());
let store = PoWChainStore::new(db.clone());
let hash = &Hash256::from("some hash".as_bytes()).to_vec();
let hash = &Hash256::from([0xAA; 32]).as_bytes().to_vec();
store.put_block_hash(hash).unwrap();
assert!(db.exists(DB_COLUMN, hash).unwrap());
@@ -48,7 +48,7 @@ mod tests {
let db = Arc::new(MemoryDB::open());
let store = PoWChainStore::new(db.clone());
let hash = &Hash256::from("some hash".as_bytes()).to_vec();
let hash = &Hash256::from([0xAA; 32]).as_bytes().to_vec();
db.put(DB_COLUMN, hash, &[0]).unwrap();
assert!(store.block_hash_exists(hash).unwrap());
@@ -59,8 +59,8 @@ mod tests {
let db = Arc::new(MemoryDB::open());
let store = PoWChainStore::new(db.clone());
let hash = &Hash256::from("some hash".as_bytes()).to_vec();
let other_hash = &Hash256::from("another hash".as_bytes()).to_vec();
let hash = &Hash256::from([0xAA; 32]).as_bytes().to_vec();
let other_hash = &Hash256::from([0xBB; 32]).as_bytes().to_vec();
db.put(DB_COLUMN, hash, &[0]).unwrap();
assert!(!store.block_hash_exists(other_hash).unwrap());