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