diff --git a/core/src/client/test_client.rs b/core/src/client/test_client.rs index b2b0514778..6ccfb1676c 100644 --- a/core/src/client/test_client.rs +++ b/core/src/client/test_client.rs @@ -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; @@ -106,7 +106,7 @@ pub struct TestBlockChainClient { /// Fixed validator keys pub validator_keys: RwLock>, /// Fixed validators - pub validators: Validators, + pub validators: NextValidators, } impl Default for TestBlockChainClient { @@ -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. @@ -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 } } diff --git a/core/src/consensus/stake/action_data.rs b/core/src/consensus/stake/action_data.rs index af14c8b33e..6272b0559a 100644 --- a/core/src/consensus/stake/action_data.rs +++ b/core/src/consensus/stake/action_data.rs @@ -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 { @@ -274,17 +276,17 @@ impl Validator { } #[derive(Debug)] -pub struct Validators(Vec); -impl Validators { +pub struct NextValidators(Vec); +impl NextValidators { pub fn from_vector_to_test(vec: Vec) -> Self { - Validators(vec) + Self(vec) } pub fn load_from_state(state: &TopLevelState) -> StateResult { - 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 { @@ -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 { @@ -384,7 +386,7 @@ impl Validators { } } -impl Deref for Validators { +impl Deref for NextValidators { type Target = Vec; fn deref(&self) -> &Self::Target { @@ -392,13 +394,13 @@ impl Deref for Validators { } } -impl From for Vec { - fn from(val: Validators) -> Self { +impl From for Vec { + fn from(val: NextValidators) -> Self { val.0 } } -impl IntoIterator for Validators { +impl IntoIterator for NextValidators { type Item = Validator; type IntoIter = vec::IntoIter; @@ -407,6 +409,49 @@ impl IntoIterator for Validators { } } +#[derive(Debug)] +pub struct CurrentValidators(Vec); +impl CurrentValidators { + pub fn load_from_state(state: &TopLevelState) -> StateResult { + 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) { + self.0 = validators; + } + + pub fn addresses(&self) -> Vec
{ + 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; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + pub mod v0 { use std::mem; @@ -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, @@ -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 { diff --git a/core/src/consensus/stake/mod.rs b/core/src/consensus/stake/mod.rs index 5134367362..771013c7eb 100644 --- a/core/src/consensus/stake/mod.rs +++ b/core/src/consensus/stake/mod.rs @@ -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; @@ -317,8 +317,8 @@ pub fn get_stakes(state: &TopLevelState) -> StateResult> { Ok(result) } -pub fn get_validators(state: &TopLevelState) -> StateResult { - Validators::load_from_state(state) +pub fn get_validators(state: &TopLevelState) -> StateResult { + NextValidators::load_from_state(state) } pub mod v0 { @@ -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) } @@ -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)?; @@ -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(¤t_validators, nomination_ends_at, &inactive_validators, &banned); let expired = candidates.drain_expired_candidates(current_term); @@ -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"), diff --git a/core/src/consensus/tendermint/engine.rs b/core/src/consensus/tendermint/engine.rs index 4404cdaef2..69bc34b565 100644 --- a/core/src/consensus/tendermint/engine.rs +++ b/core/src/consensus/tendermint/engine.rs @@ -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 => {} @@ -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(); @@ -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(); @@ -448,10 +459,30 @@ fn calculate_pending_rewards_of_the_previous_term( let mut missed_signatures = HashMap::::with_capacity(MAX_NUM_OF_VALIDATORS); let mut signed_blocks = HashMap::::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() { @@ -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(); diff --git a/core/src/consensus/tendermint/worker.rs b/core/src/consensus/tendermint/worker.rs index f9abe27922..627d0a77d8 100644 --- a/core/src/consensus/tendermint/worker.rs +++ b/core/src/consensus/tendermint/worker.rs @@ -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; @@ -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()) @@ -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(()) } diff --git a/core/src/consensus/validator_set/dynamic_validator.rs b/core/src/consensus/validator_set/dynamic_validator.rs index 829e9cd5df..1bcaacf13c 100644 --- a/core/src/consensus/validator_set/dynamic_validator.rs +++ b/core/src/consensus/validator_set/dynamic_validator.rs @@ -18,13 +18,13 @@ use std::sync::{Arc, Weak}; use ckey::{public_to_address, Address, Public}; use ctypes::util::unexpected::OutOfBounds; -use ctypes::BlockHash; +use ctypes::{BlockHash, Header}; use parking_lot::RwLock; use super::{RoundRobinValidator, ValidatorSet}; use crate::client::ConsensusClient; use crate::consensus::bit_set::BitSet; -use crate::consensus::stake::{get_validators, Validator}; +use crate::consensus::stake::{get_validators, CurrentValidators, Validator}; use crate::consensus::EngineError; /// Validator set containing a known set of public keys. @@ -41,7 +41,7 @@ impl DynamicValidator { } } - fn validators(&self, parent: BlockHash) -> Option> { + fn next_validators(&self, parent: BlockHash) -> Option> { let client: Arc = self.client.read().as_ref().and_then(Weak::upgrade).expect("Client is not initialized"); let block_id = parent.into(); @@ -64,12 +64,35 @@ impl DynamicValidator { } } + fn current_validators(&self, hash: BlockHash) -> Option> { + let client: Arc = + self.client.read().as_ref().and_then(Weak::upgrade).expect("Client is not initialized"); + let block_id = hash.into(); + let term_id = client.current_term_id(block_id).expect( + "valdators() is called when creating a block or verifying a block. + Minor creates a block only when the parent block is imported. + The n'th block is verified only when the parent block is imported.", + ); + if term_id == 0 { + return None + } + let state = client.state_at(block_id)?; + let validators = CurrentValidators::load_from_state(&state).unwrap(); + if validators.is_empty() { + None + } else { + let mut validators: Vec<_> = (*validators).clone(); + validators.reverse(); + Some(validators) + } + } + fn validators_pubkey(&self, parent: BlockHash) -> Option> { - self.validators(parent).map(|validators| validators.into_iter().map(|val| *val.pubkey()).collect()) + self.next_validators(parent).map(|validators| validators.into_iter().map(|val| *val.pubkey()).collect()) } pub fn proposer_index(&self, parent: BlockHash, prev_proposer_index: usize, proposed_view: usize) -> usize { - if let Some(validators) = self.validators(parent) { + if let Some(validators) = self.next_validators(parent) { let num_validators = validators.len(); proposed_view % num_validators } else { @@ -77,6 +100,48 @@ impl DynamicValidator { (prev_proposer_index + proposed_view + 1) % num_validators } } + + pub fn check_enough_votes_with_validators( + &self, + validators: &[Validator], + votes: &BitSet, + ) -> Result<(), EngineError> { + let mut voted_delegation = 0u64; + let n_validators = validators.len(); + for index in votes.true_index_iter() { + assert!(index < n_validators); + let validator = validators.get(index).ok_or_else(|| { + EngineError::ValidatorNotExist { + height: 0, // FIXME + index, + } + })?; + voted_delegation += validator.delegation(); + } + let total_delegation: u64 = validators.iter().map(Validator::delegation).sum(); + if voted_delegation * 3 > total_delegation * 2 { + Ok(()) + } else { + let threshold = total_delegation as usize * 2 / 3; + Err(EngineError::BadSealFieldSize(OutOfBounds { + min: Some(threshold), + max: Some(total_delegation as usize), + found: voted_delegation as usize, + })) + } + } + + pub fn check_enough_votes_with_header(&self, header: &Header, votes: &BitSet) -> Result<(), EngineError> { + let hash = header.hash(); + let parent = *header.parent_hash(); + let validators = self.current_validators(hash).or_else(move || self.next_validators(parent)); + + if let Some(validators) = validators { + self.check_enough_votes_with_validators(&validators, votes) + } else { + self.initial_list.check_enough_votes(header.parent_hash(), votes) + } + } } impl ValidatorSet for DynamicValidator { @@ -136,7 +201,7 @@ impl ValidatorSet for DynamicValidator { } fn count(&self, parent: &BlockHash) -> usize { - if let Some(validators) = self.validators(*parent) { + if let Some(validators) = self.next_validators(*parent) { validators.len() } else { self.initial_list.count(parent) @@ -144,30 +209,8 @@ impl ValidatorSet for DynamicValidator { } fn check_enough_votes(&self, parent: &BlockHash, votes: &BitSet) -> Result<(), EngineError> { - if let Some(validators) = self.validators(*parent) { - let mut voted_delegation = 0u64; - let n_validators = validators.len(); - for index in votes.true_index_iter() { - assert!(index < n_validators); - let validator = validators.get(index).ok_or_else(|| { - EngineError::ValidatorNotExist { - height: 0, // FIXME - index, - } - })?; - voted_delegation += validator.delegation(); - } - let total_delegation: u64 = validators.iter().map(Validator::delegation).sum(); - if voted_delegation * 3 > total_delegation * 2 { - Ok(()) - } else { - let threshold = total_delegation as usize * 2 / 3; - Err(EngineError::BadSealFieldSize(OutOfBounds { - min: Some(threshold), - max: Some(total_delegation as usize), - found: voted_delegation as usize, - })) - } + if let Some(validators) = self.next_validators(*parent) { + self.check_enough_votes_with_validators(&validators, votes) } else { self.initial_list.check_enough_votes(parent, votes) }