github.com/decred/dcrd/blockchain@v1.2.1/chainview.go (about)

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