Skip to content

Save current block's validator in the state #1922

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions core/src/client/test_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ use crate::client::{
AccountData, BlockChainClient, BlockChainTrait, BlockProducer, BlockStatus, ConsensusClient, EngineInfo,
ImportBlock, ImportResult, MiningBlockChainClient, StateInfo, StateOrBlock, TermInfo,
};
use crate::consensus::stake::{Validator, Validators};
use crate::consensus::stake::{NextValidators, Validator};
use crate::consensus::EngineError;
use crate::db::{COL_STATE, NUM_COLUMNS};
use crate::encoded;
Expand Down Expand Up @@ -106,7 +106,7 @@ pub struct TestBlockChainClient {
/// Fixed validator keys
pub validator_keys: RwLock<HashMap<Public, Private>>,
/// Fixed validators
pub validators: Validators,
pub validators: NextValidators,
}

impl Default for TestBlockChainClient {
Expand Down Expand Up @@ -160,7 +160,7 @@ impl TestBlockChainClient {
history: RwLock::new(None),
term_id: Some(1),
validator_keys: RwLock::new(HashMap::new()),
validators: Validators::from_vector_to_test(vec![]),
validators: NextValidators::from_vector_to_test(vec![]),
};

// insert genesis hash.
Expand Down Expand Up @@ -325,14 +325,14 @@ impl TestBlockChainClient {
self.validator_keys.write().insert(*key_pair.public(), *key_pair.private());
pubkeys.push(*key_pair.public());
}
let fixed_validators: Validators = Validators::from_vector_to_test(
let fixed_validators: NextValidators = NextValidators::from_vector_to_test(
pubkeys.into_iter().map(|pubkey| Validator::new_for_test(0, 0, pubkey)).collect(),
);

self.validators = fixed_validators;
}

pub fn get_validators(&self) -> &Validators {
pub fn get_validators(&self) -> &NextValidators {
&self.validators
}
}
Expand Down
71 changes: 58 additions & 13 deletions core/src/consensus/stake/action_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ lazy_static! {
pub static ref JAIL_KEY: H256 = ActionDataKeyBuilder::new(CUSTOM_ACTION_HANDLER_ID, 1).append(&"Jail").into_key();
pub static ref BANNED_KEY: H256 =
ActionDataKeyBuilder::new(CUSTOM_ACTION_HANDLER_ID, 1).append(&"Banned").into_key();
pub static ref VALIDATORS_KEY: H256 =
pub static ref NEXT_VALIDATORS_KEY: H256 =
ActionDataKeyBuilder::new(CUSTOM_ACTION_HANDLER_ID, 1).append(&"Validators").into_key();
pub static ref CURRENT_VALIDATORS_KEY: H256 =
ActionDataKeyBuilder::new(CUSTOM_ACTION_HANDLER_ID, 1).append(&"CurrentValidators").into_key();
}

pub fn get_delegation_key(address: &Address) -> H256 {
Expand Down Expand Up @@ -274,17 +276,17 @@ impl Validator {
}

#[derive(Debug)]
pub struct Validators(Vec<Validator>);
impl Validators {
pub struct NextValidators(Vec<Validator>);
impl NextValidators {
pub fn from_vector_to_test(vec: Vec<Validator>) -> Self {
Validators(vec)
Self(vec)
}

pub fn load_from_state(state: &TopLevelState) -> StateResult<Self> {
let key = &*VALIDATORS_KEY;
let key = &*NEXT_VALIDATORS_KEY;
let validators = state.action_data(&key)?.map(|data| decode_list(&data)).unwrap_or_default();

Ok(Validators(validators))
Ok(Self(validators))
}

pub fn elect(state: &TopLevelState) -> StateResult<Self> {
Expand Down Expand Up @@ -335,7 +337,7 @@ impl Validators {


pub fn save_to_state(&self, state: &mut TopLevelState) -> StateResult<()> {
let key = &*VALIDATORS_KEY;
let key = &*NEXT_VALIDATORS_KEY;
if !self.is_empty() {
state.update_action_data(&key, encode_list(&self.0).to_vec())?;
} else {
Expand Down Expand Up @@ -384,21 +386,21 @@ impl Validators {
}
}

impl Deref for Validators {
impl Deref for NextValidators {
type Target = Vec<Validator>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl From<Validators> for Vec<Validator> {
fn from(val: Validators) -> Self {
impl From<NextValidators> for Vec<Validator> {
fn from(val: NextValidators) -> Self {
val.0
}
}

impl IntoIterator for Validators {
impl IntoIterator for NextValidators {
type Item = Validator;
type IntoIter = vec::IntoIter<Self::Item>;

Expand All @@ -407,6 +409,49 @@ impl IntoIterator for Validators {
}
}

#[derive(Debug)]
pub struct CurrentValidators(Vec<Validator>);
impl CurrentValidators {
pub fn load_from_state(state: &TopLevelState) -> StateResult<Self> {
let key = &*CURRENT_VALIDATORS_KEY;
let validators = state.action_data(&key)?.map(|data| decode_list(&data)).unwrap_or_default();

Ok(Self(validators))
}

pub fn save_to_state(&self, state: &mut TopLevelState) -> StateResult<()> {
let key = &*CURRENT_VALIDATORS_KEY;
if !self.is_empty() {
state.update_action_data(&key, encode_list(&self.0).to_vec())?;
} else {
state.remove_action_data(&key);
}
Ok(())
}

pub fn update(&mut self, validators: Vec<Validator>) {
self.0 = validators;
}

pub fn addresses(&self) -> Vec<Address> {
self.0.iter().rev().map(|v| public_to_address(&v.pubkey)).collect()
}

pub fn get_validator(&self, index: usize) -> &Validator {
let len = self.0.len();
// NOTE: validator list is reversed when reading a validator by index
self.0.iter().nth_back(index % len).unwrap()
}
}

impl Deref for CurrentValidators {
type Target = Vec<Validator>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

pub mod v0 {
use std::mem;

Expand Down Expand Up @@ -603,7 +648,7 @@ impl Candidates {

pub fn renew_candidates(
&mut self,
validators: &Validators,
validators: &NextValidators,
nomination_ends_at: u64,
inactive_validators: &[Address],
banned: &Banned,
Expand Down Expand Up @@ -1868,7 +1913,7 @@ mod tests {
}
candidates.save_to_state(&mut state).unwrap();

let dummy_validators = Validators(
let dummy_validators = NextValidators(
pubkeys[0..5]
.iter()
.map(|pubkey| Validator {
Expand Down
14 changes: 7 additions & 7 deletions core/src/consensus/stake/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use parking_lot::RwLock;
use primitives::{Bytes, H256};
use rlp::{Decodable, Rlp};

pub use self::action_data::{Banned, Validator, Validators};
pub use self::action_data::{Banned, CurrentValidators, NextValidators, Validator};
use self::action_data::{Candidates, Delegation, Jail, ReleaseResult, StakeAccount, Stakeholders};
pub use self::actions::Action;
pub use self::distribute::fee_distribute;
Expand Down Expand Up @@ -317,8 +317,8 @@ pub fn get_stakes(state: &TopLevelState) -> StateResult<HashMap<Address, u64>> {
Ok(result)
}

pub fn get_validators(state: &TopLevelState) -> StateResult<Validators> {
Validators::load_from_state(state)
pub fn get_validators(state: &TopLevelState) -> StateResult<NextValidators> {
NextValidators::load_from_state(state)
}

pub mod v0 {
Expand Down Expand Up @@ -379,7 +379,7 @@ pub mod v1 {
}

pub fn update_validator_weights(state: &mut TopLevelState, block_author: &Address) -> StateResult<()> {
let mut validators = Validators::load_from_state(state)?;
let mut validators = NextValidators::load_from_state(state)?;
validators.update_weight(block_author);
validators.save_to_state(state)
}
Expand Down Expand Up @@ -451,7 +451,7 @@ pub fn on_term_close(

jail(state, inactive_validators, custody_until, kick_at)?;

let validators = Validators::elect(state)?;
let validators = NextValidators::elect(state)?;
validators.save_to_state(state)?;

state.increase_term_id(last_term_finished_block_num)?;
Expand All @@ -469,7 +469,7 @@ fn update_candidates(
let mut candidates = Candidates::load_from_state(state)?;
let nomination_ends_at = current_term + nomination_expiration;

let current_validators = Validators::load_from_state(state)?;
let current_validators = NextValidators::load_from_state(state)?;
candidates.renew_candidates(&current_validators, nomination_ends_at, &inactive_validators, &banned);

let expired = candidates.drain_expired_candidates(current_term);
Expand Down Expand Up @@ -519,7 +519,7 @@ pub fn ban(state: &mut TopLevelState, informant: &Public, criminal: Address) ->

let mut candidates = Candidates::load_from_state(state)?;
let mut jailed = Jail::load_from_state(state)?;
let mut validators = Validators::load_from_state(state)?;
let mut validators = NextValidators::load_from_state(state)?;

let deposit = match (candidates.remove(&criminal), jailed.remove(&criminal)) {
(Some(_), Some(_)) => unreachable!("A candidate that are jailed cannot exist"),
Expand Down
54 changes: 46 additions & 8 deletions core/src/consensus/tendermint/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,17 @@ impl ConsensusEngine for Tendermint {
let block_number = block.header().number();
let metadata = block.state().metadata()?.expect("Metadata must exist");
let era = metadata.term_params().map_or(0, |p| p.era());

match era {
0 => {}
1 => {
let mut validators = stake::CurrentValidators::load_from_state(block.state())?;
validators.update(stake::NextValidators::load_from_state(block.state())?.clone());
validators.save_to_state(block.state_mut())?;
}
_ => unimplemented!(),
}

if block_number == metadata.last_term_finished_block_num() + 1 {
match era {
0 => {}
Expand Down Expand Up @@ -274,7 +285,7 @@ impl ConsensusEngine for Tendermint {

stake::v0::move_current_to_previous_intermediate_rewards(block.state_mut())?;

let validators = stake::Validators::load_from_state(block.state())?
let validators = stake::NextValidators::load_from_state(block.state())?
.into_iter()
.map(|val| public_to_address(val.pubkey()))
.collect();
Expand All @@ -286,7 +297,7 @@ impl ConsensusEngine for Tendermint {
}

let start_of_the_current_term = metadata.last_term_finished_block_num() + 1;
let validators = stake::Validators::load_from_state(block.state())?
let validators = stake::NextValidators::load_from_state(block.state())?
.into_iter()
.map(|val| public_to_address(val.pubkey()))
.collect();
Expand Down Expand Up @@ -448,10 +459,30 @@ fn calculate_pending_rewards_of_the_previous_term(
let mut missed_signatures = HashMap::<Address, (usize, usize)>::with_capacity(MAX_NUM_OF_VALIDATORS);
let mut signed_blocks = HashMap::<Address, usize>::with_capacity(MAX_NUM_OF_VALIDATORS);

let era = {
let end_of_the_current_term_header = chain
.block_header(&start_of_the_current_term_header.parent_hash().into())
.expect("The parent of the term end block must exist");
let state = chain
.state_at(end_of_the_current_term_header.parent_hash().into())
.expect("The state at parent of the term end block must exist");
let metadata = state.metadata()?.expect("Metadata of the term end block should exist");
metadata.term_params().map_or(0, |p| p.era())
};

let mut header = start_of_the_current_term_header;
let mut parent_validators = {
let grand_parent_header = chain.block_header(&header.parent_hash().into()).unwrap();
validators.addresses(&grand_parent_header.parent_hash())
match era {
0 => {
let grand_parent_header = chain.block_header(&header.parent_hash().into()).unwrap();
validators.addresses(&grand_parent_header.parent_hash())
}
1 => {
let state = chain.state_at(header.parent_hash().into()).expect("The block's state must exist");
stake::CurrentValidators::load_from_state(&state)?.addresses()
}
_ => unimplemented!(),
}
};
while start_of_the_previous_term != header.number() {
for index in TendermintSealView::new(&header.seal()).bitset()?.true_index_iter() {
Expand All @@ -461,10 +492,17 @@ fn calculate_pending_rewards_of_the_previous_term(

header = chain.block_header(&header.parent_hash().into()).unwrap();
parent_validators = {
// The seal of the current block has the signatures of the parent block.
// It needs the hash of the grand parent block to find the validators of the parent block.
let grand_parent_header = chain.block_header(&header.parent_hash().into()).unwrap();
validators.addresses(&grand_parent_header.parent_hash())
match era {
0 => {
let grand_parent_header = chain.block_header(&header.parent_hash().into()).unwrap();
validators.addresses(&grand_parent_header.parent_hash())
}
1 => {
let state = chain.state_at(header.hash().into()).expect("The block's state must exist");
stake::CurrentValidators::load_from_state(&state)?.addresses()
}
_ => unimplemented!(),
}
};

let author = header.author();
Expand Down
22 changes: 14 additions & 8 deletions core/src/consensus/tendermint/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use super::backup::{backup, restore, BackupView};
use super::message::*;
use super::network;
use super::params::TimeGapParams;
use super::stake::CUSTOM_ACTION_HANDLER_ID;
use super::stake::{CurrentValidators, CUSTOM_ACTION_HANDLER_ID};
use super::types::{Height, Proposal, Step, TendermintSealView, TendermintState, TwoThirdsMajority, View};
use super::vote_collector::{DoubleVote, VoteCollector};
use super::vote_regression_checker::VoteRegressionChecker;
Expand Down Expand Up @@ -1244,13 +1244,19 @@ impl Worker {
};

let mut voted_validators = BitSet::new();
let grand_parent_hash = self
.client()
.block_header(&(*header.parent_hash()).into())
.expect("The parent block must exist")
.parent_hash();
let parent = self.client().block_header(&(*header.parent_hash()).into()).expect("The parent block must exist");
let grand_parent_hash = parent.parent_hash();
for (bitset_index, signature) in seal_view.signatures()? {
let public = self.validators.get(&grand_parent_hash, bitset_index);
let public = {
let state = self.client().state_at(parent.hash().into()).expect("The parent state must exist");
let validators = CurrentValidators::load_from_state(&state)?;
// This happens when era == 0
if validators.is_empty() {
self.validators.get(&grand_parent_hash, bitset_index)
} else {
*validators.get_validator(bitset_index).pubkey()
}
};
if !verify_schnorr(&public, &signature, &precommit_vote_on.hash())? {
let address = public_to_address(&public);
return Err(EngineError::BlockNotAuthorized(address.to_owned()).into())
Expand All @@ -1263,7 +1269,7 @@ impl Worker {
if header.number() == 1 {
return Ok(())
}
self.validators.check_enough_votes(&grand_parent_hash, &voted_validators)?;
self.validators.check_enough_votes_with_header(&parent.decode(), &voted_validators)?;
Ok(())
}

Expand Down
Loading