Remove old transition functions
This commit is contained in:
parent
486865e803
commit
2256f52a44
@ -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)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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 ¬aries_indicies {
|
||||
let voted = match new_partial_crosslink {
|
||||
None => false,
|
||||
Some(pc) => pc.0.voter_bitfield.get_bit(¬ary)
|
||||
};
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
} }
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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(¤t_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(¤t_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");
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user