2016-04-25 11:12:09 +02:00
|
|
|
// Copyright (c) 2013-2017 The btcsuite developers
|
2013-08-28 18:45:21 +02:00
|
|
|
// Use of this source code is governed by an ISC
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
2015-01-30 21:54:30 +01:00
|
|
|
package blockchain
|
2013-08-28 18:45:21 +02:00
|
|
|
|
|
|
|
import (
|
2016-08-08 21:04:33 +02:00
|
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
2013-08-28 18:45:21 +02:00
|
|
|
)
|
|
|
|
|
2017-08-17 08:34:53 +02:00
|
|
|
// log2FloorMasks defines the masks to use when quickly calculating
|
|
|
|
// floor(log2(x)) in a constant log2(32) = 5 steps, where x is a uint32, using
|
|
|
|
// shifts. They are derived from (2^(2^x) - 1) * (2^(2^x)), for x in 4..0.
|
|
|
|
var log2FloorMasks = []uint32{0xffff0000, 0xff00, 0xf0, 0xc, 0x2}
|
|
|
|
|
|
|
|
// fastLog2Floor calculates and returns floor(log2(x)) in a constant 5 steps.
|
|
|
|
func fastLog2Floor(n uint32) uint8 {
|
|
|
|
rv := uint8(0)
|
|
|
|
exponent := uint8(16)
|
|
|
|
for i := 0; i < 5; i++ {
|
|
|
|
if n&log2FloorMasks[i] != 0 {
|
|
|
|
rv += exponent
|
|
|
|
n >>= exponent
|
|
|
|
}
|
|
|
|
exponent >>= 1
|
|
|
|
}
|
|
|
|
return rv
|
|
|
|
}
|
|
|
|
|
2013-08-28 18:45:21 +02:00
|
|
|
// BlockLocator is used to help locate a specific block. The algorithm for
|
|
|
|
// building the block locator is to add the hashes in reverse order until
|
|
|
|
// the genesis block is reached. In order to keep the list of locator hashes
|
2017-08-17 08:34:53 +02:00
|
|
|
// to a reasonable number of entries, first the most recent 12 block hashes are
|
|
|
|
// added, then the step is doubled each loop iteration to exponentially decrease
|
|
|
|
// the number of hashes as a function of the distance from the block being
|
|
|
|
// located.
|
2013-10-13 09:06:18 +02:00
|
|
|
//
|
|
|
|
// For example, assume you have a block chain with a side chain as depicted
|
|
|
|
// below:
|
|
|
|
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
|
|
|
// \-> 16a -> 17a
|
|
|
|
//
|
|
|
|
// The block locator for block 17a would be the hashes of blocks:
|
2017-08-17 08:34:53 +02:00
|
|
|
// [17a 16a 15 14 13 12 11 10 9 8 7 6 4 genesis]
|
2016-08-08 21:04:33 +02:00
|
|
|
type BlockLocator []*chainhash.Hash
|
2013-08-28 18:45:21 +02:00
|
|
|
|
2017-08-17 08:34:53 +02:00
|
|
|
// blockLocator returns a block locator for the passed block node.
|
2013-08-28 18:45:21 +02:00
|
|
|
//
|
2017-08-17 08:34:53 +02:00
|
|
|
// See BlockLocator for details on the algorithm used to create a block locator.
|
2015-08-26 06:03:18 +02:00
|
|
|
//
|
2016-04-25 11:12:09 +02:00
|
|
|
// This function MUST be called with the block index lock held (for reads).
|
2017-08-17 08:34:53 +02:00
|
|
|
func blockLocator(node *blockNode) BlockLocator {
|
|
|
|
if node == nil {
|
|
|
|
return nil
|
2013-08-28 18:45:21 +02:00
|
|
|
}
|
|
|
|
|
2017-08-17 08:34:53 +02:00
|
|
|
// Calculate the max number of entries that will ultimately be in the
|
|
|
|
// block locator. See the description of the algorithm for how these
|
|
|
|
// numbers are derived.
|
|
|
|
var maxEntries uint8
|
|
|
|
if node.height <= 12 {
|
|
|
|
maxEntries = uint8(node.height) + 1
|
2013-08-28 18:45:21 +02:00
|
|
|
} else {
|
2017-08-17 08:34:53 +02:00
|
|
|
// Requested hash itself + previous 10 entries + genesis block.
|
|
|
|
// Then floor(log2(height-10)) entries for the skip portion.
|
|
|
|
adjustedHeight := uint32(node.height) - 10
|
|
|
|
maxEntries = 12 + fastLog2Floor(adjustedHeight)
|
2013-08-28 18:45:21 +02:00
|
|
|
}
|
2017-08-17 08:34:53 +02:00
|
|
|
locator := make(BlockLocator, 0, maxEntries)
|
2013-08-28 18:45:21 +02:00
|
|
|
|
2017-08-17 08:34:53 +02:00
|
|
|
step := int32(1)
|
|
|
|
for node != nil {
|
|
|
|
locator = append(locator, &node.hash)
|
2013-08-28 18:45:21 +02:00
|
|
|
|
2017-08-17 08:34:53 +02:00
|
|
|
// Nothing more to add once the genesis block has been added.
|
|
|
|
if node.height == 0 {
|
|
|
|
break
|
|
|
|
}
|
2015-08-26 06:03:18 +02:00
|
|
|
|
2017-08-17 08:34:53 +02:00
|
|
|
// Calculate height of previous node to include ensuring the
|
|
|
|
// final node is the genesis block.
|
|
|
|
height := node.height - step
|
|
|
|
if height < 0 {
|
|
|
|
height = 0
|
2013-08-28 18:45:21 +02:00
|
|
|
}
|
|
|
|
|
2017-08-17 08:34:53 +02:00
|
|
|
// Walk backwards through the nodes to the correct ancestor.
|
|
|
|
node = node.Ancestor(height)
|
|
|
|
|
|
|
|
// Once 11 entries have been included, start doubling the
|
|
|
|
// distance between included hashes.
|
|
|
|
if len(locator) > 10 {
|
|
|
|
step *= 2
|
|
|
|
}
|
|
|
|
}
|
2013-08-28 18:45:21 +02:00
|
|
|
|
|
|
|
return locator
|
|
|
|
}
|
|
|
|
|
2015-08-26 06:03:18 +02:00
|
|
|
// BlockLocatorFromHash returns a block locator for the passed block hash.
|
|
|
|
// See BlockLocator for details on the algorithm used to create a block locator.
|
|
|
|
//
|
|
|
|
// In addition to the general algorithm referenced above, there are a couple of
|
|
|
|
// special cases which are handled:
|
|
|
|
//
|
|
|
|
// - If the genesis hash is passed, there are no previous hashes to add and
|
|
|
|
// therefore the block locator will only consist of the genesis hash
|
2017-08-17 08:34:53 +02:00
|
|
|
// - If the passed hash is not currently known, the block locator will be for
|
|
|
|
// the latest known tip of the main (best) chain.
|
2015-08-26 06:03:18 +02:00
|
|
|
//
|
|
|
|
// This function is safe for concurrent access.
|
2016-08-08 21:04:33 +02:00
|
|
|
func (b *BlockChain) BlockLocatorFromHash(hash *chainhash.Hash) BlockLocator {
|
2015-08-26 06:03:18 +02:00
|
|
|
b.chainLock.RLock()
|
2016-04-25 11:12:09 +02:00
|
|
|
b.index.RLock()
|
2017-08-17 08:34:53 +02:00
|
|
|
node, exists := b.index.index[*hash]
|
|
|
|
if !exists {
|
|
|
|
node = b.bestNode
|
|
|
|
}
|
|
|
|
locator := blockLocator(node)
|
2016-04-25 11:12:09 +02:00
|
|
|
b.index.RUnlock()
|
2015-08-26 06:03:18 +02:00
|
|
|
b.chainLock.RUnlock()
|
|
|
|
return locator
|
|
|
|
}
|
|
|
|
|
2013-08-28 18:45:21 +02:00
|
|
|
// LatestBlockLocator returns a block locator for the latest known tip of the
|
|
|
|
// main (best) chain.
|
2015-08-26 06:03:18 +02:00
|
|
|
//
|
|
|
|
// This function is safe for concurrent access.
|
2013-08-28 18:45:21 +02:00
|
|
|
func (b *BlockChain) LatestBlockLocator() (BlockLocator, error) {
|
2015-08-26 06:03:18 +02:00
|
|
|
b.chainLock.RLock()
|
2016-04-25 11:12:09 +02:00
|
|
|
b.index.RLock()
|
2017-08-17 08:34:53 +02:00
|
|
|
locator := blockLocator(b.bestNode)
|
2016-04-25 11:12:09 +02:00
|
|
|
b.index.RUnlock()
|
2015-08-26 06:03:18 +02:00
|
|
|
b.chainLock.RUnlock()
|
|
|
|
return locator, nil
|
2013-08-28 18:45:21 +02:00
|
|
|
}
|