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  }