diem_types/
term_state.rs

1// Copyright 2021 Conflux Foundation. All rights reserved.
2// Conflux is free software and distributed under GNU General Public License.
3// See http://www.gnu.org/licenses/
4
5use 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::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;
51// The view to start election in the whole PoS consensus protocol.
52
53pub 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    /// This struct is only used locally, so loaded public keys must be valid.
104    #[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/// A node becomes its voting power number of ElectionNodes for election.
115#[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    /// (VRF.val, NodeID)
265    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    /// The current active term.
316    /// After the first `TERM_LIST_LEN` terms, it should be the term of
317    /// `term_list[TERM_LIST_LEN-1]`
318    current_term: u64,
319    /// The maintained term list. It should always have a length `TERM_LIST_LEN
320    /// + 2`. The first `TERM_LIST_LEN` terms are used to validate new
321    /// election transactions, and the last 2 terms are open for election.
322    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    /// Add a new node to term list after a valid Election transaction has been
373    /// executed.
374    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            // Hash after appending the nonce to get multiple identifier for
393            // election.
394            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            // The initial terms are not open for election.
415            return;
416        }
417        // This double-check should always pass.
418        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        // TODO(lpl): Optimize by adding hash set to each term or adding another
453        // field to node_map.
454        let start_term_offset = target_term_offset - (TERM_LIST_LEN - 1);
455
456        // The checking of `target_view` ensures that this is in range of
457        // `term_list`.
458        // For any valid `target_term_offset`, always checks to the end of
459        // `term_list` because it's within the service time of
460        // `target_term`.
461        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(Clone, Serialize, Eq, PartialEq, Deserialize)]
471#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
472pub struct PosState {
473    /// All the nodes that have staked in PoW.
474    /// Nodes are only inserted and will never be removed.
475    node_map: HashMap<AccountAddress, NodeData>,
476    /// `current_view / TERM_LIST_LEN == term_list.current_term` is always
477    /// true. This is not the same as `RoundState.current_view` because the
478    /// view does not increase for blocks following a pending
479    /// reconfiguration block.
480    current_view: Round,
481    /// Current epoch state
482    epoch_state: EpochState,
483    term_list: TermList,
484
485    /// Track the nodes that have retired and are waiting to be unlocked.
486    /// Nodes that are enqueued early will also become unlocked early.
487    retiring_nodes: VecDeque<AccountAddress>,
488    /// Current pivot decision.
489    pivot_decision: PivotBlockDecision,
490
491    node_map_hint: HashMap<View, HashSet<AccountAddress>>,
492    unlock_event_hint: HashSet<AccountAddress>,
493
494    /// If `skipped` is `true`, this PosState belongs to a block following a
495    /// reconfiguration block, so this block is not executed and the
496    /// PosState is the same as its parent. These skipped blocks have the
497    /// same view as their parents and should not be saved as `CommittedBlock`.
498    skipped: bool,
499}
500
501impl Debug for PosState {
502    fn fmt(
503        &self, f: &mut Formatter<'_>,
504    ) -> std::result::Result<(), std::fmt::Error> {
505        f.debug_struct("PosState")
506            .field("view", &self.current_view)
507            .field("node_map_size", &self.node_map.len())
508            .field("term_list", &self.term_list)
509            .field("epoch_state", &self.epoch_state)
510            .finish()
511    }
512}
513
514impl PosState {
515    pub fn new(
516        initial_seed: Vec<u8>, initial_nodes: Vec<(NodeID, u64)>,
517        initial_committee: Vec<(AccountAddress, u64)>,
518        genesis_pivot_decision: PivotBlockDecision,
519    ) -> Self {
520        let mut node_map = HashMap::new();
521        let mut node_list = BTreeMap::default();
522        for (node_id, total_voting_power) in initial_nodes {
523            let mut lock_status = NodeLockStatus::default();
524            // The genesis block should not have updates for lock status.
525            lock_status.new_lock(0, total_voting_power, true, &mut Vec::new());
526            node_map.insert(
527                node_id.addr.clone(),
528                NodeData {
529                    public_key: node_id.public_key.clone(),
530                    vrf_public_key: Some(node_id.vrf_public_key.clone()),
531                    lock_status,
532                },
533            );
534        }
535        for (addr, voting_power) in initial_committee {
536            // VRF output of initial terms will not be used, because these terms
537            // are not open for election.
538            node_list.insert(addr, voting_power);
539        }
540        let mut term_list = Vec::new();
541        let initial_term = TermData {
542            start_view: 0,
543            seed: initial_seed.clone(),
544            node_list: NodeList::Elected(ElectedMap(node_list.clone())),
545        };
546        term_list.push(initial_term);
547        // TODO(lpl): The initial terms can have different node list.
548        // Duplicate the initial term for the first TERM_LIST_LEN + 2 terms.
549        for i in 0..(TERM_LIST_LEN + 1) {
550            let last_term = term_list.last().unwrap();
551            let mut next_term =
552                last_term.next_term(Default::default(), initial_seed.clone());
553            if i < TERM_LIST_LEN - 1 {
554                let _ = next_term.node_list.finalize_elect();
555            }
556            term_list.push(next_term);
557        }
558        let mut pos_state = PosState {
559            node_map,
560            current_view: 0,
561            epoch_state: EpochState::empty(),
562            term_list: TermList {
563                current_term: 0,
564                term_list,
565                electing_index: TERM_LIST_LEN,
566                candy_rewards: ElectedMap(node_list),
567            },
568            retiring_nodes: Default::default(),
569            pivot_decision: genesis_pivot_decision,
570            node_map_hint: Default::default(),
571            unlock_event_hint: Default::default(),
572            skipped: false,
573        };
574        let (verifier, vrf_seed) = pos_state.get_committee_at(0).unwrap();
575        pos_state.epoch_state = EpochState::new(0, verifier, vrf_seed);
576        pos_state
577    }
578
579    pub fn new_empty() -> Self {
580        Self {
581            node_map: Default::default(),
582            current_view: 0,
583            epoch_state: EpochState::empty(),
584            term_list: TermList {
585                current_term: 0,
586                term_list: Default::default(),
587                electing_index: 0,
588                candy_rewards: Default::default(),
589            },
590            retiring_nodes: Default::default(),
591            node_map_hint: Default::default(),
592            unlock_event_hint: Default::default(),
593            pivot_decision: PivotBlockDecision {
594                block_hash: Default::default(),
595                height: 0,
596            },
597            skipped: false,
598        }
599    }
600
601    pub fn set_skipped(&mut self, skipped: bool) { self.skipped = skipped; }
602
603    pub fn set_pivot_decision(&mut self, pivot_decision: PivotBlockDecision) {
604        self.pivot_decision = pivot_decision;
605    }
606
607    pub fn pivot_decision(&self) -> &PivotBlockDecision { &self.pivot_decision }
608
609    // pub fn current_term_seed(&self) -> &Vec<u8> {
610    //     self.target_term_seed(self.term_list.current_term)
611    // }
612
613    pub fn target_term_seed(&self, target_term: u64) -> &Vec<u8> {
614        &self
615            .term_list
616            .get_term_by_number(target_term)
617            .expect("term not in term list")
618            .seed
619    }
620
621    pub fn epoch_state(&self) -> &EpochState { &self.epoch_state }
622
623    pub fn term_list(&self) -> &TermList { &self.term_list }
624
625    pub fn account_node_data(
626        &self, account_address: AccountAddress,
627    ) -> Option<&NodeData> {
628        self.node_map.get(&account_address)
629    }
630}
631
632/// Read-only functions use in `TransactionValidator`
633impl PosState {
634    /// Binds `sender` to a slot the submitter actually controls by
635    /// requiring `node_map[sender].public_key == auth_pk`. Returns the
636    /// `NodeData` so callers can reuse the borrow.
637    fn check_sender_owns_auth_key(
638        &self, sender: &AccountAddress, auth_pk: &ConsensusPublicKey,
639        not_registered: DiscardedVMStatus,
640    ) -> Result<&NodeData, DiscardedVMStatus> {
641        let node = self.account_node_data(*sender).ok_or(not_registered)?;
642        if &node.public_key != auth_pk {
643            return Err(DiscardedVMStatus::AUTHENTICATOR_KEY_MISMATCH);
644        }
645        Ok(node)
646    }
647
648    pub fn validate_election_simple(
649        &self, sender: &AccountAddress, auth_pk: &ConsensusPublicKey,
650        election_tx: &ElectionPayload,
651    ) -> Option<DiscardedVMStatus> {
652        let node_id = NodeID::new(
653            election_tx.public_key.clone(),
654            election_tx.vrf_public_key.clone(),
655        );
656        diem_trace!(
657            "validate_election_simple: {:?} {}",
658            node_id.addr,
659            election_tx.target_term
660        );
661        // Sender must match the addr derived from the payload's
662        // declared keys, so a registered submitter can't stuff another
663        // node's payload keys.
664        if *sender != node_id.addr {
665            return Some(DiscardedVMStatus::ELECTION_SIGNER_MISMATCH);
666        }
667        let node = match self.check_sender_owns_auth_key(
668            sender,
669            auth_pk,
670            DiscardedVMStatus::ELECTION_NON_EXISTENT_NODE,
671        ) {
672            Ok(node) => node,
673            Err(err) => return Some(err),
674        };
675
676        let target_view = match POS_STATE_CONFIG
677            .get_starting_view_for_term(election_tx.target_term)
678        {
679            None => {
680                return Some(DiscardedVMStatus::ELECTION_TARGET_TERM_NOT_OPEN)
681            }
682            Some(v) => v,
683        };
684
685        if node.lock_status.available_votes() == 0 {
686            return Some(DiscardedVMStatus::ELECTION_WITHOUT_VOTES);
687        }
688        // Do not check `ELECTION_TERM_END_ROUND` because we are using the
689        // committed state in this simple validation.
690        if target_view
691            <= self.current_view
692                + POS_STATE_CONFIG.election_term_end_round(self.current_view)
693        {
694            return Some(DiscardedVMStatus::ELECTION_TARGET_TERM_NOT_OPEN);
695        }
696        None
697    }
698
699    pub fn validate_pivot_decision_simple(
700        &self, sender: &AccountAddress, auth_pk: &ConsensusPublicKey,
701        pivot_decision_tx: &PivotBlockDecision,
702    ) -> Option<DiscardedVMStatus> {
703        // node_map (not active-committee): PivotDecision is gossiped
704        // per round and committee gating would drop newly-joined
705        // members at every term boundary.
706        if let Err(err) = self.check_sender_owns_auth_key(
707            sender,
708            auth_pk,
709            DiscardedVMStatus::PIVOT_DECISION_SENDER_NOT_REGISTERED,
710        ) {
711            return Some(err);
712        }
713        if pivot_decision_tx.height <= self.pivot_decision.height {
714            return Some(DiscardedVMStatus::PIVOT_DECISION_HEIGHT_TOO_OLD);
715        }
716        None
717    }
718
719    pub fn validate_dispute_simple(
720        &self, sender: &AccountAddress, auth_pk: &ConsensusPublicKey,
721    ) -> Option<DiscardedVMStatus> {
722        // Registered-node gating; `verify_dispute` runs at execute time.
723        self.check_sender_owns_auth_key(
724            sender,
725            auth_pk,
726            DiscardedVMStatus::DISPUTE_SENDER_NOT_REGISTERED,
727        )
728        .err()
729    }
730}
731
732/// Read-only functions used in `execute_block`
733impl PosState {
734    pub fn validate_election(
735        &self, election_tx: &ElectionPayload,
736    ) -> Result<()> {
737        let node_id = NodeID::new(
738            election_tx.public_key.clone(),
739            election_tx.vrf_public_key.clone(),
740        );
741        diem_trace!(
742            "validate_election: {:?} {}",
743            node_id.addr,
744            election_tx.target_term
745        );
746        let node = match self.node_map.get(&node_id.addr) {
747            Some(node) => node,
748            None => return Err(anyhow!("Election for non-existent node.")),
749        };
750
751        if node.lock_status.available_votes() == 0 {
752            bail!("Election without any votes");
753        }
754        let target_view = match POS_STATE_CONFIG
755            .get_starting_view_for_term(election_tx.target_term)
756        {
757            None => {
758                bail!("target view overflows, election_tx={:?}", election_tx)
759            }
760            Some(v) => v,
761        };
762        if target_view
763            > self.current_view
764                + POS_STATE_CONFIG.election_term_start_round(self.current_view)
765            || target_view
766                <= self.current_view
767                    + POS_STATE_CONFIG
768                        .election_term_end_round(self.current_view)
769        {
770            bail!(
771                "Target term is not open for election: target={} current={}",
772                target_view,
773                self.current_view
774            );
775        }
776
777        let target_term_offset =
778            (election_tx.target_term - self.term_list.start_term()) as usize;
779        assert_eq!(target_term_offset, self.term_list.electing_index);
780
781        let target_term = &self.term_list.electing_term();
782        if election_tx
783            .vrf_proof
784            .verify(&target_term.seed, node.vrf_public_key.as_ref().unwrap())
785            .is_err()
786        {
787            bail!("Invalid VRF proof for election")
788        }
789
790        if target_term.node_list.has_elected(&node_id.addr) {
791            bail!("The sender has elected for this term")
792        }
793
794        if node.lock_status.available_votes()
795            <= self
796                .term_list
797                .serving_votes(target_term_offset, &node_id.addr)
798        {
799            bail!("Election without enough votes");
800        }
801
802        Ok(())
803    }
804
805    pub fn validate_pivot_decision(
806        &self, pivot_decision_tx: &PivotBlockDecision,
807        signature: MultiConsensusSignature,
808    ) -> Result<()> {
809        if pivot_decision_tx.height <= self.pivot_decision.height {
810            return Err(anyhow!(format!(
811                "Pivot Decision height too small, found[{}], expect[{}]",
812                pivot_decision_tx.height, self.pivot_decision.height
813            )));
814        }
815        let senders: Vec<_> = self
816            .epoch_state
817            .verifier()
818            .address_to_validator_info()
819            .keys()
820            .cloned()
821            .collect();
822        let public_keys: Vec<ConsensusPublicKey> = senders
823            .iter()
824            .map(|sender| {
825                self.epoch_state.verifier().get_public_key(sender).unwrap()
826            })
827            .collect();
828        let public_key = MultiConsensusPublicKey::new(public_keys);
829        if let Err(e) = signature.verify(pivot_decision_tx, &public_key) {
830            return Err(anyhow!(format!(
831                "Pivot Decision verification failed [{:?}]",
832                e
833            )));
834        }
835        let signers = signature.get_signers(&senders)?;
836        if let Err(e) = self
837            .epoch_state()
838            .verifier()
839            .check_voting_power(signers.iter())
840        {
841            return Err(anyhow!(format!(
842                "Pivot Decision voting power check failed [{:?}]",
843                e
844            )));
845        }
846        Ok(())
847    }
848
849    pub fn validate_dispute(
850        &self, dispute_payload: &DisputePayload,
851    ) -> Result<()> {
852        if let Some(node_status) = self.node_map.get(&dispute_payload.address) {
853            if node_status.lock_status.exempt_from_forfeit().is_none() {
854                Ok(())
855            } else {
856                bail!(
857                    "Dispute a forfeited node: {:?}",
858                    dispute_payload.address
859                );
860            }
861        } else {
862            bail!("Unknown dispute node: {:?}", dispute_payload.address);
863        }
864    }
865
866    /// Return `(validator_set, term_seed)`.
867    pub fn get_committee_at(
868        &self, term: u64,
869    ) -> Result<(ValidatorVerifier, Vec<u8>)> {
870        diem_debug!(
871            "Get committee at term {} in view {}, term list start at {}",
872            term,
873            self.current_view,
874            self.term_list.start_term()
875        );
876        let mut voting_power_map = BTreeMap::new();
877        for term_data in self.term_list.committee_for_term(term) {
878            for (addr, votes) in term_data.node_list.committee().0.iter() {
879                *voting_power_map.entry(addr.clone()).or_insert(0 as u64) +=
880                    votes;
881            }
882        }
883        let mut address_to_validator_info = BTreeMap::new();
884        for (addr, voting_power) in voting_power_map {
885            let node_data = self.node_map.get(&addr).expect("node in node_map");
886            // Retired nodes are not removed from term_list,
887            // but we do not include them in the new committee.
888            let voting_power = std::cmp::min(
889                voting_power,
890                node_data.lock_status.available_votes(),
891            );
892            if voting_power > 0 {
893                address_to_validator_info.insert(
894                    addr,
895                    ValidatorConsensusInfo::new(
896                        node_data.public_key.clone(),
897                        node_data.vrf_public_key.clone(),
898                        voting_power,
899                    ),
900                );
901            }
902        }
903
904        Ok((
905            ValidatorVerifier::new(address_to_validator_info),
906            self.term_list.term_list[0].seed.clone(),
907        ))
908    }
909
910    /// TODO(lpl): Return VDF seed for the term.
911    /// Return `Some(target_term)` if `author` should send its election
912    /// transaction.
913    pub fn next_elect_term(&self, author: &AccountAddress) -> Option<u64> {
914        if self.current_view
915            < POS_STATE_CONFIG.first_start_election_view() as u64
916        {
917            return None;
918        }
919
920        if self.term_list.electing_term().node_list.has_elected(author) {
921            return None;
922        }
923
924        if let Some(node) = self.node_map.get(author) {
925            let available_votes = node.lock_status.available_votes();
926            let serving_votes = self
927                .term_list
928                .serving_votes(self.term_list.electing_index, author);
929
930            return if available_votes > serving_votes {
931                Some(self.term_list.electing_term_number())
932            } else {
933                None
934            };
935        }
936
937        None
938    }
939
940    pub fn final_serving_view(&self, author: &AccountAddress) -> Option<Round> {
941        let mut final_elected_term = None;
942        for term in self.term_list.term_list.iter().rev() {
943            match &term.node_list {
944                NodeList::Electing(heap) => {
945                    if heap.1.contains(author) {
946                        final_elected_term = Some(term.get_term());
947                        break;
948                    }
949                }
950                NodeList::Elected(map) => {
951                    if map.0.contains_key(author) {
952                        final_elected_term = Some(term.get_term());
953                        break;
954                    }
955                }
956            }
957        }
958        final_elected_term.map(|t| {
959            POS_STATE_CONFIG
960                .get_starting_view_for_term(t + TERM_LIST_LEN as u64)
961                .expect("checked term")
962                + 1
963        })
964    }
965
966    pub fn get_unlock_events(&self) -> Vec<ContractEvent> {
967        let mut unlocked_nodes = Vec::new();
968        for addr in &self.unlock_event_hint {
969            let node = self.node_map.get(&addr).expect("exists");
970            let unlock_event = ContractEvent::new(
971                UnlockEvent::event_key(),
972                bcs::to_bytes(&UnlockEvent {
973                    node_id: *addr,
974                    unlocked: node.lock_status.unlocked_votes(),
975                })
976                .unwrap(),
977            );
978            unlocked_nodes.push(unlock_event);
979        }
980
981        return unlocked_nodes;
982    }
983
984    pub fn current_view(&self) -> u64 { self.current_view }
985
986    pub fn skipped(&self) -> bool { self.skipped }
987
988    pub fn next_evicted_term(&mut self) -> BTreeMap<H256, u64> {
989        let candy_rewards = std::mem::take(&mut self.term_list.candy_rewards);
990        candy_rewards
991            .0
992            .iter()
993            .map(|(id, cnt)| (H256::from(id.to_u8()), *cnt))
994            .collect()
995    }
996}
997
998/// Write functions used apply changes (process events in PoS and PoW)
999impl PosState {
1000    pub fn register_node(&mut self, node_id: NodeID) -> Result<()> {
1001        diem_trace!("register_node: {:?}", node_id);
1002        ensure!(
1003            !self.node_map.contains_key(&node_id.addr),
1004            "register an already registered address"
1005        );
1006        self.node_map.insert(
1007            node_id.addr,
1008            NodeData {
1009                public_key: node_id.public_key,
1010                vrf_public_key: Some(node_id.vrf_public_key),
1011                lock_status: NodeLockStatus::default(),
1012            },
1013        );
1014        Ok(())
1015    }
1016
1017    pub fn update_voting_power(
1018        &mut self, addr: &AccountAddress, increased_voting_power: u64,
1019    ) -> Result<()> {
1020        diem_trace!(
1021            "update_voting_power: {:?} {}",
1022            addr,
1023            increased_voting_power
1024        );
1025        let mut update_views = Vec::new();
1026        match self.node_map.get_mut(addr) {
1027            Some(node_status) => node_status.lock_status.new_lock(
1028                self.current_view,
1029                increased_voting_power,
1030                false,
1031                &mut update_views,
1032            ),
1033            None => bail!("increase voting power of a non-existent node!"),
1034        };
1035        self.record_update_views(addr, update_views);
1036        Ok(())
1037    }
1038
1039    pub fn new_node_elected(&mut self, event: &ElectionEvent) -> Result<()> {
1040        diem_debug!(
1041            "new_node_elected: {:?} {:?}",
1042            event.node_id,
1043            event.start_term
1044        );
1045        let author = &event.node_id.addr;
1046        let available_votes = self
1047            .node_map
1048            .get(author)
1049            .expect("checked in execution")
1050            .lock_status
1051            .available_votes();
1052        let target_term_offset =
1053            (event.start_term - self.term_list.start_term()) as usize;
1054        let serving_votes =
1055            self.term_list.serving_votes(target_term_offset, author);
1056        let voting_power = available_votes.saturating_sub(serving_votes);
1057        if voting_power > 0 {
1058            // A workaround for too much staked CFX in Testnet.
1059            let bounded_power = std::cmp::min(
1060                voting_power,
1061                POS_STATE_CONFIG.max_nonce_per_account(self.current_view()),
1062            );
1063            self.term_list.new_node_elected(event, bounded_power)?;
1064        } else {
1065            diem_warn!("No votes can be elected: {:?} {:?}. available: {}, serving: {}.", event.node_id,
1066            event.start_term,available_votes,serving_votes);
1067        }
1068        Ok(())
1069    }
1070
1071    /// `get_new_committee` has been called before this to produce an
1072    /// EpochState. And `next_view` will not be called for blocks following
1073    /// a pending reconfiguration block.
1074    pub fn next_view(&mut self) -> Result<Option<EpochState>> {
1075        // Increase view after updating node status above to get a correct
1076        // `status_start_view`.
1077        self.current_view += 1;
1078
1079        diem_debug!("current view {}", self.current_view);
1080
1081        // Update the status for the all.
1082        self.unlock_event_hint.clear();
1083
1084        if let Some(addresses) = self.node_map_hint.remove(&self.current_view) {
1085            for address in addresses {
1086                let node = self.node_map.get_mut(&address).expect("exists");
1087                let new_votes_unlocked =
1088                    node.lock_status.update(self.current_view);
1089                if new_votes_unlocked {
1090                    self.unlock_event_hint.insert(address);
1091                }
1092            }
1093        }
1094
1095        let epoch_state = if self.current_view == 1 {
1096            let (verifier, term_seed) = self.get_committee_at(0)?;
1097            // genesis
1098            Some(EpochState::new(1, verifier, term_seed.clone()))
1099        } else {
1100            let (term, view_in_term) =
1101                POS_STATE_CONFIG.get_term_view(self.current_view);
1102            if view_in_term == 0 {
1103                let new_term = term;
1104                let (verifier, term_seed) = self.get_committee_at(new_term)?;
1105                // generate new epoch for new term.
1106                self.term_list.new_term(
1107                    new_term,
1108                    self.pivot_decision.block_hash.as_bytes().to_vec(),
1109                );
1110                // TODO(lpl): If we allow epoch changes within a term, this
1111                // should be updated.
1112                Some(EpochState::new(new_term + 1, verifier, term_seed.clone()))
1113            } else if self.current_view
1114                >= POS_STATE_CONFIG.first_end_election_view()
1115                && view_in_term
1116                    == POS_STATE_CONFIG.round_per_term(self.current_view) / 2
1117            {
1118                self.term_list.finalize_election();
1119                None
1120            } else {
1121                None
1122            }
1123        };
1124        if let Some(epoch_state) = &epoch_state {
1125            self.epoch_state = epoch_state.clone();
1126        }
1127        Ok(epoch_state)
1128    }
1129
1130    pub fn retire_node(
1131        &mut self, addr: &AccountAddress, votes: u64,
1132    ) -> Result<()> {
1133        diem_trace!("retire_node: {:?} {}", addr, votes);
1134        let mut update_views = Vec::new();
1135        match self.node_map.get_mut(&addr) {
1136            Some(node) => {
1137                node.lock_status.new_unlock(
1138                    self.current_view,
1139                    votes,
1140                    &mut update_views,
1141                );
1142            }
1143            None => bail!("Retiring node does not exist"),
1144        };
1145        self.record_update_views(addr, update_views);
1146        Ok(())
1147    }
1148
1149    pub fn force_retire_node(&mut self, addr: &AccountAddress) -> Result<()> {
1150        diem_trace!("force_retire_node: {:?}", addr);
1151        let mut update_views = Vec::new();
1152        match self.node_map.get_mut(&addr) {
1153            Some(node) => node
1154                .lock_status
1155                .force_retire(self.current_view, &mut update_views),
1156            None => bail!("Force retiring node does not exist"),
1157        };
1158        self.record_update_views(addr, update_views);
1159        Ok(())
1160    }
1161
1162    pub fn forfeit_node(&mut self, addr: &AccountAddress) -> Result<()> {
1163        diem_trace!("forfeit_node: {:?}", addr);
1164        let mut update_views = Vec::new();
1165        match self.node_map.get_mut(&addr) {
1166            Some(node) => node
1167                .lock_status
1168                .forfeit(self.current_view, &mut update_views),
1169            None => bail!("Forfeiting node does not exist"),
1170        }
1171        self.record_update_views(addr, update_views);
1172        Ok(())
1173    }
1174}
1175
1176impl PosState {
1177    pub fn record_update_views(
1178        &mut self, address: &AccountAddress, views: Vec<View>,
1179    ) {
1180        for view in views {
1181            diem_trace!(
1182                "{:?} will update lock status at view {}",
1183                address,
1184                view
1185            );
1186            self.node_map_hint.entry(view).or_default().insert(*address);
1187        }
1188    }
1189}
1190
1191// impl Default for PosState {
1192//     fn default() -> Self {
1193//         Self {
1194//             node_map: Default::default(),
1195//             current_view: 0,
1196//             term_list: TermList {
1197//                 current_term: 0,
1198//                 term_list: Default::default(),
1199//             },
1200//         }
1201//     }
1202// }
1203
1204#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
1205pub struct ElectionEvent {
1206    node_id: NodeID,
1207    vrf_output: HashValue,
1208    start_term: u64,
1209}
1210
1211impl ElectionEvent {
1212    pub fn new(
1213        public_key: ConsensusPublicKey, vrf_public_key: ConsensusVRFPublicKey,
1214        vrf_output: HashValue, start_term: u64,
1215    ) -> Self {
1216        Self {
1217            node_id: NodeID::new(public_key, vrf_public_key),
1218            vrf_output,
1219            start_term,
1220        }
1221    }
1222}
1223
1224impl ElectionEvent {
1225    pub fn event_key() -> EventKey {
1226        EventKey::new_from_address(
1227            &account_config::election_select_address(),
1228            3,
1229        )
1230    }
1231
1232    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
1233        bcs::from_bytes(bytes).map_err(Into::into)
1234    }
1235}
1236
1237#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
1238pub struct RetireEvent {
1239    pub node_id: AccountAddress,
1240    pub votes: u64,
1241}
1242
1243impl RetireEvent {
1244    pub fn new(node_id: AccountAddress, votes: u64) -> Self {
1245        RetireEvent { node_id, votes }
1246    }
1247
1248    pub fn event_key() -> EventKey {
1249        EventKey::new_from_address(&account_config::retire_address(), 4)
1250    }
1251
1252    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
1253        bcs::from_bytes(bytes).map_err(Into::into)
1254    }
1255
1256    pub fn matches_staking_event(
1257        &self, staking_event: &StakingEvent,
1258    ) -> Result<bool> {
1259        match staking_event {
1260            StakingEvent::Retire(addr_h256, votes) => {
1261                let addr = AccountAddress::from_bytes(addr_h256)?;
1262                Ok(self.node_id == addr && self.votes == *votes)
1263            }
1264            _ => Ok(false),
1265        }
1266    }
1267}
1268
1269#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
1270pub struct RegisterEvent {
1271    pub node_id: NodeID,
1272}
1273
1274impl RegisterEvent {
1275    pub fn new(
1276        public_key: ConsensusPublicKey, vrf_public_key: ConsensusVRFPublicKey,
1277    ) -> Self {
1278        Self {
1279            node_id: NodeID::new(public_key, vrf_public_key),
1280        }
1281    }
1282
1283    pub fn event_key() -> EventKey {
1284        EventKey::new_from_address(&account_config::register_address(), 5)
1285    }
1286
1287    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
1288        bcs::from_bytes(bytes).map_err(Into::into)
1289    }
1290
1291    pub fn matches_staking_event(
1292        &self, staking_event: &StakingEvent,
1293    ) -> Result<bool> {
1294        match staking_event {
1295            StakingEvent::Register(
1296                addr_h256,
1297                bls_pub_key_bytes,
1298                vrf_pub_key_bytes,
1299            ) => {
1300                let addr = AccountAddress::from_bytes(addr_h256)?;
1301                let public_key =
1302                    ConsensusPublicKey::try_from(bls_pub_key_bytes.as_slice())?;
1303                let vrf_public_key = ConsensusVRFPublicKey::try_from(
1304                    vrf_pub_key_bytes.as_slice(),
1305                )?;
1306                let node_id =
1307                    NodeID::new(public_key.clone(), vrf_public_key.clone());
1308                ensure!(
1309                    node_id.addr == addr,
1310                    "register event has unmatching address and keys"
1311                );
1312                Ok(self.node_id == node_id)
1313            }
1314            _ => Ok(false),
1315        }
1316    }
1317}
1318
1319#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
1320pub struct UpdateVotingPowerEvent {
1321    pub node_address: AccountAddress,
1322    pub voting_power: u64,
1323}
1324
1325impl UpdateVotingPowerEvent {
1326    pub fn new(node_address: AccountAddress, voting_power: u64) -> Self {
1327        Self {
1328            node_address,
1329            voting_power,
1330        }
1331    }
1332
1333    pub fn event_key() -> EventKey {
1334        EventKey::new_from_address(
1335            &account_config::update_voting_power_address(),
1336            6,
1337        )
1338    }
1339
1340    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
1341        bcs::from_bytes(bytes).map_err(Into::into)
1342    }
1343
1344    pub fn matches_staking_event(
1345        &self, staking_event: &StakingEvent,
1346    ) -> Result<bool> {
1347        match staking_event {
1348            StakingEvent::IncreaseStake(addr_h256, updated_voting_power) => {
1349                let addr = AccountAddress::from_bytes(addr_h256)?;
1350                Ok(self.node_address == addr
1351                    && self.voting_power == *updated_voting_power)
1352            }
1353            _ => Ok(false),
1354        }
1355    }
1356}
1357
1358#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
1359#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
1360pub struct NodeID {
1361    pub public_key: ConsensusPublicKey,
1362    pub vrf_public_key: ConsensusVRFPublicKey,
1363
1364    /// Computed based on other fields.
1365    pub addr: AccountAddress,
1366}
1367
1368impl NodeID {
1369    pub fn new(
1370        public_key: ConsensusPublicKey, vrf_public_key: ConsensusVRFPublicKey,
1371    ) -> Self {
1372        let addr = from_consensus_public_key(&public_key, &vrf_public_key);
1373        Self {
1374            public_key,
1375            vrf_public_key,
1376            addr,
1377        }
1378    }
1379}
1380
1381impl Ord for NodeID {
1382    fn cmp(&self, other: &Self) -> Ordering { self.addr.cmp(&other.addr) }
1383}
1384
1385impl PartialOrd for NodeID {
1386    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1387        Some(self.cmp(other))
1388    }
1389}
1390
1391#[derive(Clone, Serialize, Deserialize)]
1392pub struct UnlockEvent {
1393    /// The node id to unlock.
1394    ///
1395    /// The management contract should unlock the corresponding account.
1396    pub node_id: AccountAddress,
1397    pub unlocked: u64,
1398}
1399
1400impl UnlockEvent {
1401    pub fn event_key() -> EventKey {
1402        EventKey::new_from_address(&account_config::unlock_address(), 5)
1403    }
1404
1405    pub fn from_bytes(bytes: &[u8]) -> anyhow::Result<Self> {
1406        bcs::from_bytes(bytes).map_err(Into::into)
1407    }
1408}
1409
1410#[derive(Clone, Serialize, Deserialize)]
1411pub struct DisputeEvent {
1412    /// The node id to dispute.
1413    pub node_id: AccountAddress,
1414}
1415
1416impl DisputeEvent {
1417    pub fn event_key() -> EventKey {
1418        EventKey::new_from_address(&account_config::dispute_address(), 6)
1419    }
1420
1421    pub fn from_bytes(bytes: &[u8]) -> anyhow::Result<Self> {
1422        bcs::from_bytes(bytes).map_err(Into::into)
1423    }
1424}
1425
1426#[cfg(test)]
1427mod tests {
1428    use super::*;
1429    use crate::{
1430        block_info::PivotBlockDecision, transaction::ElectionPayload,
1431        validator_config::ConsensusVRFProof,
1432    };
1433    use diem_crypto::{
1434        bls::BLSPrivateKey,
1435        ec_vrf::{EcVrfPrivateKey, EcVrfProof},
1436        PrivateKey, Uniform,
1437    };
1438    use rand::{rngs::StdRng, SeedableRng};
1439
1440    struct Keys {
1441        bls_pk: ConsensusPublicKey,
1442        vrf_pk: ConsensusVRFPublicKey,
1443        addr: AccountAddress,
1444    }
1445
1446    fn keys_from_seed(seed: u64) -> Keys {
1447        let mut rng = StdRng::seed_from_u64(seed);
1448        let bls_sk = BLSPrivateKey::generate(&mut rng);
1449        let bls_pk = bls_sk.public_key();
1450        let vrf_sk = EcVrfPrivateKey::generate(&mut rng);
1451        let vrf_pk = vrf_sk.public_key();
1452        let node_id = NodeID::new(bls_pk.clone(), vrf_pk.clone());
1453        Keys {
1454            bls_pk,
1455            vrf_pk,
1456            addr: node_id.addr,
1457        }
1458    }
1459
1460    // simple-validation doesn't verify the VRF proof, so any bytes work.
1461    fn dummy_vrf_proof() -> ConsensusVRFProof {
1462        EcVrfProof::try_from(&[][..]).unwrap()
1463    }
1464
1465    fn state_with_node(k: &Keys) -> PosState {
1466        let mut state = PosState::new_empty();
1467        state
1468            .register_node(NodeID::new(k.bls_pk.clone(), k.vrf_pk.clone()))
1469            .expect("register_node");
1470        state
1471    }
1472
1473    #[test]
1474    fn pivot_decision_rejects_unregistered_sender() {
1475        let alice = keys_from_seed(1);
1476        let mallory = keys_from_seed(2);
1477        let state = state_with_node(&alice);
1478
1479        let tx = PivotBlockDecision {
1480            block_hash: Default::default(),
1481            height: 1,
1482        };
1483        assert_eq!(
1484            state.validate_pivot_decision_simple(
1485                &mallory.addr,
1486                &mallory.bls_pk,
1487                &tx
1488            ),
1489            Some(DiscardedVMStatus::PIVOT_DECISION_SENDER_NOT_REGISTERED),
1490        );
1491    }
1492
1493    #[test]
1494    fn pivot_decision_rejects_auth_key_mismatch() {
1495        let alice = keys_from_seed(1);
1496        let mallory = keys_from_seed(2);
1497        let state = state_with_node(&alice);
1498
1499        let tx = PivotBlockDecision {
1500            block_hash: Default::default(),
1501            height: 1,
1502        };
1503        assert_eq!(
1504            state.validate_pivot_decision_simple(
1505                &alice.addr,
1506                &mallory.bls_pk,
1507                &tx
1508            ),
1509            Some(DiscardedVMStatus::AUTHENTICATOR_KEY_MISMATCH),
1510        );
1511    }
1512
1513    #[test]
1514    fn pivot_decision_accepts_legitimate_self_signed() {
1515        let alice = keys_from_seed(1);
1516        let state = state_with_node(&alice);
1517
1518        let tx = PivotBlockDecision {
1519            block_hash: Default::default(),
1520            height: 1,
1521        };
1522        assert_eq!(
1523            state.validate_pivot_decision_simple(
1524                &alice.addr,
1525                &alice.bls_pk,
1526                &tx
1527            ),
1528            None,
1529        );
1530    }
1531
1532    #[test]
1533    fn dispute_rejects_unregistered_sender() {
1534        let alice = keys_from_seed(1);
1535        let mallory = keys_from_seed(2);
1536        let state = state_with_node(&alice);
1537
1538        assert_eq!(
1539            state.validate_dispute_simple(&mallory.addr, &mallory.bls_pk),
1540            Some(DiscardedVMStatus::DISPUTE_SENDER_NOT_REGISTERED),
1541        );
1542    }
1543
1544    #[test]
1545    fn dispute_rejects_auth_key_mismatch() {
1546        let alice = keys_from_seed(1);
1547        let mallory = keys_from_seed(2);
1548        let state = state_with_node(&alice);
1549
1550        assert_eq!(
1551            state.validate_dispute_simple(&alice.addr, &mallory.bls_pk),
1552            Some(DiscardedVMStatus::AUTHENTICATOR_KEY_MISMATCH),
1553        );
1554    }
1555
1556    #[test]
1557    fn dispute_accepts_legitimate_self_signed() {
1558        let alice = keys_from_seed(1);
1559        let state = state_with_node(&alice);
1560
1561        assert_eq!(
1562            state.validate_dispute_simple(&alice.addr, &alice.bls_pk),
1563            None,
1564        );
1565    }
1566
1567    fn election_payload_for(k: &Keys) -> ElectionPayload {
1568        ElectionPayload {
1569            public_key: k.bls_pk.clone(),
1570            vrf_public_key: k.vrf_pk.clone(),
1571            target_term: 1,
1572            vrf_proof: dummy_vrf_proof(),
1573        }
1574    }
1575
1576    #[test]
1577    fn election_rejects_signer_mismatch() {
1578        let alice = keys_from_seed(1);
1579        let mallory = keys_from_seed(2);
1580        let state = state_with_node(&alice);
1581
1582        let payload = election_payload_for(&alice);
1583        assert_eq!(
1584            state.validate_election_simple(
1585                &mallory.addr,
1586                &mallory.bls_pk,
1587                &payload
1588            ),
1589            Some(DiscardedVMStatus::ELECTION_SIGNER_MISMATCH),
1590        );
1591    }
1592
1593    #[test]
1594    fn election_rejects_auth_key_mismatch() {
1595        let alice = keys_from_seed(1);
1596        let mallory = keys_from_seed(2);
1597        let state = state_with_node(&alice);
1598
1599        let payload = election_payload_for(&alice);
1600        assert_eq!(
1601            state.validate_election_simple(
1602                &alice.addr,
1603                &mallory.bls_pk,
1604                &payload
1605            ),
1606            Some(DiscardedVMStatus::AUTHENTICATOR_KEY_MISMATCH),
1607        );
1608    }
1609
1610    #[test]
1611    fn check_sender_owns_auth_key_unregistered() {
1612        let alice = keys_from_seed(1);
1613        let mallory = keys_from_seed(2);
1614        let state = state_with_node(&alice);
1615
1616        assert_eq!(
1617            state
1618                .check_sender_owns_auth_key(
1619                    &mallory.addr,
1620                    &mallory.bls_pk,
1621                    DiscardedVMStatus::ELECTION_NON_EXISTENT_NODE,
1622                )
1623                .err(),
1624            Some(DiscardedVMStatus::ELECTION_NON_EXISTENT_NODE),
1625        );
1626    }
1627
1628    #[test]
1629    fn check_sender_owns_auth_key_accepts_matching() {
1630        let alice = keys_from_seed(1);
1631        let state = state_with_node(&alice);
1632
1633        assert!(state
1634            .check_sender_owns_auth_key(
1635                &alice.addr,
1636                &alice.bls_pk,
1637                DiscardedVMStatus::ELECTION_NON_EXISTENT_NODE,
1638            )
1639            .is_ok());
1640    }
1641}