Add comments, test for crosslink_processing()

This commit is contained in:
Paul Hauner 2018-07-15 13:47:54 +10:00
parent 6e9738fd6f
commit 13b2a674c9

View File

@ -83,10 +83,14 @@ pub fn process_crosslinks(
-> (Vec<i64>, Vec<CrosslinkRecord>) -> (Vec<i64>, Vec<CrosslinkRecord>)
{ {
assert!(partial_crosslinks.len() > 0, "No crosslinks present."); assert!(partial_crosslinks.len() > 0, "No crosslinks present.");
/*
* Create a map of shard_id -> (partial_crosslink, vote_count)
* to store the partial crosslink with the most votes for
* each shard.
*/
let mut shard_pc_map: let mut shard_pc_map:
HashMap<u16, (&PartialCrosslinkRecord, u64)> = HashMap::new(); HashMap<u16, (&PartialCrosslinkRecord, u64)> = HashMap::new();
for pc in partial_crosslinks { for pc in partial_crosslinks {
let vote_count = pc.voter_bitfield.num_true_bits(); let vote_count = pc.voter_bitfield.num_true_bits();
let mut competiting_vote_count = 0; let mut competiting_vote_count = 0;
@ -100,36 +104,44 @@ pub fn process_crosslinks(
shard_pc_map.insert(pc.shard_id, (pc, vote_count)); shard_pc_map.insert(pc.shard_id, (pc, vote_count));
} }
} }
let mut new_partial_crosslinks: Vec<&PartialCrosslinkRecord> = Vec::new(); // All shards which may are to be included in the next state.
shard_pc_map.iter_mut()
.for_each(|(_, v)| new_partial_crosslinks.push(v.0));
let crosslink_shards = get_crosslink_shards(&cry_state, &config); let crosslink_shards = get_crosslink_shards(&cry_state, &config);
// A list of balance deltas for each validator.
let mut deltas = vec![0_i64; cry_state.num_active_validators()]; let mut deltas = vec![0_i64; cry_state.num_active_validators()];
// A cloned list of validator records from crystallized state.
let mut new_crosslink_records: Vec<CrosslinkRecord> let mut new_crosslink_records: Vec<CrosslinkRecord>
= Vec::new(); = cry_state.crosslink_records.to_vec();
/*
* Loop through all shards up for inclusion in the next crystallized
* state and replace the existing CrosslinkRecord if we have a new
* PartialCrosslinkRecord with a quorum.
*/
for shard_id in &crosslink_shards { for shard_id in &crosslink_shards {
// Set of validator indicies for a given shard.
let notaries_indicies = get_crosslink_notaries( let notaries_indicies = get_crosslink_notaries(
&cry_state, &cry_state,
&shard_id, &shard_id,
&crosslink_shards); &crosslink_shards);
// Attempt to retrieve a partial crosslink for the current shard_id.
let new_partial_crosslink = shard_pc_map.get(&shard_id); let new_partial_crosslink = shard_pc_map.get(&shard_id);
// Retrieve present enshrined crosslink record for this shard.
let previous_crosslink_epoch = let previous_crosslink_epoch =
match cry_state.crosslink_records.get(*shard_id as usize) { match cry_state.crosslink_records.get(*shard_id as usize) {
None => panic!("shard_id not known by \ None => panic!("shard_id not known by \
crystallized state."), crystallized state."),
Some(c) => c.epoch Some(c) => c.epoch
}; };
// Determine rewards
let current_epoch = cry_state.current_epoch; let current_epoch = cry_state.current_epoch;
assert!(current_epoch >= previous_crosslink_epoch, "Previous crosslink \ assert!(current_epoch >= previous_crosslink_epoch, "Previous crosslink \
epoch cannot be > current epoch."); epoch cannot be > current epoch.");
let crosslink_distance = cry_state.current_epoch- previous_crosslink_epoch; let crosslink_distance = cry_state.current_epoch- previous_crosslink_epoch;
let online_reward: i64 = if crosslink_distance <= 2 { 3 } else { 0 }; let online_reward: i64 = if crosslink_distance <= 2 { 3 } else { 0 };
let offline_penalty: i64 = (crosslink_distance as i64).saturating_mul(2); let offline_penalty: i64 = (crosslink_distance as i64).saturating_mul(2);
// Loop through each notary for this shard and penalise/reward depending
// on if they voted or not.
for notary in &notaries_indicies { for notary in &notaries_indicies {
let voted = match new_partial_crosslink { let voted = match new_partial_crosslink {
None => false, None => false,
@ -140,13 +152,18 @@ pub fn process_crosslinks(
false => deltas[*notary] -= offline_penalty false => deltas[*notary] -= offline_penalty
}; };
} }
/*
* If there is a PartialCrosslinkRecord with a quorum of votes for
* this shard, create a new CrosslinkRecord. By default, if there
* is not a new partial record, the old CrosslinkRecord will be
* maintained.
*/
match new_partial_crosslink { match new_partial_crosslink {
None => { None => {},
new_crosslink_records[*shard_id as usize] =
cry_state.crosslink_records[*shard_id as usize].clone()
},
Some(pc) => { Some(pc) => {
let votes = pc.1; let votes = pc.1;
// If there are 2/3 or more votes from the notaries for this
// partial crosslink record, create a new CrosslinkRecord.
if ((votes as usize) * 3) >= (notaries_indicies.len() * 2) { if ((votes as usize) * 3) >= (notaries_indicies.len() * 2) {
new_crosslink_records[*shard_id as usize] = new_crosslink_records[*shard_id as usize] =
CrosslinkRecord { CrosslinkRecord {
@ -167,7 +184,7 @@ mod tests {
use super::*; use super::*;
use super::super::shuffling::get_shuffling; use super::super::shuffling::get_shuffling;
use super::super::super::validator_record::ValidatorRecord; use super::super::super::validator_record::ValidatorRecord;
use super::super::super::super::utils::types::Sha256Digest; use super::super::super::super::utils::types::{ Sha256Digest, Bitfield };
#[test] #[test]
fn test_crosslink_shard_count_with_varying_active_vals() { fn test_crosslink_shard_count_with_varying_active_vals() {
@ -272,7 +289,7 @@ mod tests {
fn test_crosslink_notaries_allocation() { fn test_crosslink_notaries_allocation() {
let mut cry_state = CrystallizedState::zero(); let mut cry_state = CrystallizedState::zero();
let mut config = Config::standard(); let mut config = Config::standard();
config.shard_count = 5; config.shard_count = 5;
config.notaries_per_crosslink = 2; config.notaries_per_crosslink = 2;
(0..10).for_each( (0..10).for_each(
@ -350,4 +367,56 @@ config.shard_count = 5;
&5, &5,
&crosslink_shards); &crosslink_shards);
} }
#[test]
fn test_crosslink_processing() {
let mut cry_state = CrystallizedState::zero();
let mut config = Config::standard();
let validator_count: usize = 10;
config.shard_count = 5;
config.notaries_per_crosslink = 2;
(0..validator_count).for_each(
|_| cry_state.active_validators.push(
ValidatorRecord::zero_with_thread_rand_pub_key()));
let s = get_shuffling(
&Sha256Digest::zero(),
&cry_state.num_active_validators(),
&config);
assert_eq!(s, [0, 9, 7, 6, 4, 1, 8, 5, 2, 3]);
cry_state.current_shuffling = s.clone();
cry_state.current_epoch = 100;
let mut partial_crosslinks: Vec<PartialCrosslinkRecord> = vec![];
for shard_id in 0..config.shard_count {
// Setup a recent crosslink record for each shard
cry_state.crosslink_records.push(CrosslinkRecord {
epoch: cry_state.current_epoch - 1,
hash: Sha256Digest::zero()
});
// Create a new partial crosslink record
let mut voter_bitfield = Bitfield::new();
(0..validator_count).for_each(|i| voter_bitfield.set_bit(&i, &true));
partial_crosslinks.push(PartialCrosslinkRecord {
shard_id,
shard_block_hash: Sha256Digest::from(shard_id as u64),
voter_bitfield
});
}
let (deltas, new_crosslinks) = process_crosslinks(
&cry_state,
&partial_crosslinks,
&config);
assert_eq!(deltas, vec![3; validator_count]);
for shard_id in 0..config.shard_count {
let c = new_crosslinks[shard_id as usize];
assert_eq!(c.epoch, cry_state.current_epoch);
assert_eq!(c.hash.low_u64(), shard_id as u64);
}
}
} }