From 0064be7e97c7badc9d7384aa017ae2b294e42ce6 Mon Sep 17 00:00:00 2001 From: Joonmo Yang Date: Tue, 26 Nov 2019 15:38:21 +0900 Subject: [PATCH 1/4] Introduce on_open_block `on_open_block` is called when the block is created, before processing any transactions included in the block. --- core/src/block.rs | 5 +++++ core/src/consensus/mod.rs | 5 +++++ core/src/consensus/tendermint/engine.rs | 9 +++++++++ core/src/miner/miner.rs | 1 + 4 files changed, 20 insertions(+) diff --git a/core/src/block.rs b/core/src/block.rs index a93b5c95ee..4e5b382248 100644 --- a/core/src/block.rs +++ b/core/src/block.rs @@ -313,6 +313,10 @@ impl<'x> OpenBlock<'x> { self.block.header.set_seal(seal); Ok(()) } + + pub fn inner_mut(&mut self) -> &mut ExecutedBlock { + &mut self.block + } } /// Just like `OpenBlock`, except that we've applied `Engine::on_close_block`, finished up the non-seal header fields. @@ -484,6 +488,7 @@ pub fn enact( let mut b = OpenBlock::try_new(engine, db, parent, Address::default(), vec![])?; b.populate_from(header); + engine.on_open_block(b.inner_mut())?; b.push_transactions(transactions, client, parent.number(), parent.timestamp())?; let term_common_params = client.term_common_params(BlockId::Hash(*header.parent_hash())); diff --git a/core/src/consensus/mod.rs b/core/src/consensus/mod.rs index 6d82c555e9..db6f0fe413 100644 --- a/core/src/consensus/mod.rs +++ b/core/src/consensus/mod.rs @@ -196,6 +196,11 @@ pub trait ConsensusEngine: Sync + Send { /// Stops any services that the may hold the Engine and makes it safe to drop. fn stop(&self) {} + /// Block transformation functions, before the transactions. + fn on_open_block(&self, _block: &mut ExecutedBlock) -> Result<(), Error> { + Ok(()) + } + /// Block transformation functions, after the transactions. fn on_close_block( &self, diff --git a/core/src/consensus/tendermint/engine.rs b/core/src/consensus/tendermint/engine.rs index 6276b90348..78f15ddc33 100644 --- a/core/src/consensus/tendermint/engine.rs +++ b/core/src/consensus/tendermint/engine.rs @@ -141,6 +141,15 @@ impl ConsensusEngine for Tendermint { fn stop(&self) {} + /// Block transformation functions, before the transactions. + fn on_open_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> { + let metadata = block.state().metadata()?.expect("Metadata must exist"); + if block.header().number() == metadata.last_term_finished_block_num() + 1 { + // FIXME: on_term_open + } + Ok(()) + } + fn on_close_block( &self, block: &mut ExecutedBlock, diff --git a/core/src/miner/miner.rs b/core/src/miner/miner.rs index 29cb422889..e4bc968757 100644 --- a/core/src/miner/miner.rs +++ b/core/src/miner/miner.rs @@ -389,6 +389,7 @@ impl Miner { return Ok(None) } } + self.engine.on_open_block(open_block.inner_mut())?; let mut invalid_transactions = Vec::new(); From 2c65cbba32fd68a0727ce707343882bb50b6458e Mon Sep 17 00:00:00 2001 From: Joonmo Yang Date: Wed, 27 Nov 2019 18:45:45 +0900 Subject: [PATCH 2/4] Precalculate the fee and save to the state --- core/src/consensus/solo/mod.rs | 15 +++++-- core/src/consensus/stake/action_data.rs | 33 +++++++++------- core/src/consensus/stake/mod.rs | 15 +++++-- core/src/consensus/tendermint/engine.rs | 52 ++++++++++++------------- 4 files changed, 67 insertions(+), 48 deletions(-) diff --git a/core/src/consensus/solo/mod.rs b/core/src/consensus/solo/mod.rs index ad147cb80f..f9f0ad22b1 100644 --- a/core/src/consensus/solo/mod.rs +++ b/core/src/consensus/solo/mod.rs @@ -19,7 +19,7 @@ mod params; use std::sync::{Arc, Weak}; use ckey::Address; -use cstate::{ActionHandler, HitHandler}; +use cstate::{ActionHandler, HitHandler, TopStateView}; use ctypes::{CommonParams, Header}; use parking_lot::RwLock; @@ -83,6 +83,16 @@ impl ConsensusEngine for Solo { Seal::Solo } + fn on_open_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> { + let block_number = block.header().number(); + let metadata = block.state().metadata()?.expect("Metadata must exist"); + if block_number == metadata.last_term_finished_block_num() + 1 { + let rewards = stake::drain_current_rewards(block.state_mut())?; + stake::update_calculated_rewards(block.state_mut(), rewards.into_iter().collect())?; + } + Ok(()) + } + fn on_close_block( &self, block: &mut ExecutedBlock, @@ -128,8 +138,7 @@ impl ConsensusEngine for Solo { } header.number() }; - stake::move_current_to_previous_intermediate_rewards(&mut block.state_mut())?; - let rewards = stake::drain_previous_rewards(&mut block.state_mut())?; + let rewards = stake::drain_calculated_rewards(&mut block.state_mut())?; for (address, reward) in rewards { self.machine.add_balance(block, &address, reward)?; } diff --git a/core/src/consensus/stake/action_data.rs b/core/src/consensus/stake/action_data.rs index 7ea28eca53..1827a96ec2 100644 --- a/core/src/consensus/stake/action_data.rs +++ b/core/src/consensus/stake/action_data.rs @@ -410,28 +410,28 @@ impl IntoIterator for Validators { #[derive(Default, Debug, PartialEq)] pub struct IntermediateRewards { - previous: BTreeMap, current: BTreeMap, + calculated: BTreeMap, } impl IntermediateRewards { pub fn load_from_state(state: &TopLevelState) -> StateResult { let key = get_intermediate_rewards_key(); let action_data = state.action_data(&key)?; - let (previous, current) = decode_map_tuple(action_data.as_ref()); + let (current, calculated) = decode_map_tuple(action_data.as_ref()); Ok(Self { - previous, current, + calculated, }) } pub fn save_to_state(&self, state: &mut TopLevelState) -> StateResult<()> { let key = get_intermediate_rewards_key(); - if self.previous.is_empty() && self.current.is_empty() { + if self.current.is_empty() && self.calculated.is_empty() { state.remove_action_data(&key); } else { - let encoded = encode_map_tuple(&self.previous, &self.current); + let encoded = encode_map_tuple(&self.current, &self.calculated); state.update_action_data(&key, encoded)?; } Ok(()) @@ -444,15 +444,20 @@ impl IntermediateRewards { *self.current.entry(address).or_insert(0) += quantity; } - pub fn drain_previous(&mut self) -> BTreeMap { + pub fn update_calculated(&mut self, rewards: BTreeMap) { + self.calculated = rewards; + } + + pub fn drain_current(&mut self) -> BTreeMap { let mut new = BTreeMap::new(); - mem::swap(&mut new, &mut self.previous); + mem::swap(&mut new, &mut self.current); new } - pub fn move_current_to_previous(&mut self) { - assert!(self.previous.is_empty()); - mem::swap(&mut self.previous, &mut self.current); + pub fn drain_calculated(&mut self) -> BTreeMap { + let mut new = BTreeMap::new(); + mem::swap(&mut new, &mut self.calculated); + new } } @@ -1159,14 +1164,12 @@ mod tests { origin_rewards.save_to_state(&mut state).unwrap(); let mut recovered_rewards = IntermediateRewards::load_from_state(&state).unwrap(); assert_eq!(origin_rewards, recovered_rewards); - let _drained = recovered_rewards.drain_previous(); + recovered_rewards.drain_current(); recovered_rewards.save_to_state(&mut state).unwrap(); let mut final_rewards = IntermediateRewards::load_from_state(&state).unwrap(); - assert_eq!(BTreeMap::new(), final_rewards.previous); - let current = final_rewards.current.clone(); - final_rewards.move_current_to_previous(); assert_eq!(BTreeMap::new(), final_rewards.current); - assert_eq!(current, final_rewards.previous); + final_rewards.drain_calculated(); + assert_eq!(BTreeMap::new(), final_rewards.calculated); } #[test] diff --git a/core/src/consensus/stake/mod.rs b/core/src/consensus/stake/mod.rs index 02789499fe..f677f816dc 100644 --- a/core/src/consensus/stake/mod.rs +++ b/core/src/consensus/stake/mod.rs @@ -328,19 +328,26 @@ pub fn add_intermediate_rewards(state: &mut TopLevelState, address: Address, rew Ok(()) } -pub fn drain_previous_rewards(state: &mut TopLevelState) -> StateResult> { +pub fn drain_current_rewards(state: &mut TopLevelState) -> StateResult> { let mut rewards = IntermediateRewards::load_from_state(state)?; - let drained = rewards.drain_previous(); + let drained = rewards.drain_current(); rewards.save_to_state(state)?; Ok(drained) } -pub fn move_current_to_previous_intermediate_rewards(state: &mut TopLevelState) -> StateResult<()> { +pub fn update_calculated_rewards(state: &mut TopLevelState, values: HashMap) -> StateResult<()> { let mut rewards = IntermediateRewards::load_from_state(state)?; - rewards.move_current_to_previous(); + rewards.update_calculated(values.into_iter().collect()); rewards.save_to_state(state) } +pub fn drain_calculated_rewards(state: &mut TopLevelState) -> StateResult> { + let mut rewards = IntermediateRewards::load_from_state(state)?; + let drained = rewards.drain_calculated(); + rewards.save_to_state(state)?; + Ok(drained) +} + pub fn update_validator_weights(state: &mut TopLevelState, block_author: &Address) -> StateResult<()> { let mut validators = Validators::load_from_state(state)?; validators.update_weight(block_author); diff --git a/core/src/consensus/tendermint/engine.rs b/core/src/consensus/tendermint/engine.rs index 78f15ddc33..2d63f1a316 100644 --- a/core/src/consensus/tendermint/engine.rs +++ b/core/src/consensus/tendermint/engine.rs @@ -143,9 +143,30 @@ impl ConsensusEngine for Tendermint { /// Block transformation functions, before the transactions. fn on_open_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> { + let client = self.client().ok_or(EngineError::CannotOpenBlock)?; + + let block_number = block.header().number(); let metadata = block.state().metadata()?.expect("Metadata must exist"); - if block.header().number() == metadata.last_term_finished_block_num() + 1 { - // FIXME: on_term_open + if block_number == metadata.last_term_finished_block_num() + 1 { + match metadata.current_term_id() { + 0 => {}, + _ => { + let rewards = stake::drain_current_rewards(block.state_mut())?; + let banned = stake::Banned::load_from_state(block.state())?; + let start_of_the_current_term_header = + encoded::Header::new(block.header().clone().rlp_bytes().to_vec()); + + let pending_rewards = calculate_pending_rewards_of_the_term( + &*client, + &*self.validators, + rewards, + start_of_the_current_term_header, + &banned, + )?; + + stake::update_calculated_rewards(block.state_mut(), pending_rewards)?; + } + } } Ok(()) } @@ -205,32 +226,11 @@ impl ConsensusEngine for Tendermint { let inactive_validators = match term { 0 => Vec::new(), _ => { - let rewards = stake::drain_previous_rewards(block.state_mut())?; - let start_of_the_current_term = metadata.last_term_finished_block_num() + 1; - - if term > 1 { - let banned = stake::Banned::load_from_state(block.state())?; - let start_of_the_current_term_header = if block_number == start_of_the_current_term { - encoded::Header::new(block.header().clone().rlp_bytes().to_vec()) - } else { - client.block_header(&start_of_the_current_term.into()).unwrap() - }; - - let pending_rewards = calculate_pending_rewards_of_the_term( - &*client, - &*self.validators, - rewards, - start_of_the_current_term_header, - &banned, - )?; - - for (address, reward) in pending_rewards { - self.machine.add_balance(block, &address, reward)?; - } + for (address, reward) in stake::drain_calculated_rewards(block.state_mut())? { + self.machine.add_balance(block, &address, reward)?; } - stake::move_current_to_previous_intermediate_rewards(block.state_mut())?; - + let start_of_the_current_term = metadata.last_term_finished_block_num() + 1; let validators = stake::Validators::load_from_state(block.state())? .into_iter() .map(|val| public_to_address(val.pubkey())) From 9fa15e9dfea2b21b621eb72642a53ecca225c44b Mon Sep 17 00:00:00 2001 From: Joonmo Yang Date: Fri, 6 Dec 2019 16:17:12 +0900 Subject: [PATCH 3/4] Introduce CurrentValidators CurrentValidators represent the list of validators for the current block. Its value is the same as the NextValidators of the previous block's state. --- core/src/client/test_client.rs | 10 ++-- core/src/consensus/stake/action_data.rs | 61 +++++++++++++++++++------ core/src/consensus/stake/mod.rs | 14 +++--- core/src/consensus/tendermint/engine.rs | 19 ++++++-- 4 files changed, 75 insertions(+), 29 deletions(-) diff --git a/core/src/client/test_client.rs b/core/src/client/test_client.rs index 962aa73e16..f8b0252119 100644 --- a/core/src/client/test_client.rs +++ b/core/src/client/test_client.rs @@ -58,7 +58,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; @@ -105,7 +105,7 @@ pub struct TestBlockChainClient { /// Fixed validator keys pub validator_keys: RwLock>, /// Fixed validators - pub validators: Validators, + pub validators: NextValidators, } impl Default for TestBlockChainClient { @@ -159,7 +159,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. @@ -324,14 +324,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 1827a96ec2..57292fcdde 100644 --- a/core/src/consensus/stake/action_data.rs +++ b/core/src/consensus/stake/action_data.rs @@ -42,8 +42,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 { @@ -275,17 +277,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 { @@ -336,7 +338,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 { @@ -385,7 +387,7 @@ impl Validators { } } -impl Deref for Validators { +impl Deref for NextValidators { type Target = Vec; fn deref(&self) -> &Self::Target { @@ -393,13 +395,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; @@ -408,6 +410,39 @@ 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; + } +} + +impl Deref for CurrentValidators { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + #[derive(Default, Debug, PartialEq)] pub struct IntermediateRewards { current: BTreeMap, @@ -544,7 +579,7 @@ impl Candidates { pub fn renew_candidates( &mut self, - validators: &Validators, + validators: &NextValidators, nomination_ends_at: u64, inactive_validators: &[Address], banned: &Banned, @@ -1754,7 +1789,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 f677f816dc..c2f0ad02ba 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, IntermediateRewards, 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 fn add_intermediate_rewards(state: &mut TopLevelState, address: Address, reward: u64) -> StateResult<()> { @@ -349,7 +349,7 @@ pub fn drain_calculated_rewards(state: &mut TopLevelState) -> StateResult 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) } @@ -421,7 +421,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)?; @@ -439,7 +439,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); @@ -489,7 +489,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 2d63f1a316..c1563ab237 100644 --- a/core/src/consensus/tendermint/engine.rs +++ b/core/src/consensus/tendermint/engine.rs @@ -147,14 +147,25 @@ impl ConsensusEngine for Tendermint { let block_number = block.header().number(); let metadata = block.state().metadata()?.expect("Metadata must exist"); + let term = metadata.current_term_id(); + + match term { + 0 => {} + _ => { + 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())?; + } + } + if block_number == metadata.last_term_finished_block_num() + 1 { - match metadata.current_term_id() { - 0 => {}, + match term { + 0 => {} _ => { let rewards = stake::drain_current_rewards(block.state_mut())?; let banned = stake::Banned::load_from_state(block.state())?; let start_of_the_current_term_header = - encoded::Header::new(block.header().clone().rlp_bytes().to_vec()); + encoded::Header::new(block.header().clone().rlp_bytes().to_vec()); let pending_rewards = calculate_pending_rewards_of_the_term( &*client, @@ -231,7 +242,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(); From 2f2d6bae07db325e2529c7a3ed181ed6d7209399 Mon Sep 17 00:00:00 2001 From: Joonmo Yang Date: Tue, 24 Dec 2019 18:23:47 +0900 Subject: [PATCH 4/4] Get tendermint validators with CurrentValidators --- core/src/consensus/simple_poa/mod.rs | 2 +- core/src/consensus/stake/action_data.rs | 6 ++ core/src/consensus/stake/mod.rs | 4 -- core/src/consensus/tendermint/engine.rs | 14 +---- .../validator_set/dynamic_validator.rs | 59 +++++++++++++++---- core/src/consensus/validator_set/mod.rs | 4 +- .../consensus/validator_set/validator_list.rs | 6 +- 7 files changed, 65 insertions(+), 30 deletions(-) diff --git a/core/src/consensus/simple_poa/mod.rs b/core/src/consensus/simple_poa/mod.rs index 7192077bd8..ebd77e9cd8 100644 --- a/core/src/consensus/simple_poa/mod.rs +++ b/core/src/consensus/simple_poa/mod.rs @@ -149,7 +149,7 @@ impl ConsensusEngine for SimplePoA { fn possible_authors(&self, _block_number: Option) -> Result>, EngineError> { // TODO: It works because the round robin validator doesn't use the parent hash. let parent = H256::from(0).into(); - Ok(Some(self.validators.addresses(&parent))) + Ok(Some(self.validators.next_addresses(&parent))) } } diff --git a/core/src/consensus/stake/action_data.rs b/core/src/consensus/stake/action_data.rs index 57292fcdde..7c59dc9135 100644 --- a/core/src/consensus/stake/action_data.rs +++ b/core/src/consensus/stake/action_data.rs @@ -443,6 +443,12 @@ impl Deref for CurrentValidators { } } +impl From for Vec { + fn from(val: CurrentValidators) -> Self { + val.0 + } +} + #[derive(Default, Debug, PartialEq)] pub struct IntermediateRewards { current: BTreeMap, diff --git a/core/src/consensus/stake/mod.rs b/core/src/consensus/stake/mod.rs index c2f0ad02ba..513f8a693c 100644 --- a/core/src/consensus/stake/mod.rs +++ b/core/src/consensus/stake/mod.rs @@ -317,10 +317,6 @@ pub fn get_stakes(state: &TopLevelState) -> StateResult> { Ok(result) } -pub fn get_validators(state: &TopLevelState) -> StateResult { - NextValidators::load_from_state(state) -} - pub fn add_intermediate_rewards(state: &mut TopLevelState, address: Address, reward: u64) -> StateResult<()> { let mut rewards = IntermediateRewards::load_from_state(state)?; rewards.add_quantity(address, reward); diff --git a/core/src/consensus/tendermint/engine.rs b/core/src/consensus/tendermint/engine.rs index c1563ab237..67a83dc4d1 100644 --- a/core/src/consensus/tendermint/engine.rs +++ b/core/src/consensus/tendermint/engine.rs @@ -342,7 +342,7 @@ impl ConsensusEngine for Tendermint { client.block_header(&(block_number - 1).into()).ok_or(EngineError::CannotOpenBlock)?.hash() // the parent of the given block number } }; - Ok(Some(self.validators.addresses(&block_hash))) + Ok(Some(self.validators.next_addresses(&block_hash))) } } @@ -389,10 +389,7 @@ fn aggregate_work_info( end_of_the_last_term + 1 }; let mut header = start_of_the_next_term_header; - let mut parent_validators = { - let parent_header = chain.block_header(&header.parent_hash().into()).unwrap(); - validators.addresses(&parent_header.parent_hash()) - }; + let mut parent_validators = validators.current_addresses(&header.parent_hash()); while start_of_the_current_term != header.number() { for index in TendermintSealView::new(&header.seal()).bitset()?.true_index_iter() { let signer = *parent_validators.get(index).expect("The seal must be the signature of the validator"); @@ -400,12 +397,7 @@ fn aggregate_work_info( } 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 parent_header = chain.block_header(&header.parent_hash().into()).unwrap(); - validators.addresses(&parent_header.parent_hash()) - }; + parent_validators = validators.current_addresses(&header.parent_hash()); let author = header.author(); let info = work_info.entry(author).or_default(); diff --git a/core/src/consensus/validator_set/dynamic_validator.rs b/core/src/consensus/validator_set/dynamic_validator.rs index 829e9cd5df..19fa775db8 100644 --- a/core/src/consensus/validator_set/dynamic_validator.rs +++ b/core/src/consensus/validator_set/dynamic_validator.rs @@ -24,7 +24,7 @@ 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::{CurrentValidators, NextValidators, Validator}; use crate::consensus::EngineError; /// Validator set containing a known set of public keys. @@ -41,10 +41,10 @@ impl DynamicValidator { } } - fn validators(&self, parent: BlockHash) -> Option> { + fn next_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 = parent.into(); + 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. @@ -54,7 +54,7 @@ impl DynamicValidator { return None } let state = client.state_at(block_id)?; - let validators = get_validators(&state).unwrap(); + let validators = NextValidators::load_from_state(&state).unwrap(); if validators.is_empty() { None } else { @@ -64,12 +64,39 @@ impl DynamicValidator { } } - fn validators_pubkey(&self, parent: BlockHash) -> Option> { - self.validators(parent).map(|validators| validators.into_iter().map(|val| *val.pubkey()).collect()) + 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.into(); + validators.reverse(); + Some(validators) + } + } + + fn validators_pubkey(&self, hash: BlockHash) -> Option> { + self.next_validators(hash).map(|validators| validators.into_iter().map(|val| *val.pubkey()).collect()) + } + + fn current_validators_pubkey(&self, hash: BlockHash) -> Option> { + self.current_validators(hash).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 { @@ -136,7 +163,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,7 +171,7 @@ impl ValidatorSet for DynamicValidator { } fn check_enough_votes(&self, parent: &BlockHash, votes: &BitSet) -> Result<(), EngineError> { - if let Some(validators) = self.validators(*parent) { + if let Some(validators) = self.next_validators(*parent) { let mut voted_delegation = 0u64; let n_validators = validators.len(); for index in votes.true_index_iter() { @@ -181,11 +208,19 @@ impl ValidatorSet for DynamicValidator { *client_lock = Some(client); } - fn addresses(&self, parent: &BlockHash) -> Vec
{ - if let Some(validators) = self.validators_pubkey(*parent) { + fn current_addresses(&self, hash: &BlockHash) -> Vec
{ + if let Some(validators) = self.current_validators_pubkey(*hash) { + validators.iter().map(public_to_address).collect() + } else { + self.initial_list.next_addresses(hash) + } + } + + fn next_addresses(&self, hash: &BlockHash) -> Vec
{ + if let Some(validators) = self.validators_pubkey(*hash) { validators.iter().map(public_to_address).collect() } else { - self.initial_list.addresses(parent) + self.initial_list.next_addresses(hash) } } } diff --git a/core/src/consensus/validator_set/mod.rs b/core/src/consensus/validator_set/mod.rs index ba8127fac3..835d0ecda5 100644 --- a/core/src/consensus/validator_set/mod.rs +++ b/core/src/consensus/validator_set/mod.rs @@ -57,5 +57,7 @@ pub trait ValidatorSet: Send + Sync { /// Allows blockchain state access. fn register_client(&self, _client: Weak) {} - fn addresses(&self, _parent: &BlockHash) -> Vec
; + fn current_addresses(&self, _hash: &BlockHash) -> Vec
; + + fn next_addresses(&self, _hash: &BlockHash) -> Vec
; } diff --git a/core/src/consensus/validator_set/validator_list.rs b/core/src/consensus/validator_set/validator_list.rs index 2c56efd72b..9f4fa278f2 100644 --- a/core/src/consensus/validator_set/validator_list.rs +++ b/core/src/consensus/validator_set/validator_list.rs @@ -105,7 +105,11 @@ impl ValidatorSet for RoundRobinValidator { *self.client.write() = Some(client); } - fn addresses(&self, _parent: &BlockHash) -> Vec
{ + fn current_addresses(&self, _hash: &BlockHash) -> Vec
{ + self.validators.iter().map(public_to_address).collect() + } + + fn next_addresses(&self, _hash: &BlockHash) -> Vec
{ self.validators.iter().map(public_to_address).collect() } }