1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use crate::{
components::consensus::{
highway_core::{
highway::SignedWireVote,
state::{self, Panorama, State},
validators::ValidatorIndex,
},
traits::Context,
},
types::{TimeDiff, Timestamp},
};
/// A vote sent to or received from the network.
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct Vote<C: Context> {
/// The list of latest messages and faults observed by the creator of this message.
pub(crate) panorama: Panorama<C>,
/// The number of earlier messages by the same creator.
pub(crate) seq_number: u64,
/// The validator who created and sent this vote.
pub(crate) creator: ValidatorIndex,
/// The block this is a vote for. Either it or its parent must be the fork choice.
pub(crate) block: C::Hash,
/// A skip list index of the creator's swimlane, i.e. the previous vote by the same creator.
///
/// For every `p = 1 << i` that divides `seq_number`, this contains an `i`-th entry pointing to
/// the older vote with `seq_number - p`.
pub(crate) skip_idx: Vec<C::Hash>,
/// This vote's timestamp, in milliseconds since the epoch.
pub(crate) timestamp: Timestamp,
/// Original signature of the `SignedWireVote`.
pub(crate) signature: C::Signature,
/// The round exponent of the current round, that this message belongs to.
///
/// The current round consists of all timestamps that agree with this one in all but the last
/// `round_exp` bits.
pub(crate) round_exp: u8,
}
impl<C: Context> Vote<C> {
/// Creates a new `Vote` from the `WireVote`, and returns the value if it contained any.
/// Values must be stored as a block, with the same hash.
pub(crate) fn new(
swvote: SignedWireVote<C>,
fork_choice: Option<&C::Hash>,
state: &State<C>,
) -> (Vote<C>, Option<C::ConsensusValue>) {
let SignedWireVote {
wire_vote: wvote,
signature,
} = swvote;
let block = if wvote.value.is_some() {
wvote.hash() // A vote with a new block votes for itself.
} else {
// If the vote didn't introduce a new block, it votes for the fork choice itself.
// `Highway::add_vote` checks that the panorama is not empty.
fork_choice
.cloned()
.expect("nonempty panorama has nonempty fork choice")
};
let mut skip_idx = Vec::new();
if let Some(hash) = wvote.panorama.get(wvote.creator).correct() {
skip_idx.push(*hash);
for i in 0..wvote.seq_number.trailing_zeros() as usize {
let old_vote = state.vote(&skip_idx[i]);
skip_idx.push(old_vote.skip_idx[i]);
}
}
let vote = Vote {
panorama: wvote.panorama,
seq_number: wvote.seq_number,
creator: wvote.creator,
block,
skip_idx,
timestamp: wvote.timestamp,
signature,
round_exp: wvote.round_exp,
};
(vote, wvote.value)
}
/// Returns the creator's previous message.
pub(crate) fn previous(&self) -> Option<&C::Hash> {
self.skip_idx.first()
}
/// Returns the time at which the round containing this vote began.
pub(crate) fn round_id(&self) -> Timestamp {
state::round_id(self.timestamp, self.round_exp)
}
/// Returns the length of the round containing this vote.
pub(crate) fn round_len(&self) -> TimeDiff {
state::round_len(self.round_exp)
}
/// Returns whether `vote` cites a new vote from `vidx` in the last panorama.
/// i.e. whether previous vote from creator of `vhash` cites different vote by `vidx`.
///
/// NOTE: Returns `false` if `vidx` is faulty or hasn't produced any votes according to the
/// creator of `vhash`.
pub(crate) fn new_hash_obs(&self, state: &State<C>, vidx: ValidatorIndex) -> bool {
let latest_obs = self.panorama[vidx].correct();
let penultimate_obs = self
.previous()
.and_then(|v| state.vote(v).panorama[vidx].correct());
match (latest_obs, penultimate_obs) {
(Some(latest_hash), Some(penultimate_hash)) => latest_hash != penultimate_hash,
_ => false,
}
}
}