Skip to content

Commit 5f8ae56

Browse files
author
Seulgi Kim
committed
Implement the dynamic validator set
1 parent 30aef79 commit 5f8ae56

7 files changed

Lines changed: 217 additions & 15 deletions

File tree

core/src/client/client.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,9 @@ impl TermInfo for Client {
793793

794794
fn state_at_term_begin(&self, id: BlockId) -> Option<TopLevelState> {
795795
if let Some(block_num) = self.last_term_finished_block_num(id) {
796+
if block_num == 0 {
797+
return None
798+
}
796799
self.state_at(block_num.into())
797800
} else {
798801
None

core/src/client/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ pub trait EngineClient: Sync + Send + BlockChainTrait + ImportBlock {
111111
fn get_kvdb(&self) -> Arc<KeyValueDB>;
112112
}
113113

114-
pub trait ConsensusClient: BlockChainTrait + EngineClient + TermInfo {}
114+
pub trait ConsensusClient: BlockChainTrait + EngineClient + EngineInfo + TermInfo + StateInfo {}
115115

116116
pub trait TermInfo {
117117
fn last_term_finished_block_num(&self, id: BlockId) -> Option<BlockNumber>;

core/src/client/test_client.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ use crate::blockchain_info::BlockChainInfo;
5656
use crate::client::ImportResult;
5757
use crate::client::{
5858
AccountData, BlockChainClient, BlockChainTrait, BlockProducer, BlockStatus, EngineInfo, ImportBlock,
59-
MiningBlockChainClient, StateOrBlock, TermInfo,
59+
MiningBlockChainClient, StateInfo, StateOrBlock, TermInfo,
6060
};
6161
use crate::db::{COL_STATE, NUM_COLUMNS};
6262
use crate::encoded;
@@ -616,3 +616,9 @@ impl TermInfo for TestBlockChainClient {
616616
None
617617
}
618618
}
619+
620+
impl StateInfo for TestBlockChainClient {
621+
fn state_at(&self, _id: BlockId) -> Option<TopLevelState> {
622+
None
623+
}
624+
}

core/src/consensus/tendermint/params.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ use std::time::Duration;
2121
use cjson;
2222
use ckey::{Address, PlatformAddress};
2323

24-
use super::super::validator_set::{new_validator_set, ValidatorSet};
24+
use super::super::validator_set::DynamicValidator;
2525
use super::types::View;
2626
use super::Step;
2727

2828
/// `Tendermint` params.
2929
pub struct TendermintParams {
3030
/// List of validators.
31-
pub validators: Arc<ValidatorSet>,
31+
pub validators: Arc<DynamicValidator>,
3232
/// Timeout durations for different steps.
3333
pub timeouts: TimeoutParams,
3434
/// Reward per block in base units.
@@ -41,7 +41,7 @@ impl From<cjson::scheme::TendermintParams> for TendermintParams {
4141
fn from(p: cjson::scheme::TendermintParams) -> Self {
4242
let dt = TimeoutParams::default();
4343
TendermintParams {
44-
validators: new_validator_set(p.validators),
44+
validators: Arc::new(DynamicValidator::new(p.validators)),
4545
timeouts: TimeoutParams {
4646
propose: p.timeout_propose.map_or(dt.propose, to_duration),
4747
propose_delta: p.timeout_propose_delta.map_or(dt.propose_delta, to_duration),

core/src/consensus/tendermint/worker.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ use crate::account_provider::AccountProvider;
4343
use crate::block::*;
4444
use crate::client::ConsensusClient;
4545
use crate::consensus::signer::EngineSigner;
46-
use crate::consensus::validator_set::ValidatorSet;
46+
use crate::consensus::validator_set::{DynamicValidator, ValidatorSet};
4747
use crate::consensus::vote_collector::VoteCollector;
4848
use crate::consensus::{EngineError, Seal};
4949
use crate::encoded;
@@ -59,7 +59,7 @@ type SpawnResult = (
5959
crossbeam::Sender<()>,
6060
);
6161

62-
pub fn spawn(validators: Arc<ValidatorSet>) -> SpawnResult {
62+
pub fn spawn(validators: Arc<DynamicValidator>) -> SpawnResult {
6363
Worker::spawn(validators)
6464
}
6565

@@ -86,7 +86,7 @@ struct Worker {
8686
/// The last confirmed view from the commit step.
8787
last_confirmed_view: View,
8888
/// Set used to determine the current validators.
89-
validators: Arc<ValidatorSet>,
89+
validators: Arc<DynamicValidator>,
9090
/// Channel to the network extension, must be set later.
9191
extension: EventSender<network::Event>,
9292
time_gap_params: TimeGapParams,
@@ -164,7 +164,7 @@ pub enum Event {
164164
impl Worker {
165165
/// Create a new instance of Tendermint engine
166166
fn new(
167-
validators: Arc<ValidatorSet>,
167+
validators: Arc<DynamicValidator>,
168168
extension: EventSender<network::Event>,
169169
client: Weak<ConsensusClient>,
170170
time_gap_params: TimeGapParams,
@@ -188,7 +188,7 @@ impl Worker {
188188
}
189189
}
190190

191-
fn spawn(validators: Arc<ValidatorSet>) -> SpawnResult {
191+
fn spawn(validators: Arc<DynamicValidator>) -> SpawnResult {
192192
let (sender, receiver) = crossbeam::unbounded();
193193
let (quit, quit_receiver) = crossbeam::bounded(1);
194194
let (external_params_initializer, external_params_receiver) = crossbeam::bounded(1);
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
// Copyright 2019 Kodebox, Inc.
2+
// This file is part of CodeChain.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as
6+
// published by the Free Software Foundation, either version 3 of the
7+
// License, or (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
17+
use std::sync::{Arc, Weak};
18+
19+
use ckey::{public_to_address, Address, Public};
20+
use ctypes::util::unexpected::OutOfBounds;
21+
use parking_lot::RwLock;
22+
use primitives::H256;
23+
24+
use super::{ValidatorList, ValidatorSet};
25+
use crate::client::ConsensusClient;
26+
use crate::consensus::bit_set::BitSet;
27+
use crate::consensus::EngineError;
28+
use consensus::stake::{get_validators, Validators};
29+
30+
/// Validator set containing a known set of public keys.
31+
pub struct DynamicValidator {
32+
initial_list: ValidatorList,
33+
client: RwLock<Option<Weak<ConsensusClient>>>,
34+
}
35+
36+
impl DynamicValidator {
37+
pub fn new(initial_validators: Vec<Public>) -> Self {
38+
DynamicValidator {
39+
initial_list: ValidatorList::new(initial_validators),
40+
client: Default::default(),
41+
}
42+
}
43+
44+
fn validators_at_term_begin(&self, parent: H256) -> Option<Validators> {
45+
let client: Arc<ConsensusClient> = self.client.read().as_ref().and_then(Weak::upgrade)?;
46+
let state = client.state_at_term_begin(parent.into())?;
47+
Some(get_validators(&state).unwrap())
48+
}
49+
50+
fn validators(&self, parent: H256) -> Option<Validators> {
51+
let client: Arc<ConsensusClient> = self.client.read().as_ref().and_then(Weak::upgrade)?;
52+
let block_id = parent.into();
53+
if client.current_term_id(block_id)? == 0 {
54+
return None
55+
}
56+
let state = client.state_at(block_id)?;
57+
Some(get_validators(&state).unwrap())
58+
}
59+
60+
fn validators_pubkey(&self, parent: H256) -> Option<Vec<Public>> {
61+
self.validators(parent).map(|validators| validators.pubkeys())
62+
}
63+
}
64+
65+
impl ValidatorSet for DynamicValidator {
66+
fn contains(&self, parent: &H256, public: &Public) -> bool {
67+
if let Some(validators) = self.validators_pubkey(*parent) {
68+
validators.into_iter().any(|pubkey| pubkey == *public)
69+
} else {
70+
self.initial_list.contains(parent, public)
71+
}
72+
}
73+
74+
fn contains_address(&self, parent: &H256, address: &Address) -> bool {
75+
if let Some(validators) = self.validators_pubkey(*parent) {
76+
validators.into_iter().any(|pubkey| public_to_address(&pubkey) == *address)
77+
} else {
78+
self.initial_list.contains_address(parent, address)
79+
}
80+
}
81+
82+
fn get(&self, parent: &H256, nonce: usize) -> Public {
83+
if let Some(validators) = self.validators_pubkey(*parent) {
84+
let n_validators = validators.len();
85+
validators.into_iter().nth(nonce % n_validators).unwrap()
86+
} else {
87+
self.initial_list.get(parent, nonce)
88+
}
89+
}
90+
91+
fn get_index(&self, parent: &H256, public: &Public) -> Option<usize> {
92+
if let Some(validators) = self.validators_pubkey(*parent) {
93+
validators.into_iter().enumerate().find(|(_index, pubkey)| pubkey == public).map(|(index, _)| index)
94+
} else {
95+
self.initial_list.get_index(parent, public)
96+
}
97+
}
98+
99+
fn get_index_by_address(&self, parent: &H256, address: &Address) -> Option<usize> {
100+
if let Some(validators) = self.validators_pubkey(*parent) {
101+
validators
102+
.into_iter()
103+
.enumerate()
104+
.find(|(_index, pubkey)| public_to_address(pubkey) == *address)
105+
.map(|(index, _)| index)
106+
} else {
107+
self.initial_list.get_index_by_address(parent, address)
108+
}
109+
}
110+
111+
fn next_block_proposer(&self, parent: &H256, view: u64) -> Option<Address> {
112+
if let Some(validators) = self.validators_pubkey(*parent) {
113+
let n_validators = validators.len();
114+
let nonce = view as usize % n_validators;
115+
Some(public_to_address(validators.get(n_validators - nonce - 1).unwrap()))
116+
} else {
117+
self.initial_list.next_block_proposer(parent, view)
118+
}
119+
}
120+
121+
fn count(&self, parent: &H256) -> usize {
122+
if let Some(validators) = self.validators(*parent) {
123+
validators.len()
124+
} else {
125+
self.initial_list.count(parent)
126+
}
127+
}
128+
129+
fn check_enough_votes(&self, parent: &H256, votes: &BitSet) -> Result<(), EngineError> {
130+
if let Some(validators_at_term_begin) = self.validators_at_term_begin(*parent) {
131+
let validators =
132+
self.validators(*parent).expect("The validator must exist in the middle of term").pubkeys();
133+
let mut weight = 0;
134+
for index in votes.true_index_iter() {
135+
let pubkey = validators.get(index).ok_or_else(|| {
136+
EngineError::ValidatorNotExist {
137+
height: 0, // FIXME
138+
index,
139+
}
140+
})?;
141+
weight += validators_at_term_begin.weight(pubkey).unwrap() as usize;
142+
}
143+
let total_weight = validators_at_term_begin.total_weight() as usize;
144+
if weight * 3 > total_weight * 2 {
145+
Ok(())
146+
} else {
147+
let threshold = total_weight * 2 / 3;
148+
Err(EngineError::BadSealFieldSize(OutOfBounds {
149+
min: Some(threshold),
150+
max: Some(total_weight),
151+
found: weight,
152+
}))
153+
}
154+
} else {
155+
self.initial_list.check_enough_votes(parent, votes)
156+
}
157+
}
158+
159+
/// Allows blockchain state access.
160+
fn register_client(&self, client: Weak<ConsensusClient>) {
161+
self.initial_list.register_client(Weak::clone(&client));
162+
let mut client_lock = self.client.write();
163+
assert!(client_lock.is_none());
164+
*client_lock = Some(client);
165+
}
166+
167+
fn addresses(&self, parent: &H256) -> Vec<Address> {
168+
if let Some(validators) = self.validators_pubkey(*parent) {
169+
validators.iter().map(public_to_address).collect()
170+
} else {
171+
self.initial_list.addresses(parent)
172+
}
173+
}
174+
}
175+
176+
#[cfg(test)]
177+
mod tests {
178+
use std::str::FromStr;
179+
180+
use ckey::Public;
181+
182+
use super::super::ValidatorSet;
183+
use super::DynamicValidator;
184+
185+
#[test]
186+
fn validator_set() {
187+
let a1 = Public::from_str("34959b60d54703e9dfe36afb1e9950a4abe34d666cbb64c92969013bc9cc74063f9e4680d9d48c4597ee623bd4b507a1b2f43a9c5766a06463f85b73a94c51d1").unwrap();
188+
let a2 = Public::from_str("8c5a25bfafceea03073e2775cfb233a46648a088c12a1ca18a5865534887ccf60e1670be65b5f8e29643f463fdf84b1cbadd6027e71d8d04496570cb6b04885d").unwrap();
189+
let set = DynamicValidator::new(vec![a1, a2]);
190+
assert!(set.contains(&Default::default(), &a1));
191+
assert_eq!(set.get(&Default::default(), 0), a1);
192+
assert_eq!(set.get(&Default::default(), 1), a2);
193+
assert_eq!(set.get(&Default::default(), 2), a1);
194+
}
195+
}

core/src/consensus/validator_set/mod.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
// You should have received a copy of the GNU Affero General Public License
1515
// along with this program. If not, see <https://www.gnu.org/licenses/>.
1616

17-
use std::sync::{Arc, Weak};
17+
use std::sync::Weak;
1818

1919
use ckey::{Address, Public};
2020
use ctypes::BlockNumber;
@@ -25,12 +25,10 @@ use super::BitSet;
2525
use crate::client::ConsensusClient;
2626
use crate::consensus::EngineError;
2727

28+
mod dynamic_validator;
2829
pub mod validator_list;
2930

30-
/// Creates a validator set from validator public keys.
31-
pub fn new_validator_set(validators: Vec<Public>) -> Arc<ValidatorSet> {
32-
Arc::new(ValidatorList::new(validators))
33-
}
31+
pub use self::dynamic_validator::DynamicValidator;
3432

3533
/// A validator set.
3634
pub trait ValidatorSet: Send + Sync {

0 commit comments

Comments
 (0)