avalanche_consensus/snowman/snowball/binary/node.rs
1use std::cell::Cell;
2
3use crate::snowman::snowball::{self, binary};
4use avalanche_types::ids::{bag::Bag, bits, Id};
5
6/// Represents a binary node with either no child, or a single child.
7/// It handles the voting on a range of identical, virtuous, snowball instances.
8/// ref. "avalanchego/snow/consensus/snowball/tree.go#binaryNode"
9#[derive(Clone, Debug)]
10pub struct Node {
11 /// Parameters inherited from the tree.
12 pub parameters: crate::Parameters,
13
14 /// Runs snowball logic.
15 pub snowball: binary::Snowball,
16
17 /// The choices preferred at every branch in their sub-tree.
18 pub preferences: Cell<[Id; 2]>,
19
20 /// The index in the Id of the choice that this node is deciding on.
21 /// Will be in the range [0, 256)
22 pub bit: Cell<i64>,
23
24 /// Used as an optimization to prevent needless tree traversals.
25 /// It is the continuation of shouldReset in the Tree struct.
26 pub should_reset: Cell<[bool; 2]>,
27
28 /// The child is the potentially none, node that votes on the next bits
29 /// in the decision.
30 pub child0: Option<Box<snowball::Node>>,
31 pub child1: Option<Box<snowball::Node>>,
32}
33
34impl Node {
35 pub fn preference(&self) -> Id {
36 let pref = self.snowball.preference() as usize;
37 let preferences = self.preferences.take();
38 let preference = preferences[pref];
39 self.preferences.set(preferences);
40 preference
41 }
42
43 pub fn decided_prefix(&self) -> i64 {
44 self.bit.get()
45 }
46
47 pub fn finalized(&self) -> bool {
48 self.snowball.finalized()
49 }
50
51 pub fn add(&mut self, id: &Id) -> snowball::Node {
52 let bit = id.bit(self.bit.get() as usize);
53 let child = match bit {
54 bits::Bit::Zero => self.child0.clone(),
55 bits::Bit::One => self.child1.clone(),
56 };
57
58 // If child is nil, then we are running an instance on the last bit. Finding
59 // two hashes that are equal up to the last bit would be really cool though.
60 // Regardless, the case is handled
61 if let Some(boxed_child) = child.clone() {
62 // +1 is used because we already explicitly check the p.bit bit
63 if bits::equal_subset(
64 self.bit.get() as usize + 1,
65 boxed_child.decided_prefix() as usize,
66 &self.preferences.get()[bit.as_usize()],
67 id,
68 ) {
69 let boxed_child = child.unwrap();
70 let added_child = match *boxed_child {
71 snowball::Node::Unary(mut unary_node) => unary_node.add(id),
72 snowball::Node::Binary(mut binary_node) => binary_node.add(id),
73 };
74 match bit {
75 bits::Bit::Zero => self.child0 = Some(Box::new(added_child)),
76 bits::Bit::One => self.child1 = Some(Box::new(added_child)),
77 }
78 }
79 }
80
81 // If child is nil, then the id has already been added to the tree, so
82 // nothing should be done
83 // If the decided prefix isn't matched, then a previous decision has made
84 // the id that is being added to have already been rejected
85 snowball::Node::Binary(self.clone())
86 }
87
88 /// Returns the new node and whether the vote was successful.
89 /// ref. "avalanchego/snow/consensus/tree.go" "binaryNode.RecordPoll"
90 pub fn record_poll(&mut self, votes: Bag, reset: bool) -> (snowball::Node, bool) {
91 // The list of votes we are passed is split into votes for bit 0
92 // and votes for bit 1
93 let split_votes = votes.split(self.bit.get() as usize);
94
95 let mut bit = 0;
96 // We only care about which bit is set if a successful poll can happen
97 if split_votes[1].len() >= self.parameters.alpha as u32 {
98 bit = 1;
99 }
100
101 let mut updated_should_reset = self.should_reset.take();
102 if reset {
103 self.snowball.record_unsuccessful_poll();
104 updated_should_reset[bit] = true;
105 // 1-bit isn't set here because it is set below anyway
106 }
107 // they didn't get the threshold of votes
108 updated_should_reset[1 - bit] = true;
109 self.should_reset.set(updated_should_reset);
110
111 if split_votes[bit].len() < self.parameters.alpha as u32 {
112 // pruned votes < alpha; didn't get enough votes
113 self.snowball.record_unsuccessful_poll();
114
115 // The winning child didn't get enough votes either
116 updated_should_reset[bit] = true;
117 self.should_reset.set(updated_should_reset);
118
119 return (snowball::Node::Binary(self.clone()), false);
120 }
121
122 // this bit got alpha votes, it was a successful poll
123 self.snowball.record_successful_poll(bit as i64);
124
125 match bit {
126 0 => {
127 if let Some(child) = self.child0.clone() {
128 // The votes are filtered to ensure that they are votes
129 // that should count for the child
130 match *child {
131 snowball::Node::Unary(mut unary_node) => {
132 let filtered_votes = split_votes[bit].filter(
133 self.bit.get() as usize + 1,
134 unary_node.decided_prefix() as usize,
135 &self.preferences.get()[bit],
136 );
137
138 let (new_child, _) = unary_node
139 .record_poll(filtered_votes, self.should_reset.get()[bit]);
140 if self.snowball.finalized() {
141 // If we are decided here, that means we must have decided
142 // due to this poll. Therefore, we must have decided on bit.
143 return (new_child, true);
144 }
145
146 let mut updated_preferences = self.preferences.take();
147 let new_child_preference = match &new_child {
148 snowball::Node::Unary(n) => n.preference(),
149 snowball::Node::Binary(n) => n.preference(),
150 };
151 updated_preferences[bit] = new_child_preference;
152 self.preferences.set(updated_preferences);
153
154 self.child0 = Some(Box::new(new_child));
155 }
156 snowball::Node::Binary(mut binary_node) => {
157 let filtered_votes = split_votes[bit].filter(
158 self.bit.get() as usize + 1,
159 binary_node.decided_prefix() as usize,
160 &self.preferences.get()[bit],
161 );
162
163 let (new_child, _) = binary_node
164 .record_poll(filtered_votes, self.should_reset.get()[bit]);
165 if self.snowball.finalized() {
166 // If we are decided here, that means we must have decided
167 // due to this poll. Therefore, we must have decided on bit.
168 return (new_child, true);
169 }
170
171 let mut updated_preferences = self.preferences.take();
172 let new_child_preference = match &new_child {
173 snowball::Node::Unary(n) => n.preference(),
174 snowball::Node::Binary(n) => n.preference(),
175 };
176 updated_preferences[bit] = new_child_preference;
177 self.preferences.set(updated_preferences);
178
179 self.child0 = Some(Box::new(new_child));
180 }
181 };
182 }
183 }
184 1 => {
185 if let Some(child) = self.child1.clone() {
186 // The votes are filtered to ensure that they are votes
187 // that should count for the child
188 match *child {
189 snowball::Node::Unary(mut unary_node) => {
190 let filtered_votes = split_votes[bit].filter(
191 self.bit.get() as usize + 1,
192 unary_node.decided_prefix() as usize,
193 &self.preferences.get()[bit],
194 );
195
196 let (new_child, _) = unary_node
197 .record_poll(filtered_votes, self.should_reset.get()[bit]);
198 if self.snowball.finalized() {
199 // If we are decided here, that means we must have decided
200 // due to this poll. Therefore, we must have decided on bit.
201 return (new_child, true);
202 }
203
204 let mut updated_preferences = self.preferences.take();
205 let new_child_preference = match &new_child {
206 snowball::Node::Unary(n) => n.preference(),
207 snowball::Node::Binary(n) => n.preference(),
208 };
209 updated_preferences[bit] = new_child_preference;
210 self.preferences.set(updated_preferences);
211
212 self.child1 = Some(Box::new(new_child));
213 }
214 snowball::Node::Binary(mut binary_node) => {
215 let filtered_votes = split_votes[bit].filter(
216 self.bit.get() as usize + 1,
217 binary_node.decided_prefix() as usize,
218 &self.preferences.get()[bit],
219 );
220
221 let (new_child, _) = binary_node
222 .record_poll(filtered_votes, self.should_reset.get()[bit]);
223 if self.snowball.finalized() {
224 // If we are decided here, that means we must have decided
225 // due to this poll. Therefore, we must have decided on bit.
226 return (new_child, true);
227 }
228
229 let mut updated_preferences = self.preferences.take();
230 let new_child_preference = match &new_child {
231 snowball::Node::Unary(n) => n.preference(),
232 snowball::Node::Binary(n) => n.preference(),
233 };
234 updated_preferences[bit] = new_child_preference;
235 self.preferences.set(updated_preferences);
236
237 self.child1 = Some(Box::new(new_child));
238 }
239 };
240 }
241 }
242 _ => panic!("unexpected preference bit {}", bit),
243 }
244
245 // We passed the reset down
246 updated_should_reset[bit] = false;
247 self.should_reset.set(updated_should_reset);
248
249 (snowball::Node::Binary(self.clone()), true)
250 }
251}
252
253/// ref. <https://doc.rust-lang.org/std/string/trait.ToString.html>
254/// ref. <https://doc.rust-lang.org/std/fmt/trait.Display.html>
255/// Use "Self.to_string()" to directly invoke this.
256impl std::fmt::Display for Node {
257 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
258 write!(f, "{} Bit = {}", self.snowball, self.decided_prefix())
259 }
260}