github.com/MetalBlockchain/metalgo@v1.11.9/snow/consensus/snowball/tree.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package snowball 5 6 import ( 7 "fmt" 8 "strings" 9 10 "github.com/MetalBlockchain/metalgo/ids" 11 "github.com/MetalBlockchain/metalgo/utils/bag" 12 ) 13 14 var ( 15 _ Consensus = (*Tree)(nil) 16 _ node = (*unaryNode)(nil) 17 _ node = (*binaryNode)(nil) 18 ) 19 20 func NewTree(factory Factory, params Parameters, choice ids.ID) Consensus { 21 t := &Tree{ 22 params: params, 23 factory: factory, 24 } 25 t.node = &unaryNode{ 26 tree: t, 27 preference: choice, 28 commonPrefix: ids.NumBits, // The initial state has no conflicts 29 snow: factory.NewUnary(params), 30 } 31 32 return t 33 } 34 35 // Tree implements the Consensus interface by using a modified patricia tree. 36 type Tree struct { 37 // node is the root that represents the first snow instance in the tree, 38 // and contains references to all the other snow instances in the tree. 39 node 40 41 // params contains all the configurations of a snow instance 42 params Parameters 43 44 // shouldReset is used as an optimization to prevent needless tree 45 // traversals. If a snow instance does not get an alpha majority, that 46 // instance needs to reset by calling RecordUnsuccessfulPoll. Because the 47 // tree splits votes based on the branch, when an instance doesn't get an 48 // alpha majority none of the children of this instance can get an alpha 49 // majority. To avoid calling RecordUnsuccessfulPoll on the full sub-tree of 50 // a node that didn't get an alpha majority, shouldReset is used to indicate 51 // that any later traversal into this sub-tree should call 52 // RecordUnsuccessfulPoll before performing any other action. 53 shouldReset bool 54 55 // factory is used to produce new snow instances as needed 56 factory Factory 57 } 58 59 func (t *Tree) Add(choice ids.ID) { 60 prefix := t.node.DecidedPrefix() 61 // Make sure that we haven't already decided against this new id 62 if ids.EqualSubset(0, prefix, t.Preference(), choice) { 63 t.node = t.node.Add(choice) 64 } 65 } 66 67 func (t *Tree) RecordPoll(votes bag.Bag[ids.ID]) bool { 68 // Get the assumed decided prefix of the root node. 69 decidedPrefix := t.node.DecidedPrefix() 70 71 // If any of the bits differ from the preference in this prefix, the vote is 72 // for a rejected operation. So, we filter out these invalid votes. 73 preference := t.Preference() 74 filteredVotes := votes.Filter(func(id ids.ID) bool { 75 return ids.EqualSubset(0, decidedPrefix, preference, id) 76 }) 77 78 // Now that the votes have been restricted to valid votes, pass them into 79 // the first snow instance 80 var successful bool 81 t.node, successful = t.node.RecordPoll(filteredVotes, t.shouldReset) 82 83 // Because we just passed the reset into the snow instance, we should no 84 // longer reset. 85 t.shouldReset = false 86 return successful 87 } 88 89 func (t *Tree) RecordUnsuccessfulPoll() { 90 t.shouldReset = true 91 } 92 93 func (t *Tree) String() string { 94 sb := strings.Builder{} 95 96 prefixes := []string{""} 97 nodes := []node{t.node} 98 99 for len(prefixes) > 0 { 100 newSize := len(prefixes) - 1 101 102 prefix := prefixes[newSize] 103 prefixes = prefixes[:newSize] 104 105 node := nodes[newSize] 106 nodes = nodes[:newSize] 107 108 s, newNodes := node.Printable() 109 110 sb.WriteString(prefix) 111 sb.WriteString(s) 112 sb.WriteString("\n") 113 114 newPrefix := prefix + " " 115 for range newNodes { 116 prefixes = append(prefixes, newPrefix) 117 } 118 nodes = append(nodes, newNodes...) 119 } 120 121 return strings.TrimSuffix(sb.String(), "\n") 122 } 123 124 type node interface { 125 // Preference returns the preferred choice of this sub-tree 126 Preference() ids.ID 127 // Return the number of assumed decided bits of this node 128 DecidedPrefix() int 129 // Adds a new choice to vote on 130 // Returns the new node 131 Add(newChoice ids.ID) node 132 // Apply the votes, reset the model if needed 133 // Returns the new node and whether the vote was successful 134 RecordPoll(votes bag.Bag[ids.ID], shouldReset bool) (newChild node, successful bool) 135 // Returns true if consensus has been reached on this node 136 Finalized() bool 137 138 Printable() (string, []node) 139 } 140 141 // unary is a node with either no children, or a single child. It handles the 142 // voting on a range of identical, unary, snow instances. 143 type unaryNode struct { 144 // tree references the tree that contains this node 145 tree *Tree 146 147 // preference is the choice that is preferred at every branch in this 148 // sub-tree 149 preference ids.ID 150 151 // decidedPrefix is the last bit in the prefix that is assumed to be decided 152 decidedPrefix int // Will be in the range [0, 255) 153 154 // commonPrefix is the last bit in the prefix that this node transitively 155 // references 156 commonPrefix int // Will be in the range (decidedPrefix, 256) 157 158 // snow wraps the unary decision logic 159 snow Unary 160 161 // shouldReset is used as an optimization to prevent needless tree 162 // traversals. It is the continuation of shouldReset in the Tree struct. 163 shouldReset bool 164 165 // child is the, possibly nil, node that votes on the next bits in the 166 // decision 167 child node 168 } 169 170 func (u *unaryNode) Preference() ids.ID { 171 return u.preference 172 } 173 174 func (u *unaryNode) DecidedPrefix() int { 175 return u.decidedPrefix 176 } 177 178 //nolint:gci,gofmt,gofumpt // this comment is formatted as intended 179 // 180 // This is by far the most complicated function in this algorithm. 181 // The intuition is that this instance represents a series of consecutive unary 182 // snowball instances, and this function's purpose is convert one of these unary 183 // snowball instances into a binary snowball instance. 184 // There are 5 possible cases. 185 // 186 // 1. None of these instances should be split, we should attempt to split a 187 // child 188 // 189 // For example, attempting to insert the value "00001" in this node: 190 // 191 // +-------------------+ <-- This node will not be split 192 // | | 193 // | 0 0 0 | 194 // | | 195 // +-------------------+ <-- Pass the add to the child 196 // ^ 197 // | 198 // 199 // Results in: 200 // 201 // +-------------------+ 202 // | | 203 // | 0 0 0 | 204 // | | 205 // +-------------------+ <-- With the modified child 206 // ^ 207 // | 208 // 209 // 2. This instance represents a series of only one unary instance and it must 210 // be split. 211 // 212 // This will return a binary choice, with one child the same as my child, 213 // and another (possibly nil child) representing a new chain to the end of 214 // the hash 215 // 216 // For example, attempting to insert the value "1" in this tree: 217 // 218 // +-------------------+ 219 // | | 220 // | 0 | 221 // | | 222 // +-------------------+ 223 // 224 // Results in: 225 // 226 // +-------------------+ 227 // | | | 228 // | 0 | 1 | 229 // | | | 230 // +-------------------+ 231 // 232 // 3. This instance must be split on the first bit 233 // 234 // This will return a binary choice, with one child equal to this instance 235 // with decidedPrefix increased by one, and another representing a new 236 // chain to the end of the hash 237 // 238 // For example, attempting to insert the value "10" in this tree: 239 // 240 // +-------------------+ 241 // | | 242 // | 0 0 | 243 // | | 244 // +-------------------+ 245 // 246 // Results in: 247 // 248 // +-------------------+ 249 // | | | 250 // | 0 | 1 | 251 // | | | 252 // +-------------------+ 253 // ^ ^ 254 // / \ 255 // +-------------------+ +-------------------+ 256 // | | | | 257 // | 0 | | 0 | 258 // | | | | 259 // +-------------------+ +-------------------+ 260 // 261 // 4. This instance must be split on the last bit 262 // 263 // This will modify this unary choice. The commonPrefix is decreased by 264 // one. The child is set to a binary instance that has a child equal to 265 // the current child and another child equal to a new unary instance to 266 // the end of the hash 267 // 268 // For example, attempting to insert the value "01" in this tree: 269 // 270 // +-------------------+ 271 // | | 272 // | 0 0 | 273 // | | 274 // +-------------------+ 275 // 276 // Results in: 277 // 278 // +-------------------+ 279 // | | 280 // | 0 | 281 // | | 282 // +-------------------+ 283 // ^ 284 // | 285 // +-------------------+ 286 // | | | 287 // | 0 | 1 | 288 // | | | 289 // +-------------------+ 290 // 291 // 5. This instance must be split on an interior bit 292 // 293 // This will modify this unary choice. The commonPrefix is set to the 294 // interior bit. The child is set to a binary instance that has a child 295 // equal to this unary choice with the decidedPrefix equal to the interior 296 // bit and another child equal to a new unary instance to the end of the 297 // hash 298 // 299 // For example, attempting to insert the value "010" in this tree: 300 // 301 // +-------------------+ 302 // | | 303 // | 0 0 0 | 304 // | | 305 // +-------------------+ 306 // 307 // Results in: 308 // 309 // +-------------------+ 310 // | | 311 // | 0 | 312 // | | 313 // +-------------------+ 314 // ^ 315 // | 316 // +-------------------+ 317 // | | | 318 // | 0 | 1 | 319 // | | | 320 // +-------------------+ 321 // ^ ^ 322 // / \ 323 // +-------------------+ +-------------------+ 324 // | | | | 325 // | 0 | | 0 | 326 // | | | | 327 // +-------------------+ +-------------------+ 328 func (u *unaryNode) Add(newChoice ids.ID) node { 329 if u.Finalized() { 330 return u // Only happens if the tree is finalized, or it's a leaf node 331 } 332 333 index, found := ids.FirstDifferenceSubset(u.decidedPrefix, u.commonPrefix, u.preference, newChoice) 334 if !found { 335 // If the first difference doesn't exist, then this node shouldn't be 336 // split 337 if u.child != nil { 338 // Because this node will finalize before any children could 339 // finalize, it must be that the newChoice will match my child's 340 // prefix 341 u.child = u.child.Add(newChoice) 342 } 343 // if u.child is nil, then we are attempting to add the same choice into 344 // the tree, which should be a noop 345 return u 346 } 347 348 // The difference was found, so this node must be split 349 bit := u.preference.Bit(uint(index)) // The currently preferred bit 350 b := &binaryNode{ 351 tree: u.tree, 352 bit: index, 353 snow: u.snow.Extend(bit), 354 shouldReset: [2]bool{u.shouldReset, u.shouldReset}, 355 } 356 b.preferences[bit] = u.preference 357 b.preferences[1-bit] = newChoice 358 359 newChildSnow := u.tree.factory.NewUnary(u.tree.params) 360 newChild := &unaryNode{ 361 tree: u.tree, 362 preference: newChoice, 363 decidedPrefix: index + 1, // The new child assumes this branch has decided in it's favor 364 commonPrefix: ids.NumBits, // The new child has no conflicts under this branch 365 snow: newChildSnow, 366 } 367 368 switch { 369 case u.decidedPrefix == u.commonPrefix-1: 370 // This node was only voting over one bit. (Case 2. from above) 371 b.children[bit] = u.child 372 if u.child != nil { 373 b.children[1-bit] = newChild 374 } 375 return b 376 case index == u.decidedPrefix: 377 // This node was split on the first bit. (Case 3. from above) 378 u.decidedPrefix++ 379 b.children[bit] = u 380 b.children[1-bit] = newChild 381 return b 382 case index == u.commonPrefix-1: 383 // This node was split on the last bit. (Case 4. from above) 384 u.commonPrefix-- 385 b.children[bit] = u.child 386 if u.child != nil { 387 b.children[1-bit] = newChild 388 } 389 u.child = b 390 return u 391 default: 392 // This node was split on an interior bit. (Case 5. from above) 393 originalDecidedPrefix := u.decidedPrefix 394 u.decidedPrefix = index + 1 395 b.children[bit] = u 396 b.children[1-bit] = newChild 397 return &unaryNode{ 398 tree: u.tree, 399 preference: u.preference, 400 decidedPrefix: originalDecidedPrefix, 401 commonPrefix: index, 402 snow: u.snow.Clone(), 403 child: b, 404 } 405 } 406 } 407 408 func (u *unaryNode) RecordPoll(votes bag.Bag[ids.ID], reset bool) (node, bool) { 409 // We are guaranteed that the votes are of IDs that have previously been 410 // added. This ensures that the provided votes all have the same bits in the 411 // range [u.decidedPrefix, u.commonPrefix) as in u.preference. 412 413 // If my parent didn't get enough votes previously, then neither did I 414 if reset { 415 u.snow.RecordUnsuccessfulPoll() 416 u.shouldReset = true // Make sure my child is also reset correctly 417 } 418 419 numVotes := votes.Len() 420 if numVotes < u.tree.params.AlphaPreference { 421 u.snow.RecordUnsuccessfulPoll() 422 u.shouldReset = true 423 return u, false 424 } 425 426 u.snow.RecordPoll(numVotes) 427 428 if u.child != nil { 429 // We are guaranteed that u.commonPrefix will equal 430 // u.child.DecidedPrefix(). Otherwise, there must have been a 431 // decision under this node, which isn't possible because 432 // beta1 <= beta2. That means that filtering the votes between 433 // u.commonPrefix and u.child.DecidedPrefix() would always result in 434 // the same set being returned. 435 436 newChild, _ := u.child.RecordPoll(votes, u.shouldReset) 437 if u.Finalized() { 438 // If I'm now decided, return my child 439 return newChild, true 440 } 441 442 // The child's preference may have changed 443 u.preference = u.child.Preference() 444 } 445 // Now that I have passed my votes to my child, I don't need to reset 446 // them 447 u.shouldReset = false 448 return u, true 449 } 450 451 func (u *unaryNode) Finalized() bool { 452 return u.snow.Finalized() 453 } 454 455 func (u *unaryNode) Printable() (string, []node) { 456 s := fmt.Sprintf("%s Bits = [%d, %d)", 457 u.snow, u.decidedPrefix, u.commonPrefix) 458 if u.child == nil { 459 return s, nil 460 } 461 return s, []node{u.child} 462 } 463 464 // binaryNode is a node with either no children, or two children. It handles the 465 // voting of a single, binary, snow instance. 466 type binaryNode struct { 467 // tree references the tree that contains this node 468 tree *Tree 469 470 // preferences are the choices that are preferred at every branch in their 471 // sub-tree 472 preferences [2]ids.ID 473 474 // bit is the index in the id of the choice this node is deciding on 475 bit int // Will be in the range [0, 256) 476 477 // snow wraps the binary decision logic 478 snow Binary 479 480 // shouldReset is used as an optimization to prevent needless tree 481 // traversals. It is the continuation of shouldReset in the Tree struct. 482 shouldReset [2]bool 483 484 // children are the, possibly nil, nodes that vote on the next bits in the 485 // decision 486 children [2]node 487 } 488 489 func (b *binaryNode) Preference() ids.ID { 490 return b.preferences[b.snow.Preference()] 491 } 492 493 func (b *binaryNode) DecidedPrefix() int { 494 return b.bit 495 } 496 497 func (b *binaryNode) Add(id ids.ID) node { 498 bit := id.Bit(uint(b.bit)) 499 child := b.children[bit] 500 // If child is nil, then we are running an instance on the last bit. Finding 501 // two hashes that are equal up to the last bit would be really cool though. 502 // Regardless, the case is handled 503 if child != nil { 504 b.children[bit] = child.Add(id) 505 } 506 // If child is nil, then the id has already been added to the tree, so 507 // nothing should be done 508 // If the decided prefix isn't matched, then a previous decision has made 509 // the id that is being added to have already been rejected 510 return b 511 } 512 513 func (b *binaryNode) RecordPoll(votes bag.Bag[ids.ID], reset bool) (node, bool) { 514 // The list of votes we are passed is split into votes for bit 0 and votes 515 // for bit 1 516 splitVotes := votes.Split(func(id ids.ID) bool { 517 return id.Bit(uint(b.bit)) == 1 518 }) 519 520 bit := 0 521 // We only care about which bit is set if a successful poll can happen 522 if splitVotes[1].Len() >= b.tree.params.AlphaPreference { 523 bit = 1 524 } 525 526 if reset { 527 b.snow.RecordUnsuccessfulPoll() 528 b.shouldReset[bit] = true 529 // 1-bit isn't set here because it is set below anyway 530 } 531 b.shouldReset[1-bit] = true // They didn't get the threshold of votes 532 533 prunedVotes := splitVotes[bit] 534 numVotes := prunedVotes.Len() 535 if numVotes < b.tree.params.AlphaPreference { 536 b.snow.RecordUnsuccessfulPoll() 537 // The winning child didn't get enough votes either 538 b.shouldReset[bit] = true 539 return b, false 540 } 541 542 b.snow.RecordPoll(numVotes, bit) 543 544 if child := b.children[bit]; child != nil { 545 newChild, _ := child.RecordPoll(prunedVotes, b.shouldReset[bit]) 546 if b.snow.Finalized() { 547 // If we are decided here, that means we must have decided due 548 // to this poll. Therefore, we must have decided on bit. 549 return newChild, true 550 } 551 b.preferences[bit] = newChild.Preference() 552 } 553 b.shouldReset[bit] = false // We passed the reset down 554 return b, true 555 } 556 557 func (b *binaryNode) Finalized() bool { 558 return b.snow.Finalized() 559 } 560 561 func (b *binaryNode) Printable() (string, []node) { 562 s := fmt.Sprintf("%s Bit = %d", b.snow, b.bit) 563 if b.children[0] == nil { 564 return s, nil 565 } 566 return s, []node{b.children[1], b.children[0]} 567 }