Remove old transition functions

This commit is contained in:
Paul Hauner 2018-08-15 13:25:51 +10:00
parent 486865e803
commit 2256f52a44
10 changed files with 0 additions and 1869 deletions

View File

@ -1,211 +0,0 @@
use super::validator_record::ValidatorRecord;
use super::utils::types::Bitfield;
use super::utils::bls::{ AggregateSignature, PublicKey };
use super::utils::logging::Logger;
use super::crystallized_state::CrystallizedState;
use super::active_state::ActiveState;
use super::config::Config;
use super::shuffling::get_shuffling;
pub fn process_recent_attesters(
cry_state: &CrystallizedState,
recent_attesters: &Vec<usize>,
config: &Config)
-> Vec<i64>
{
let mut deltas: Vec<i64> = vec![0; cry_state.num_active_validators()];
for v in recent_attesters {
deltas[*v] += config.attester_reward;
}
deltas
}
// For a given state set and skip_count, return a proposer and set
// of attestors.
pub fn get_attesters_and_proposer(
cry_state: &CrystallizedState,
act_state: &ActiveState,
skip_count: &u64,
config: &Config,
log:&Logger)
-> (Vec<usize>, usize)
{
let active_validator_count = cry_state.num_active_validators();
assert!(active_validator_count >= 2, "must be >=2 active validators");
let shuffled_validator_indicies = get_shuffling(
&act_state.randao,
&active_validator_count,
config);
let proposer_count: usize = 1;
let ideal_validator_count: usize = (config.attester_count as usize)
+ (*skip_count as usize) + proposer_count;
assert!(ideal_validator_count >= 2,
"ideal_validator_count must be >=2");
/*
* If there are adequate validators to allocate a full set of assesters and
* a proposer, then do so. Otherwise, the amount of attesters will need to be
* validator_count - 1.
*/
match ideal_validator_count > active_validator_count {
true => {
/*
* The active validator count is too low.
*/
warn!(log, "active validator count is low";
"active_validator_count" => active_validator_count,
"ideal_validator_count" => ideal_validator_count);
(shuffled_validator_indicies[0..active_validator_count - 1].to_vec(),
shuffled_validator_indicies[active_validator_count - 1])
}
false => {
/*
* The active validator count is adequate.
*/
(shuffled_validator_indicies[0..ideal_validator_count - 1].to_vec(),
shuffled_validator_indicies[ideal_validator_count - 1])
}
}
}
#[allow(unused_variables)]
pub fn process_attestations(
validators: &Vec<ValidatorRecord>,
attestation_indicies: &Vec<usize>,
attestation_bitfield: &Bitfield,
msg: &Vec<u8>,
aggregate_sig: &AggregateSignature)
-> Option<Vec<usize>>
{
let mut key_msg_tuples: Vec<(&PublicKey, &[u8])> = vec![];
let mut attesters: Vec<usize> = vec![];
assert_eq!(
attestation_indicies.len(), attestation_bitfield.len(),
"Bitfield length does not match required attestors."
);
for (bitfield_bit, validators_index) in attestation_indicies.iter().enumerate() {
if attestation_bitfield.get_bit(&bitfield_bit) {
key_msg_tuples.push(
(&validators[*validators_index].pubkey,
&msg)
);
attesters.push(*validators_index);
}
}
// TODO: figure out why this assert exists in the Python impl.
assert!(attesters.len() <= 128, "Max attesters is 128.");
/*
// TODO: ensure signature verification actually takes place.
// It is completely bypassed here.
match aggregate_sig.verify(&key_msg_tuples) {
false => None,
true => Some(attesters)
}
*/
Some(attesters)
}
#[cfg(test)]
mod tests {
use super::*;
use super::super::utils::logging::test_logger;
#[test]
fn test_process_recent_attesters() {
let mut cry_state = CrystallizedState::zero();
let mut config = Config::standard();
let validator_count = 20;
config.attester_reward = 12;
let mut recent_attesters: Vec<usize> = vec![];
for i in 0..validator_count {
cry_state.active_validators
.push(ValidatorRecord::zero_with_thread_rand_pub_key());
if i % 2 == 0 {
recent_attesters.push(i);
}
}
let d = process_recent_attesters(
&cry_state,
&recent_attesters,
&config);
for i in 0..validator_count {
if i % 2 == 0 {
assert_eq!(d[i], config.attester_reward);
} else {
assert_eq!(d[i], 0);
}
}
}
#[test]
fn test_attester_and_proposer_selection() {
let mut cry_state = CrystallizedState::zero();
(0..10).for_each(
|_| cry_state.active_validators.push(
ValidatorRecord::zero_with_thread_rand_pub_key()));
let act_state = ActiveState::zero();
let (attestors, proposer) = get_attesters_and_proposer(
&cry_state,
&act_state,
&0,
&Config::standard(),
&test_logger());
assert_eq!(attestors, [0, 9, 7, 6, 4, 1, 8, 5, 2]);
assert_eq!(proposer, 3);
}
#[test]
#[should_panic(expected = "must be >=2 active validators")]
fn test_attester_and_proposer_selection_with_zero_active_validators() {
let mut cry_state = CrystallizedState::zero();
cry_state.active_validators = Vec::new();
let act_state = ActiveState::zero();
let (_attestors, _proposer) = get_attesters_and_proposer(
&cry_state,
&act_state,
&0,
&Config::standard(),
&test_logger());
}
#[test]
fn test_attestation_processing() {
let validator_count = 10;
let mut validators: Vec<ValidatorRecord> = vec![];
let mut attestation_indicies: Vec<usize> = vec![];
let mut bitfield = Bitfield::new();
let mut agg_sig = AggregateSignature::new();
let msg = "Message that's longer than 16 chars".as_bytes();
for i in 0..validator_count {
let (v, keypair) =
ValidatorRecord::zero_with_thread_rand_keypair();
validators.push(v);
attestation_indicies.push(i);
bitfield.set_bit(&i, &true);
let sig = keypair.sign(&msg);
agg_sig.aggregate(&sig);
}
let result = process_attestations(
&validators,
&attestation_indicies,
&bitfield,
&msg.to_vec(),
&agg_sig);
match result {
None => panic!("Verification failed."),
Some(x) => println!("{:?}", x)
};
}
}

View File

@ -1,513 +0,0 @@
use std::collections::HashMap;
use std::cmp::min;
use super::bytes::{ BytesMut, BufMut };
use super::aggregate_vote::AggregateVote;
use super::crystallized_state::CrystallizedState;
use super::crosslink_record::CrosslinkRecord;
use super::partial_crosslink_record::PartialCrosslinkRecord;
use super::config::Config;
const AGG_VOTE_MSG_SIZE: i32 = 2 + 32 + 32 + 8 + 8;
// Given an aggregate_vote and a crystallized_state,
// return a byte array for signing or verification.
pub fn get_crosslink_aggvote_msg(
agg_vote: &AggregateVote,
cry_state: &CrystallizedState)
-> Vec<u8>
{
let mut buf = BytesMut::with_capacity(AGG_VOTE_MSG_SIZE as usize);
buf.put_u16_be(agg_vote.shard_id);
buf.extend_from_slice(&agg_vote.shard_block_hash.to_vec());
buf.extend_from_slice(&cry_state.current_checkpoint.to_vec());
buf.put_u64_be(cry_state.current_epoch);
buf.put_u64_be(cry_state.last_justified_epoch);
buf.to_vec()
}
// Returns the maximum possible shards for a given validator_count
// and configuration.
pub fn get_crosslink_shards_count(
active_validator_count: &usize,
config: &Config)
-> u16
{
let system_shard_count: u16 = config.shard_count;
let notaries_per_crosslink: u16 = config.notaries_per_crosslink;
assert!(notaries_per_crosslink > 0, "Shards must require > 0 notaries.");
let notarisable_shard_count =
*active_validator_count as u64 / notaries_per_crosslink as u64;
min(notarisable_shard_count, system_shard_count as u64) as u16
}
pub fn get_crosslink_shards(
cry_state: &CrystallizedState,
config: &Config)
-> Vec<u16>
{
let max_shard_count: u16 = config.shard_count;
let first_shard: u16 = cry_state.next_shard;
assert!(first_shard < max_shard_count, "CrystallizedState.next_shard \
must be less than Config.shard_count.");
let shard_count = get_crosslink_shards_count(
&cry_state.num_active_validators(),
&config);
let unwrapped_shards: u16 = min(first_shard + shard_count, max_shard_count);
let wrapped_shards: u16 = (first_shard + shard_count) % max_shard_count;
let mut crosslink_shards: Vec<u16> = (first_shard..unwrapped_shards).collect();
crosslink_shards.append(&mut (0_u16..wrapped_shards).collect());
crosslink_shards
}
pub fn get_crosslink_notaries(
cry_state: &CrystallizedState,
shard_id: &u16,
crosslink_shards: &Vec<u16>)
-> Vec<usize>
{
let shard_crosslink_index = crosslink_shards.iter().
position(|&s| s == *shard_id);
match shard_crosslink_index {
None => panic!("shard_id not in crosslink_shards."),
Some(i) => {
let crosslink_shards_count = crosslink_shards.len();
assert!(crosslink_shards_count > 0,
"crosslink_shards_count must be > 0");
let active_validators = cry_state.num_active_validators();
assert!(active_validators > 0,
"active_validators must be > 0");
let start = active_validators * i / crosslink_shards_count;
let end = active_validators * (i + 1) / crosslink_shards_count;
assert!(cry_state.current_shuffling.len() == active_validators,
"Crystallized state shuffling does not match active \
validator count");
cry_state.current_shuffling[start..end].to_vec()
}
}
}
pub fn process_crosslinks(
cry_state: &CrystallizedState,
partial_crosslinks: &Vec<PartialCrosslinkRecord>,
config: &Config)
-> (Vec<i64>, Vec<CrosslinkRecord>)
{
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:
HashMap<u16, (&PartialCrosslinkRecord, u64)> = HashMap::new();
for pc in partial_crosslinks {
let vote_count = pc.voter_bitfield.num_true_bits();
let mut competiting_vote_count = 0;
match shard_pc_map.get(&pc.shard_id) {
Some(&competitor) => competiting_vote_count = competitor.1,
None => {}
}
// Here we implicitly avoid adding crosslinks with 0 votes
// to our shard_pc_map.
if vote_count > competiting_vote_count {
shard_pc_map.insert(pc.shard_id, (pc, vote_count));
}
}
// All shards which may are to be included in the next state.
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()];
// A cloned list of validator records from crystallized state.
let mut new_crosslink_records: Vec<CrosslinkRecord>
= 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 {
// Set of validator indicies for a given shard.
let notaries_indicies = get_crosslink_notaries(
&cry_state,
&shard_id,
&crosslink_shards);
// Attempt to retrieve a partial crosslink for the current shard_id.
let new_partial_crosslink = shard_pc_map.get(&shard_id);
// Retrieve present enshrined crosslink record for this shard.
let previous_crosslink_epoch =
match cry_state.crosslink_records.get(*shard_id as usize) {
None => panic!("shard_id not known by \
crystallized state."),
Some(c) => c.epoch
};
// Determine rewards
let current_epoch = cry_state.current_epoch;
assert!(current_epoch >= previous_crosslink_epoch, "Previous crosslink \
epoch cannot be > current epoch.");
let crosslink_distance = cry_state.current_epoch- previous_crosslink_epoch;
let online_reward: i64 = if crosslink_distance <= 2 { 3 } else { 0 };
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 {
let voted = match new_partial_crosslink {
None => false,
Some(pc) => pc.0.voter_bitfield.get_bit(&notary)
};
match voted {
true => deltas[*notary] += online_reward,
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 {
None => {},
Some(pc) => {
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) {
new_crosslink_records[*shard_id as usize] =
CrosslinkRecord {
epoch: current_epoch,
hash: pc.0.shard_block_hash
};
}
}
}
}
(deltas, new_crosslink_records)
}
#[cfg(test)]
mod tests {
use super::*;
use super::super::shuffling::get_shuffling;
use super::super::super::validator_record::ValidatorRecord;
use super::super::super::super::utils::types::{ Sha256Digest, Bitfield };
#[test]
fn test_crosslink_aggvote_msg() {
let mut cs_state = CrystallizedState::zero();
let mut agg_vote = AggregateVote::zero();
// All zeros
let m1 = get_crosslink_aggvote_msg(&agg_vote, &cs_state);
assert_eq!(m1,
vec![0_u8; AGG_VOTE_MSG_SIZE as usize],
"failed all zeros test");
// With some values
agg_vote.shard_id = 42;
cs_state.current_epoch = 99;
cs_state.last_justified_epoch = 123;
let m2 = get_crosslink_aggvote_msg(&agg_vote, &cs_state);
assert_eq!(m2[0..2], [0, 42]);
assert_eq!(m2[2..34], [0; 32]); // TODO: test with non-zero hash
assert_eq!(m2[34..66], [0; 32]); // TODO: test with non-zero hash
assert_eq!(m2[66..74], [0, 0, 0, 0, 0, 0, 0, 99]);
assert_eq!(m2[74..82], [0, 0, 0, 0, 0, 0, 0, 123]);
}
#[test]
fn test_crosslink_shard_count_with_varying_active_vals() {
let mut config = Config::standard();
config.shard_count = 10;
config.notaries_per_crosslink = 10;
let mut c = get_crosslink_shards_count(
&100,
&config);
assert_eq!(c, 10);
c = get_crosslink_shards_count(
&101,
&config);
assert_eq!(c, 10);
c = get_crosslink_shards_count(
&99,
&config);
assert_eq!(c, 9);
c = get_crosslink_shards_count(
&0,
&config);
assert_eq!(c, 0);
}
#[test]
#[should_panic(expected = "must require > 0 notaries.")]
fn test_crosslink_shard_count_with_zero_notaries_per_crosslink() {
let mut config = Config::standard();
config.shard_count = 10;
config.notaries_per_crosslink = 0;
let validators: u16 = 10;
let _ = get_crosslink_shards_count(
&(validators as usize),
&config);
}
#[test]
fn test_crosslink_shard_getter_with_5_shards() {
let mut cry_state = CrystallizedState::zero();
let mut config = Config::standard();
config.shard_count = 5;
config.notaries_per_crosslink = 2;
(0..10).for_each(
|_| cry_state.active_validators.push(
ValidatorRecord::zero_with_thread_rand_pub_key()));
cry_state.next_shard = 0;
let c = get_crosslink_shards(
&cry_state,
&config);
assert_eq!(c, [0, 1, 2, 3, 4]);
cry_state.next_shard = 4;
let c = get_crosslink_shards(
&cry_state,
&config);
assert_eq!(c, [4, 0, 1, 2, 3]);
cry_state.next_shard = 1;
let c = get_crosslink_shards(
&cry_state,
&config);
assert_eq!(c, [1, 2, 3, 4, 0]);
cry_state.next_shard = 3;
let c = get_crosslink_shards(
&cry_state,
&config);
assert_eq!(c, [3, 4, 0, 1, 2]);
}
#[test]
#[should_panic(expected = "next_shard must be less than Config.shard_count")]
fn test_crosslink_shard_getter_with_too_large_next_shard() {
let mut cry_state = CrystallizedState::zero();
let mut config = Config::standard();
config.shard_count = 1;
config.notaries_per_crosslink = 2;
(0..2).for_each(
|_| cry_state.active_validators.push(
ValidatorRecord::zero_with_thread_rand_pub_key()));
cry_state.next_shard = 6;
let _ = get_crosslink_shards(
&cry_state,
&config);
}
#[test]
fn test_crosslink_notaries_allocation() {
let mut cry_state = CrystallizedState::zero();
let mut config = Config::standard();
config.shard_count = 5;
config.notaries_per_crosslink = 2;
(0..10).for_each(
|_| cry_state.active_validators.push(
ValidatorRecord::zero_with_thread_rand_pub_key()));
cry_state.next_shard = 0;
let crosslink_shards = get_crosslink_shards(
&cry_state,
&config);
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();
let mut n = get_crosslink_notaries(
&cry_state,
&0,
&crosslink_shards);
assert_eq!(n, [0, 9]);
n = get_crosslink_notaries(
&cry_state,
&1,
&crosslink_shards);
assert_eq!(n, [7, 6]);
n = get_crosslink_notaries(
&cry_state,
&2,
&crosslink_shards);
assert_eq!(n, [4, 1]);
n = get_crosslink_notaries(
&cry_state,
&3,
&crosslink_shards);
assert_eq!(n, [8, 5]);
n = get_crosslink_notaries(
&cry_state,
&4,
&crosslink_shards);
assert_eq!(n, [2, 3]);
}
#[test]
#[should_panic(expected = "shard_id not in crosslink_shards")]
fn test_crosslink_notaries_allocation_with_invalid_shard() {
let mut cry_state = CrystallizedState::zero();
let mut config = Config::standard();
config.shard_count = 5;
config.notaries_per_crosslink = 2;
(0..10).for_each(
|_| cry_state.active_validators.push(
ValidatorRecord::zero_with_thread_rand_pub_key()));
cry_state.next_shard = 0;
let crosslink_shards = get_crosslink_shards(
&cry_state,
&config);
cry_state.current_shuffling = get_shuffling(
&Sha256Digest::zero(),
&cry_state.num_active_validators(),
&config);
let _ = get_crosslink_notaries(
&cry_state,
&5,
&crosslink_shards);
}
#[test]
fn test_crosslink_processing_with_perfect_partials() {
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);
}
}
#[test]
fn test_crosslink_processing_with_no_voting() {
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
partial_crosslinks.push(PartialCrosslinkRecord {
shard_id,
shard_block_hash: Sha256Digest::from(shard_id as u64),
voter_bitfield: Bitfield::new()
});
}
let (deltas, new_crosslinks) = process_crosslinks(
&cry_state,
&partial_crosslinks,
&config);
assert_eq!(deltas, vec![-2; 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 - 1);
assert_eq!(c.hash.low_u64(), 0);
}
}
}

View File

@ -1,177 +0,0 @@
use super::crystallized_state::CrystallizedState;
use super::utils::types::{ Bitfield, U256 };
use super::utils::logging::Logger;
pub fn process_ffg_deposits(
cry_state: &CrystallizedState,
ffg_vote_bitfield: &Bitfield,
log: &Logger)
-> (Vec<i64>, u64, U256, bool, bool)
{
let active_validators: usize = cry_state.num_active_validators();
let finality_distance: u64 = cry_state.finality_distance();
let online_reward: u64 = if finality_distance <= 2 { 6 } else { 0 };
let offline_penalty: u64 = finality_distance.saturating_mul(3);
let mut total_vote_count: u64 = 0;
let mut total_vote_deposits = U256::zero();
let mut deltas = vec![0_i64; active_validators];
for i in 0..active_validators {
if ffg_vote_bitfield.get_bit(&i) {
total_vote_deposits = total_vote_deposits
.saturating_add(cry_state.active_validators[i].balance);
deltas[i] += online_reward as i64;
total_vote_count += 1;
} else {
deltas[i] -= offline_penalty as i64;
}
}
// Justify if total voting deposits is greater than 2/3 the total deposits.
let should_justify = total_vote_deposits.saturating_mul(U256::from(3))
>= cry_state.total_deposits.saturating_mul(U256::from(2));
let mut should_finalize = false;
if should_justify {
if cry_state.last_justified_epoch ==
cry_state.current_epoch.saturating_sub(1) {
should_finalize = true;
}
}
info!(log, "counted ffg votes";
"total_vote_count" => total_vote_count,
"total_vote_deposits" => total_vote_deposits.low_u64());
(deltas, total_vote_count, total_vote_deposits, should_justify, should_finalize)
}
#[cfg(test)]
mod tests {
use super::*;
use super::super::utils::types::{ Address, Sha256Digest };
use super::super::utils::logging::test_logger;
use super::super::super::validator_record::ValidatorRecord;
use super::super::
utils::test_helpers::get_dangerous_test_keypair;
#[test]
fn test_deposit_processing_scenario_1() {
let log = test_logger();
let mut cry_state = CrystallizedState::zero();
let mut bitfield = Bitfield::new();
let mut total_deposits = U256::zero();
let individual_deposit = U256::from(1);
// load some validators into the cry state and flag
// they have all voted
for i in 0..10 {
cry_state.active_validators.push(ValidatorRecord {
pubkey: get_dangerous_test_keypair().public,
withdrawal_shard: 0,
withdrawal_address: Address::zero(),
randao_commitment: Sha256Digest::zero(),
balance: individual_deposit,
switch_dynasty: 0
});
total_deposits = total_deposits + individual_deposit;
bitfield.set_bit(&i, &true);
}
cry_state.current_epoch = 100;
cry_state.last_justified_epoch = 99;
cry_state.last_finalized_epoch = 98;
cry_state.total_deposits = total_deposits;
let (deltas, total_vote_count, total_vote_deposits,
should_justify, should_finalize) = process_ffg_deposits(
&cry_state, &bitfield, &log);
assert_eq!(deltas, [6; 10]);
assert_eq!(total_vote_count, 10);
assert_eq!(total_vote_deposits, total_deposits);
assert_eq!(should_justify, true);
assert_eq!(should_finalize, true);
}
#[test]
fn test_deposit_processing_scenario_2() {
let log = test_logger();
let mut cry_state = CrystallizedState::zero();
let bitfield = Bitfield::new();
let individual_deposit = U256::from(0);
// load some validators into the cry state and flag
// they have all voted
for _ in 0..10 {
cry_state.active_validators.push(ValidatorRecord {
pubkey: get_dangerous_test_keypair().public,
withdrawal_shard: 0,
withdrawal_address: Address::zero(),
randao_commitment: Sha256Digest::zero(),
balance: individual_deposit,
switch_dynasty: 0
});
}
cry_state.current_epoch = 100;
cry_state.last_justified_epoch = 99;
cry_state.last_finalized_epoch = 98;
cry_state.total_deposits = U256::from(10);
let (deltas, total_vote_count, total_vote_deposits,
should_justify, should_finalize) = process_ffg_deposits(
&cry_state, &bitfield, &log);
assert_eq!(deltas, [-6; 10]);
assert_eq!(total_vote_count, 0);
assert_eq!(total_vote_deposits, U256::zero());
assert_eq!(should_justify, false);
assert_eq!(should_finalize, false);
}
#[test]
fn test_deposit_processing_scenario_3() {
let log = test_logger();
let mut cry_state = CrystallizedState::zero();
let mut bitfield = Bitfield::new();
let mut total_deposits = U256::zero();
let individual_deposit = U256::from(50);
// load some validators into the cry state and flag
// some have voted
for i in 0..10 {
cry_state.active_validators.push(ValidatorRecord {
pubkey: get_dangerous_test_keypair().public,
withdrawal_shard: 0,
withdrawal_address: Address::zero(),
randao_commitment: Sha256Digest::zero(),
balance: individual_deposit,
switch_dynasty: 0,
});
if i < 5 {
bitfield.set_bit(&i, &true);
total_deposits = total_deposits + individual_deposit;
}
}
cry_state.current_epoch = 100;
cry_state.last_justified_epoch = 99;
cry_state.last_finalized_epoch = 98;
cry_state.total_deposits = U256::from(5);
let (deltas, total_vote_count, total_vote_deposits,
should_justify, should_finalize) = process_ffg_deposits(
&cry_state, &bitfield, &log);
assert_eq!(deltas[0..5].to_vec(), [6;5]);
assert_eq!(deltas[5..10].to_vec(), [-6;5]);
assert_eq!(total_vote_count, 5);
assert_eq!(total_vote_deposits, total_deposits);
assert_eq!(should_justify, true);
assert_eq!(should_finalize, true);
}
}

View File

@ -1,203 +0,0 @@
use super::active_state::ActiveState;
use super::crystallized_state::CrystallizedState;
use super::validator_record::ValidatorRecord;
use super::utils::types::{ Bitfield, U256, Sha256Digest };
use super::utils::logging::Logger;
use super::config::Config;
use super::deposits::process_ffg_deposits;
use super::crosslinks::process_crosslinks;
use super::attestors::process_recent_attesters;
use super::proposers::process_recent_proposers;
use super::validators::get_incremented_validator_sets;
use super::shuffling::get_shuffling;
pub fn initialize_new_epoch(
cry_state: &CrystallizedState,
act_state: &ActiveState,
config: &Config,
log: &Logger)
-> (CrystallizedState, ActiveState)
{
/*
* Clone the cry_state active validators and the
* act_state ffg bitfield for later modification.
*/
let mut new_validator_records: Vec<ValidatorRecord> =
cry_state.active_validators.to_vec();
// TODO: why isnt this mut?
let ffg_voter_bitfield: Bitfield =
act_state.ffg_voter_bitfield.clone();
/*
* For each active_validator in the cry_state, reward/penalize
* them according to their presence in the ffg voter bitfield
* (also with consideration to the cry_state finality distance).
* These rewards/penalties are represented in the ffg_deltas vec.
*
* Determines if justification should take place based upon
* the ratio of total deposits to voting deposits. If justification
* is possible, finalize if the previous epoch was also justified.
*/
let (ffg_deltas, _, _, should_justify, should_finalize) =
process_ffg_deposits (
&cry_state,
&ffg_voter_bitfield,
&log);
info!(log, "processed ffg deposits";
"should_justify" => should_justify,
"should_finalize" => should_finalize);
/*
* For all the partial crosslinks in the active state, return a vec of
* complete crosslink records representing the most popular partial
* record for each shard_id.
*
* During this process, create a vec of deltas rewarding/penalizing each
* validator for thier votes/non-votes on their allocated shard_ids.
*/
let (crosslink_notaries_deltas, new_crosslinks) =
process_crosslinks(
&cry_state,
&act_state.partial_crosslinks,
&config);
info!(log, "processed crosslinks";
"new_crosslinks_count" => new_crosslinks.len());
/*
* Create a vec of deltas rewarding/penalizing each validator
* for their votes/non-votes on blocks during the last epoch.
*/
let recent_attesters_deltas = process_recent_attesters(
&cry_state,
&act_state.recent_attesters,
&config);
/*
* Create a vec of deltas rewarding/penalizing each validator
* for their block proposals during the past epoch.
*/
let recent_proposers_deltas = process_recent_proposers(
&cry_state,
&act_state.recent_proposers);
/*
* For each validator, update their balances as per the deltas calculated
* previously in this function.
*/
for (i, validator) in new_validator_records.iter_mut().enumerate() {
let balance: i64 =
validator.balance.low_u64() as i64 +
ffg_deltas[i] +
crosslink_notaries_deltas[i] +
recent_attesters_deltas[i] +
recent_proposers_deltas[i];
if balance > 0 {
validator.balance = U256::from(balance as u64);
} else {
validator.balance = U256::zero();
}
}
/*
* Determine the new total deposit sum, determined by the individual
* rewards/penalities accrued by validators during this epoch.
*/
let deposit_sum: i64 =
ffg_deltas.iter().sum::<i64>() +
crosslink_notaries_deltas.iter().sum::<i64>() +
recent_attesters_deltas.iter().sum::<i64>() +
recent_proposers_deltas.iter().sum::<i64>();
info!(log, "processed validator deltas";
"new_total_deposits" => deposit_sum);
let total_deposits: U256 = match deposit_sum > 0 {
true => U256::from(deposit_sum as u64),
false => U256::zero()
};
let last_justified_epoch = match should_justify {
true => cry_state.current_epoch,
false => cry_state.last_justified_epoch
};
let (last_finalized_epoch, dynasty) = match should_finalize {
true => (cry_state.current_epoch - 1, cry_state.dynasty + 1),
false => (cry_state.last_finalized_epoch, cry_state.dynasty)
};
/*
* If finalization should take place, "increment" the validator sets.
* This involves exiting validators who's balance is too low (from
* deltas) or who's dynasty has ended and inducting queued validators
* (if possible).
*/
let (new_queued_validators, new_active_validators, new_exited_validators) =
match should_finalize
{
true => get_incremented_validator_sets(
&cry_state,
&new_validator_records,
&config,
&log),
false => (cry_state.queued_validators.to_vec(),
cry_state.active_validators.to_vec(),
cry_state.exited_validators.to_vec())
};
/*
* Get the validator shuffling for the new epoch, based upon
* the rando of the supplied active state.
*/
let shuffling = get_shuffling(
&act_state.randao,
&new_active_validators.len(),
&config);
/*
* Generate a new CrystallizedState
*/
let new_cry_state = CrystallizedState {
active_validators: new_active_validators,
queued_validators: new_queued_validators,
exited_validators: new_exited_validators,
current_shuffling: shuffling,
current_epoch: cry_state.current_epoch + 1,
last_justified_epoch,
last_finalized_epoch,
dynasty,
// TODO: why is this zero?
next_shard: 0,
// TODO: currenct checkpoint wasnt in reference implementation
current_checkpoint: Sha256Digest::zero(),
crosslink_records: new_crosslinks,
total_deposits
};
info!(log, "created new crystallized state";
"epoch" => new_cry_state.current_epoch,
"last_justified_epoch" => new_cry_state.last_justified_epoch,
"last_finalized_epoch" => new_cry_state.last_finalized_epoch);
/*
* Replicate the supplied active state, but reset the fields which
* accumulate things during the course of an epoch (e.g, recent_proposers,
* partial_crosslinks, etc)
*/
let new_act_state = ActiveState {
height: act_state.height,
randao: act_state.randao,
ffg_voter_bitfield: Bitfield::new(),
recent_attesters: vec![],
partial_crosslinks: vec![],
total_skip_count: act_state.total_skip_count,
recent_proposers: vec![]
};
info!(log, "reset active state";
"height" => new_act_state.height);
(new_cry_state, new_act_state)
}

View File

@ -1,192 +0,0 @@
use std::collections::HashMap;
use super::crystallized_state::CrystallizedState;
use super::partial_crosslink_record::PartialCrosslinkRecord;
use super::aggregate_vote::AggregateVote;
use super::config::Config;
use super::utils::types::Bitfield;
use super::utils::bls::PublicKey;
use super::crosslinks::{
get_crosslink_shards,
get_crosslink_aggvote_msg,
get_crosslink_notaries };
pub fn update_ffg_and_crosslink_progress(
cry_state: &CrystallizedState,
partial_crosslinks: &Vec<PartialCrosslinkRecord>,
ffg_voter_bitfield: &Bitfield,
aggregate_votes: &Vec<AggregateVote>,
config: &Config)
-> (Vec<PartialCrosslinkRecord>, Bitfield, usize)
{
let mut vote_key_bitfield_map: HashMap<Vec<u8>, Bitfield> =
HashMap::new();
for pc in partial_crosslinks {
vote_key_bitfield_map.insert(pc.vote_key(), pc.voter_bitfield.clone());
}
let mut global_bitfield = ffg_voter_bitfield.clone();
let mut total_voters: usize = 0;
let crosslink_shards: Vec<u16> = get_crosslink_shards(
&cry_state, &config);
for av in aggregate_votes {
let attestation = get_crosslink_aggvote_msg(
&av,
&cry_state);
let validator_indicies = get_crosslink_notaries(
&cry_state,
&av.shard_id,
&crosslink_shards);
let mut crosslink_bitfield = match vote_key_bitfield_map.get(&av.vote_key()) {
None => Bitfield::new(),
Some(existing_bitfield) => existing_bitfield.clone()
};
let mut public_keys: Vec<&PublicKey> = vec![];
for (i, vi) in validator_indicies.iter().enumerate() {
if av.notary_bitfield.get_bit(&i) {
public_keys.push(&cry_state.active_validators[i].pubkey);
if global_bitfield.get_bit(&vi) == false {
global_bitfield.set_bit(&vi, &true);
crosslink_bitfield.set_bit(&i, &true);
total_voters += 1;
}
}
}
// TODO: add bls verfification here, it is completely bypassed
assert_eq!(attestation, attestation); // fixes warning
vote_key_bitfield_map.insert(av.vote_key(), crosslink_bitfield);
}
let mut new_partial_crosslinks: Vec<PartialCrosslinkRecord> = vec![];
for (vote_key, bitfield) in vote_key_bitfield_map {
new_partial_crosslinks.push(PartialCrosslinkRecord::new_from_vote_key(
&vote_key,
bitfield));
}
(new_partial_crosslinks, global_bitfield, total_voters)
}
#[cfg(test)]
mod tests {
use super::*;
use super::super::shuffling::get_shuffling;
use super::super::super::validator_record::ValidatorRecord;
use super::super::super::super::utils::types::{ Sha256Digest, Bitfield };
use super::super::super::super::utils::bls::AggregateSignature;
#[test]
fn test_update_ffg_and_crosslink_progress_scenario_1() {
let mut cry_state = CrystallizedState::zero();
let mut config = Config::standard();
// Create some shard_ids and associated hashes
let shard_ids: Vec<u16> = (0..10).collect();
let shard_hashes: Vec<Sha256Digest> = shard_ids.iter()
.map(|_| Sha256Digest::random()).collect();
// Define which shards with have partial crosslinks and which will
// have aggregate votes. Note: there should be some overlap here.
let shards_with_partial_crosslinks: Vec<u16> = shard_ids[0..5].to_vec();
let shards_with_aggregate_votes: Vec<u16> = shard_ids[4..10].to_vec();
// Update the config to neatly fit the shards we created.
config.shard_count = shard_ids.len() as u16;
config.notaries_per_crosslink = 10;
// Create just enough validators to notarise each shard
let validator_count: usize =
(config.shard_count * config.notaries_per_crosslink) as usize;
// Load active validators into the cry_state
(0..validator_count).for_each(
|_| cry_state.active_validators.push(
ValidatorRecord::zero_with_thread_rand_pub_key()));
// Set a shuffling for the validators
let s = get_shuffling(
&Sha256Digest::zero(),
&cry_state.num_active_validators(),
&config);
cry_state.current_shuffling = s.clone();
// Create the required partial crosslink records
let partial_crosslinks: Vec<PartialCrosslinkRecord> =
shards_with_partial_crosslinks
.iter()
.map(|i| {
let mut bitfield = Bitfield::new();
// Only the first 7 validators should sign the partial xlinks
for i in 0..7 {
bitfield.set_bit(&i, &true);
}
PartialCrosslinkRecord {
shard_id: *i,
shard_block_hash: shard_hashes[*i as usize],
voter_bitfield: bitfield
}
}).collect();
let mut total_aggregate_sig_votes = 0;
let mut aggregate_sig_bitfield = Bitfield::new();
// Create the required aggregate votes
let aggregate_votes: Vec<AggregateVote> = shards_with_aggregate_votes
.iter()
.map(|i| {
let validator_indicies = get_crosslink_notaries(
&cry_state,
i,
&shard_ids);
let mut bitfield = Bitfield::new();
// Only the last 2 validators should sign the aggregate votes
for i in 8..10 {
bitfield.set_bit(&i, &true);
total_aggregate_sig_votes += 1;
aggregate_sig_bitfield.set_bit(&validator_indicies[i], &true)
}
AggregateVote {
shard_id: *i,
shard_block_hash: shard_hashes[*i as usize],
notary_bitfield: bitfield,
aggregate_sig: AggregateSignature::new()
}
}).collect();
assert_eq!(aggregate_votes.len(), shards_with_aggregate_votes.len(),
"test setup failed.");
let (new_partial_crosslinks, global_bitfield, vote_count) =
update_ffg_and_crosslink_progress(
&cry_state,
&partial_crosslinks,
&Bitfield::new(),
&aggregate_votes,
&config);
assert_eq!(total_aggregate_sig_votes, vote_count,
"The total votes returned did not \
match our running tally.");
assert_eq!(total_aggregate_sig_votes,
global_bitfield.num_true_bits() as usize,
"The FFG field did not have as many true \
bits as expected.");
assert!(aggregate_sig_bitfield == global_bitfield);
for pc in new_partial_crosslinks {
let id = pc.shard_id;
let mut vote_count = 0;
if shards_with_partial_crosslinks.contains(&id) {
vote_count += 7;
}
if shards_with_aggregate_votes.contains(&id) {
vote_count += 2;
}
assert_eq!(pc.voter_bitfield.num_true_bits(), vote_count,
"shard_id {} failed.", id);
assert_eq!(pc.shard_block_hash, shard_hashes[id as usize]);
}
}
}

View File

@ -1,76 +0,0 @@
extern crate rlp;
use super::bytes;
use super::config;
use super::utils;
use super::blake2;
use super::active_state;
use super::aggregate_vote;
use super::crystallized_state;
use super::crosslink_record;
use super::partial_crosslink_record;
use super::recent_proposer_record;
use super::validator_record;
use super::block;
pub mod new_active_state;
pub mod crosslinks;
pub mod deposits;
pub mod epoch;
pub mod ffg;
pub mod proposers;
pub mod shuffling;
pub mod validators;
pub mod attestors;
use super::block::Block;
use super::config::Config;
use super::crystallized_state::CrystallizedState;
use super::active_state::ActiveState;
use super::transition::epoch::initialize_new_epoch;
use super::transition::new_active_state::compute_new_active_state;
use super::utils::logging::Logger;
pub fn compute_state_transition (
parent_cry_state: &CrystallizedState,
parent_act_state: &ActiveState,
parent_block: &Block,
block: &Block,
config: &Config,
log: &Logger)
-> (CrystallizedState, ActiveState)
{
let is_new_epoch = parent_act_state.height %
config.epoch_length == 0;
/*
* If this transition will push the chain into a new epoch,
* calculate a new crystallized state and "reset" the
* current active state. Otherwise, continue with the existing
* state pair.
*/
let (cry_state, mut act_state) = match is_new_epoch {
false => (parent_cry_state.clone(), parent_act_state.clone()),
true => initialize_new_epoch(
&parent_cry_state,
&parent_act_state,
&config,
&log)
};
if is_new_epoch {
info!(log, "initialized new epoch";
"epoch" => cry_state.current_epoch);
}
act_state = compute_new_active_state(
&cry_state,
&act_state,
&parent_block,
&block,
&config,
&log);
(cry_state, act_state)
}

View File

@ -1,137 +0,0 @@
use std::ops::BitXor;
use super::active_state::ActiveState;
use super::crystallized_state::CrystallizedState;
use super::recent_proposer_record::RecentPropserRecord;
use super::block::Block;
use super::utils::types::Sha256Digest;
use super::utils::logging::Logger;
use super::config::Config;
use super::super::pubkeystore::verify_block;
use super::rlp;
use super::attestors::{
process_attestations,
get_attesters_and_proposer
};
use super::ffg::update_ffg_and_crosslink_progress;
/// Computes the active state generated by applying a new block
/// to a parent block.
///
/// The `CrystallizedState` and `ActiveState` represented by
/// parent_block.state_hash must also be supplied.
///
/// # Panics
/// Will panic if the block verification or state transition fails.
/// Some situations may include; signature verification failure or
/// insufficient active validators.
pub fn compute_new_active_state(
cry_state: &CrystallizedState,
act_state: &ActiveState,
parent_block: &Block,
new_block: &Block,
config: &Config,
log: &Logger)
-> ActiveState
{
/*
* For the present state pair, determine the proposer and attester
* set for the newly presented block (`new_block`).
*/
let (attestation_indicies, proposer) = get_attesters_and_proposer(
&cry_state,
&act_state,
&new_block.skip_count,
&config,
&log);
info!(log, "calculated attesters and proposers";
"attesters_count" => attestation_indicies.len(),
"proposer_index" => proposer);
/*
* Ensure that the new_block bitfield accurately represents the
* public keys which have signed the previous block. I.e., for each
* `true` bit in the new_block bitfield, ensure the corresponding
* attester has signed the aggregate signature.
*
* As it is possible for there to be no attesters, an option
* is returned. The option unwraps to a vec of validator indicies
* which attested to the block.
*/
let parent_block_rlp = rlp::encode(parent_block);
let attesters_option = process_attestations(
&cry_state.active_validators,
&attestation_indicies,
&new_block.attestation_bitfield,
&parent_block_rlp.to_vec(),
&new_block.attestation_aggregate_sig);
let attesters = attesters_option.expect("No attesters available.");
/*
* Ensure the new_block has been signed by the proposer dictated
* by the state pair.
*/
let verified = verify_block(&new_block, &proposer);
assert!(verified, "Block sig verification failed.");
info!(log, "verified block signature";
"proposer_index" => proposer);
/*
* Collect all the aggregate votes and partial crosslinks
* into a new vec of partial crosslinks. Each entry in this
* new vec will be unique for the "shard_id" field -- each of these
* entries will contain the binary AND of all attestation bitfields
* for that relevant shard_id.
*
* During this process, the aggregate signatures are tested
* against each attester in the respective bitfields (i.e.,
* signatures are verified).
*
* Also returned is a count of all validators who attested to a
* shard, as well as a bitfield representing the same thing.
*/
let (partial_crosslinks, ffg_voter_bitfield, total_new_voters) =
update_ffg_and_crosslink_progress(
&cry_state,
&act_state.partial_crosslinks,
&act_state.ffg_voter_bitfield,
&new_block.shard_aggregate_votes,
&config);
/*
* Create a new RecentProposerRecord, with a reward determined
* by:
*
* - The number of attestations to last_block.
* - The number of crosslink attestations present in the
* previous active_state and new_block.
*/
let proposer = RecentPropserRecord {
index: proposer,
randao_commitment: Sha256Digest::zero(),
balance_delta: (attesters.len() + total_new_voters) as i64
};
/*
* Determine fields for the new active state, then return it.
*/
let height = act_state.height + 1;
let randao = act_state.randao.bitxor(new_block.randao_reveal);
let mut recent_attesters = act_state.recent_attesters.to_vec();
recent_attesters.extend_from_slice(&attesters);
let total_skip_count = act_state.total_skip_count + new_block.skip_count;
let mut recent_proposers = act_state.recent_proposers.to_vec();
recent_proposers.push(proposer);
ActiveState {
height,
randao,
ffg_voter_bitfield,
recent_attesters,
partial_crosslinks,
total_skip_count,
recent_proposers
} }

View File

@ -1,54 +0,0 @@
use super::crystallized_state::CrystallizedState;
use super::recent_proposer_record::RecentPropserRecord;
pub fn process_recent_proposers(
cry_state: &CrystallizedState,
recent_proposers: &Vec<RecentPropserRecord>)
-> Vec<i64>
{
let mut deltas: Vec<i64> = vec![0; cry_state.num_active_validators()];
for p in recent_proposers {
deltas[p.index] += p.balance_delta;
}
deltas
}
#[cfg(test)]
mod tests {
use super::*;
use super::super::utils::types::Sha256Digest;
use super::super::validator_record::ValidatorRecord;
#[test]
fn test_process_recent_proposers() {
let mut cry_state = CrystallizedState::zero();
let validator_count = 20;
let mut recent_proposers: Vec<RecentPropserRecord> = vec![];
for i in 0..validator_count {
cry_state.active_validators
.push(ValidatorRecord::zero_with_thread_rand_pub_key());
if i % 2 == 0 {
recent_proposers.push(RecentPropserRecord {
index: i,
randao_commitment: Sha256Digest::zero(),
balance_delta: 10
});
}
}
let d = process_recent_proposers(
&cry_state,
&recent_proposers);
for i in 0..validator_count {
if i % 2 == 0 {
assert_eq!(d[i], 10);
} else {
assert_eq!(d[i], 0);
}
}
}
}

View File

@ -1,124 +0,0 @@
use super::config::Config;
use super::blake2::{ Blake2s, Digest };
use super::utils::types::Sha256Digest;
// Interprets a 3-byte slice from a [u8] as an integer.
fn get_shift_from_source(source: &[u8], offset: usize) -> usize {
(source[offset + 2] as usize) |
((source[offset + 1] as usize) << 8) |
((source[offset ] as usize) << 16)
}
// Given entropy in the form of `seed`, return a shuffled list of validators
// indicies of size `validator_count` or `sample`.
pub fn get_shuffling(
seed: &Sha256Digest,
validator_count: &usize,
config: &Config)
-> Vec<usize>
{
assert!(*validator_count > 0, "cannot shuffle 0 validators");
let mut output: Vec<usize> = (0..*validator_count).collect();
assert!(*validator_count <= (config.max_validators as usize),
"validator_count exceeds max_validators");
// Do the first blake hash round
let mut source = Blake2s::new();
source.input(&seed);
let mut v = 0;
while v < *validator_count {
let current_source = source.result();
let mut source_offset = 0;
while source_offset < 30 {
let m = get_shift_from_source(&current_source, source_offset);
let shuffled_position: usize = (m % (validator_count - v)) + v;
output.swap(v as usize, shuffled_position as usize);
v += 1;
if v >= *validator_count { break; }
source_offset += 3;
}
// Re-hash the source (TODO: this does one extra hash, can be optimised)
source = Blake2s::new();
source.input(&current_source);
}
output
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_shuffling_shift_fn() {
let mut x = get_shift_from_source(
&vec![0_u8, 0, 1],
0);
assert_eq!((x as u32), 1);
x = get_shift_from_source(
&vec![0_u8, 1, 1],
0);
assert_eq!(x, 257);
x = get_shift_from_source(
&vec![1_u8, 1, 1],
0);
assert_eq!(x, 65793);
x = get_shift_from_source(
&vec![255_u8, 1, 1],
0);
assert_eq!(x, 16711937);
}
#[test]
fn test_shuffling() {
let s = get_shuffling(
&Sha256Digest::zero(),
&10,
&Config::standard());
assert_eq!(s,
vec!(0, 9, 7, 6, 4, 1, 8, 5, 2, 3),
"10 validator shuffle was not as expected");
}
#[test]
fn test_shuffling_32() {
let s = get_shuffling(
&Sha256Digest::zero(),
&32,
&Config::standard());
assert_eq!(s,
vec!(30, 0, 31, 9, 22, 10, 20, 2, 13, 21, 1, 7, 29, 28, 3,
27, 6, 8, 25, 15, 12, 26, 4, 18, 16, 23, 19, 11, 14,
17, 5, 24),
"32 Validator shuffle was not as expected");
}
#[test]
fn test_shuffling_unique() {
let s = get_shuffling(
&Sha256Digest::zero(),
&20,
&Config::standard());
assert_eq!(false,
(1..s.len()).any(|i| s[i..].contains(&s[i-1])),
"Validator Shuffle Non-Unique")
}
#[test]
fn test_shuffling_with_gt_half_max_validators() {
let mut config = Config::standard();
config.max_validators = 19;
let s = get_shuffling(
&Sha256Digest::zero(),
&10,
&Config::standard());
assert_eq!(s,
vec!(0, 9, 7, 6, 4, 1, 8, 5, 2, 3),
"10 validator shuffle was not as expected");
}
}

View File

@ -1,182 +0,0 @@
use std::cmp::min;
use super::crystallized_state::CrystallizedState;
use super::validator_record::ValidatorRecord;
use super::utils::logging::Logger;
use super::config::Config;
pub fn get_incremented_validator_sets(
cry_state: &CrystallizedState,
active_validators: &Vec<ValidatorRecord>,
config: &Config,
log: &Logger)
-> (Vec<ValidatorRecord>, Vec<ValidatorRecord>, Vec<ValidatorRecord>)
{
let mut new_active_validators: Vec<ValidatorRecord> = vec![];
let mut new_exited_validators: Vec<ValidatorRecord>
= cry_state.exited_validators.clone();
let next_dynasty = cry_state.dynasty + 1;
let mut ejection_count = 0;
for v in active_validators {
if (v.balance <= config.eject_balance) |
(v.switch_dynasty == next_dynasty) {
new_exited_validators.push(v.clone());
ejection_count += 1;
}
else {
new_active_validators.push(v.clone());
}
}
let induction_count = min(
cry_state.num_queued_validators(),
cry_state.num_active_validators() / 30 + 1);
let mut first_ineligable = induction_count;
for i in 0..induction_count {
if cry_state.queued_validators[i].switch_dynasty > next_dynasty {
first_ineligable = i;
break;
}
new_active_validators.push(cry_state.queued_validators[i].clone());
}
let new_queued_validators = cry_state.
queued_validators[first_ineligable..cry_state.queued_validators.len()]
.to_vec();
info!(log, "updated validator sets";
"inducted_count" => induction_count,
"ejected_count" => ejection_count);
(new_queued_validators, new_active_validators, new_exited_validators)
}
#[cfg(test)]
mod tests {
use super::*;
use super::super::utils::types::U256;
use super::super::utils::logging::test_logger;
fn test_setup() -> (CrystallizedState, Config) {
let mut cry_state = CrystallizedState::zero();
let mut config = Config::standard();
config.shard_count = 5;
config.notaries_per_crosslink = 2;
config.default_balance = U256::from(32000);
config.eject_balance = U256::from(16000);
cry_state.current_epoch = 100;
cry_state.dynasty = 100;
(cry_state, config)
}
#[test]
fn test_incrementing_validator_sets_scenario_1() {
let (mut cry_state, config) = test_setup();
let validator_count = 10;
let mut a: Vec<ValidatorRecord> = vec![];
let mut q: Vec<ValidatorRecord> = vec![];
let mut x: Vec<ValidatorRecord> = vec![];
(0..validator_count).for_each(|_| {
let mut v = ValidatorRecord::zero_with_thread_rand_pub_key();
v.switch_dynasty = cry_state.dynasty + 5;
v.balance = config.default_balance.clone();
a.push(v)
});
(0..validator_count).for_each(|_| {
let mut v = ValidatorRecord::zero_with_thread_rand_pub_key();
v.switch_dynasty = cry_state.dynasty + 1;
v.balance = config.default_balance.clone();
q.push(v)
});
(0..validator_count).for_each(|_| {
let mut v = ValidatorRecord::zero_with_thread_rand_pub_key();
v.switch_dynasty = cry_state.dynasty - 1;
v.balance = config.default_balance.clone();
x.push(v)
});
cry_state.active_validators = a.to_vec();
cry_state.queued_validators = q.to_vec();
cry_state.exited_validators = x.to_vec();
let (nq, na, nx) = get_incremented_validator_sets(
&cry_state,
&a,
&config,
&test_logger());
let inducted = validator_count / 30 + 1;
assert!(inducted > 0);
assert_eq!(na.len(), validator_count + inducted, "new active incorrect");
assert_eq!(nq.len(), validator_count - inducted, "new queued incorrect");
assert_eq!(nx.len(), validator_count, "new exited incorrect");
}
#[test]
fn test_incrementing_validator_sets_scenario_2() {
let (mut cry_state, config) = test_setup();
let validator_count = 60;
let expiring_active = 5;
let eligable_queued = 1;
let mut a: Vec<ValidatorRecord> = vec![];
let mut q: Vec<ValidatorRecord> = vec![];
let mut x: Vec<ValidatorRecord> = vec![];
(0..validator_count).for_each(|i| {
let mut v = ValidatorRecord::zero_with_thread_rand_pub_key();
if i < expiring_active {
v.switch_dynasty = cry_state.dynasty + 1;
} else {
v.switch_dynasty = cry_state.dynasty + 5;
}
v.balance = config.default_balance.clone();
a.push(v)
});
(0..validator_count).for_each(|i| {
let mut v = ValidatorRecord::zero_with_thread_rand_pub_key();
if i < eligable_queued {
v.switch_dynasty = cry_state.dynasty + 1;
} else {
v.switch_dynasty = cry_state.dynasty + 5;
}
v.balance = config.default_balance.clone();
q.push(v)
});
(0..validator_count).for_each(|_| {
let mut v = ValidatorRecord::zero_with_thread_rand_pub_key();
v.switch_dynasty = cry_state.dynasty - 1;
v.balance = config.default_balance.clone();
x.push(v)
});
cry_state.active_validators = a.to_vec();
cry_state.queued_validators = q.to_vec();
cry_state.exited_validators = x.to_vec();
let (nq, na, nx) = get_incremented_validator_sets(
&cry_state,
&a,
&config,
&test_logger());
let inducted = validator_count / 30 + 1;
assert!(inducted > eligable_queued, "this test requires more inductable \
validators than there are eligable.");
assert_eq!(na.len(), validator_count - expiring_active + eligable_queued,
"new active incorrect");
assert_eq!(nq.len(), validator_count - eligable_queued,
"new queued incorrect");
assert_eq!(nx.len(), validator_count + expiring_active,
"new exited incorrect");
}
}