1use std::{
6 cmp::Ordering,
7 collections::{BTreeMap, BinaryHeap, HashMap, HashSet, VecDeque},
8 convert::TryFrom,
9 fmt::{Debug, Formatter},
10};
11
12use anyhow::{anyhow, bail, ensure, Result};
13#[cfg(any(test, feature = "fuzzing"))]
14use proptest_derive::Arbitrary;
15use serde::{Deserialize, Serialize};
16
17use cfx_types::H256;
18use diem_crypto::{
19 bls::deserialize_bls_public_key_unchecked, vrf_number_with_nonce,
20 HashValue, Signature, VRFProof,
21};
22use diem_logger::prelude::*;
23pub use incentives::*;
24use lock_status::NodeLockStatus;
25use move_core_types::vm_status::DiscardedVMStatus;
26use pos_state_config::{PosStateConfigTrait, POS_STATE_CONFIG};
27use pow_types::StakingEvent;
28
29use crate::{
30 account_address::{from_consensus_public_key, AccountAddress},
31 account_config,
32 block_info::{PivotBlockDecision, Round, View},
33 contract_event::ContractEvent,
34 epoch_state::EpochState,
35 event::EventKey,
36 transaction::{DisputePayload, ElectionPayload},
37 validator_config::{
38 ConsensusPublicKey, ConsensusVRFPublicKey, MultiConsensusPublicKey,
39 MultiConsensusSignature,
40 },
41 validator_verifier::{ValidatorConsensusInfo, ValidatorVerifier},
42};
43
44pub mod lock_status;
45pub mod pos_state_config;
46
47pub const TERM_LIST_LEN: usize = 6;
48pub const ROUND_PER_TERM: Round = 60;
49pub const IN_QUEUE_LOCKED_VIEWS: u64 = 10080;
50pub const OUT_QUEUE_LOCKED_VIEWS: u64 = 10080;
51pub const TERM_MAX_SIZE: usize = 10000;
54pub const TERM_ELECTED_SIZE: usize = 50;
55
56mod incentives {
57 use super::{TERM_ELECTED_SIZE, TERM_LIST_LEN, TERM_MAX_SIZE};
58 use crate::term_state::pos_state_config::{
59 PosStateConfigTrait, POS_STATE_CONFIG,
60 };
61
62 const BONUS_VOTE_MAX_SIZE: u64 = 100;
63
64 pub const MAX_TERM_POINTS: u64 = 6_000_000;
65
66 const ELECTION_PERCENTAGE: u64 = 20;
67 const COMMITTEE_PERCENTAGE: u64 = 75;
68 const LEADER_PERCENTAGE: u64 = 3;
69 const BONUS_VOTE_PERCENTAGE: u64 = 2;
70
71 pub const ELECTION_POINTS: u64 =
72 MAX_TERM_POINTS * ELECTION_PERCENTAGE / 100 / (TERM_MAX_SIZE as u64);
73 pub const COMMITTEE_POINTS: u64 = MAX_TERM_POINTS * COMMITTEE_PERCENTAGE
74 / 100
75 / (TERM_ELECTED_SIZE as u64)
76 / (TERM_LIST_LEN as u64);
77
78 pub fn leader_points(view: u64) -> u64 {
79 MAX_TERM_POINTS * LEADER_PERCENTAGE
80 / 100
81 / POS_STATE_CONFIG.round_per_term(view)
82 }
83
84 pub fn bonus_vote_points(view: u64) -> u64 {
85 MAX_TERM_POINTS * BONUS_VOTE_PERCENTAGE
86 / 100
87 / POS_STATE_CONFIG.round_per_term(view)
88 / BONUS_VOTE_MAX_SIZE
89 }
90}
91
92#[derive(Copy, Clone, Eq, PartialEq, Serialize, Deserialize, Debug)]
93#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
94pub enum NodeStatus {
95 Accepted,
96 Retired,
97 Unlocked,
98}
99
100#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
101#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
102pub struct NodeData {
103 #[serde(deserialize_with = "deserialize_bls_public_key_unchecked")]
105 public_key: ConsensusPublicKey,
106 vrf_public_key: Option<ConsensusVRFPublicKey>,
107 lock_status: NodeLockStatus,
108}
109
110impl NodeData {
111 pub fn lock_status(&self) -> &NodeLockStatus { &self.lock_status }
112}
113
114#[derive(
116 Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Ord, PartialOrd,
117)]
118#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
119pub struct ElectionNodeID {
120 node_id: NodeID,
121 nonce: u64,
122}
123
124impl ElectionNodeID {
125 pub fn new(node_id: NodeID, nonce: u64) -> Self {
126 ElectionNodeID { node_id, nonce }
127 }
128}
129
130#[derive(Clone, Default, Debug, Serialize, Deserialize)]
131#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
132pub struct ElectingHeap(
133 BinaryHeap<(HashValue, ElectionNodeID)>,
134 HashSet<AccountAddress>,
135);
136
137#[derive(Clone, Default, Debug, Eq, PartialEq, Serialize, Deserialize)]
138#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
139pub struct ElectedMap(BTreeMap<AccountAddress, u64>);
140
141pub type CandyMap = ElectedMap;
142
143#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
144#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
145pub enum NodeList {
146 Electing(ElectingHeap),
147 Elected(ElectedMap),
148}
149
150impl Default for NodeList {
151 fn default() -> Self { NodeList::Electing(Default::default()) }
152}
153
154impl NodeList {
155 fn len(&self) -> usize {
156 match self {
157 NodeList::Electing(heap) => heap.0.len(),
158 NodeList::Elected(map) => map.0.len(),
159 }
160 }
161
162 fn add_node(&mut self, vrf_output: HashValue, node_id: ElectionNodeID) {
163 if let NodeList::Electing(heap) = self {
164 heap.add_node(vrf_output, node_id);
165 } else {
166 panic!("The term is finalized");
167 }
168 }
169
170 #[must_use]
171 fn finalize_elect(&mut self) -> CandyMap {
172 if let NodeList::Electing(heap) = self {
173 let electing_heap = std::mem::take(heap);
174 let (elected_heap, candy_map) = electing_heap.finalize();
175 *self = NodeList::Elected(elected_heap);
176 return candy_map;
177 } else {
178 panic!("The term is finalized");
179 }
180 }
181
182 fn has_elected(&self, addr: &AccountAddress) -> bool {
183 if let NodeList::Electing(heap) = self {
184 heap.1.contains(addr)
185 } else {
186 panic!("The term is finalized");
187 }
188 }
189
190 fn serving_votes(&self, address: &AccountAddress) -> u64 {
191 if let NodeList::Elected(map) = self {
192 map.0.get(address).cloned().unwrap_or(0)
193 } else {
194 panic!("The term is not finalized");
195 }
196 }
197
198 fn committee(&self) -> &ElectedMap {
199 if let NodeList::Elected(map) = self {
200 map
201 } else {
202 panic!("The term is not finalized");
203 }
204 }
205}
206
207impl ElectedMap {
208 pub fn inner(&self) -> &BTreeMap<AccountAddress, u64> { &self.0 }
209}
210
211impl ElectingHeap {
212 pub fn read_top_electing(&self) -> BTreeMap<AccountAddress, u64> {
213 let mut top_electing: BTreeMap<AccountAddress, u64> = BTreeMap::new();
214 let mut clone = self.clone();
215 let mut count = 0usize;
216 while let Some((_, node_id)) = clone.0.pop() {
217 *top_electing.entry(node_id.node_id.addr).or_insert(0) += 1;
218 count += 1;
219 if count >= POS_STATE_CONFIG.term_elected_size() {
220 break;
221 }
222 }
223 top_electing
224 }
225
226 fn finalize(mut self) -> (ElectedMap, CandyMap) {
227 let mut elected_map = ElectedMap::default();
228 let mut count = 0usize;
229 while let Some((_, node_id)) = self.0.pop() {
230 *elected_map.0.entry(node_id.node_id.addr).or_insert(0) += 1;
231 count += 1;
232 if count >= POS_STATE_CONFIG.term_elected_size() {
233 break;
234 }
235 }
236 let mut candy_map = elected_map.clone();
237 for (_, node_id) in self.0.into_vec().drain(..) {
238 *candy_map.0.entry(node_id.node_id.addr).or_insert(0) += 1;
239 }
240 (elected_map, candy_map)
241 }
242
243 pub fn add_node(&mut self, hash: HashValue, node_id: ElectionNodeID) {
244 let is_not_full_set = self.0.len() < POS_STATE_CONFIG.term_max_size();
245 self.1.insert(node_id.node_id.addr.clone());
246 if self
247 .0
248 .peek()
249 .map_or(true, |(max_value, _)| is_not_full_set || hash < *max_value)
250 {
251 self.0.push((hash, node_id.clone()));
252 if self.0.len() > POS_STATE_CONFIG.term_max_size() {
253 self.0.pop();
254 }
255 }
256 }
257}
258
259#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
260#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
261pub struct TermData {
262 start_view: Round,
263 seed: Vec<u8>,
264 node_list: NodeList,
266}
267
268impl TermData {
269 pub fn start_view(&self) -> u64 { self.start_view }
270
271 pub fn get_term(&self) -> u64 {
272 POS_STATE_CONFIG.get_term_view(self.start_view).0
273 }
274
275 pub fn node_list(&self) -> &NodeList { &self.node_list }
276}
277
278impl PartialEq for ElectingHeap {
279 fn eq(&self, other: &Self) -> bool {
280 if self.1 != other.1 {
281 return false;
282 }
283 let mut iter_self = self.0.iter();
284 let mut iter_other = other.0.iter();
285 while let Some(node) = iter_self.next() {
286 match iter_other.next() {
287 None => return false,
288 Some(other_node) => {
289 if node != other_node {
290 return false;
291 }
292 }
293 }
294 }
295 iter_other.next().is_none()
296 }
297}
298
299impl Eq for ElectingHeap {}
300
301impl TermData {
302 fn next_term(&self, node_list: NodeList, seed: Vec<u8>) -> Self {
303 TermData {
304 start_view: self.start_view
305 + POS_STATE_CONFIG.round_per_term(self.start_view),
306 seed,
307 node_list,
308 }
309 }
310}
311
312#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
313#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
314pub struct TermList {
315 current_term: u64,
319 term_list: Vec<TermData>,
323 candy_rewards: CandyMap,
324 electing_index: usize,
325}
326
327impl TermList {
328 fn start_term(&self) -> u64 {
329 self.current_term.saturating_sub(TERM_LIST_LEN as u64 - 1)
330 }
331
332 fn committee_for_term(&self, term: u64) -> &[TermData] {
333 let first_term = term.saturating_sub(TERM_LIST_LEN as u64 - 1) as usize;
334 let last_term = first_term + TERM_LIST_LEN - 1;
335 if first_term < self.start_term() as usize
336 || last_term >= self.electing_term_number() as usize
337 {
338 panic!(
339 "Can not get committee for term {}, current term {}",
340 term, self.current_term
341 );
342 }
343 let start_offset = first_term - self.start_term() as usize;
344 let end_offset = last_term - self.start_term() as usize;
345 &self.term_list[start_offset..=end_offset]
346 }
347
348 fn get_term_by_number(&self, term_number: u64) -> Option<&TermData> {
349 let start_term = self.start_term();
350 if term_number < start_term {
351 return None;
352 }
353 self.term_list.get((term_number - start_term) as usize)
354 }
355
356 fn electing_term_number(&self) -> u64 {
357 self.start_term() + self.electing_index as u64
358 }
359
360 fn electing_term_mut(&mut self) -> &mut TermData {
361 &mut self.term_list[self.electing_index]
362 }
363
364 fn electing_term(&self) -> &TermData {
365 &self.term_list[self.electing_index]
366 }
367
368 pub fn term_list(&self) -> &Vec<TermData> { &self.term_list }
369}
370
371impl TermList {
372 pub fn new_node_elected(
375 &mut self, event: &ElectionEvent, voting_power: u64,
376 ) -> anyhow::Result<()> {
377 if event.start_term != self.electing_term_number() {
378 bail!("term is not open for election, opening term {}, election term {}", self.electing_term_number(),event.start_term);
379 }
380 let term = self.electing_term_mut();
381
382 if term.node_list.has_elected(&event.node_id.addr) {
383 diem_warn!(
384 "The author {} has participated election for term {}",
385 event.node_id.addr,
386 event.start_term
387 );
388 return Ok(());
389 }
390
391 for nonce in 0..voting_power {
392 let priority = vrf_number_with_nonce(&event.vrf_output, nonce);
395 term.node_list.add_node(
396 priority,
397 ElectionNodeID::new(event.node_id.clone(), nonce),
398 );
399 }
400 Ok(())
401 }
402
403 pub fn new_term(&mut self, new_term: u64, new_seed: Vec<u8>) {
404 diem_debug!(
405 "new_term={}, start_view:{:?}",
406 new_term,
407 self.term_list
408 .iter()
409 .map(|t| (t.start_view, t.node_list.len()))
410 .collect::<Vec<_>>()
411 );
412 self.current_term = new_term;
413 if new_term < TERM_LIST_LEN as u64 {
414 return;
416 }
417 debug_assert!(
419 Some(self.term_list[TERM_LIST_LEN].start_view)
420 == POS_STATE_CONFIG.get_starting_view_for_term(new_term)
421 );
422 self.term_list.remove(0);
423 let new_term = self
424 .term_list
425 .last()
426 .unwrap()
427 .next_term(Default::default(), new_seed);
428 self.term_list.push(new_term);
429 self.electing_index -= 1;
430 assert_eq!(self.electing_index, 6);
431 }
432
433 pub fn finalize_election(&mut self) {
434 diem_debug!(
435 "Finalize election of term {}",
436 self.electing_term_number()
437 );
438 let finalize_term = self.electing_term_mut();
439 let candy_map = finalize_term.node_list.finalize_elect();
440 self.candy_rewards = candy_map;
441 self.electing_index += 1;
442 assert_eq!(self.electing_index, 7);
443 }
444
445 fn serving_votes(
446 &self, target_term_offset: usize, author: &AccountAddress,
447 ) -> u64 {
448 assert!(
449 target_term_offset >= TERM_LIST_LEN - 1
450 && target_term_offset < TERM_LIST_LEN + 2
451 );
452 let start_term_offset = target_term_offset - (TERM_LIST_LEN - 1);
455
456 let mut serving_votes = Vec::with_capacity(TERM_LIST_LEN);
462 for i in start_term_offset..target_term_offset {
463 let term = &self.term_list[i];
464 serving_votes.push(term.node_list.serving_votes(author));
465 }
466 return serving_votes.iter().sum();
467 }
468}
469
470#[derive(Clone, Serialize, Eq, PartialEq, Deserialize)]
471#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
472pub struct PosState {
473 node_map: HashMap<AccountAddress, NodeData>,
476 current_view: Round,
481 epoch_state: EpochState,
483 term_list: TermList,
484
485 retiring_nodes: VecDeque<AccountAddress>,
488 pivot_decision: PivotBlockDecision,
490
491 node_map_hint: HashMap<View, HashSet<AccountAddress>>,
492 unlock_event_hint: HashSet<AccountAddress>,
493
494 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 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 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 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 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
632impl PosState {
634 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 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 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 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 self.check_sender_owns_auth_key(
724 sender,
725 auth_pk,
726 DiscardedVMStatus::DISPUTE_SENDER_NOT_REGISTERED,
727 )
728 .err()
729 }
730}
731
732impl 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 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 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 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
998impl 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 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 pub fn next_view(&mut self) -> Result<Option<EpochState>> {
1075 self.current_view += 1;
1078
1079 diem_debug!("current view {}", self.current_view);
1080
1081 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 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 self.term_list.new_term(
1107 new_term,
1108 self.pivot_decision.block_hash.as_bytes().to_vec(),
1109 );
1110 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#[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 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 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 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 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}