blockchain: Refactor to use new chain view.
- Remove inMainChain from block nodes since that can now be efficiently determined by using the chain view - Track the best chain via a chain view instead of a single block node - Use the tip of the best chain view everywhere bestNode was used - Update chain view tip instead of updating best node - Change reorg logic to use more efficient chain view fork finding logic - Change block locator code over to use more efficient chain view logic - Remove now unused block-index-based block locator code - Move BlockLocator definition to chain.go - Move BlockLocatorFromHash and LatestBlockLocator to chain.go - Update both to use more efficient chain view logic - Rework IsCheckpointCandidate to use block index and chain view - Optimize MainChainHasBlock to use chain view instead of hitting db - Move to chain.go since it no longer involves database I/O - Removed error return since it can no longer fail - Optimize BlockHeightByHash to use chain view instead of hitting db - Move to chain.go since it no longer involves database I/O - Removed error return since it can no longer fail - Optimize BlockHashByHeight to use chain view instead of hitting db - Move to chain.go since it no longer involves database I/O - Removed error return since it can no longer fail - Optimize HeightRange to use chain view instead of hitting db - Move to chain.go since it no longer involves database I/O - Optimize BlockByHeight to use chain view for main chain check - Optimize BlockByHash to use chain view for main chain check
This commit is contained in:
parent
08955805d5
commit
20910511e9
13 changed files with 295 additions and 446 deletions
|
@ -40,11 +40,6 @@ type blockNode struct {
|
|||
// height is the position in the block chain.
|
||||
height int32
|
||||
|
||||
// inMainChain denotes whether the block node is currently on the
|
||||
// the main chain or not. This is used to help find the common
|
||||
// ancestor when switching chains.
|
||||
inMainChain bool
|
||||
|
||||
// Some fields from block headers to aid in best chain selection and
|
||||
// reconstructing headers from memory. These must be treated as
|
||||
// immutable and are intentionally ordered to avoid padding on 64-bit
|
||||
|
|
|
@ -1,136 +0,0 @@
|
|||
// Copyright (c) 2013-2017 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
//
|
||||
// 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:
|
||||
// [17a 16a 15 14 13 12 11 10 9 8 7 6 4 genesis]
|
||||
type BlockLocator []*chainhash.Hash
|
||||
|
||||
// blockLocator returns a block locator for the passed block node.
|
||||
//
|
||||
// See BlockLocator for details on the algorithm used to create a block locator.
|
||||
//
|
||||
// This function MUST be called with the block index lock held (for reads).
|
||||
func blockLocator(node *blockNode) BlockLocator {
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
} else {
|
||||
// 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)
|
||||
}
|
||||
locator := make(BlockLocator, 0, maxEntries)
|
||||
|
||||
step := int32(1)
|
||||
for node != nil {
|
||||
locator = append(locator, &node.hash)
|
||||
|
||||
// Nothing more to add once the genesis block has been added.
|
||||
if node.height == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// Calculate height of previous node to include ensuring the
|
||||
// final node is the genesis block.
|
||||
height := node.height - step
|
||||
if height < 0 {
|
||||
height = 0
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
return locator
|
||||
}
|
||||
|
||||
// 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
|
||||
// - If the passed hash is not currently known, the block locator will be for
|
||||
// the latest known tip of the main (best) chain.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) BlockLocatorFromHash(hash *chainhash.Hash) BlockLocator {
|
||||
b.chainLock.RLock()
|
||||
b.index.RLock()
|
||||
node, exists := b.index.index[*hash]
|
||||
if !exists {
|
||||
node = b.bestNode
|
||||
}
|
||||
locator := blockLocator(node)
|
||||
b.index.RUnlock()
|
||||
b.chainLock.RUnlock()
|
||||
return locator
|
||||
}
|
||||
|
||||
// LatestBlockLocator returns a block locator for the latest known tip of the
|
||||
// main (best) chain.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) LatestBlockLocator() (BlockLocator, error) {
|
||||
b.chainLock.RLock()
|
||||
b.index.RLock()
|
||||
locator := blockLocator(b.bestNode)
|
||||
b.index.RUnlock()
|
||||
b.chainLock.RUnlock()
|
||||
return locator, nil
|
||||
}
|
|
@ -24,6 +24,22 @@ const (
|
|||
maxOrphanBlocks = 100
|
||||
)
|
||||
|
||||
// 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
|
||||
// to a reasonable number of entries, first the most recent previous 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.
|
||||
//
|
||||
// For example, assume 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:
|
||||
// [17a 16a 15 14 13 12 11 10 9 8 7 6 4 genesis]
|
||||
type BlockLocator []*chainhash.Hash
|
||||
|
||||
// orphanBlock represents a block that we don't yet have the parent for. It
|
||||
// is a normal block plus an expiration time to prevent caching the orphan
|
||||
// forever.
|
||||
|
@ -105,10 +121,17 @@ type BlockChain struct {
|
|||
// fields in this struct below this point.
|
||||
chainLock sync.RWMutex
|
||||
|
||||
// These fields are related to the memory block index. The best node
|
||||
// is protected by the chain lock and the index has its own locks.
|
||||
bestNode *blockNode
|
||||
index *blockIndex
|
||||
// These fields are related to the memory block index. They both have
|
||||
// their own locks, however they are often also protected by the chain
|
||||
// lock to help prevent logic races when blocks are being processed.
|
||||
//
|
||||
// index houses the entire block index in memory. The block index is
|
||||
// a tree-shaped structure.
|
||||
//
|
||||
// bestChain tracks the current active chain by making use of an
|
||||
// efficient chain view into the block index.
|
||||
index *blockIndex
|
||||
bestChain *chainView
|
||||
|
||||
// These fields are related to handling of orphan blocks. They are
|
||||
// protected by a combination of the chain lock and the orphan lock.
|
||||
|
@ -336,7 +359,7 @@ func (b *BlockChain) CalcSequenceLock(tx *btcutil.Tx, utxoView *UtxoViewpoint, m
|
|||
b.chainLock.Lock()
|
||||
defer b.chainLock.Unlock()
|
||||
|
||||
return b.calcSequenceLock(b.bestNode, tx, utxoView, mempool)
|
||||
return b.calcSequenceLock(b.bestChain.Tip(), tx, utxoView, mempool)
|
||||
}
|
||||
|
||||
// calcSequenceLock computes the relative lock-times for the passed
|
||||
|
@ -493,21 +516,15 @@ func (b *BlockChain) getReorganizeNodes(node *blockNode) (*list.List, *list.List
|
|||
// to attach to the main tree. Push them onto the list in reverse order
|
||||
// so they are attached in the appropriate order when iterating the list
|
||||
// later.
|
||||
ancestor := node
|
||||
for ; ancestor.parent != nil; ancestor = ancestor.parent {
|
||||
if ancestor.inMainChain {
|
||||
break
|
||||
}
|
||||
attachNodes.PushFront(ancestor)
|
||||
forkNode := b.bestChain.FindFork(node)
|
||||
for n := node; n != nil && n != forkNode; n = n.parent {
|
||||
attachNodes.PushFront(n)
|
||||
}
|
||||
|
||||
// Start from the end of the main chain and work backwards until the
|
||||
// common ancestor adding each block to the list of nodes to detach from
|
||||
// the main chain.
|
||||
for n := b.bestNode; n != nil && n.parent != nil; n = n.parent {
|
||||
if n.hash.IsEqual(&ancestor.hash) {
|
||||
break
|
||||
}
|
||||
for n := b.bestChain.Tip(); n != nil && n != forkNode; n = n.parent {
|
||||
detachNodes.PushBack(n)
|
||||
}
|
||||
|
||||
|
@ -542,7 +559,7 @@ func dbMaybeStoreBlock(dbTx database.Tx, block *btcutil.Block) error {
|
|||
func (b *BlockChain) connectBlock(node *blockNode, block *btcutil.Block, view *UtxoViewpoint, stxos []spentTxOut) error {
|
||||
// Make sure it's extending the end of the best chain.
|
||||
prevHash := &block.MsgBlock().Header.PrevBlock
|
||||
if !prevHash.IsEqual(&b.bestNode.hash) {
|
||||
if !prevHash.IsEqual(&b.bestChain.Tip().hash) {
|
||||
return AssertError("connectBlock must be called with a block " +
|
||||
"that extends the main chain")
|
||||
}
|
||||
|
@ -630,12 +647,9 @@ func (b *BlockChain) connectBlock(node *blockNode, block *btcutil.Block, view *U
|
|||
// now that the modifications have been committed to the database.
|
||||
view.commit()
|
||||
|
||||
// Add the new node to the memory main chain indices for faster lookups.
|
||||
node.inMainChain = true
|
||||
b.index.AddNode(node)
|
||||
|
||||
// This node is now the end of the best chain.
|
||||
b.bestNode = node
|
||||
b.index.AddNode(node)
|
||||
b.bestChain.SetTip(node)
|
||||
|
||||
// Update the state for the best block. Notice how this replaces the
|
||||
// entire struct instead of updating the existing one. This effectively
|
||||
|
@ -662,7 +676,7 @@ func (b *BlockChain) connectBlock(node *blockNode, block *btcutil.Block, view *U
|
|||
// This function MUST be called with the chain state lock held (for writes).
|
||||
func (b *BlockChain) disconnectBlock(node *blockNode, block *btcutil.Block, view *UtxoViewpoint) error {
|
||||
// Make sure the node being disconnected is the end of the best chain.
|
||||
if !node.hash.IsEqual(&b.bestNode.hash) {
|
||||
if !node.hash.IsEqual(&b.bestChain.Tip().hash) {
|
||||
return AssertError("disconnectBlock must be called with the " +
|
||||
"block at the end of the main chain")
|
||||
}
|
||||
|
@ -672,7 +686,7 @@ func (b *BlockChain) disconnectBlock(node *blockNode, block *btcutil.Block, view
|
|||
var prevBlock *btcutil.Block
|
||||
err := b.db.View(func(dbTx database.Tx) error {
|
||||
var err error
|
||||
prevBlock, err = dbFetchBlockByHash(dbTx, &prevNode.hash)
|
||||
prevBlock, err = dbFetchBlockByNode(dbTx, prevNode)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -740,11 +754,8 @@ func (b *BlockChain) disconnectBlock(node *blockNode, block *btcutil.Block, view
|
|||
// now that the modifications have been committed to the database.
|
||||
view.commit()
|
||||
|
||||
// Mark block as being in a side chain.
|
||||
node.inMainChain = false
|
||||
|
||||
// This node's parent is now the end of the best chain.
|
||||
b.bestNode = node.parent
|
||||
b.bestChain.SetTip(node.parent)
|
||||
|
||||
// Update the state for the best block. Notice how this replaces the
|
||||
// entire struct instead of updating the existing one. This effectively
|
||||
|
@ -803,13 +814,13 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List, flags
|
|||
// database and using that information to unspend all of the spent txos
|
||||
// and remove the utxos created by the blocks.
|
||||
view := NewUtxoViewpoint()
|
||||
view.SetBestHash(&b.bestNode.hash)
|
||||
view.SetBestHash(&b.bestChain.Tip().hash)
|
||||
for e := detachNodes.Front(); e != nil; e = e.Next() {
|
||||
n := e.Value.(*blockNode)
|
||||
var block *btcutil.Block
|
||||
err := b.db.View(func(dbTx database.Tx) error {
|
||||
var err error
|
||||
block, err = dbFetchBlockByHash(dbTx, &n.hash)
|
||||
block, err = dbFetchBlockByNode(dbTx, n)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -860,20 +871,9 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List, flags
|
|||
n := e.Value.(*blockNode)
|
||||
var block *btcutil.Block
|
||||
err := b.db.View(func(dbTx database.Tx) error {
|
||||
// NOTE: This block is not in the main chain, so the
|
||||
// block has to be loaded directly from the database
|
||||
// instead of using the dbFetchBlockByHash function.
|
||||
blockBytes, err := dbTx.FetchBlock(&n.hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
block, err = btcutil.NewBlockFromBytes(blockBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
block.SetHeight(n.height)
|
||||
return nil
|
||||
var err error
|
||||
block, err = dbFetchBlockByNode(dbTx, n)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -904,7 +904,7 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List, flags
|
|||
// view to be valid from the viewpoint of each block being connected or
|
||||
// disconnected.
|
||||
view = NewUtxoViewpoint()
|
||||
view.SetBestHash(&b.bestNode.hash)
|
||||
view.SetBestHash(&b.bestChain.Tip().hash)
|
||||
|
||||
// Disconnect blocks from the main chain.
|
||||
for i, e := 0, detachNodes.Front(); e != nil; i, e = i+1, e.Next() {
|
||||
|
@ -997,7 +997,7 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla
|
|||
// We are extending the main (best) chain with a new block. This is the
|
||||
// most common case.
|
||||
parentHash := &block.MsgBlock().Header.PrevBlock
|
||||
if parentHash.IsEqual(&b.bestNode.hash) {
|
||||
if parentHash.IsEqual(&b.bestChain.Tip().hash) {
|
||||
// Perform several checks to verify the block can be connected
|
||||
// to the main chain without violating any rules and without
|
||||
// actually connecting the block.
|
||||
|
@ -1051,9 +1051,6 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla
|
|||
b.index.index[node.hash] = node
|
||||
b.index.Unlock()
|
||||
|
||||
// Mark node as in a side chain.
|
||||
node.inMainChain = false
|
||||
|
||||
// Disconnect it from the parent node when the function returns when
|
||||
// running in dry run mode.
|
||||
if dryRun {
|
||||
|
@ -1066,21 +1063,14 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla
|
|||
|
||||
// We're extending (or creating) a side chain, but the cumulative
|
||||
// work for this new side chain is not enough to make it the new chain.
|
||||
if node.workSum.Cmp(b.bestNode.workSum) <= 0 {
|
||||
if node.workSum.Cmp(b.bestChain.Tip().workSum) <= 0 {
|
||||
// Skip Logging info when the dry run flag is set.
|
||||
if dryRun {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Find the fork point.
|
||||
fork := node
|
||||
for ; fork.parent != nil; fork = fork.parent {
|
||||
if fork.inMainChain {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Log information about how the block is forking the chain.
|
||||
fork := b.bestChain.FindFork(node)
|
||||
if fork.hash.IsEqual(parentHash) {
|
||||
log.Infof("FORK: Block %v forks the chain at height %d"+
|
||||
"/block %v, but does not cause a reorganize",
|
||||
|
@ -1127,7 +1117,7 @@ func (b *BlockChain) isCurrent() bool {
|
|||
// Not current if the latest main (best) chain height is before the
|
||||
// latest known good checkpoint (when checkpoints are enabled).
|
||||
checkpoint := b.LatestCheckpoint()
|
||||
if checkpoint != nil && b.bestNode.height < checkpoint.Height {
|
||||
if checkpoint != nil && b.bestChain.Tip().height < checkpoint.Height {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -1137,7 +1127,7 @@ func (b *BlockChain) isCurrent() bool {
|
|||
// The chain appears to be current if none of the checks reported
|
||||
// otherwise.
|
||||
minus24Hours := b.timeSource.AdjustedTime().Add(-24 * time.Hour).Unix()
|
||||
return b.bestNode.timestamp >= minus24Hours
|
||||
return b.bestChain.Tip().timestamp >= minus24Hours
|
||||
}
|
||||
|
||||
// IsCurrent returns whether or not the chain believes it is current. Several
|
||||
|
@ -1187,6 +1177,119 @@ func (b *BlockChain) FetchHeader(hash *chainhash.Hash) (wire.BlockHeader, error)
|
|||
return *header, nil
|
||||
}
|
||||
|
||||
// MainChainHasBlock returns whether or not the block with the given hash is in
|
||||
// the main chain.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) MainChainHasBlock(hash *chainhash.Hash) bool {
|
||||
node := b.index.LookupNode(hash)
|
||||
return node != nil && b.bestChain.Contains(node)
|
||||
}
|
||||
|
||||
// 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, this function will
|
||||
// return the block locator for the latest known tip of the main (best) chain if
|
||||
// the passed hash is not currently known.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) BlockLocatorFromHash(hash *chainhash.Hash) BlockLocator {
|
||||
b.chainLock.RLock()
|
||||
node := b.index.LookupNode(hash)
|
||||
locator := b.bestChain.blockLocator(node)
|
||||
b.chainLock.RUnlock()
|
||||
return locator
|
||||
}
|
||||
|
||||
// LatestBlockLocator returns a block locator for the latest known tip of the
|
||||
// main (best) chain.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) LatestBlockLocator() (BlockLocator, error) {
|
||||
b.chainLock.RLock()
|
||||
locator := b.bestChain.BlockLocator(nil)
|
||||
b.chainLock.RUnlock()
|
||||
return locator, nil
|
||||
}
|
||||
|
||||
// BlockHeightByHash returns the height of the block with the given hash in the
|
||||
// main chain.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) BlockHeightByHash(hash *chainhash.Hash) (int32, error) {
|
||||
node := b.index.LookupNode(hash)
|
||||
if node == nil || !b.bestChain.Contains(node) {
|
||||
str := fmt.Sprintf("block %s is not in the main chain", hash)
|
||||
return 0, errNotInMainChain(str)
|
||||
}
|
||||
|
||||
return node.height, nil
|
||||
}
|
||||
|
||||
// BlockHashByHeight returns the hash of the block at the given height in the
|
||||
// main chain.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) BlockHashByHeight(blockHeight int32) (*chainhash.Hash, error) {
|
||||
node := b.bestChain.NodeByHeight(blockHeight)
|
||||
if node == nil {
|
||||
str := fmt.Sprintf("no block at height %d exists", blockHeight)
|
||||
return nil, errNotInMainChain(str)
|
||||
|
||||
}
|
||||
|
||||
return &node.hash, nil
|
||||
}
|
||||
|
||||
// HeightRange returns a range of block hashes for the given start and end
|
||||
// heights. It is inclusive of the start height and exclusive of the end
|
||||
// height. The end height will be limited to the current main chain height.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) HeightRange(startHeight, endHeight int32) ([]chainhash.Hash, error) {
|
||||
// Ensure requested heights are sane.
|
||||
if startHeight < 0 {
|
||||
return nil, fmt.Errorf("start height of fetch range must not "+
|
||||
"be less than zero - got %d", startHeight)
|
||||
}
|
||||
if endHeight < startHeight {
|
||||
return nil, fmt.Errorf("end height of fetch range must not "+
|
||||
"be less than the start height - got start %d, end %d",
|
||||
startHeight, endHeight)
|
||||
}
|
||||
|
||||
// There is nothing to do when the start and end heights are the same,
|
||||
// so return now to avoid the chain view lock.
|
||||
if startHeight == endHeight {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Grab a lock on the chain view to prevent it from changing due to a
|
||||
// reorg while building the hashes.
|
||||
b.bestChain.mtx.Lock()
|
||||
defer b.bestChain.mtx.Unlock()
|
||||
|
||||
// When the requested start height is after the most recent best chain
|
||||
// height, there is nothing to do.
|
||||
latestHeight := b.bestChain.tip().height
|
||||
if startHeight > latestHeight {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Limit the ending height to the latest height of the chain.
|
||||
if endHeight > latestHeight+1 {
|
||||
endHeight = latestHeight + 1
|
||||
}
|
||||
|
||||
// Fetch as many as are available within the specified range.
|
||||
hashes := make([]chainhash.Hash, 0, endHeight-startHeight)
|
||||
for i := startHeight; i < endHeight; i++ {
|
||||
hashes = append(hashes, b.bestChain.nodeByHeight(i).hash)
|
||||
}
|
||||
return hashes, nil
|
||||
}
|
||||
|
||||
// IndexManager provides a generic interface that the is called when blocks are
|
||||
// connected and disconnected to and from the tip of the main chain for the
|
||||
// purpose of supporting optional indexes.
|
||||
|
@ -1309,6 +1412,7 @@ func New(config *Config) (*BlockChain, error) {
|
|||
blocksPerRetarget: int32(targetTimespan / targetTimePerBlock),
|
||||
index: newBlockIndex(config.DB, params),
|
||||
hashCache: config.HashCache,
|
||||
bestChain: newChainView(nil),
|
||||
orphans: make(map[chainhash.Hash]*orphanBlock),
|
||||
prevOrphans: make(map[chainhash.Hash][]*orphanBlock),
|
||||
warningCaches: newThresholdCaches(vbNumBits),
|
||||
|
@ -1335,9 +1439,10 @@ func New(config *Config) (*BlockChain, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
bestNode := b.bestChain.Tip()
|
||||
log.Infof("Chain state (height %d, hash %v, totaltx %d, work %v)",
|
||||
b.bestNode.height, b.bestNode.hash, b.stateSnapshot.TotalTxns,
|
||||
b.bestNode.workSum)
|
||||
bestNode.height, bestNode.hash, b.stateSnapshot.TotalTxns,
|
||||
bestNode.workSum)
|
||||
|
||||
return &b, nil
|
||||
}
|
||||
|
|
|
@ -125,14 +125,14 @@ func TestCalcSequenceLock(t *testing.T) {
|
|||
|
||||
// Generate enough synthetic blocks to activate CSV.
|
||||
chain := newFakeChain(netParams)
|
||||
node := chain.bestNode
|
||||
node := chain.bestChain.Tip()
|
||||
blockTime := node.Header().Timestamp
|
||||
numBlocksToActivate := (netParams.MinerConfirmationWindow * 3)
|
||||
for i := uint32(0); i < numBlocksToActivate; i++ {
|
||||
blockTime = blockTime.Add(time.Second)
|
||||
node = newFakeNode(node, blockVersion, 0, blockTime)
|
||||
chain.index.AddNode(node)
|
||||
chain.bestNode = node
|
||||
chain.bestChain.SetTip(node)
|
||||
}
|
||||
|
||||
// Create a utxo view with a fake utxo for the inputs used in the
|
||||
|
|
|
@ -1060,8 +1060,7 @@ func (b *BlockChain) createChainState() error {
|
|||
genesisBlock := btcutil.NewBlock(b.chainParams.GenesisBlock)
|
||||
header := &genesisBlock.MsgBlock().Header
|
||||
node := newBlockNode(header, 0)
|
||||
node.inMainChain = true
|
||||
b.bestNode = node
|
||||
b.bestChain.SetTip(node)
|
||||
|
||||
// Add the new node to the index which is used for faster lookups.
|
||||
b.index.AddNode(node)
|
||||
|
@ -1071,8 +1070,8 @@ func (b *BlockChain) createChainState() error {
|
|||
numTxns := uint64(len(genesisBlock.MsgBlock().Transactions))
|
||||
blockSize := uint64(genesisBlock.MsgBlock().SerializeSize())
|
||||
blockWeight := uint64(GetBlockWeight(genesisBlock))
|
||||
b.stateSnapshot = newBestState(b.bestNode, blockSize, blockWeight,
|
||||
numTxns, numTxns, time.Unix(b.bestNode.timestamp, 0))
|
||||
b.stateSnapshot = newBestState(node, blockSize, blockWeight, numTxns,
|
||||
numTxns, time.Unix(node.timestamp, 0))
|
||||
|
||||
// Create the initial the database chain state including creating the
|
||||
// necessary index buckets and inserting the genesis block.
|
||||
|
@ -1108,13 +1107,13 @@ func (b *BlockChain) createChainState() error {
|
|||
|
||||
// Add the genesis block hash to height and height to hash
|
||||
// mappings to the index.
|
||||
err = dbPutBlockIndex(dbTx, &b.bestNode.hash, b.bestNode.height)
|
||||
err = dbPutBlockIndex(dbTx, &node.hash, node.height)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Store the current best chain state into the database.
|
||||
err = dbPutBestState(dbTx, b.stateSnapshot, b.bestNode.workSum)
|
||||
err = dbPutBestState(dbTx, b.stateSnapshot, node.workSum)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1154,6 +1153,7 @@ func (b *BlockChain) initChainState() error {
|
|||
log.Infof("Loading block index. This might take a while...")
|
||||
bestHeight := int32(state.height)
|
||||
blockNodes := make([]blockNode, bestHeight+1)
|
||||
var tip *blockNode
|
||||
for height := int32(0); height <= bestHeight; height++ {
|
||||
header, err := dbFetchHeaderByHeight(dbTx, height)
|
||||
if err != nil {
|
||||
|
@ -1164,25 +1164,29 @@ func (b *BlockChain) initChainState() error {
|
|||
// and add it to the block index.
|
||||
node := &blockNodes[height]
|
||||
initBlockNode(node, header, height)
|
||||
if parent := b.bestNode; parent != nil {
|
||||
node.parent = parent
|
||||
node.workSum = node.workSum.Add(parent.workSum,
|
||||
if tip != nil {
|
||||
node.parent = tip
|
||||
node.workSum = node.workSum.Add(tip.workSum,
|
||||
node.workSum)
|
||||
}
|
||||
node.inMainChain = true
|
||||
b.index.AddNode(node)
|
||||
|
||||
// This node is now the end of the best chain.
|
||||
b.bestNode = node
|
||||
tip = node
|
||||
}
|
||||
|
||||
// Ensure the resulting best node matches the stored best state
|
||||
// hash.
|
||||
if b.bestNode.hash != state.hash {
|
||||
// Ensure the resulting best chain matches the stored best state
|
||||
// hash and set the best chain view accordingly.
|
||||
if tip == nil || tip.hash != state.hash {
|
||||
var tipHash chainhash.Hash
|
||||
if tip != nil {
|
||||
tipHash = tip.hash
|
||||
}
|
||||
return AssertError(fmt.Sprintf("initChainState: block "+
|
||||
"index chain tip %s does not match stored "+
|
||||
"best state %s", b.bestNode.hash, state.hash))
|
||||
"best state %s", tipHash, state.hash))
|
||||
}
|
||||
b.bestChain.SetTip(tip)
|
||||
|
||||
// Load the raw block bytes for the best block.
|
||||
blockBytes, err := dbTx.FetchBlock(&state.hash)
|
||||
|
@ -1199,8 +1203,8 @@ func (b *BlockChain) initChainState() error {
|
|||
blockSize := uint64(len(blockBytes))
|
||||
blockWeight := uint64(GetBlockWeight(btcutil.NewBlock(&block)))
|
||||
numTxns := uint64(len(block.Transactions))
|
||||
b.stateSnapshot = newBestState(b.bestNode, blockSize, blockWeight,
|
||||
numTxns, state.totalTxns, b.bestNode.CalcPastMedianTime())
|
||||
b.stateSnapshot = newBestState(tip, blockSize, blockWeight,
|
||||
numTxns, state.totalTxns, tip.CalcPastMedianTime())
|
||||
isStateInitialized = true
|
||||
|
||||
return nil
|
||||
|
@ -1247,44 +1251,12 @@ func dbFetchHeaderByHeight(dbTx database.Tx, height int32) (*wire.BlockHeader, e
|
|||
return dbFetchHeaderByHash(dbTx, hash)
|
||||
}
|
||||
|
||||
// dbFetchBlockByHash uses an existing database transaction to retrieve the raw
|
||||
// block for the provided hash, deserialize it, retrieve the appropriate height
|
||||
// from the index, and return a btcutil.Block with the height set.
|
||||
func dbFetchBlockByHash(dbTx database.Tx, hash *chainhash.Hash) (*btcutil.Block, error) {
|
||||
// First find the height associated with the provided hash in the index.
|
||||
blockHeight, err := dbFetchHeightByHash(dbTx, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Load the raw block bytes from the database.
|
||||
blockBytes, err := dbTx.FetchBlock(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create the encapsulated block and set the height appropriately.
|
||||
block, err := btcutil.NewBlockFromBytes(blockBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
block.SetHeight(blockHeight)
|
||||
|
||||
return block, nil
|
||||
}
|
||||
|
||||
// dbFetchBlockByHeight uses an existing database transaction to retrieve the
|
||||
// raw block for the provided height, deserialize it, and return a btcutil.Block
|
||||
// dbFetchBlockByNode uses an existing database transaction to retrieve the
|
||||
// raw block for the provided node, deserialize it, and return a btcutil.Block
|
||||
// with the height set.
|
||||
func dbFetchBlockByHeight(dbTx database.Tx, height int32) (*btcutil.Block, error) {
|
||||
// First find the hash associated with the provided height in the index.
|
||||
hash, err := dbFetchHashByHeight(dbTx, height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func dbFetchBlockByNode(dbTx database.Tx, node *blockNode) (*btcutil.Block, error) {
|
||||
// Load the raw block bytes from the database.
|
||||
blockBytes, err := dbTx.FetchBlock(hash)
|
||||
blockBytes, err := dbTx.FetchBlock(&node.hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1294,67 +1266,27 @@ func dbFetchBlockByHeight(dbTx database.Tx, height int32) (*btcutil.Block, error
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
block.SetHeight(height)
|
||||
block.SetHeight(node.height)
|
||||
|
||||
return block, nil
|
||||
}
|
||||
|
||||
// dbMainChainHasBlock uses an existing database transaction to return whether
|
||||
// or not the main chain contains the block identified by the provided hash.
|
||||
func dbMainChainHasBlock(dbTx database.Tx, hash *chainhash.Hash) bool {
|
||||
hashIndex := dbTx.Metadata().Bucket(hashIndexBucketName)
|
||||
return hashIndex.Get(hash[:]) != nil
|
||||
}
|
||||
|
||||
// MainChainHasBlock returns whether or not the block with the given hash is in
|
||||
// the main chain.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) MainChainHasBlock(hash *chainhash.Hash) (bool, error) {
|
||||
var exists bool
|
||||
err := b.db.View(func(dbTx database.Tx) error {
|
||||
exists = dbMainChainHasBlock(dbTx, hash)
|
||||
return nil
|
||||
})
|
||||
return exists, err
|
||||
}
|
||||
|
||||
// BlockHeightByHash returns the height of the block with the given hash in the
|
||||
// main chain.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) BlockHeightByHash(hash *chainhash.Hash) (int32, error) {
|
||||
var height int32
|
||||
err := b.db.View(func(dbTx database.Tx) error {
|
||||
var err error
|
||||
height, err = dbFetchHeightByHash(dbTx, hash)
|
||||
return err
|
||||
})
|
||||
return height, err
|
||||
}
|
||||
|
||||
// BlockHashByHeight returns the hash of the block at the given height in the
|
||||
// main chain.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) BlockHashByHeight(blockHeight int32) (*chainhash.Hash, error) {
|
||||
var hash *chainhash.Hash
|
||||
err := b.db.View(func(dbTx database.Tx) error {
|
||||
var err error
|
||||
hash, err = dbFetchHashByHeight(dbTx, blockHeight)
|
||||
return err
|
||||
})
|
||||
return hash, err
|
||||
}
|
||||
|
||||
// BlockByHeight returns the block at the given height in the main chain.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) BlockByHeight(blockHeight int32) (*btcutil.Block, error) {
|
||||
// Lookup the block height in the best chain.
|
||||
node := b.bestChain.NodeByHeight(blockHeight)
|
||||
if node == nil {
|
||||
str := fmt.Sprintf("no block at height %d exists", blockHeight)
|
||||
return nil, errNotInMainChain(str)
|
||||
}
|
||||
|
||||
// Load the block from the database and return it.
|
||||
var block *btcutil.Block
|
||||
err := b.db.View(func(dbTx database.Tx) error {
|
||||
var err error
|
||||
block, err = dbFetchBlockByHeight(dbTx, blockHeight)
|
||||
block, err = dbFetchBlockByNode(dbTx, node)
|
||||
return err
|
||||
})
|
||||
return block, err
|
||||
|
@ -1365,70 +1297,20 @@ func (b *BlockChain) BlockByHeight(blockHeight int32) (*btcutil.Block, error) {
|
|||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) BlockByHash(hash *chainhash.Hash) (*btcutil.Block, error) {
|
||||
// Lookup the block hash in block index and ensure it is in the best
|
||||
// chain.
|
||||
node := b.index.LookupNode(hash)
|
||||
if node == nil || !b.bestChain.Contains(node) {
|
||||
str := fmt.Sprintf("block %s is not in the main chain", hash)
|
||||
return nil, errNotInMainChain(str)
|
||||
}
|
||||
|
||||
// Load the block from the database and return it.
|
||||
var block *btcutil.Block
|
||||
err := b.db.View(func(dbTx database.Tx) error {
|
||||
var err error
|
||||
block, err = dbFetchBlockByHash(dbTx, hash)
|
||||
block, err = dbFetchBlockByNode(dbTx, node)
|
||||
return err
|
||||
})
|
||||
return block, err
|
||||
}
|
||||
|
||||
// HeightRange returns a range of block hashes for the given start and end
|
||||
// heights. It is inclusive of the start height and exclusive of the end
|
||||
// height. The end height will be limited to the current main chain height.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) HeightRange(startHeight, endHeight int32) ([]chainhash.Hash, error) {
|
||||
// Ensure requested heights are sane.
|
||||
if startHeight < 0 {
|
||||
return nil, fmt.Errorf("start height of fetch range must not "+
|
||||
"be less than zero - got %d", startHeight)
|
||||
}
|
||||
if endHeight < startHeight {
|
||||
return nil, fmt.Errorf("end height of fetch range must not "+
|
||||
"be less than the start height - got start %d, end %d",
|
||||
startHeight, endHeight)
|
||||
}
|
||||
|
||||
// There is nothing to do when the start and end heights are the same,
|
||||
// so return now to avoid the chain lock and a database transaction.
|
||||
if startHeight == endHeight {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Grab a lock on the chain to prevent it from changing due to a reorg
|
||||
// while building the hashes.
|
||||
b.chainLock.RLock()
|
||||
defer b.chainLock.RUnlock()
|
||||
|
||||
// When the requested start height is after the most recent best chain
|
||||
// height, there is nothing to do.
|
||||
latestHeight := b.bestNode.height
|
||||
if startHeight > latestHeight {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Limit the ending height to the latest height of the chain.
|
||||
if endHeight > latestHeight+1 {
|
||||
endHeight = latestHeight + 1
|
||||
}
|
||||
|
||||
// Fetch as many as are available within the specified range.
|
||||
var hashList []chainhash.Hash
|
||||
err := b.db.View(func(dbTx database.Tx) error {
|
||||
hashes := make([]chainhash.Hash, 0, endHeight-startHeight)
|
||||
for i := startHeight; i < endHeight; i++ {
|
||||
hash, err := dbFetchHashByHeight(dbTx, i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hashes = append(hashes, *hash)
|
||||
}
|
||||
|
||||
// Set the list to be returned to the constructed list.
|
||||
hashList = hashes
|
||||
return nil
|
||||
})
|
||||
return hashList, err
|
||||
}
|
||||
|
|
|
@ -12,6 +12,25 @@ import (
|
|||
// in a week on average.
|
||||
const approxNodesPerWeek = 6 * 24 * 7
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// chainView provides a flat view of a specific branch of the block chain from
|
||||
// its tip back to the genesis block and provides various convenience functions
|
||||
// for comparing chains.
|
||||
|
|
|
@ -6,10 +6,10 @@ package blockchain
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/database"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
@ -99,7 +99,7 @@ func (b *BlockChain) findPreviousCheckpoint() (*blockNode, error) {
|
|||
// that is already available.
|
||||
for i := numCheckpoints - 1; i >= 0; i-- {
|
||||
node := b.index.LookupNode(checkpoints[i].Hash)
|
||||
if node == nil || !node.inMainChain {
|
||||
if node == nil || !b.bestChain.Contains(node) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -130,7 +130,7 @@ func (b *BlockChain) findPreviousCheckpoint() (*blockNode, error) {
|
|||
// When there is a next checkpoint and the height of the current best
|
||||
// chain does not exceed it, the current checkpoint lockin is still
|
||||
// the latest known checkpoint.
|
||||
if b.bestNode.height < b.nextCheckpoint.Height {
|
||||
if b.bestChain.Tip().height < b.nextCheckpoint.Height {
|
||||
return b.checkpointNode, nil
|
||||
}
|
||||
|
||||
|
@ -201,69 +201,61 @@ func (b *BlockChain) IsCheckpointCandidate(block *btcutil.Block) (bool, error) {
|
|||
b.chainLock.RLock()
|
||||
defer b.chainLock.RUnlock()
|
||||
|
||||
var isCandidate bool
|
||||
err := b.db.View(func(dbTx database.Tx) error {
|
||||
// A checkpoint must be in the main chain.
|
||||
blockHeight, err := dbFetchHeightByHash(dbTx, block.Hash())
|
||||
if err != nil {
|
||||
// Only return an error if it's not due to the block not
|
||||
// being in the main chain.
|
||||
if !isNotInMainChainErr(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// A checkpoint must be in the main chain.
|
||||
node := b.index.LookupNode(block.Hash())
|
||||
if node == nil || !b.bestChain.Contains(node) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Ensure the height of the passed block and the entry for the
|
||||
// block in the main chain match. This should always be the
|
||||
// case unless the caller provided an invalid block.
|
||||
if blockHeight != block.Height() {
|
||||
return fmt.Errorf("passed block height of %d does not "+
|
||||
"match the main chain height of %d",
|
||||
block.Height(), blockHeight)
|
||||
}
|
||||
// Ensure the height of the passed block and the entry for the block in
|
||||
// the main chain match. This should always be the case unless the
|
||||
// caller provided an invalid block.
|
||||
if node.height != block.Height() {
|
||||
return false, fmt.Errorf("passed block height of %d does not "+
|
||||
"match the main chain height of %d", block.Height(),
|
||||
node.height)
|
||||
}
|
||||
|
||||
// A checkpoint must be at least CheckpointConfirmations blocks
|
||||
// before the end of the main chain.
|
||||
mainChainHeight := b.bestNode.height
|
||||
if blockHeight > (mainChainHeight - CheckpointConfirmations) {
|
||||
return nil
|
||||
}
|
||||
// A checkpoint must be at least CheckpointConfirmations blocks
|
||||
// before the end of the main chain.
|
||||
mainChainHeight := b.bestChain.Tip().height
|
||||
if node.height > (mainChainHeight - CheckpointConfirmations) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Get the previous block header.
|
||||
prevHash := &block.MsgBlock().Header.PrevBlock
|
||||
prevHeader, err := dbFetchHeaderByHash(dbTx, prevHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// A checkpoint must be have at least one block after it.
|
||||
//
|
||||
// This should always succeed since the check above already made sure it
|
||||
// is CheckpointConfirmations back, but be safe in case the constant
|
||||
// changes.
|
||||
nextNode := b.bestChain.Next(node)
|
||||
if nextNode == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Get the next block header.
|
||||
nextHeader, err := dbFetchHeaderByHeight(dbTx, blockHeight+1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// A checkpoint must be have at least one block before it.
|
||||
if node.parent == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// A checkpoint must have timestamps for the block and the
|
||||
// blocks on either side of it in order (due to the median time
|
||||
// allowance this is not always the case).
|
||||
prevTime := prevHeader.Timestamp
|
||||
curTime := block.MsgBlock().Header.Timestamp
|
||||
nextTime := nextHeader.Timestamp
|
||||
if prevTime.After(curTime) || nextTime.Before(curTime) {
|
||||
return nil
|
||||
}
|
||||
// A checkpoint must have timestamps for the block and the blocks on
|
||||
// either side of it in order (due to the median time allowance this is
|
||||
// not always the case).
|
||||
prevTime := time.Unix(node.parent.timestamp, 0)
|
||||
curTime := block.MsgBlock().Header.Timestamp
|
||||
nextTime := time.Unix(nextNode.timestamp, 0)
|
||||
if prevTime.After(curTime) || nextTime.Before(curTime) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// A checkpoint must have transactions that only contain
|
||||
// standard scripts.
|
||||
for _, tx := range block.Transactions() {
|
||||
if isNonstandardTransaction(tx) {
|
||||
return nil
|
||||
}
|
||||
// A checkpoint must have transactions that only contain standard
|
||||
// scripts.
|
||||
for _, tx := range block.Transactions() {
|
||||
if isNonstandardTransaction(tx) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
// All of the checks passed, so the block is a candidate.
|
||||
isCandidate = true
|
||||
return nil
|
||||
})
|
||||
return isCandidate, err
|
||||
// All of the checks passed, so the block is a candidate.
|
||||
return true, nil
|
||||
}
|
||||
|
|
|
@ -262,7 +262,6 @@ func newFakeChain(params *chaincfg.Params) *BlockChain {
|
|||
// Create a genesis block node and block index index populated with it
|
||||
// for use when creating the fake chain below.
|
||||
node := newBlockNode(¶ms.GenesisBlock.Header, 0)
|
||||
node.inMainChain = true
|
||||
index := newBlockIndex(nil, params)
|
||||
index.AddNode(node)
|
||||
|
||||
|
@ -276,9 +275,9 @@ func newFakeChain(params *chaincfg.Params) *BlockChain {
|
|||
maxRetargetTimespan: targetTimespan * adjustmentFactor,
|
||||
blocksPerRetarget: int32(targetTimespan / targetTimePerBlock),
|
||||
index: index,
|
||||
bestChain: newChainView(node),
|
||||
warningCaches: newThresholdCaches(vbNumBits),
|
||||
deploymentCaches: newThresholdCaches(chaincfg.DefinedDeployments),
|
||||
bestNode: node,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -306,7 +306,7 @@ func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode, newBlockTim
|
|||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) CalcNextRequiredDifficulty(timestamp time.Time) (uint32, error) {
|
||||
b.chainLock.Lock()
|
||||
difficulty, err := b.calcNextRequiredDifficulty(b.bestNode, timestamp)
|
||||
difficulty, err := b.calcNextRequiredDifficulty(b.bestChain.Tip(), timestamp)
|
||||
b.chainLock.Unlock()
|
||||
return difficulty, err
|
||||
}
|
||||
|
|
|
@ -284,15 +284,7 @@ func (m *Manager) Init(chain *blockchain.BlockChain) error {
|
|||
|
||||
// Loop until the tip is a block that exists in the main chain.
|
||||
initialHeight := height
|
||||
for {
|
||||
exists, err := chain.MainChainHasBlock(hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
break
|
||||
}
|
||||
|
||||
for !chain.MainChainHasBlock(hash) {
|
||||
// At this point the index tip is orphaned, so load the
|
||||
// orphaned block from the database directly and
|
||||
// disconnect it from the index. The block has to be
|
||||
|
|
|
@ -265,7 +265,7 @@ func (b *BlockChain) thresholdState(prevNode *blockNode, checker thresholdCondit
|
|||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) ThresholdState(deploymentID uint32) (ThresholdState, error) {
|
||||
b.chainLock.Lock()
|
||||
state, err := b.deploymentState(b.bestNode, deploymentID)
|
||||
state, err := b.deploymentState(b.bestChain.Tip(), deploymentID)
|
||||
b.chainLock.Unlock()
|
||||
|
||||
return state, err
|
||||
|
@ -277,7 +277,7 @@ func (b *BlockChain) ThresholdState(deploymentID uint32) (ThresholdState, error)
|
|||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) IsDeploymentActive(deploymentID uint32) (bool, error) {
|
||||
b.chainLock.Lock()
|
||||
state, err := b.deploymentState(b.bestNode, deploymentID)
|
||||
state, err := b.deploymentState(b.bestChain.Tip(), deploymentID)
|
||||
b.chainLock.Unlock()
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
@ -316,7 +316,7 @@ func (b *BlockChain) initThresholdCaches() error {
|
|||
// threshold state for each of them. This will ensure the caches are
|
||||
// populated and any states that needed to be recalculated due to
|
||||
// definition changes is done now.
|
||||
prevNode := b.bestNode.parent
|
||||
prevNode := b.bestChain.Tip().parent
|
||||
for bit := uint32(0); bit < vbNumBits; bit++ {
|
||||
checker := bitConditionChecker{bit: bit, chain: b}
|
||||
cache := &b.warningCaches[bit]
|
||||
|
@ -340,13 +340,14 @@ func (b *BlockChain) initThresholdCaches() error {
|
|||
if b.isCurrent() {
|
||||
// Warn if a high enough percentage of the last blocks have
|
||||
// unexpected versions.
|
||||
if err := b.warnUnknownVersions(b.bestNode); err != nil {
|
||||
bestNode := b.bestChain.Tip()
|
||||
if err := b.warnUnknownVersions(bestNode); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Warn if any unknown new rules are either about to activate or
|
||||
// have already been activated.
|
||||
if err := b.warnUnknownRuleActivations(b.bestNode); err != nil {
|
||||
if err := b.warnUnknownRuleActivations(bestNode); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1256,7 +1256,7 @@ func (b *BlockChain) CheckConnectBlock(block *btcutil.Block) error {
|
|||
b.chainLock.Lock()
|
||||
defer b.chainLock.Unlock()
|
||||
|
||||
prevNode := b.bestNode
|
||||
prevNode := b.bestChain.Tip()
|
||||
newNode := newBlockNode(&block.MsgBlock().Header, prevNode.height+1)
|
||||
newNode.parent = prevNode
|
||||
newNode.workSum.Add(prevNode.workSum, newNode.workSum)
|
||||
|
|
|
@ -224,7 +224,7 @@ func (b *BlockChain) calcNextBlockVersion(prevNode *blockNode) (int32, error) {
|
|||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) CalcNextBlockVersion() (int32, error) {
|
||||
b.chainLock.Lock()
|
||||
version, err := b.calcNextBlockVersion(b.bestNode)
|
||||
version, err := b.calcNextBlockVersion(b.bestChain.Tip())
|
||||
b.chainLock.Unlock()
|
||||
return version, err
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue