Skip to content

Commit e6411ca

Browse files
foriequal0mergify[bot]
authored andcommitted
Implement self nomination
1 parent 9bf06ac commit e6411ca

File tree

9 files changed

+732
-149
lines changed

9 files changed

+732
-149
lines changed

core/src/consensus/solo/mod.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ use ctypes::{CommonParams, Header};
2323

2424
use self::params::SoloParams;
2525
use super::stake;
26-
use super::validator_set;
2726
use super::{ConsensusEngine, Seal};
2827
use crate::block::{ExecutedBlock, IsBlock};
2928
use crate::codechain_machine::CodeChainMachine;
@@ -44,10 +43,7 @@ impl<M> Solo<M> {
4443
if params.enable_hit_handler {
4544
action_handlers.push(Arc::new(HitHandler::new()));
4645
}
47-
action_handlers.push(Arc::new(stake::Stake::new(
48-
params.genesis_stakes.clone(),
49-
Arc::new(validator_set::null_validator::NullValidator {}),
50-
)));
46+
action_handlers.push(Arc::new(stake::Stake::new(params.genesis_stakes.clone())));
5147

5248
Solo {
5349
params,

core/src/consensus/stake/action_data.rs

Lines changed: 241 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@
1616

1717
#[cfg(test)]
1818
use std::collections::btree_map;
19-
use std::collections::btree_map::Entry;
20-
use std::collections::{btree_set, BTreeMap, BTreeSet};
19+
use std::collections::btree_map::{BTreeMap, Entry};
20+
use std::collections::btree_set::{self, BTreeSet};
2121
use std::mem;
2222

2323
use ckey::Address;
2424
use cstate::{ActionData, ActionDataKeyBuilder, StateResult, TopLevelState, TopState, TopStateView};
2525
use ctypes::errors::RuntimeError;
2626
use primitives::H256;
27-
use rlp::{Decodable, Encodable, Rlp, RlpStream};
27+
use rlp::{decode_list, Decodable, Encodable, Rlp, RlpStream};
2828

2929
use super::CUSTOM_ACTION_HANDLER_ID;
3030

@@ -35,6 +35,8 @@ pub fn get_account_key(address: &Address) -> H256 {
3535
lazy_static! {
3636
pub static ref STAKEHOLDER_ADDRESSES_KEY: H256 =
3737
ActionDataKeyBuilder::new(CUSTOM_ACTION_HANDLER_ID, 1).append(&"StakeholderAddresses").into_key();
38+
pub static ref CANDIDATES_KEY: H256 =
39+
ActionDataKeyBuilder::new(CUSTOM_ACTION_HANDLER_ID, 1).append(&"Candidates").into_key();
3840
}
3941

4042
pub fn get_delegation_key(address: &Address) -> H256 {
@@ -46,6 +48,7 @@ pub fn get_intermediate_rewards_key() -> H256 {
4648
}
4749

4850
pub type StakeQuantity = u64;
51+
pub type Deposit = u64;
4952

5053
pub struct StakeAccount<'a> {
5154
pub address: &'a Address,
@@ -194,7 +197,6 @@ impl<'a> Delegation<'a> {
194197
.into())
195198
}
196199

197-
#[cfg(test)]
198200
pub fn get_quantity(&self, delegatee: &Address) -> StakeQuantity {
199201
self.delegatees.get(delegatee).cloned().unwrap_or(0)
200202
}
@@ -257,6 +259,62 @@ impl IntermediateRewards {
257259
}
258260
}
259261

262+
pub struct Candidates(BTreeMap<Address, Candidate>);
263+
#[derive(Clone, Debug, Eq, PartialEq, RlpEncodable, RlpDecodable)]
264+
pub struct Candidate {
265+
pub address: Address,
266+
pub deposit: Deposit,
267+
pub nomination_ends_at: u64,
268+
}
269+
270+
impl Candidates {
271+
pub fn load_from_state(state: &TopLevelState) -> StateResult<Candidates> {
272+
let key = *CANDIDATES_KEY;
273+
let candidates = state.action_data(&key)?.map(|data| decode_list::<Candidate>(&data)).unwrap_or_default();
274+
let indexed = candidates.into_iter().map(|c| (c.address, c)).collect();
275+
Ok(Candidates(indexed))
276+
}
277+
278+
pub fn save_to_state(&self, state: &mut TopLevelState) -> StateResult<()> {
279+
let key = *CANDIDATES_KEY;
280+
if !self.0.is_empty() {
281+
let encoded = encode_iter(self.0.values());
282+
state.update_action_data(&key, encoded)?;
283+
} else {
284+
state.remove_action_data(&key);
285+
}
286+
Ok(())
287+
}
288+
289+
pub fn get_candidate(&self, account: &Address) -> Option<&Candidate> {
290+
self.0.get(&account)
291+
}
292+
293+
#[cfg(test)]
294+
pub fn len(&self) -> usize {
295+
self.0.len()
296+
}
297+
298+
pub fn add_deposit(&mut self, address: &Address, quantity: Deposit, nomination_ends_at: u64) {
299+
let candidate = self.0.entry(*address).or_insert(Candidate {
300+
address: *address,
301+
deposit: 0,
302+
nomination_ends_at: 0,
303+
});
304+
candidate.deposit += quantity;
305+
if candidate.nomination_ends_at < nomination_ends_at {
306+
candidate.nomination_ends_at = nomination_ends_at;
307+
}
308+
}
309+
310+
pub fn drain_expired_candidates(&mut self, term_index: u64) -> Vec<Candidate> {
311+
let (expired, retained): (Vec<_>, Vec<_>) =
312+
self.0.values().cloned().partition(|c| c.nomination_ends_at <= term_index);
313+
self.0 = retained.into_iter().map(|c| (c.address, c)).collect();
314+
expired
315+
}
316+
}
317+
260318
fn decode_set<V>(data: Option<&ActionData>) -> BTreeSet<V>
261319
where
262320
V: Ord + Decodable, {
@@ -354,6 +412,18 @@ where
354412
rlp.drain().into_vec()
355413
}
356414

415+
fn encode_iter<'a, V, I>(iter: I) -> Vec<u8>
416+
where
417+
V: 'a + Encodable,
418+
I: ExactSizeIterator<Item = &'a V> + Clone, {
419+
let mut rlp = RlpStream::new();
420+
rlp.begin_list(iter.clone().count());
421+
for value in iter {
422+
rlp.append(value);
423+
}
424+
rlp.drain().into_vec()
425+
}
426+
357427
#[cfg(test)]
358428
mod tests {
359429
use super::*;
@@ -719,4 +789,171 @@ mod tests {
719789
assert_eq!(BTreeMap::new(), final_rewards.current);
720790
assert_eq!(current, final_rewards.previous);
721791
}
792+
793+
#[test]
794+
fn candidates_deposit_add() {
795+
let mut state = helpers::get_temp_state();
796+
797+
// Prepare
798+
let account = Address::random();
799+
let deposits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
800+
801+
for deposit in deposits.iter() {
802+
let mut candidates = Candidates::load_from_state(&state).unwrap();
803+
candidates.add_deposit(&account, *deposit, 0);
804+
candidates.save_to_state(&mut state).unwrap();
805+
}
806+
807+
// Assert
808+
let candidates = Candidates::load_from_state(&state).unwrap();
809+
let candidate = candidates.get_candidate(&account);
810+
assert_ne!(candidate, None);
811+
assert_eq!(candidate.unwrap().deposit, 55);
812+
}
813+
814+
#[test]
815+
fn candidates_deposit_can_be_zero() {
816+
let mut state = helpers::get_temp_state();
817+
818+
// Prepare
819+
let account = Address::random();
820+
let mut candidates = Candidates::load_from_state(&state).unwrap();
821+
candidates.add_deposit(&account, 0, 10);
822+
candidates.save_to_state(&mut state).unwrap();
823+
824+
// Assert
825+
let candidates = Candidates::load_from_state(&state).unwrap();
826+
let candidate = candidates.get_candidate(&account);
827+
assert_ne!(candidate, None);
828+
assert_eq!(candidate.unwrap().deposit, 0);
829+
assert_eq!(candidate.unwrap().nomination_ends_at, 10, "Can be a candidate with 0 deposit");
830+
}
831+
832+
#[test]
833+
fn candidates_deposit_should_update_nomination_ends_at() {
834+
let mut state = helpers::get_temp_state();
835+
836+
// Prepare
837+
let account = Address::random();
838+
let deposit_and_nomination_ends_at = [(10, 11), (20, 22), (30, 33), (0, 44)];
839+
840+
for (deposit, nomination_ends_at) in &deposit_and_nomination_ends_at {
841+
let mut candidates = Candidates::load_from_state(&state).unwrap();
842+
candidates.add_deposit(&account, *deposit, *nomination_ends_at);
843+
candidates.save_to_state(&mut state).unwrap();
844+
}
845+
846+
// Assert
847+
let candidates = Candidates::load_from_state(&state).unwrap();
848+
let candidate = candidates.get_candidate(&account);
849+
assert_ne!(candidate, None);
850+
assert_eq!(candidate.unwrap().deposit, 60);
851+
assert_eq!(
852+
candidate.unwrap().nomination_ends_at,
853+
44,
854+
"nomination_ends_at should be updated incrementally, and including zero deposit"
855+
);
856+
}
857+
858+
#[test]
859+
fn candidates_can_remove_expired_deposit() {
860+
let mut state = helpers::get_temp_state();
861+
862+
// Prepare
863+
let candidates_prepared = [
864+
Candidate {
865+
address: Address::from(0),
866+
deposit: 20,
867+
nomination_ends_at: 11,
868+
},
869+
Candidate {
870+
address: Address::from(1),
871+
deposit: 30,
872+
nomination_ends_at: 22,
873+
},
874+
Candidate {
875+
address: Address::from(2),
876+
deposit: 40,
877+
nomination_ends_at: 33,
878+
},
879+
Candidate {
880+
address: Address::from(3),
881+
deposit: 50,
882+
nomination_ends_at: 44,
883+
},
884+
];
885+
886+
for Candidate {
887+
address,
888+
deposit,
889+
nomination_ends_at,
890+
} in &candidates_prepared
891+
{
892+
let mut candidates = Candidates::load_from_state(&state).unwrap();
893+
candidates.add_deposit(&address, *deposit, *nomination_ends_at);
894+
candidates.save_to_state(&mut state).unwrap();
895+
}
896+
897+
// Remove Expired
898+
let mut candidates = Candidates::load_from_state(&state).unwrap();
899+
let expired = candidates.drain_expired_candidates(22);
900+
candidates.save_to_state(&mut state).unwrap();
901+
902+
// Assert
903+
assert_eq!(expired[..], candidates_prepared[0..=1],);
904+
let candidates = Candidates::load_from_state(&state).unwrap();
905+
assert_eq!(candidates.len(), 2);
906+
assert_eq!(candidates.get_candidate(&candidates_prepared[2].address), Some(&candidates_prepared[2]));
907+
assert_eq!(candidates.get_candidate(&candidates_prepared[3].address), Some(&candidates_prepared[3]));
908+
}
909+
910+
#[test]
911+
fn candidates_expire_all_cleanup_state() {
912+
let mut state = helpers::get_temp_state();
913+
914+
// Prepare
915+
let candidates_prepared = [
916+
Candidate {
917+
address: Address::from(0),
918+
deposit: 20,
919+
nomination_ends_at: 11,
920+
},
921+
Candidate {
922+
address: Address::from(1),
923+
deposit: 30,
924+
nomination_ends_at: 22,
925+
},
926+
Candidate {
927+
address: Address::from(2),
928+
deposit: 40,
929+
nomination_ends_at: 33,
930+
},
931+
Candidate {
932+
address: Address::from(3),
933+
deposit: 50,
934+
nomination_ends_at: 44,
935+
},
936+
];
937+
938+
for Candidate {
939+
address,
940+
deposit,
941+
nomination_ends_at,
942+
} in &candidates_prepared
943+
{
944+
let mut candidates = Candidates::load_from_state(&state).unwrap();
945+
candidates.add_deposit(&address, *deposit, *nomination_ends_at);
946+
candidates.save_to_state(&mut state).unwrap();
947+
}
948+
949+
// Remove Expired
950+
let mut candidates = Candidates::load_from_state(&state).unwrap();
951+
let expired = candidates.drain_expired_candidates(99);
952+
candidates.save_to_state(&mut state).unwrap();
953+
954+
// Assert
955+
assert_eq!(expired[..], candidates_prepared[0..4]);
956+
let result = state.action_data(&*CANDIDATES_KEY).unwrap();
957+
assert_eq!(result, None);
958+
}
722959
}

core/src/consensus/stake/actions.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@
1616

1717
use ckey::{Address, Signature};
1818
use ctypes::CommonParams;
19+
use primitives::Bytes;
1920
use rlp::{Decodable, DecoderError, Encodable, RlpStream, UntrustedRlp};
2021

2122
const ACTION_TAG_TRANSFER_CCS: u8 = 1;
2223
const ACTION_TAG_DELEGATE_CCS: u8 = 2;
2324
const ACTION_TAG_REVOKE: u8 = 3;
25+
const ACTION_TAG_SELF_NOMINATE: u8 = 4;
2426
const ACTION_TAG_CHANGE_PARAMS: u8 = 0xFF;
2527

2628
#[derive(Debug, PartialEq)]
@@ -37,6 +39,10 @@ pub enum Action {
3739
address: Address,
3840
quantity: u64,
3941
},
42+
SelfNominate {
43+
deposit: u64,
44+
metadata: Bytes,
45+
},
4046
ChangeParams {
4147
metadata_seq: u64,
4248
params: Box<CommonParams>,
@@ -65,6 +71,12 @@ impl Encodable for Action {
6571
} => {
6672
s.begin_list(3).append(&ACTION_TAG_REVOKE).append(address).append(quantity);
6773
}
74+
Action::SelfNominate {
75+
deposit,
76+
metadata,
77+
} => {
78+
s.begin_list(3).append(&ACTION_TAG_SELF_NOMINATE).append(deposit).append(metadata);
79+
}
6880
Action::ChangeParams {
6981
metadata_seq,
7082
params,
@@ -125,6 +137,19 @@ impl Decodable for Action {
125137
quantity: rlp.val_at(2)?,
126138
})
127139
}
140+
ACTION_TAG_SELF_NOMINATE => {
141+
let item_count = rlp.item_count()?;
142+
if item_count != 3 {
143+
return Err(DecoderError::RlpInvalidLength {
144+
expected: 3,
145+
got: item_count,
146+
})
147+
}
148+
Ok(Action::SelfNominate {
149+
deposit: rlp.val_at(1)?,
150+
metadata: rlp.val_at(2)?,
151+
})
152+
}
128153
ACTION_TAG_CHANGE_PARAMS => {
129154
let item_count = rlp.item_count()?;
130155
if item_count < 4 {

0 commit comments

Comments
 (0)