github.com/lbryio/lbcd@v0.22.119/blockchain/chainview.go (about)

     1  // Copyright (c) 2017 The btcsuite developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package blockchain
     6  
     7  import (
     8  	"sync"
     9  )
    10  
    11  // approxNodesPerWeek is an approximation of the number of new blocks there are
    12  // in a week on average.
    13  const approxNodesPerWeek = 6 * 24 * 7
    14  
    15  // log2FloorMasks defines the masks to use when quickly calculating
    16  // floor(log2(x)) in a constant log2(32) = 5 steps, where x is a uint32, using
    17  // shifts.  They are derived from (2^(2^x) - 1) * (2^(2^x)), for x in 4..0.
    18  var log2FloorMasks = []uint32{0xffff0000, 0xff00, 0xf0, 0xc, 0x2}
    19  
    20  // fastLog2Floor calculates and returns floor(log2(x)) in a constant 5 steps.
    21  func fastLog2Floor(n uint32) uint8 {
    22  	rv := uint8(0)
    23  	exponent := uint8(16)
    24  	for i := 0; i < 5; i++ {
    25  		if n&log2FloorMasks[i] != 0 {
    26  			rv += exponent
    27  			n >>= exponent
    28  		}
    29  		exponent >>= 1
    30  	}
    31  	return rv
    32  }
    33  
    34  // chainView provides a flat view of a specific branch of the block chain from
    35  // its tip back to the genesis block and provides various convenience functions
    36  // for comparing chains.
    37  //
    38  // For example, assume a block chain with a side chain as depicted below:
    39  //
    40  //	genesis -> 1 -> 2 -> 3 -> 4  -> 5 ->  6  -> 7  -> 8
    41  //	                      \-> 4a -> 5a -> 6a
    42  //
    43  // The chain view for the branch ending in 6a consists of:
    44  //
    45  //	genesis -> 1 -> 2 -> 3 -> 4a -> 5a -> 6a
    46  type chainView struct {
    47  	mtx   sync.Mutex
    48  	nodes []*blockNode
    49  }
    50  
    51  // newChainView returns a new chain view for the given tip block node.  Passing
    52  // nil as the tip will result in a chain view that is not initialized.  The tip
    53  // can be updated at any time via the setTip function.
    54  func newChainView(tip *blockNode) *chainView {
    55  	// The mutex is intentionally not held since this is a constructor.
    56  	var c chainView
    57  	c.setTip(tip)
    58  	return &c
    59  }
    60  
    61  // genesis returns the genesis block for the chain view.  This only differs from
    62  // the exported version in that it is up to the caller to ensure the lock is
    63  // held.
    64  //
    65  // This function MUST be called with the view mutex locked (for reads).
    66  func (c *chainView) genesis() *blockNode {
    67  	if len(c.nodes) == 0 {
    68  		return nil
    69  	}
    70  
    71  	return c.nodes[0]
    72  }
    73  
    74  // Genesis returns the genesis block for the chain view.
    75  //
    76  // This function is safe for concurrent access.
    77  func (c *chainView) Genesis() *blockNode {
    78  	c.mtx.Lock()
    79  	genesis := c.genesis()
    80  	c.mtx.Unlock()
    81  	return genesis
    82  }
    83  
    84  // tip returns the current tip block node for the chain view.  It will return
    85  // nil if there is no tip.  This only differs from the exported version in that
    86  // it is up to the caller to ensure the lock is held.
    87  //
    88  // This function MUST be called with the view mutex locked (for reads).
    89  func (c *chainView) tip() *blockNode {
    90  	if len(c.nodes) == 0 {
    91  		return nil
    92  	}
    93  
    94  	return c.nodes[len(c.nodes)-1]
    95  }
    96  
    97  // Tip returns the current tip block node for the chain view.  It will return
    98  // nil if there is no tip.
    99  //
   100  // This function is safe for concurrent access.
   101  func (c *chainView) Tip() *blockNode {
   102  	c.mtx.Lock()
   103  	tip := c.tip()
   104  	c.mtx.Unlock()
   105  	return tip
   106  }
   107  
   108  // setTip sets the chain view to use the provided block node as the current tip
   109  // and ensures the view is consistent by populating it with the nodes obtained
   110  // by walking backwards all the way to genesis block as necessary.  Further
   111  // calls will only perform the minimum work needed, so switching between chain
   112  // tips is efficient.  This only differs from the exported version in that it is
   113  // up to the caller to ensure the lock is held.
   114  //
   115  // This function MUST be called with the view mutex locked (for writes).
   116  func (c *chainView) setTip(node *blockNode) {
   117  	if node == nil {
   118  		// Keep the backing array around for potential future use.
   119  		c.nodes = c.nodes[:0]
   120  		return
   121  	}
   122  
   123  	// Create or resize the slice that will hold the block nodes to the
   124  	// provided tip height.  When creating the slice, it is created with
   125  	// some additional capacity for the underlying array as append would do
   126  	// in order to reduce overhead when extending the chain later.  As long
   127  	// as the underlying array already has enough capacity, simply expand or
   128  	// contract the slice accordingly.  The additional capacity is chosen
   129  	// such that the array should only have to be extended about once a
   130  	// week.
   131  	needed := node.height + 1
   132  	if int32(cap(c.nodes)) < needed {
   133  		nodes := make([]*blockNode, needed, needed+approxNodesPerWeek)
   134  		copy(nodes, c.nodes)
   135  		c.nodes = nodes
   136  	} else {
   137  		prevLen := int32(len(c.nodes))
   138  		c.nodes = c.nodes[0:needed]
   139  		for i := prevLen; i < needed; i++ {
   140  			c.nodes[i] = nil
   141  		}
   142  	}
   143  
   144  	for node != nil && c.nodes[node.height] != node {
   145  		c.nodes[node.height] = node
   146  		node = node.parent
   147  	}
   148  }
   149  
   150  // SetTip sets the chain view to use the provided block node as the current tip
   151  // and ensures the view is consistent by populating it with the nodes obtained
   152  // by walking backwards all the way to genesis block as necessary.  Further
   153  // calls will only perform the minimum work needed, so switching between chain
   154  // tips is efficient.
   155  //
   156  // This function is safe for concurrent access.
   157  func (c *chainView) SetTip(node *blockNode) {
   158  	c.mtx.Lock()
   159  	c.setTip(node)
   160  	c.mtx.Unlock()
   161  }
   162  
   163  // height returns the height of the tip of the chain view.  It will return -1 if
   164  // there is no tip (which only happens if the chain view has not been
   165  // initialized).  This only differs from the exported version in that it is up
   166  // to the caller to ensure the lock is held.
   167  //
   168  // This function MUST be called with the view mutex locked (for reads).
   169  func (c *chainView) height() int32 {
   170  	return int32(len(c.nodes) - 1)
   171  }
   172  
   173  // Height returns the height of the tip of the chain view.  It will return -1 if
   174  // there is no tip (which only happens if the chain view has not been
   175  // initialized).
   176  //
   177  // This function is safe for concurrent access.
   178  func (c *chainView) Height() int32 {
   179  	c.mtx.Lock()
   180  	height := c.height()
   181  	c.mtx.Unlock()
   182  	return height
   183  }
   184  
   185  // nodeByHeight returns the block node at the specified height.  Nil will be
   186  // returned if the height does not exist.  This only differs from the exported
   187  // version in that it is up to the caller to ensure the lock is held.
   188  //
   189  // This function MUST be called with the view mutex locked (for reads).
   190  func (c *chainView) nodeByHeight(height int32) *blockNode {
   191  	if height < 0 || height >= int32(len(c.nodes)) {
   192  		return nil
   193  	}
   194  
   195  	return c.nodes[height]
   196  }
   197  
   198  // NodeByHeight returns the block node at the specified height.  Nil will be
   199  // returned if the height does not exist.
   200  //
   201  // This function is safe for concurrent access.
   202  func (c *chainView) NodeByHeight(height int32) *blockNode {
   203  	c.mtx.Lock()
   204  	node := c.nodeByHeight(height)
   205  	c.mtx.Unlock()
   206  	return node
   207  }
   208  
   209  // Equals returns whether or not two chain views are the same.  Uninitialized
   210  // views (tip set to nil) are considered equal.
   211  //
   212  // This function is safe for concurrent access.
   213  func (c *chainView) Equals(other *chainView) bool {
   214  	c.mtx.Lock()
   215  	other.mtx.Lock()
   216  	equals := len(c.nodes) == len(other.nodes) && c.tip() == other.tip()
   217  	other.mtx.Unlock()
   218  	c.mtx.Unlock()
   219  	return equals
   220  }
   221  
   222  // contains returns whether or not the chain view contains the passed block
   223  // node.  This only differs from the exported version in that it is up to the
   224  // caller to ensure the lock is held.
   225  //
   226  // This function MUST be called with the view mutex locked (for reads).
   227  func (c *chainView) contains(node *blockNode) bool {
   228  	return c.nodeByHeight(node.height) == node
   229  }
   230  
   231  // Contains returns whether or not the chain view contains the passed block
   232  // node.
   233  //
   234  // This function is safe for concurrent access.
   235  func (c *chainView) Contains(node *blockNode) bool {
   236  	c.mtx.Lock()
   237  	contains := c.contains(node)
   238  	c.mtx.Unlock()
   239  	return contains
   240  }
   241  
   242  // next returns the successor to the provided node for the chain view.  It will
   243  // return nil if there is no successor or the provided node is not part of the
   244  // view.  This only differs from the exported version in that it is up to the
   245  // caller to ensure the lock is held.
   246  //
   247  // See the comment on the exported function for more details.
   248  //
   249  // This function MUST be called with the view mutex locked (for reads).
   250  func (c *chainView) next(node *blockNode) *blockNode {
   251  	if node == nil || !c.contains(node) {
   252  		return nil
   253  	}
   254  
   255  	return c.nodeByHeight(node.height + 1)
   256  }
   257  
   258  // Next returns the successor to the provided node for the chain view.  It will
   259  // return nil if there is no successfor or the provided node is not part of the
   260  // view.
   261  //
   262  // For example, assume a block chain with a side chain as depicted below:
   263  //
   264  //	genesis -> 1 -> 2 -> 3 -> 4  -> 5 ->  6  -> 7  -> 8
   265  //	                      \-> 4a -> 5a -> 6a
   266  //
   267  // Further, assume the view is for the longer chain depicted above.  That is to
   268  // say it consists of:
   269  //
   270  //	genesis -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8
   271  //
   272  // Invoking this function with block node 5 would return block node 6 while
   273  // invoking it with block node 5a would return nil since that node is not part
   274  // of the view.
   275  //
   276  // This function is safe for concurrent access.
   277  func (c *chainView) Next(node *blockNode) *blockNode {
   278  	c.mtx.Lock()
   279  	next := c.next(node)
   280  	c.mtx.Unlock()
   281  	return next
   282  }
   283  
   284  // findFork returns the final common block between the provided node and the
   285  // the chain view.  It will return nil if there is no common block.  This only
   286  // differs from the exported version in that it is up to the caller to ensure
   287  // the lock is held.
   288  //
   289  // See the exported FindFork comments for more details.
   290  //
   291  // This function MUST be called with the view mutex locked (for reads).
   292  func (c *chainView) findFork(node *blockNode) *blockNode {
   293  	// No fork point for node that doesn't exist.
   294  	if node == nil {
   295  		return nil
   296  	}
   297  
   298  	// When the height of the passed node is higher than the height of the
   299  	// tip of the current chain view, walk backwards through the nodes of
   300  	// the other chain until the heights match (or there or no more nodes in
   301  	// which case there is no common node between the two).
   302  	//
   303  	// NOTE: This isn't strictly necessary as the following section will
   304  	// find the node as well, however, it is more efficient to avoid the
   305  	// contains check since it is already known that the common node can't
   306  	// possibly be past the end of the current chain view.  It also allows
   307  	// this code to take advantage of any potential future optimizations to
   308  	// the Ancestor function such as using an O(log n) skip list.
   309  	chainHeight := c.height()
   310  	if node.height > chainHeight {
   311  		node = node.Ancestor(chainHeight)
   312  	}
   313  
   314  	// Walk the other chain backwards as long as the current one does not
   315  	// contain the node or there are no more nodes in which case there is no
   316  	// common node between the two.
   317  	for node != nil && !c.contains(node) {
   318  		node = node.parent
   319  	}
   320  
   321  	return node
   322  }
   323  
   324  // FindFork returns the final common block between the provided node and the
   325  // the chain view.  It will return nil if there is no common block.
   326  //
   327  // For example, assume a block chain with a side chain as depicted below:
   328  //
   329  //	genesis -> 1 -> 2 -> ... -> 5 -> 6  -> 7  -> 8
   330  //	                             \-> 6a -> 7a
   331  //
   332  // Further, assume the view is for the longer chain depicted above.  That is to
   333  // say it consists of:
   334  //
   335  //	genesis -> 1 -> 2 -> ... -> 5 -> 6 -> 7 -> 8.
   336  //
   337  // Invoking this function with block node 7a would return block node 5 while
   338  // invoking it with block node 7 would return itself since it is already part of
   339  // the branch formed by the view.
   340  //
   341  // This function is safe for concurrent access.
   342  func (c *chainView) FindFork(node *blockNode) *blockNode {
   343  	c.mtx.Lock()
   344  	fork := c.findFork(node)
   345  	c.mtx.Unlock()
   346  	return fork
   347  }
   348  
   349  // blockLocator returns a block locator for the passed block node.  The passed
   350  // node can be nil in which case the block locator for the current tip
   351  // associated with the view will be returned.  This only differs from the
   352  // exported version in that it is up to the caller to ensure the lock is held.
   353  //
   354  // See the exported BlockLocator function comments for more details.
   355  //
   356  // This function MUST be called with the view mutex locked (for reads).
   357  func (c *chainView) blockLocator(node *blockNode) BlockLocator {
   358  	// Use the current tip if requested.
   359  	if node == nil {
   360  		node = c.tip()
   361  	}
   362  	if node == nil {
   363  		return nil
   364  	}
   365  
   366  	// Calculate the max number of entries that will ultimately be in the
   367  	// block locator.  See the description of the algorithm for how these
   368  	// numbers are derived.
   369  	var maxEntries uint8
   370  	if node.height <= 12 {
   371  		maxEntries = uint8(node.height) + 1
   372  	} else {
   373  		// Requested hash itself + previous 10 entries + genesis block.
   374  		// Then floor(log2(height-10)) entries for the skip portion.
   375  		adjustedHeight := uint32(node.height) - 10
   376  		maxEntries = 12 + fastLog2Floor(adjustedHeight)
   377  	}
   378  	locator := make(BlockLocator, 0, maxEntries)
   379  
   380  	step := int32(1)
   381  	for node != nil {
   382  		locator = append(locator, &node.hash)
   383  
   384  		// Nothing more to add once the genesis block has been added.
   385  		if node.height == 0 {
   386  			break
   387  		}
   388  
   389  		// Calculate height of previous node to include ensuring the
   390  		// final node is the genesis block.
   391  		height := node.height - step
   392  		if height < 0 {
   393  			height = 0
   394  		}
   395  
   396  		// When the node is in the current chain view, all of its
   397  		// ancestors must be too, so use a much faster O(1) lookup in
   398  		// that case.  Otherwise, fall back to walking backwards through
   399  		// the nodes of the other chain to the correct ancestor.
   400  		if c.contains(node) {
   401  			node = c.nodes[height]
   402  		} else {
   403  			node = node.Ancestor(height)
   404  		}
   405  
   406  		// Once 11 entries have been included, start doubling the
   407  		// distance between included hashes.
   408  		if len(locator) > 10 {
   409  			step *= 2
   410  		}
   411  	}
   412  
   413  	return locator
   414  }
   415  
   416  // BlockLocator returns a block locator for the passed block node.  The passed
   417  // node can be nil in which case the block locator for the current tip
   418  // associated with the view will be returned.
   419  //
   420  // See the BlockLocator type for details on the algorithm used to create a block
   421  // locator.
   422  //
   423  // This function is safe for concurrent access.
   424  func (c *chainView) BlockLocator(node *blockNode) BlockLocator {
   425  	c.mtx.Lock()
   426  	locator := c.blockLocator(node)
   427  	c.mtx.Unlock()
   428  	return locator
   429  }