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}