1use std::{
6 cmp::Ordering,
7 collections::{BTreeMap, BinaryHeap, HashMap, HashSet, VecDeque},
8 convert::TryFrom,
9 fmt::{Debug, Formatter},
10};
11
12use anyhow::{anyhow, bail, ensure, Result};
13#[cfg(any(test, feature = "fuzzing"))]
14use proptest_derive::Arbitrary;
15use serde::{Deserialize, Serialize};
16
17use cfx_types::H256;
18use diem_crypto::{
19 bls::deserialize_bls_public_key_unchecked, vrf_number_with_nonce,
20 HashValue, Signature, VRFProof,
21};
22use diem_logger::prelude::*;
23pub use incentives::*;
24use lock_status::{ForfeitRule, NodeLockStatus};
25use move_core_types::vm_status::DiscardedVMStatus;
26use pos_state_config::{PosStateConfigTrait, POS_STATE_CONFIG};
27use pow_types::StakingEvent;
28
29use crate::{
30 account_address::{from_consensus_public_key, AccountAddress},
31 account_config,
32 block_info::{PivotBlockDecision, Round, View},
33 contract_event::ContractEvent,
34 epoch_state::EpochState,
35 event::EventKey,
36 transaction::{DisputePayload, ElectionPayload},
37 validator_config::{
38 ConsensusPublicKey, ConsensusVRFPublicKey, MultiConsensusPublicKey,
39 MultiConsensusSignature,
40 },
41 validator_verifier::{ValidatorConsensusInfo, ValidatorVerifier},
42};
43
44pub mod lock_status;
45pub mod pos_state_config;
46
47pub const TERM_LIST_LEN: usize = 6;
48pub const ROUND_PER_TERM: Round = 60;
49pub const IN_QUEUE_LOCKED_VIEWS: u64 = 10080;
50pub const OUT_QUEUE_LOCKED_VIEWS: u64 = 10080;
51pub const TERM_MAX_SIZE: usize = 10000;
54pub const TERM_ELECTED_SIZE: usize = 50;
55
56mod incentives {
57 use super::{TERM_ELECTED_SIZE, TERM_LIST_LEN, TERM_MAX_SIZE};
58 use crate::term_state::pos_state_config::{
59 PosStateConfigTrait, POS_STATE_CONFIG,
60 };
61
62 const BONUS_VOTE_MAX_SIZE: u64 = 100;
63
64 pub const MAX_TERM_POINTS: u64 = 6_000_000;
65
66 const ELECTION_PERCENTAGE: u64 = 20;
67 const COMMITTEE_PERCENTAGE: u64 = 75;
68 const LEADER_PERCENTAGE: u64 = 3;
69 const BONUS_VOTE_PERCENTAGE: u64 = 2;
70
71 pub const ELECTION_POINTS: u64 =
72 MAX_TERM_POINTS * ELECTION_PERCENTAGE / 100 / (TERM_MAX_SIZE as u64);
73 pub const COMMITTEE_POINTS: u64 = MAX_TERM_POINTS * COMMITTEE_PERCENTAGE
74 / 100
75 / (TERM_ELECTED_SIZE as u64)
76 / (TERM_LIST_LEN as u64);
77
78 pub fn leader_points(view: u64) -> u64 {
79 MAX_TERM_POINTS * LEADER_PERCENTAGE
80 / 100
81 / POS_STATE_CONFIG.round_per_term(view)
82 }
83
84 pub fn bonus_vote_points(view: u64) -> u64 {
85 MAX_TERM_POINTS * BONUS_VOTE_PERCENTAGE
86 / 100
87 / POS_STATE_CONFIG.round_per_term(view)
88 / BONUS_VOTE_MAX_SIZE
89 }
90}
91
92#[derive(Copy, Clone, Eq, PartialEq, Serialize, Deserialize, Debug)]
93#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
94pub enum NodeStatus {
95 Accepted,
96 Retired,
97 Unlocked,
98}
99
100#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
101#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
102pub struct NodeData {
103 #[serde(deserialize_with = "deserialize_bls_public_key_unchecked")]
105 public_key: ConsensusPublicKey,
106 vrf_public_key: Option<ConsensusVRFPublicKey>,
107 lock_status: NodeLockStatus,
108}
109
110impl NodeData {
111 pub fn lock_status(&self) -> &NodeLockStatus { &self.lock_status }
112}
113
114#[derive(
116 Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Ord, PartialOrd,
117)]
118#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
119pub struct ElectionNodeID {
120 node_id: NodeID,
121 nonce: u64,
122}
123
124impl ElectionNodeID {
125 pub fn new(node_id: NodeID, nonce: u64) -> Self {
126 ElectionNodeID { node_id, nonce }
127 }
128}
129
130#[derive(Clone, Default, Debug, Serialize, Deserialize)]
131#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
132pub struct ElectingHeap(
133 BinaryHeap<(HashValue, ElectionNodeID)>,
134 HashSet<AccountAddress>,
135);
136
137#[derive(Clone, Default, Debug, Eq, PartialEq, Serialize, Deserialize)]
138#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
139pub struct ElectedMap(BTreeMap<AccountAddress, u64>);
140
141pub type CandyMap = ElectedMap;
142
143#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
144#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
145pub enum NodeList {
146 Electing(ElectingHeap),
147 Elected(ElectedMap),
148}
149
150impl Default for NodeList {
151 fn default() -> Self { NodeList::Electing(Default::default()) }
152}
153
154impl NodeList {
155 fn len(&self) -> usize {
156 match self {
157 NodeList::Electing(heap) => heap.0.len(),
158 NodeList::Elected(map) => map.0.len(),
159 }
160 }
161
162 fn add_node(&mut self, vrf_output: HashValue, node_id: ElectionNodeID) {
163 if let NodeList::Electing(heap) = self {
164 heap.add_node(vrf_output, node_id);
165 } else {
166 panic!("The term is finalized");
167 }
168 }
169
170 #[must_use]
171 fn finalize_elect(&mut self) -> CandyMap {
172 if let NodeList::Electing(heap) = self {
173 let electing_heap = std::mem::take(heap);
174 let (elected_heap, candy_map) = electing_heap.finalize();
175 *self = NodeList::Elected(elected_heap);
176 return candy_map;
177 } else {
178 panic!("The term is finalized");
179 }
180 }
181
182 fn has_elected(&self, addr: &AccountAddress) -> bool {
183 if let NodeList::Electing(heap) = self {
184 heap.1.contains(addr)
185 } else {
186 panic!("The term is finalized");
187 }
188 }
189
190 fn serving_votes(&self, address: &AccountAddress) -> u64 {
191 if let NodeList::Elected(map) = self {
192 map.0.get(address).cloned().unwrap_or(0)
193 } else {
194 panic!("The term is not finalized");
195 }
196 }
197
198 fn committee(&self) -> &ElectedMap {
199 if let NodeList::Elected(map) = self {
200 map
201 } else {
202 panic!("The term is not finalized");
203 }
204 }
205}
206
207impl ElectedMap {
208 pub fn inner(&self) -> &BTreeMap<AccountAddress, u64> { &self.0 }
209}
210
211impl ElectingHeap {
212 pub fn read_top_electing(&self) -> BTreeMap<AccountAddress, u64> {
213 let mut top_electing: BTreeMap<AccountAddress, u64> = BTreeMap::new();
214 let mut clone = self.clone();
215 let mut count = 0usize;
216 while let Some((_, node_id)) = clone.0.pop() {
217 *top_electing.entry(node_id.node_id.addr).or_insert(0) += 1;
218 count += 1;
219 if count >= POS_STATE_CONFIG.term_elected_size() {
220 break;
221 }
222 }
223 top_electing
224 }
225
226 fn finalize(mut self) -> (ElectedMap, CandyMap) {
227 let mut elected_map = ElectedMap::default();
228 let mut count = 0usize;
229 while let Some((_, node_id)) = self.0.pop() {
230 *elected_map.0.entry(node_id.node_id.addr).or_insert(0) += 1;
231 count += 1;
232 if count >= POS_STATE_CONFIG.term_elected_size() {
233 break;
234 }
235 }
236 let mut candy_map = elected_map.clone();
237 for (_, node_id) in self.0.into_vec().drain(..) {
238 *candy_map.0.entry(node_id.node_id.addr).or_insert(0) += 1;
239 }
240 (elected_map, candy_map)
241 }
242
243 pub fn add_node(&mut self, hash: HashValue, node_id: ElectionNodeID) {
244 let is_not_full_set = self.0.len() < POS_STATE_CONFIG.term_max_size();
245 self.1.insert(node_id.node_id.addr.clone());
246 if self
247 .0
248 .peek()
249 .map_or(true, |(max_value, _)| is_not_full_set || hash < *max_value)
250 {
251 self.0.push((hash, node_id.clone()));
252 if self.0.len() > POS_STATE_CONFIG.term_max_size() {
253 self.0.pop();
254 }
255 }
256 }
257}
258
259#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
260#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
261pub struct TermData {
262 start_view: Round,
263 seed: Vec<u8>,
264 node_list: NodeList,
266}
267
268impl TermData {
269 pub fn start_view(&self) -> u64 { self.start_view }
270
271 pub fn get_term(&self) -> u64 {
272 POS_STATE_CONFIG.get_term_view(self.start_view).0
273 }
274
275 pub fn node_list(&self) -> &NodeList { &self.node_list }
276}
277
278impl PartialEq for ElectingHeap {
279 fn eq(&self, other: &Self) -> bool {
280 if self.1 != other.1 {
281 return false;
282 }
283 let mut iter_self = self.0.iter();
284 let mut iter_other = other.0.iter();
285 while let Some(node) = iter_self.next() {
286 match iter_other.next() {
287 None => return false,
288 Some(other_node) => {
289 if node != other_node {
290 return false;
291 }
292 }
293 }
294 }
295 iter_other.next().is_none()
296 }
297}
298
299impl Eq for ElectingHeap {}
300
301impl TermData {
302 fn next_term(&self, node_list: NodeList, seed: Vec<u8>) -> Self {
303 TermData {
304 start_view: self.start_view
305 + POS_STATE_CONFIG.round_per_term(self.start_view),
306 seed,
307 node_list,
308 }
309 }
310}
311
312#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
313#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
314pub struct TermList {
315 current_term: u64,
319 term_list: Vec<TermData>,
323 candy_rewards: CandyMap,
324 electing_index: usize,
325}
326
327impl TermList {
328 fn start_term(&self) -> u64 {
329 self.current_term.saturating_sub(TERM_LIST_LEN as u64 - 1)
330 }
331
332 fn committee_for_term(&self, term: u64) -> &[TermData] {
333 let first_term = term.saturating_sub(TERM_LIST_LEN as u64 - 1) as usize;
334 let last_term = first_term + TERM_LIST_LEN - 1;
335 if first_term < self.start_term() as usize
336 || last_term >= self.electing_term_number() as usize
337 {
338 panic!(
339 "Can not get committee for term {}, current term {}",
340 term, self.current_term
341 );
342 }
343 let start_offset = first_term - self.start_term() as usize;
344 let end_offset = last_term - self.start_term() as usize;
345 &self.term_list[start_offset..=end_offset]
346 }
347
348 fn get_term_by_number(&self, term_number: u64) -> Option<&TermData> {
349 let start_term = self.start_term();
350 if term_number < start_term {
351 return None;
352 }
353 self.term_list.get((term_number - start_term) as usize)
354 }
355
356 fn electing_term_number(&self) -> u64 {
357 self.start_term() + self.electing_index as u64
358 }
359
360 fn electing_term_mut(&mut self) -> &mut TermData {
361 &mut self.term_list[self.electing_index]
362 }
363
364 fn electing_term(&self) -> &TermData {
365 &self.term_list[self.electing_index]
366 }
367
368 pub fn term_list(&self) -> &Vec<TermData> { &self.term_list }
369}
370
371impl TermList {
372 pub fn new_node_elected(
375 &mut self, event: &ElectionEvent, voting_power: u64,
376 ) -> anyhow::Result<()> {
377 if event.start_term != self.electing_term_number() {
378 bail!("term is not open for election, opening term {}, election term {}", self.electing_term_number(),event.start_term);
379 }
380 let term = self.electing_term_mut();
381
382 if term.node_list.has_elected(&event.node_id.addr) {
383 diem_warn!(
384 "The author {} has participated election for term {}",
385 event.node_id.addr,
386 event.start_term
387 );
388 return Ok(());
389 }
390
391 for nonce in 0..voting_power {
392 let priority = vrf_number_with_nonce(&event.vrf_output, nonce);
395 term.node_list.add_node(
396 priority,
397 ElectionNodeID::new(event.node_id.clone(), nonce),
398 );
399 }
400 Ok(())
401 }
402
403 pub fn new_term(&mut self, new_term: u64, new_seed: Vec<u8>) {
404 diem_debug!(
405 "new_term={}, start_view:{:?}",
406 new_term,
407 self.term_list
408 .iter()
409 .map(|t| (t.start_view, t.node_list.len()))
410 .collect::<Vec<_>>()
411 );
412 self.current_term = new_term;
413 if new_term < TERM_LIST_LEN as u64 {
414 return;
416 }
417 debug_assert!(
419 Some(self.term_list[TERM_LIST_LEN].start_view)
420 == POS_STATE_CONFIG.get_starting_view_for_term(new_term)
421 );
422 self.term_list.remove(0);
423 let new_term = self
424 .term_list
425 .last()
426 .unwrap()
427 .next_term(Default::default(), new_seed);
428 self.term_list.push(new_term);
429 self.electing_index -= 1;
430 assert_eq!(self.electing_index, 6);
431 }
432
433 pub fn finalize_election(&mut self) {
434 diem_debug!(
435 "Finalize election of term {}",
436 self.electing_term_number()
437 );
438 let finalize_term = self.electing_term_mut();
439 let candy_map = finalize_term.node_list.finalize_elect();
440 self.candy_rewards = candy_map;
441 self.electing_index += 1;
442 assert_eq!(self.electing_index, 7);
443 }
444
445 fn serving_votes(
446 &self, target_term_offset: usize, author: &AccountAddress,
447 ) -> u64 {
448 assert!(
449 target_term_offset >= TERM_LIST_LEN - 1
450 && target_term_offset < TERM_LIST_LEN + 2
451 );
452 let start_term_offset = target_term_offset - (TERM_LIST_LEN - 1);
455
456 let mut serving_votes = Vec::with_capacity(TERM_LIST_LEN);
462 for i in start_term_offset..target_term_offset {
463 let term = &self.term_list[i];
464 serving_votes.push(term.node_list.serving_votes(author));
465 }
466 return serving_votes.iter().sum();
467 }
468}
469
470#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
471#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
472pub struct DisputeRecord {
473 last_offense_epoch: u64,
476 lock_until: View,
478}
479
480#[derive(Clone, Serialize, Eq, PartialEq, Deserialize)]
481#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
482pub struct PosState {
483 node_map: HashMap<AccountAddress, NodeData>,
486 current_view: Round,
491 epoch_state: EpochState,
493 term_list: TermList,
494
495 retiring_nodes: VecDeque<AccountAddress>,
498 pivot_decision: PivotBlockDecision,
500
501 node_map_hint: HashMap<View, HashSet<AccountAddress>>,
502 unlock_event_hint: HashSet<AccountAddress>,
503
504 skipped: bool,
509
510 dispute_records: BTreeMap<AccountAddress, DisputeRecord>,
515}
516
517#[derive(Deserialize)]
521struct PosStateV1 {
522 node_map: HashMap<AccountAddress, NodeData>,
523 current_view: Round,
524 epoch_state: EpochState,
525 term_list: TermList,
526 retiring_nodes: VecDeque<AccountAddress>,
527 pivot_decision: PivotBlockDecision,
528 node_map_hint: HashMap<View, HashSet<AccountAddress>>,
529 unlock_event_hint: HashSet<AccountAddress>,
530 skipped: bool,
531}
532
533#[derive(Serialize)]
535struct PosStateV1Ref<'a> {
536 node_map: &'a HashMap<AccountAddress, NodeData>,
537 current_view: &'a Round,
538 epoch_state: &'a EpochState,
539 term_list: &'a TermList,
540 retiring_nodes: &'a VecDeque<AccountAddress>,
541 pivot_decision: &'a PivotBlockDecision,
542 node_map_hint: &'a HashMap<View, HashSet<AccountAddress>>,
543 unlock_event_hint: &'a HashSet<AccountAddress>,
544 skipped: &'a bool,
545}
546
547impl From<PosStateV1> for PosState {
548 fn from(v1: PosStateV1) -> Self {
549 Self {
550 node_map: v1.node_map,
551 current_view: v1.current_view,
552 epoch_state: v1.epoch_state,
553 term_list: v1.term_list,
554 retiring_nodes: v1.retiring_nodes,
555 pivot_decision: v1.pivot_decision,
556 node_map_hint: v1.node_map_hint,
557 unlock_event_hint: v1.unlock_event_hint,
558 skipped: v1.skipped,
559 dispute_records: BTreeMap::new(),
563 }
564 }
565}
566
567impl PosState {
570 pub fn encode_persisted(&self) -> Result<Vec<u8>> {
571 if self.dispute_records.is_empty()
575 && !POS_STATE_CONFIG.cip173_active(self.current_view)
576 {
577 bcs::to_bytes(&PosStateV1Ref {
578 node_map: &self.node_map,
579 current_view: &self.current_view,
580 epoch_state: &self.epoch_state,
581 term_list: &self.term_list,
582 retiring_nodes: &self.retiring_nodes,
583 pivot_decision: &self.pivot_decision,
584 node_map_hint: &self.node_map_hint,
585 unlock_event_hint: &self.unlock_event_hint,
586 skipped: &self.skipped,
587 })
588 .map_err(Into::into)
589 } else {
590 bcs::to_bytes(self).map_err(Into::into)
591 }
592 }
593
594 pub fn decode_persisted(data: &[u8]) -> Result<Self> {
595 let current_err = match bcs::from_bytes::<PosState>(data) {
599 Ok(state) => return Ok(state),
600 Err(e) => e,
601 };
602 let legacy: PosStateV1 =
603 bcs::from_bytes(data).map_err(|legacy_err| {
604 anyhow!(
605 "PoS state decodes in neither layout: current={}, legacy={}",
606 current_err,
607 legacy_err
608 )
609 })?;
610 ensure!(
614 legacy.current_view <= POS_STATE_CONFIG.cip173_transition_view(),
615 "PoS state at view {} is stored in the pre-CIP-173 layout, so it \
616 was written past the transition view by a binary without \
617 CIP-173; its dispute state is missing and cannot be recovered",
618 legacy.current_view
619 );
620 Ok(legacy.into())
621 }
622}
623
624impl Debug for PosState {
625 fn fmt(
626 &self, f: &mut Formatter<'_>,
627 ) -> std::result::Result<(), std::fmt::Error> {
628 f.debug_struct("PosState")
629 .field("view", &self.current_view)
630 .field("node_map_size", &self.node_map.len())
631 .field("term_list", &self.term_list)
632 .field("epoch_state", &self.epoch_state)
633 .finish()
634 }
635}
636
637impl PosState {
638 pub fn new(
639 initial_seed: Vec<u8>, initial_nodes: Vec<(NodeID, u64)>,
640 initial_committee: Vec<(AccountAddress, u64)>,
641 genesis_pivot_decision: PivotBlockDecision,
642 ) -> Self {
643 let mut node_map = HashMap::new();
644 let mut node_list = BTreeMap::default();
645 for (node_id, total_voting_power) in initial_nodes {
646 let mut lock_status = NodeLockStatus::default();
647 lock_status.new_lock(
649 0,
650 total_voting_power,
651 true,
652 None,
653 &mut Vec::new(),
654 );
655 node_map.insert(
656 node_id.addr.clone(),
657 NodeData {
658 public_key: node_id.public_key.clone(),
659 vrf_public_key: Some(node_id.vrf_public_key.clone()),
660 lock_status,
661 },
662 );
663 }
664 for (addr, voting_power) in initial_committee {
665 node_list.insert(addr, voting_power);
668 }
669 let mut term_list = Vec::new();
670 let initial_term = TermData {
671 start_view: 0,
672 seed: initial_seed.clone(),
673 node_list: NodeList::Elected(ElectedMap(node_list.clone())),
674 };
675 term_list.push(initial_term);
676 for i in 0..(TERM_LIST_LEN + 1) {
679 let last_term = term_list.last().unwrap();
680 let mut next_term =
681 last_term.next_term(Default::default(), initial_seed.clone());
682 if i < TERM_LIST_LEN - 1 {
683 let _ = next_term.node_list.finalize_elect();
684 }
685 term_list.push(next_term);
686 }
687 let mut pos_state = PosState {
688 node_map,
689 current_view: 0,
690 epoch_state: EpochState::empty(),
691 term_list: TermList {
692 current_term: 0,
693 term_list,
694 electing_index: TERM_LIST_LEN,
695 candy_rewards: ElectedMap(node_list),
696 },
697 retiring_nodes: Default::default(),
698 pivot_decision: genesis_pivot_decision,
699 node_map_hint: Default::default(),
700 unlock_event_hint: Default::default(),
701 skipped: false,
702 dispute_records: Default::default(),
703 };
704 let (verifier, vrf_seed) = pos_state.get_committee_at(0).unwrap();
705 pos_state.epoch_state = EpochState::new(0, verifier, vrf_seed);
706 pos_state
707 }
708
709 pub fn new_empty() -> Self {
710 Self {
711 node_map: Default::default(),
712 current_view: 0,
713 epoch_state: EpochState::empty(),
714 term_list: TermList {
715 current_term: 0,
716 term_list: Default::default(),
717 electing_index: 0,
718 candy_rewards: Default::default(),
719 },
720 retiring_nodes: Default::default(),
721 node_map_hint: Default::default(),
722 unlock_event_hint: Default::default(),
723 pivot_decision: PivotBlockDecision {
724 block_hash: Default::default(),
725 height: 0,
726 },
727 skipped: false,
728 dispute_records: Default::default(),
729 }
730 }
731
732 pub fn set_skipped(&mut self, skipped: bool) { self.skipped = skipped; }
733
734 pub fn set_pivot_decision(&mut self, pivot_decision: PivotBlockDecision) {
735 self.pivot_decision = pivot_decision;
736 }
737
738 pub fn pivot_decision(&self) -> &PivotBlockDecision { &self.pivot_decision }
739
740 pub fn target_term_seed(&self, target_term: u64) -> &Vec<u8> {
745 &self
746 .term_list
747 .get_term_by_number(target_term)
748 .expect("term not in term list")
749 .seed
750 }
751
752 pub fn epoch_state(&self) -> &EpochState { &self.epoch_state }
753
754 pub fn term_list(&self) -> &TermList { &self.term_list }
755
756 pub fn account_node_data(
757 &self, account_address: AccountAddress,
758 ) -> Option<&NodeData> {
759 self.node_map.get(&account_address)
760 }
761}
762
763impl PosState {
765 fn check_sender_owns_auth_key(
769 &self, sender: &AccountAddress, auth_pk: &ConsensusPublicKey,
770 not_registered: DiscardedVMStatus,
771 ) -> Result<&NodeData, DiscardedVMStatus> {
772 let node = self.account_node_data(*sender).ok_or(not_registered)?;
773 if &node.public_key != auth_pk {
774 return Err(DiscardedVMStatus::AUTHENTICATOR_KEY_MISMATCH);
775 }
776 Ok(node)
777 }
778
779 pub fn validate_election_simple(
780 &self, sender: &AccountAddress, auth_pk: &ConsensusPublicKey,
781 election_tx: &ElectionPayload,
782 ) -> Option<DiscardedVMStatus> {
783 let node_id = NodeID::new(
784 election_tx.public_key.clone(),
785 election_tx.vrf_public_key.clone(),
786 );
787 diem_trace!(
788 "validate_election_simple: {:?} {}",
789 node_id.addr,
790 election_tx.target_term
791 );
792 if *sender != node_id.addr {
796 return Some(DiscardedVMStatus::ELECTION_SIGNER_MISMATCH);
797 }
798 let node = match self.check_sender_owns_auth_key(
799 sender,
800 auth_pk,
801 DiscardedVMStatus::ELECTION_NON_EXISTENT_NODE,
802 ) {
803 Ok(node) => node,
804 Err(err) => return Some(err),
805 };
806
807 let target_view = match POS_STATE_CONFIG
808 .get_starting_view_for_term(election_tx.target_term)
809 {
810 None => {
811 return Some(DiscardedVMStatus::ELECTION_TARGET_TERM_NOT_OPEN)
812 }
813 Some(v) => v,
814 };
815
816 if node.lock_status.available_votes() == 0 {
817 return Some(DiscardedVMStatus::ELECTION_WITHOUT_VOTES);
818 }
819 if target_view
822 <= self.current_view
823 + POS_STATE_CONFIG.election_term_end_round(self.current_view)
824 {
825 return Some(DiscardedVMStatus::ELECTION_TARGET_TERM_NOT_OPEN);
826 }
827 None
828 }
829
830 pub fn validate_pivot_decision_simple(
831 &self, sender: &AccountAddress, auth_pk: &ConsensusPublicKey,
832 pivot_decision_tx: &PivotBlockDecision,
833 ) -> Option<DiscardedVMStatus> {
834 if let Err(err) = self.check_sender_owns_auth_key(
838 sender,
839 auth_pk,
840 DiscardedVMStatus::PIVOT_DECISION_SENDER_NOT_REGISTERED,
841 ) {
842 return Some(err);
843 }
844 if pivot_decision_tx.height <= self.pivot_decision.height {
845 return Some(DiscardedVMStatus::PIVOT_DECISION_HEIGHT_TOO_OLD);
846 }
847 None
848 }
849
850 pub fn validate_dispute_simple(
851 &self, sender: &AccountAddress, auth_pk: &ConsensusPublicKey,
852 ) -> Option<DiscardedVMStatus> {
853 self.check_sender_owns_auth_key(
855 sender,
856 auth_pk,
857 DiscardedVMStatus::DISPUTE_SENDER_NOT_REGISTERED,
858 )
859 .err()
860 }
861}
862
863impl PosState {
865 pub fn validate_election(
866 &self, election_tx: &ElectionPayload,
867 ) -> Result<()> {
868 let node_id = NodeID::new(
869 election_tx.public_key.clone(),
870 election_tx.vrf_public_key.clone(),
871 );
872 diem_trace!(
873 "validate_election: {:?} {}",
874 node_id.addr,
875 election_tx.target_term
876 );
877 let node = match self.node_map.get(&node_id.addr) {
878 Some(node) => node,
879 None => return Err(anyhow!("Election for non-existent node.")),
880 };
881
882 if node.lock_status.available_votes() == 0 {
883 bail!("Election without any votes");
884 }
885 let target_view = match POS_STATE_CONFIG
886 .get_starting_view_for_term(election_tx.target_term)
887 {
888 None => {
889 bail!("target view overflows, election_tx={:?}", election_tx)
890 }
891 Some(v) => v,
892 };
893 if target_view
894 > self.current_view
895 + POS_STATE_CONFIG.election_term_start_round(self.current_view)
896 || target_view
897 <= self.current_view
898 + POS_STATE_CONFIG
899 .election_term_end_round(self.current_view)
900 {
901 bail!(
902 "Target term is not open for election: target={} current={}",
903 target_view,
904 self.current_view
905 );
906 }
907
908 let target_term_offset =
909 (election_tx.target_term - self.term_list.start_term()) as usize;
910 assert_eq!(target_term_offset, self.term_list.electing_index);
911
912 let target_term = &self.term_list.electing_term();
913 if election_tx
914 .vrf_proof
915 .verify(&target_term.seed, node.vrf_public_key.as_ref().unwrap())
916 .is_err()
917 {
918 bail!("Invalid VRF proof for election")
919 }
920
921 if target_term.node_list.has_elected(&node_id.addr) {
922 bail!("The sender has elected for this term")
923 }
924
925 if node.lock_status.available_votes()
926 <= self
927 .term_list
928 .serving_votes(target_term_offset, &node_id.addr)
929 {
930 bail!("Election without enough votes");
931 }
932
933 Ok(())
934 }
935
936 pub fn validate_pivot_decision(
937 &self, pivot_decision_tx: &PivotBlockDecision,
938 signature: MultiConsensusSignature,
939 ) -> Result<()> {
940 if pivot_decision_tx.height <= self.pivot_decision.height {
941 return Err(anyhow!(format!(
942 "Pivot Decision height too small, found[{}], expect[{}]",
943 pivot_decision_tx.height, self.pivot_decision.height
944 )));
945 }
946 let senders: Vec<_> = self
947 .epoch_state
948 .verifier()
949 .address_to_validator_info()
950 .keys()
951 .cloned()
952 .collect();
953 let public_keys: Vec<ConsensusPublicKey> = senders
954 .iter()
955 .map(|sender| {
956 self.epoch_state.verifier().get_public_key(sender).unwrap()
957 })
958 .collect();
959 let public_key = MultiConsensusPublicKey::new(public_keys);
960 if let Err(e) = signature.verify(pivot_decision_tx, &public_key) {
961 return Err(anyhow!(format!(
962 "Pivot Decision verification failed [{:?}]",
963 e
964 )));
965 }
966 let signers = signature.get_signers(&senders)?;
967 if let Err(e) = self
968 .epoch_state()
969 .verifier()
970 .check_voting_power(signers.iter())
971 {
972 return Err(anyhow!(format!(
973 "Pivot Decision voting power check failed [{:?}]",
974 e
975 )));
976 }
977 Ok(())
978 }
979
980 pub fn validate_dispute(
981 &self, dispute_payload: &DisputePayload, offense_epoch: u64,
982 ) -> Result<()> {
983 let node =
984 self.node_map.get(&dispute_payload.address).ok_or_else(|| {
985 anyhow!("Unknown dispute node: {:?}", dispute_payload.address)
986 })?;
987 ensure!(
988 node.lock_status.exempt_from_forfeit().is_none(),
989 "Dispute a forfeited node: {:?}",
990 dispute_payload.address
991 );
992 if POS_STATE_CONFIG.cip173_active(self.current_view) {
993 self.check_dispute_admissible(
994 &dispute_payload.address,
995 offense_epoch,
996 )?;
997 }
998 Ok(())
999 }
1000
1001 fn check_dispute_admissible(
1002 &self, address: &AccountAddress, offense_epoch: u64,
1003 ) -> Result<()> {
1004 let first_admissible = POS_STATE_CONFIG
1005 .dispute_first_admissible_epoch()
1006 .ok_or_else(|| {
1007 anyhow!("CIP-173 active with no scheduled transition view")
1008 })?;
1009 ensure!(
1010 offense_epoch >= first_admissible,
1011 "Dispute evidence predates CIP-173: offence epoch {}, first \
1012 admissible {}",
1013 offense_epoch,
1014 first_admissible
1015 );
1016 ensure!(
1017 offense_epoch <= self.epoch_state.epoch,
1018 "Dispute evidence claims an unreached epoch: offence epoch {}, \
1019 current epoch {}",
1020 offense_epoch,
1021 self.epoch_state.epoch
1022 );
1023 if let Some(record) = self.dispute_records.get(address) {
1024 ensure!(
1025 offense_epoch > record.last_offense_epoch,
1026 "Dispute evidence already punished: offence epoch {}, \
1027 watermark {}",
1028 offense_epoch,
1029 record.last_offense_epoch
1030 );
1031 }
1032 Ok(())
1033 }
1034
1035 pub fn get_committee_at(
1037 &self, term: u64,
1038 ) -> Result<(ValidatorVerifier, Vec<u8>)> {
1039 diem_debug!(
1040 "Get committee at term {} in view {}, term list start at {}",
1041 term,
1042 self.current_view,
1043 self.term_list.start_term()
1044 );
1045 let mut voting_power_map = BTreeMap::new();
1046 for term_data in self.term_list.committee_for_term(term) {
1047 for (addr, votes) in term_data.node_list.committee().0.iter() {
1048 *voting_power_map.entry(addr.clone()).or_insert(0 as u64) +=
1049 votes;
1050 }
1051 }
1052 let mut address_to_validator_info = BTreeMap::new();
1053 for (addr, voting_power) in voting_power_map {
1054 let node_data = self.node_map.get(&addr).expect("node in node_map");
1055 let voting_power = std::cmp::min(
1058 voting_power,
1059 node_data.lock_status.available_votes(),
1060 );
1061 if voting_power > 0 {
1062 address_to_validator_info.insert(
1063 addr,
1064 ValidatorConsensusInfo::new(
1065 node_data.public_key.clone(),
1066 node_data.vrf_public_key.clone(),
1067 voting_power,
1068 ),
1069 );
1070 }
1071 }
1072
1073 Ok((
1074 ValidatorVerifier::new(address_to_validator_info),
1075 self.term_list.term_list[0].seed.clone(),
1076 ))
1077 }
1078
1079 pub fn next_elect_term(&self, author: &AccountAddress) -> Option<u64> {
1083 if self.current_view
1084 < POS_STATE_CONFIG.first_start_election_view() as u64
1085 {
1086 return None;
1087 }
1088
1089 if self.term_list.electing_term().node_list.has_elected(author) {
1090 return None;
1091 }
1092
1093 if let Some(node) = self.node_map.get(author) {
1094 let available_votes = node.lock_status.available_votes();
1095 let serving_votes = self
1096 .term_list
1097 .serving_votes(self.term_list.electing_index, author);
1098
1099 return if available_votes > serving_votes {
1100 Some(self.term_list.electing_term_number())
1101 } else {
1102 None
1103 };
1104 }
1105
1106 None
1107 }
1108
1109 pub fn final_serving_view(&self, author: &AccountAddress) -> Option<Round> {
1110 let mut final_elected_term = None;
1111 for term in self.term_list.term_list.iter().rev() {
1112 match &term.node_list {
1113 NodeList::Electing(heap) => {
1114 if heap.1.contains(author) {
1115 final_elected_term = Some(term.get_term());
1116 break;
1117 }
1118 }
1119 NodeList::Elected(map) => {
1120 if map.0.contains_key(author) {
1121 final_elected_term = Some(term.get_term());
1122 break;
1123 }
1124 }
1125 }
1126 }
1127 final_elected_term.map(|t| {
1128 POS_STATE_CONFIG
1129 .get_starting_view_for_term(t + TERM_LIST_LEN as u64)
1130 .expect("checked term")
1131 + 1
1132 })
1133 }
1134
1135 pub fn get_unlock_events(&self) -> Vec<ContractEvent> {
1136 let mut unlocked_nodes = Vec::new();
1137 for addr in &self.unlock_event_hint {
1138 let node = self.node_map.get(&addr).expect("exists");
1139 let unlock_event = ContractEvent::new(
1140 UnlockEvent::event_key(),
1141 bcs::to_bytes(&UnlockEvent {
1142 node_id: *addr,
1143 unlocked: node.lock_status.unlocked_votes(),
1144 })
1145 .unwrap(),
1146 );
1147 unlocked_nodes.push(unlock_event);
1148 }
1149
1150 return unlocked_nodes;
1151 }
1152
1153 pub fn current_view(&self) -> u64 { self.current_view }
1154
1155 pub fn skipped(&self) -> bool { self.skipped }
1156
1157 pub fn next_evicted_term(&mut self) -> BTreeMap<H256, u64> {
1158 let candy_rewards = std::mem::take(&mut self.term_list.candy_rewards);
1159 candy_rewards
1160 .0
1161 .iter()
1162 .map(|(id, cnt)| (H256::from(id.to_u8()), *cnt))
1163 .collect()
1164 }
1165}
1166
1167impl PosState {
1169 pub fn register_node(&mut self, node_id: NodeID) -> Result<()> {
1170 diem_trace!("register_node: {:?}", node_id);
1171 ensure!(
1172 !self.node_map.contains_key(&node_id.addr),
1173 "register an already registered address"
1174 );
1175 self.node_map.insert(
1176 node_id.addr,
1177 NodeData {
1178 public_key: node_id.public_key,
1179 vrf_public_key: Some(node_id.vrf_public_key),
1180 lock_status: NodeLockStatus::default(),
1181 },
1182 );
1183 Ok(())
1184 }
1185
1186 pub fn update_voting_power(
1187 &mut self, addr: &AccountAddress, increased_voting_power: u64,
1188 ) -> Result<()> {
1189 diem_trace!(
1190 "update_voting_power: {:?} {}",
1191 addr,
1192 increased_voting_power
1193 );
1194 let view = self.current_view;
1195 let dispute_lock_until = self
1196 .dispute_records
1197 .get(addr)
1198 .map(|record| record.lock_until)
1199 .filter(|lock_until| view < *lock_until);
1200 let mut update_views = Vec::new();
1201 match self.node_map.get_mut(addr) {
1202 Some(node_status) => node_status.lock_status.new_lock(
1203 view,
1204 increased_voting_power,
1205 false,
1206 dispute_lock_until,
1207 &mut update_views,
1208 ),
1209 None => bail!("increase voting power of a non-existent node!"),
1210 };
1211 self.record_update_views(addr, update_views);
1212 Ok(())
1213 }
1214
1215 pub fn new_node_elected(&mut self, event: &ElectionEvent) -> Result<()> {
1216 diem_debug!(
1217 "new_node_elected: {:?} {:?}",
1218 event.node_id,
1219 event.start_term
1220 );
1221 let author = &event.node_id.addr;
1222 let available_votes = self
1223 .node_map
1224 .get(author)
1225 .expect("checked in execution")
1226 .lock_status
1227 .available_votes();
1228 let target_term_offset =
1229 (event.start_term - self.term_list.start_term()) as usize;
1230 let serving_votes =
1231 self.term_list.serving_votes(target_term_offset, author);
1232 let voting_power = available_votes.saturating_sub(serving_votes);
1233 if voting_power > 0 {
1234 let bounded_power = std::cmp::min(
1236 voting_power,
1237 POS_STATE_CONFIG.max_nonce_per_account(self.current_view()),
1238 );
1239 self.term_list.new_node_elected(event, bounded_power)?;
1240 } else {
1241 diem_warn!("No votes can be elected: {:?} {:?}. available: {}, serving: {}.", event.node_id,
1242 event.start_term,available_votes,serving_votes);
1243 }
1244 Ok(())
1245 }
1246
1247 pub fn next_view(&mut self) -> Result<Option<EpochState>> {
1251 self.current_view += 1;
1254
1255 diem_debug!("current view {}", self.current_view);
1256
1257 self.unlock_event_hint.clear();
1259
1260 if let Some(addresses) = self.node_map_hint.remove(&self.current_view) {
1261 for address in addresses {
1262 let node = self.node_map.get_mut(&address).expect("exists");
1263 let new_votes_unlocked =
1264 node.lock_status.update(self.current_view);
1265 if new_votes_unlocked {
1266 self.unlock_event_hint.insert(address);
1267 }
1268 }
1269 }
1270
1271 let epoch_state = if self.current_view == 1 {
1272 let (verifier, term_seed) = self.get_committee_at(0)?;
1273 Some(EpochState::new(1, verifier, term_seed.clone()))
1275 } else {
1276 let (term, view_in_term) =
1277 POS_STATE_CONFIG.get_term_view(self.current_view);
1278 if view_in_term == 0 {
1279 let new_term = term;
1280 let (verifier, term_seed) = self.get_committee_at(new_term)?;
1281 self.term_list.new_term(
1283 new_term,
1284 self.pivot_decision.block_hash.as_bytes().to_vec(),
1285 );
1286 Some(EpochState::new(new_term + 1, verifier, term_seed.clone()))
1289 } else if self.current_view
1290 >= POS_STATE_CONFIG.first_end_election_view()
1291 && view_in_term
1292 == POS_STATE_CONFIG.round_per_term(self.current_view) / 2
1293 {
1294 self.term_list.finalize_election();
1295 None
1296 } else {
1297 None
1298 }
1299 };
1300 if let Some(epoch_state) = &epoch_state {
1301 self.epoch_state = epoch_state.clone();
1302 }
1303 Ok(epoch_state)
1304 }
1305
1306 pub fn retire_node(
1307 &mut self, addr: &AccountAddress, votes: u64,
1308 ) -> Result<()> {
1309 diem_trace!("retire_node: {:?} {}", addr, votes);
1310 let mut update_views = Vec::new();
1311 match self.node_map.get_mut(&addr) {
1312 Some(node) => {
1313 node.lock_status.new_unlock(
1314 self.current_view,
1315 votes,
1316 &mut update_views,
1317 );
1318 }
1319 None => bail!("Retiring node does not exist"),
1320 };
1321 self.record_update_views(addr, update_views);
1322 Ok(())
1323 }
1324
1325 pub fn force_retire_node(&mut self, addr: &AccountAddress) -> Result<()> {
1326 diem_trace!("force_retire_node: {:?}", addr);
1327 let mut update_views = Vec::new();
1328 match self.node_map.get_mut(&addr) {
1329 Some(node) => node
1330 .lock_status
1331 .force_retire(self.current_view, &mut update_views),
1332 None => bail!("Force retiring node does not exist"),
1333 };
1334 self.record_update_views(addr, update_views);
1335 Ok(())
1336 }
1337
1338 pub fn forfeit_node(
1340 &mut self, addr: &AccountAddress, offense_epoch: Option<u64>,
1341 ) -> Result<()> {
1342 diem_trace!("forfeit_node: {:?} {:?}", addr, offense_epoch);
1343 let view = self.current_view;
1344 let previous = self.dispute_records.get(addr).copied();
1345
1346 let (rule, record) = match POS_STATE_CONFIG.dispute_locked_views(view) {
1347 None => (ForfeitRule::FreezeWithdrawable, None),
1348 Some(locked_views) if !POS_STATE_CONFIG.cip173_active(view) => (
1349 ForfeitRule::RelockOnActive {
1350 deadline: view.saturating_add(locked_views),
1351 },
1352 None,
1353 ),
1354 Some(locked_views) => {
1355 let offense_epoch = offense_epoch.ok_or_else(|| {
1356 anyhow!("CIP-173 dispute event without an offence epoch")
1357 })?;
1358 if previous
1359 .map_or(false, |r| offense_epoch <= r.last_offense_epoch)
1360 {
1361 return Ok(());
1366 }
1367 let deadline = view
1368 .saturating_add(locked_views)
1369 .max(previous.map_or(0, |r| r.lock_until));
1370 (
1371 ForfeitRule::RelockAll { deadline },
1372 Some(DisputeRecord {
1373 last_offense_epoch: offense_epoch,
1374 lock_until: deadline,
1375 }),
1376 )
1377 }
1378 };
1379
1380 let mut update_views = Vec::new();
1381 match self.node_map.get_mut(&addr) {
1382 Some(node) => node.lock_status.forfeit(rule, &mut update_views),
1383 None => bail!("Forfeiting node does not exist"),
1384 }
1385 if let Some(record) = record {
1386 self.dispute_records.insert(*addr, record);
1387 }
1388 self.record_update_views(addr, update_views);
1389 Ok(())
1390 }
1391}
1392
1393impl PosState {
1394 pub fn record_update_views(
1395 &mut self, address: &AccountAddress, views: Vec<View>,
1396 ) {
1397 for view in views {
1398 diem_trace!(
1399 "{:?} will update lock status at view {}",
1400 address,
1401 view
1402 );
1403 self.node_map_hint.entry(view).or_default().insert(*address);
1404 }
1405 }
1406}
1407
1408#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
1422pub struct ElectionEvent {
1423 node_id: NodeID,
1424 vrf_output: HashValue,
1425 start_term: u64,
1426}
1427
1428impl ElectionEvent {
1429 pub fn new(
1430 public_key: ConsensusPublicKey, vrf_public_key: ConsensusVRFPublicKey,
1431 vrf_output: HashValue, start_term: u64,
1432 ) -> Self {
1433 Self {
1434 node_id: NodeID::new(public_key, vrf_public_key),
1435 vrf_output,
1436 start_term,
1437 }
1438 }
1439}
1440
1441impl ElectionEvent {
1442 pub fn event_key() -> EventKey {
1443 EventKey::new_from_address(
1444 &account_config::election_select_address(),
1445 3,
1446 )
1447 }
1448
1449 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
1450 bcs::from_bytes(bytes).map_err(Into::into)
1451 }
1452}
1453
1454#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
1455pub struct RetireEvent {
1456 pub node_id: AccountAddress,
1457 pub votes: u64,
1458}
1459
1460impl RetireEvent {
1461 pub fn new(node_id: AccountAddress, votes: u64) -> Self {
1462 RetireEvent { node_id, votes }
1463 }
1464
1465 pub fn event_key() -> EventKey {
1466 EventKey::new_from_address(&account_config::retire_address(), 4)
1467 }
1468
1469 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
1470 bcs::from_bytes(bytes).map_err(Into::into)
1471 }
1472
1473 pub fn matches_staking_event(
1474 &self, staking_event: &StakingEvent,
1475 ) -> Result<bool> {
1476 match staking_event {
1477 StakingEvent::Retire(addr_h256, votes) => {
1478 let addr = AccountAddress::from_bytes(addr_h256)?;
1479 Ok(self.node_id == addr && self.votes == *votes)
1480 }
1481 _ => Ok(false),
1482 }
1483 }
1484}
1485
1486#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
1487pub struct RegisterEvent {
1488 pub node_id: NodeID,
1489}
1490
1491impl RegisterEvent {
1492 pub fn new(
1493 public_key: ConsensusPublicKey, vrf_public_key: ConsensusVRFPublicKey,
1494 ) -> Self {
1495 Self {
1496 node_id: NodeID::new(public_key, vrf_public_key),
1497 }
1498 }
1499
1500 pub fn event_key() -> EventKey {
1501 EventKey::new_from_address(&account_config::register_address(), 5)
1502 }
1503
1504 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
1505 bcs::from_bytes(bytes).map_err(Into::into)
1506 }
1507
1508 pub fn matches_staking_event(
1509 &self, staking_event: &StakingEvent,
1510 ) -> Result<bool> {
1511 match staking_event {
1512 StakingEvent::Register(
1513 addr_h256,
1514 bls_pub_key_bytes,
1515 vrf_pub_key_bytes,
1516 ) => {
1517 let addr = AccountAddress::from_bytes(addr_h256)?;
1518 let public_key =
1519 ConsensusPublicKey::try_from(bls_pub_key_bytes.as_slice())?;
1520 let vrf_public_key = ConsensusVRFPublicKey::try_from(
1521 vrf_pub_key_bytes.as_slice(),
1522 )?;
1523 let node_id =
1524 NodeID::new(public_key.clone(), vrf_public_key.clone());
1525 ensure!(
1526 node_id.addr == addr,
1527 "register event has unmatching address and keys"
1528 );
1529 Ok(self.node_id == node_id)
1530 }
1531 _ => Ok(false),
1532 }
1533 }
1534}
1535
1536#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
1537pub struct UpdateVotingPowerEvent {
1538 pub node_address: AccountAddress,
1539 pub voting_power: u64,
1540}
1541
1542impl UpdateVotingPowerEvent {
1543 pub fn new(node_address: AccountAddress, voting_power: u64) -> Self {
1544 Self {
1545 node_address,
1546 voting_power,
1547 }
1548 }
1549
1550 pub fn event_key() -> EventKey {
1551 EventKey::new_from_address(
1552 &account_config::update_voting_power_address(),
1553 6,
1554 )
1555 }
1556
1557 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
1558 bcs::from_bytes(bytes).map_err(Into::into)
1559 }
1560
1561 pub fn matches_staking_event(
1562 &self, staking_event: &StakingEvent,
1563 ) -> Result<bool> {
1564 match staking_event {
1565 StakingEvent::IncreaseStake(addr_h256, updated_voting_power) => {
1566 let addr = AccountAddress::from_bytes(addr_h256)?;
1567 Ok(self.node_address == addr
1568 && self.voting_power == *updated_voting_power)
1569 }
1570 _ => Ok(false),
1571 }
1572 }
1573}
1574
1575#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
1576#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
1577pub struct NodeID {
1578 pub public_key: ConsensusPublicKey,
1579 pub vrf_public_key: ConsensusVRFPublicKey,
1580
1581 pub addr: AccountAddress,
1583}
1584
1585impl NodeID {
1586 pub fn new(
1587 public_key: ConsensusPublicKey, vrf_public_key: ConsensusVRFPublicKey,
1588 ) -> Self {
1589 let addr = from_consensus_public_key(&public_key, &vrf_public_key);
1590 Self {
1591 public_key,
1592 vrf_public_key,
1593 addr,
1594 }
1595 }
1596}
1597
1598impl Ord for NodeID {
1599 fn cmp(&self, other: &Self) -> Ordering { self.addr.cmp(&other.addr) }
1600}
1601
1602impl PartialOrd for NodeID {
1603 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1604 Some(self.cmp(other))
1605 }
1606}
1607
1608#[derive(Clone, Serialize, Deserialize)]
1609pub struct UnlockEvent {
1610 pub node_id: AccountAddress,
1614 pub unlocked: u64,
1615}
1616
1617impl UnlockEvent {
1618 pub fn event_key() -> EventKey {
1619 EventKey::new_from_address(&account_config::unlock_address(), 5)
1620 }
1621
1622 pub fn from_bytes(bytes: &[u8]) -> anyhow::Result<Self> {
1623 bcs::from_bytes(bytes).map_err(Into::into)
1624 }
1625}
1626
1627#[derive(Clone, Serialize, Deserialize)]
1628pub struct DisputeEvent {
1629 pub node_id: AccountAddress,
1631}
1632
1633impl DisputeEvent {
1634 pub fn event_key() -> EventKey {
1635 EventKey::new_from_address(&account_config::dispute_address(), 6)
1636 }
1637
1638 pub fn from_bytes(bytes: &[u8]) -> anyhow::Result<Self> {
1639 bcs::from_bytes(bytes).map_err(Into::into)
1640 }
1641}
1642
1643#[derive(Clone, Serialize, Deserialize)]
1646pub struct DisputeEventV2 {
1647 pub node_id: AccountAddress,
1648 pub offense_epoch: u64,
1649}
1650
1651impl DisputeEventV2 {
1652 pub fn event_key() -> EventKey {
1653 EventKey::new_from_address(&account_config::dispute_address(), 7)
1654 }
1655
1656 pub fn from_bytes(bytes: &[u8]) -> anyhow::Result<Self> {
1657 bcs::from_bytes(bytes).map_err(Into::into)
1658 }
1659}
1660
1661pub fn decode_dispute_event(
1665 event: &ContractEvent,
1666) -> Option<anyhow::Result<(AccountAddress, Option<u64>)>> {
1667 if *event.key() == DisputeEvent::event_key() {
1668 Some(
1669 DisputeEvent::from_bytes(event.event_data())
1670 .map(|e| (e.node_id, None)),
1671 )
1672 } else if *event.key() == DisputeEventV2::event_key() {
1673 Some(
1674 DisputeEventV2::from_bytes(event.event_data())
1675 .map(|e| (e.node_id, Some(e.offense_epoch))),
1676 )
1677 } else {
1678 None
1679 }
1680}
1681
1682#[cfg(test)]
1683mod tests {
1684 use super::*;
1685 use crate::{
1686 block_info::PivotBlockDecision,
1687 term_state::pos_state_config::PosStateConfig,
1688 transaction::ElectionPayload, validator_config::ConsensusVRFProof,
1689 };
1690 use diem_crypto::{
1691 bls::BLSPrivateKey,
1692 ec_vrf::{EcVrfPrivateKey, EcVrfProof},
1693 PrivateKey, Uniform,
1694 };
1695 use rand::{rngs::StdRng, SeedableRng};
1696
1697 struct Keys {
1698 bls_pk: ConsensusPublicKey,
1699 vrf_pk: ConsensusVRFPublicKey,
1700 addr: AccountAddress,
1701 }
1702
1703 fn keys_from_seed(seed: u64) -> Keys {
1704 let mut rng = StdRng::seed_from_u64(seed);
1705 let bls_sk = BLSPrivateKey::generate(&mut rng);
1706 let bls_pk = bls_sk.public_key();
1707 let vrf_sk = EcVrfPrivateKey::generate(&mut rng);
1708 let vrf_pk = vrf_sk.public_key();
1709 let node_id = NodeID::new(bls_pk.clone(), vrf_pk.clone());
1710 Keys {
1711 bls_pk,
1712 vrf_pk,
1713 addr: node_id.addr,
1714 }
1715 }
1716
1717 fn dummy_vrf_proof() -> ConsensusVRFProof {
1719 EcVrfProof::try_from(&[][..]).unwrap()
1720 }
1721
1722 fn state_with_node(k: &Keys) -> PosState {
1723 let mut state = PosState::new_empty();
1724 state
1725 .register_node(NodeID::new(k.bls_pk.clone(), k.vrf_pk.clone()))
1726 .expect("register_node");
1727 state
1728 }
1729
1730 #[test]
1731 fn pivot_decision_rejects_unregistered_sender() {
1732 let alice = keys_from_seed(1);
1733 let mallory = keys_from_seed(2);
1734 let state = state_with_node(&alice);
1735
1736 let tx = PivotBlockDecision {
1737 block_hash: Default::default(),
1738 height: 1,
1739 };
1740 assert_eq!(
1741 state.validate_pivot_decision_simple(
1742 &mallory.addr,
1743 &mallory.bls_pk,
1744 &tx
1745 ),
1746 Some(DiscardedVMStatus::PIVOT_DECISION_SENDER_NOT_REGISTERED),
1747 );
1748 }
1749
1750 #[test]
1751 fn pivot_decision_rejects_auth_key_mismatch() {
1752 let alice = keys_from_seed(1);
1753 let mallory = keys_from_seed(2);
1754 let state = state_with_node(&alice);
1755
1756 let tx = PivotBlockDecision {
1757 block_hash: Default::default(),
1758 height: 1,
1759 };
1760 assert_eq!(
1761 state.validate_pivot_decision_simple(
1762 &alice.addr,
1763 &mallory.bls_pk,
1764 &tx
1765 ),
1766 Some(DiscardedVMStatus::AUTHENTICATOR_KEY_MISMATCH),
1767 );
1768 }
1769
1770 #[test]
1771 fn pivot_decision_accepts_legitimate_self_signed() {
1772 let alice = keys_from_seed(1);
1773 let state = state_with_node(&alice);
1774
1775 let tx = PivotBlockDecision {
1776 block_hash: Default::default(),
1777 height: 1,
1778 };
1779 assert_eq!(
1780 state.validate_pivot_decision_simple(
1781 &alice.addr,
1782 &alice.bls_pk,
1783 &tx
1784 ),
1785 None,
1786 );
1787 }
1788
1789 #[test]
1790 fn dispute_rejects_unregistered_sender() {
1791 let alice = keys_from_seed(1);
1792 let mallory = keys_from_seed(2);
1793 let state = state_with_node(&alice);
1794
1795 assert_eq!(
1796 state.validate_dispute_simple(&mallory.addr, &mallory.bls_pk),
1797 Some(DiscardedVMStatus::DISPUTE_SENDER_NOT_REGISTERED),
1798 );
1799 }
1800
1801 #[test]
1802 fn dispute_rejects_auth_key_mismatch() {
1803 let alice = keys_from_seed(1);
1804 let mallory = keys_from_seed(2);
1805 let state = state_with_node(&alice);
1806
1807 assert_eq!(
1808 state.validate_dispute_simple(&alice.addr, &mallory.bls_pk),
1809 Some(DiscardedVMStatus::AUTHENTICATOR_KEY_MISMATCH),
1810 );
1811 }
1812
1813 #[test]
1814 fn dispute_accepts_legitimate_self_signed() {
1815 let alice = keys_from_seed(1);
1816 let state = state_with_node(&alice);
1817
1818 assert_eq!(
1819 state.validate_dispute_simple(&alice.addr, &alice.bls_pk),
1820 None,
1821 );
1822 }
1823
1824 const TRANSITION: View = 2 * ROUND_PER_TERM;
1826 const FIRST_EPOCH: u64 = 3;
1828 const DISPUTE_LOCK: u64 = 1000;
1829
1830 fn install_config() {
1831 const M: u64 = u64::MAX;
1832 POS_STATE_CONFIG.get_or_init(|| {
1833 PosStateConfig::new(
1834 ROUND_PER_TERM,
1835 TERM_MAX_SIZE,
1836 TERM_ELECTED_SIZE,
1837 IN_QUEUE_LOCKED_VIEWS,
1838 OUT_QUEUE_LOCKED_VIEWS,
1839 M,
1840 0,
1841 0,
1842 M,
1843 M,
1844 M,
1845 0,
1846 0,
1847 ROUND_PER_TERM,
1848 0,
1849 DISPUTE_LOCK,
1850 TRANSITION,
1851 )
1852 });
1853 }
1854
1855 fn staked_state(k: &Keys, view: View, epoch: u64, votes: u64) -> PosState {
1856 install_config();
1857 let mut state = state_with_node(k);
1858 state.current_view = view;
1859 state.epoch_state.epoch = epoch;
1860 state.update_voting_power(&k.addr, votes).expect("staked");
1861 state
1862 }
1863
1864 fn dispute_of(k: &Keys) -> DisputePayload {
1865 DisputePayload {
1866 address: k.addr,
1867 bls_pub_key: k.bls_pk.clone(),
1868 vrf_pub_key: k.vrf_pk.clone(),
1869 conflicting_votes: crate::transaction::ConflictSignature::Vote((
1870 vec![],
1871 vec![],
1872 )),
1873 }
1874 }
1875
1876 fn lock_status_of<'a>(
1877 state: &'a PosState, k: &Keys,
1878 ) -> &'a lock_status::NodeLockStatus {
1879 &state.node_map.get(&k.addr).expect("registered").lock_status
1880 }
1881
1882 fn exits_of(state: &PosState, k: &Keys) -> Vec<View> {
1883 lock_status_of(state, k)
1884 .out_queue
1885 .iter()
1886 .map(|item| item.view)
1887 .collect()
1888 }
1889
1890 #[test]
1891 fn dispute_locks_stake_and_records_the_offence() {
1892 let alice = keys_from_seed(1);
1893 let mut state = staked_state(&alice, TRANSITION, FIRST_EPOCH, 10);
1894 assert_eq!(lock_status_of(&state, &alice).available_votes(), 10);
1895
1896 state
1897 .forfeit_node(&alice.addr, Some(FIRST_EPOCH))
1898 .expect("forfeit");
1899
1900 assert_eq!(lock_status_of(&state, &alice).available_votes(), 0);
1901 assert_eq!(exits_of(&state, &alice), vec![TRANSITION + DISPUTE_LOCK]);
1902 let record = *state.dispute_records.get(&alice.addr).expect("recorded");
1903 assert_eq!(record.last_offense_epoch, FIRST_EPOCH);
1904 assert_eq!(record.lock_until, TRANSITION + DISPUTE_LOCK);
1905 }
1906
1907 #[test]
1911 fn a_deposit_during_the_lock_buys_no_voting_power() {
1912 let alice = keys_from_seed(1);
1913 let mut state = staked_state(&alice, TRANSITION, FIRST_EPOCH, 10);
1914 state
1915 .forfeit_node(&alice.addr, Some(FIRST_EPOCH))
1916 .expect("forfeit");
1917 let lock_until = state.dispute_records[&alice.addr].lock_until;
1918
1919 state.current_view = TRANSITION + 1;
1920 state.update_voting_power(&alice.addr, 50).expect("deposit");
1921
1922 assert_eq!(lock_status_of(&state, &alice).available_votes(), 0);
1923 assert!(exits_of(&state, &alice)
1924 .iter()
1925 .all(|exit| *exit >= lock_until));
1926
1927 state.current_view = lock_until;
1928 state.update_voting_power(&alice.addr, 7).expect("deposit");
1929 assert_eq!(lock_status_of(&state, &alice).available_votes(), 7);
1930 }
1931
1932 #[test]
1935 fn a_lock_that_predates_the_gate_is_not_retrofitted() {
1936 let alice = keys_from_seed(1);
1937 let mut state = staked_state(&alice, TRANSITION - 1, FIRST_EPOCH, 10);
1938 state.forfeit_node(&alice.addr, None).expect("forfeit");
1939 assert_eq!(lock_status_of(&state, &alice).available_votes(), 0);
1940
1941 state.current_view = TRANSITION;
1942 state.update_voting_power(&alice.addr, 50).expect("deposit");
1943 assert_eq!(lock_status_of(&state, &alice).available_votes(), 50);
1944 }
1945
1946 #[test]
1947 fn replayed_evidence_neither_errors_nor_extends_the_lock() {
1948 let alice = keys_from_seed(1);
1949 let mut state = staked_state(&alice, TRANSITION, FIRST_EPOCH, 10);
1950 state
1951 .forfeit_node(&alice.addr, Some(FIRST_EPOCH))
1952 .expect("forfeit");
1953 let exits = exits_of(&state, &alice);
1954
1955 state.current_view = TRANSITION + 10;
1956 assert!(state
1957 .validate_dispute(&dispute_of(&alice), FIRST_EPOCH)
1958 .is_err());
1959
1960 state
1963 .forfeit_node(&alice.addr, Some(FIRST_EPOCH))
1964 .expect("duplicate is a no-op");
1965 assert_eq!(exits_of(&state, &alice), exits);
1966 }
1967
1968 #[test]
1969 fn a_later_offence_postpones_the_deadline() {
1970 let alice = keys_from_seed(1);
1971 let mut state = staked_state(&alice, TRANSITION, FIRST_EPOCH, 10);
1972 state
1973 .forfeit_node(&alice.addr, Some(FIRST_EPOCH))
1974 .expect("forfeit");
1975
1976 state.current_view = TRANSITION + 10;
1977 state.epoch_state.epoch = FIRST_EPOCH + 1;
1978 state
1979 .validate_dispute(&dispute_of(&alice), FIRST_EPOCH + 1)
1980 .expect("a fresh offence is admissible");
1981 state
1982 .forfeit_node(&alice.addr, Some(FIRST_EPOCH + 1))
1983 .expect("forfeit");
1984
1985 let record = *state.dispute_records.get(&alice.addr).unwrap();
1986 assert_eq!(record.last_offense_epoch, FIRST_EPOCH + 1);
1987 assert_eq!(record.lock_until, TRANSITION + 10 + DISPUTE_LOCK);
1988 assert_eq!(
1989 exits_of(&state, &alice),
1990 vec![TRANSITION + 10 + DISPUTE_LOCK]
1991 );
1992 }
1993
1994 #[test]
1995 fn a_dispute_never_shortens_a_withdrawal_already_in_flight() {
1996 let alice = keys_from_seed(1);
1997 let mut state = staked_state(&alice, TRANSITION, FIRST_EPOCH, 10);
1998 state.retire_node(&alice.addr, 10).expect("retire");
2000 let exits = exits_of(&state, &alice);
2001 assert!(exits.iter().all(|view| *view > TRANSITION + DISPUTE_LOCK));
2002
2003 state
2004 .forfeit_node(&alice.addr, Some(FIRST_EPOCH))
2005 .expect("forfeit");
2006
2007 assert_eq!(exits_of(&state, &alice), exits);
2008 }
2009
2010 #[test]
2011 fn evidence_outside_the_admissible_epochs_is_refused() {
2012 let alice = keys_from_seed(1);
2013 let state = staked_state(&alice, TRANSITION, FIRST_EPOCH, 10);
2014
2015 assert!(state
2016 .validate_dispute(&dispute_of(&alice), FIRST_EPOCH - 1)
2017 .is_err());
2018 assert!(state
2019 .validate_dispute(&dispute_of(&alice), FIRST_EPOCH + 1)
2020 .is_err());
2021 assert!(state
2022 .validate_dispute(&dispute_of(&alice), FIRST_EPOCH)
2023 .is_ok());
2024 }
2025
2026 #[test]
2027 fn a_pre_activation_dispute_leaves_no_record() {
2028 let alice = keys_from_seed(1);
2029 let mut state = staked_state(&alice, TRANSITION - 1, FIRST_EPOCH, 10);
2030
2031 state.forfeit_node(&alice.addr, None).expect("forfeit");
2032
2033 assert!(state.dispute_records.get(&alice.addr).is_none());
2034 assert_eq!(lock_status_of(&state, &alice).available_votes(), 0);
2035 }
2036
2037 #[test]
2038 fn persisted_layout_follows_the_transition_view() {
2039 let alice = keys_from_seed(1);
2040
2041 let before = staked_state(&alice, TRANSITION - 1, FIRST_EPOCH, 10);
2042 let legacy = before.encode_persisted().expect("encode");
2043 assert!(
2044 bcs::from_bytes::<PosState>(&legacy).is_err(),
2045 "a legacy row must not decode as the current layout, or the \
2046 fallback would be reached by states that do not need it"
2047 );
2048 let decoded = PosState::decode_persisted(&legacy).expect("decode");
2049 assert!(decoded.dispute_records.is_empty());
2050 assert_eq!(decoded, before);
2051
2052 let mut after = staked_state(&alice, TRANSITION, FIRST_EPOCH, 10);
2053 let empty_but_gated = after.encode_persisted().expect("encode");
2054 assert_eq!(
2055 PosState::decode_persisted(&empty_but_gated).expect("decode"),
2056 after
2057 );
2058 after
2059 .forfeit_node(&alice.addr, Some(FIRST_EPOCH))
2060 .expect("forfeit");
2061 let with_record = after.encode_persisted().expect("encode");
2062 assert_eq!(
2063 PosState::decode_persisted(&with_record).expect("decode"),
2064 after
2065 );
2066 }
2067
2068 #[test]
2069 fn the_legacy_layout_is_the_current_one_without_its_last_field() {
2070 let alice = keys_from_seed(1);
2071 let state = staked_state(&alice, TRANSITION - 1, FIRST_EPOCH, 10);
2072 assert!(state.dispute_records.is_empty());
2073
2074 assert_eq!(
2077 bcs::to_bytes(&state).expect("encode"),
2078 [state.encode_persisted().expect("encode").as_slice(), &[0u8]]
2079 .concat()
2080 );
2081 }
2082
2083 #[test]
2086 fn a_dispute_survives_a_restart_on_each_side_of_the_gate() {
2087 let alice = keys_from_seed(1);
2088 let before = staked_state(&alice, TRANSITION - 1, FIRST_EPOCH, 10);
2089
2090 let mut state =
2091 PosState::decode_persisted(&before.encode_persisted().unwrap())
2092 .expect("legacy row still readable");
2093 state.current_view = TRANSITION;
2094 state
2095 .forfeit_node(&alice.addr, Some(FIRST_EPOCH))
2096 .expect("forfeit");
2097
2098 let restarted =
2099 PosState::decode_persisted(&state.encode_persisted().unwrap())
2100 .expect("current row readable");
2101 assert_eq!(restarted, state);
2102 assert_eq!(
2103 restarted.dispute_records[&alice.addr].last_offense_epoch,
2104 FIRST_EPOCH
2105 );
2106 assert!(restarted
2107 .validate_dispute(&dispute_of(&alice), FIRST_EPOCH)
2108 .is_err());
2109 }
2110
2111 #[test]
2112 fn a_legacy_row_is_readable_up_to_the_transition_view_and_no_further() {
2113 let alice = keys_from_seed(1);
2114 let state = staked_state(&alice, 0, FIRST_EPOCH, 10);
2115
2116 let legacy_at = |view: View| {
2117 bcs::to_bytes(&PosStateV1Ref {
2118 node_map: &state.node_map,
2119 current_view: &view,
2120 epoch_state: &state.epoch_state,
2121 term_list: &state.term_list,
2122 retiring_nodes: &state.retiring_nodes,
2123 pivot_decision: &state.pivot_decision,
2124 node_map_hint: &state.node_map_hint,
2125 unlock_event_hint: &state.unlock_event_hint,
2126 skipped: &state.skipped,
2127 })
2128 .unwrap()
2129 };
2130
2131 assert!(PosState::decode_persisted(&legacy_at(TRANSITION - 1)).is_ok());
2132 assert!(PosState::decode_persisted(&legacy_at(TRANSITION)).is_ok());
2133 assert!(PosState::decode_persisted(&legacy_at(TRANSITION + 1)).is_err());
2134 }
2135
2136 fn election_payload_for(k: &Keys) -> ElectionPayload {
2137 ElectionPayload {
2138 public_key: k.bls_pk.clone(),
2139 vrf_public_key: k.vrf_pk.clone(),
2140 target_term: 1,
2141 vrf_proof: dummy_vrf_proof(),
2142 }
2143 }
2144
2145 #[test]
2146 fn election_rejects_signer_mismatch() {
2147 let alice = keys_from_seed(1);
2148 let mallory = keys_from_seed(2);
2149 let state = state_with_node(&alice);
2150
2151 let payload = election_payload_for(&alice);
2152 assert_eq!(
2153 state.validate_election_simple(
2154 &mallory.addr,
2155 &mallory.bls_pk,
2156 &payload
2157 ),
2158 Some(DiscardedVMStatus::ELECTION_SIGNER_MISMATCH),
2159 );
2160 }
2161
2162 #[test]
2163 fn election_rejects_auth_key_mismatch() {
2164 let alice = keys_from_seed(1);
2165 let mallory = keys_from_seed(2);
2166 let state = state_with_node(&alice);
2167
2168 let payload = election_payload_for(&alice);
2169 assert_eq!(
2170 state.validate_election_simple(
2171 &alice.addr,
2172 &mallory.bls_pk,
2173 &payload
2174 ),
2175 Some(DiscardedVMStatus::AUTHENTICATOR_KEY_MISMATCH),
2176 );
2177 }
2178
2179 #[test]
2180 fn check_sender_owns_auth_key_unregistered() {
2181 let alice = keys_from_seed(1);
2182 let mallory = keys_from_seed(2);
2183 let state = state_with_node(&alice);
2184
2185 assert_eq!(
2186 state
2187 .check_sender_owns_auth_key(
2188 &mallory.addr,
2189 &mallory.bls_pk,
2190 DiscardedVMStatus::ELECTION_NON_EXISTENT_NODE,
2191 )
2192 .err(),
2193 Some(DiscardedVMStatus::ELECTION_NON_EXISTENT_NODE),
2194 );
2195 }
2196
2197 #[test]
2198 fn check_sender_owns_auth_key_accepts_matching() {
2199 let alice = keys_from_seed(1);
2200 let state = state_with_node(&alice);
2201
2202 assert!(state
2203 .check_sender_owns_auth_key(
2204 &alice.addr,
2205 &alice.bls_pk,
2206 DiscardedVMStatus::ELECTION_NON_EXISTENT_NODE,
2207 )
2208 .is_ok());
2209 }
2210}