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::{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;
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(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
471#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
472pub struct DisputeRecord {
473    /// Outlives `lock_until`: it is what stops the same evidence being filed
474    /// again once the lock expires. Accepting `n` consumes every epoch below.
475    last_offense_epoch: u64,
476    /// Absolute, so a lost callback loses the bookkeeping but not the penalty.
477    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    /// All the nodes that have staked in PoW.
484    /// Nodes are only inserted and will never be removed.
485    node_map: HashMap<AccountAddress, NodeData>,
486    /// `current_view / TERM_LIST_LEN == term_list.current_term` is always
487    /// true. This is not the same as `RoundState.current_view` because the
488    /// view does not increase for blocks following a pending
489    /// reconfiguration block.
490    current_view: Round,
491    /// Current epoch state
492    epoch_state: EpochState,
493    term_list: TermList,
494
495    /// Track the nodes that have retired and are waiting to be unlocked.
496    /// Nodes that are enqueued early will also become unlocked early.
497    retiring_nodes: VecDeque<AccountAddress>,
498    /// Current pivot decision.
499    pivot_decision: PivotBlockDecision,
500
501    node_map_hint: HashMap<View, HashSet<AccountAddress>>,
502    unlock_event_hint: HashSet<AccountAddress>,
503
504    /// If `skipped` is `true`, this PosState belongs to a block following a
505    /// reconfiguration block, so this block is not executed and the
506    /// PosState is the same as its parent. These skipped blocks have the
507    /// same view as their parents and should not be saved as `CommittedBlock`.
508    skipped: bool,
509
510    /// Must stay last: BCS is a non-backtracking prefix parser, so a tail
511    /// field is unambiguously absent from an older encoding, while one added
512    /// earlier could be absorbed by a following length-prefixed field and
513    /// decode as garbage.
514    dispute_records: BTreeMap<AccountAddress, DisputeRecord>,
515}
516
517/// The layout before CIP-173 appended `dispute_records`. Must reuse the same
518/// inner types: `NodeData`'s unchecked BLS deserializer accepts keys the
519/// checked one rejects.
520#[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/// Encoding twin of [`PosStateV1`]; borrows to avoid cloning `node_map`.
534#[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            // The only reachable answer: a CIP-156 lock is indistinguishable
560            // from an ordinary withdrawal, so locks already running cannot be
561            // reconstructed and keep the pre-CIP-173 rules until they expire.
562            dispute_records: BTreeMap::new(),
563        }
564    }
565}
566
567/// The on-disk encoding. Kept off the `Serialize`/`Deserialize` impls so a
568/// future in-memory use of `PosState` cannot inherit the format fork.
569impl PosState {
570    pub fn encode_persisted(&self) -> Result<Vec<u8>> {
571        // Past the gate the new layout goes out even when the map is empty, so
572        // that a binary without CIP-173 fails to read the row instead of
573        // booting and executing disputes under rules the chain has left.
574        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        // Complete BCS encodings are prefix-free, so the trial order is safe:
596        // old bytes hit `Eof` under the current layout, and current bytes
597        // leave a trailing map that `from_bytes` rejects as `RemainingInput`.
598        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        // A state *at* the transition view came from a block whose parent was
611        // still pre-CIP-173, so an older binary that stopped here was in the
612        // right. One view further and it executed a block it should not have.
613        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            // The genesis block should not have updates for lock status.
648            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            // VRF output of initial terms will not be used, because these terms
666            // are not open for election.
667            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        // TODO(lpl): The initial terms can have different node list.
677        // Duplicate the initial term for the first TERM_LIST_LEN + 2 terms.
678        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 current_term_seed(&self) -> &Vec<u8> {
741    //     self.target_term_seed(self.term_list.current_term)
742    // }
743
744    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
763/// Read-only functions use in `TransactionValidator`
764impl PosState {
765    /// Binds `sender` to a slot the submitter actually controls by
766    /// requiring `node_map[sender].public_key == auth_pk`. Returns the
767    /// `NodeData` so callers can reuse the borrow.
768    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        // Sender must match the addr derived from the payload's
793        // declared keys, so a registered submitter can't stuff another
794        // node's payload keys.
795        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        // Do not check `ELECTION_TERM_END_ROUND` because we are using the
820        // committed state in this simple validation.
821        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        // node_map (not active-committee): PivotDecision is gossiped
835        // per round and committee gating would drop newly-joined
836        // members at every term boundary.
837        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        // Registered-node gating; `verify_dispute` runs at execute time.
854        self.check_sender_owns_auth_key(
855            sender,
856            auth_pk,
857            DiscardedVMStatus::DISPUTE_SENDER_NOT_REGISTERED,
858        )
859        .err()
860    }
861}
862
863/// Read-only functions used in `execute_block`
864impl 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    /// Return `(validator_set, term_seed)`.
1036    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            // Retired nodes are not removed from term_list,
1056            // but we do not include them in the new committee.
1057            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    /// TODO(lpl): Return VDF seed for the term.
1080    /// Return `Some(target_term)` if `author` should send its election
1081    /// transaction.
1082    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
1167/// Write functions used apply changes (process events in PoS and PoW)
1168impl 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            // A workaround for too much staked CFX in Testnet.
1235            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    /// `get_new_committee` has been called before this to produce an
1248    /// EpochState. And `next_view` will not be called for blocks following
1249    /// a pending reconfiguration block.
1250    pub fn next_view(&mut self) -> Result<Option<EpochState>> {
1251        // Increase view after updating node status above to get a correct
1252        // `status_start_view`.
1253        self.current_view += 1;
1254
1255        diem_debug!("current view {}", self.current_view);
1256
1257        // Update the status for the all.
1258        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            // genesis
1274            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                // generate new epoch for new term.
1282                self.term_list.new_term(
1283                    new_term,
1284                    self.pivot_decision.block_hash.as_bytes().to_vec(),
1285                );
1286                // TODO(lpl): If we allow epoch changes within a term, this
1287                // should be updated.
1288                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    /// `None` for the pre-CIP-173 event, which carries no offence epoch.
1339    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                    // Both copies of a duplicated dispute are validated
1362                    // against the same parent state, so both reach here.
1363                    // Rejecting the second would invalidate the whole block an
1364                    // honest proposer built.
1365                    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// impl Default for PosState {
1409//     fn default() -> Self {
1410//         Self {
1411//             node_map: Default::default(),
1412//             current_view: 0,
1413//             term_list: TermList {
1414//                 current_term: 0,
1415//                 term_list: Default::default(),
1416//             },
1417//         }
1418//     }
1419// }
1420
1421#[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    /// Computed based on other fields.
1582    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    /// The node id to unlock.
1611    ///
1612    /// The management contract should unlock the corresponding account.
1613    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    /// The node id to dispute.
1630    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/// A separate event key rather than a field on `DisputeEvent`, so the
1644/// pre-activation encoding stays byte-identical for replay.
1645#[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
1661/// Decode either dispute event, `None` for any other key. Both versions are
1662/// recognised in one place so a third consumer cannot support one and silently
1663/// drop the other.
1664pub 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    // simple-validation doesn't verify the VRF proof, so any bytes work.
1718    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    /// Two whole terms in, so the transition view starts one.
1825    const TRANSITION: View = 2 * ROUND_PER_TERM;
1826    /// Epoch `n` is term `n - 1`, so this is the first epoch CIP-173 admits.
1827    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    /// Wider than CIP-156 as published, which lets a disputed voter "stake
1908    /// more votes to become active votes again"; the penalty is on the
1909    /// validator, so it matches force retirement instead.
1910    #[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    /// Locks already running at activation cannot be reconstructed, so those
1933    /// validators keep the old rule until they expire.
1934    #[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        // A duplicate inside an already-validated block must pass through as
1961        // a no-op, or it would invalidate the whole block.
1962        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        // Retire, so the stake leaves at a view well past the dispute lock.
1999        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        // Pins the hand-written mirror to the real field order; a round-trip
2075        // through the mirror alone would agree with itself however wrong.
2076        assert_eq!(
2077            bcs::to_bytes(&state).expect("encode"),
2078            [state.encode_persisted().expect("encode").as_slice(), &[0u8]]
2079                .concat()
2080        );
2081    }
2082
2083    /// A migration that dropped the watermark would surface here as evidence
2084    /// that can be replayed after the restart.
2085    #[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}