blockchain: Refactor main block index logic.
This refactors the block index logic into a separate struct and introduces an individual lock for it so it can be queried independent of the chain lock.
This commit is contained in:
parent
59169540c3
commit
1ecfea4928
10 changed files with 529 additions and 432 deletions
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2013-2016 The btcsuite developers
|
// Copyright (c) 2013-2017 The btcsuite developers
|
||||||
// Use of this source code is governed by an ISC
|
// Use of this source code is governed by an ISC
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -28,9 +28,9 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags)
|
||||||
|
|
||||||
// Get a block node for the block previous to this one. Will be nil
|
// Get a block node for the block previous to this one. Will be nil
|
||||||
// if this is the genesis block.
|
// if this is the genesis block.
|
||||||
prevNode, err := b.getPrevNodeFromBlock(block)
|
prevNode, err := b.index.PrevNodeFromBlock(block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("getPrevNodeFromBlock: %v", err)
|
log.Errorf("PrevNodeFromBlock: %v", err)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
465
blockchain/blockindex.go
Normal file
465
blockchain/blockindex.go
Normal file
|
@ -0,0 +1,465 @@
|
||||||
|
// Copyright (c) 2015-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 (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"sort"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
"github.com/btcsuite/btcd/database"
|
||||||
|
"github.com/btcsuite/btcd/wire"
|
||||||
|
"github.com/btcsuite/btcutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// blockNode represents a block within the block chain and is primarily used to
|
||||||
|
// aid in selecting the best chain to be the main chain. The main chain is
|
||||||
|
// stored into the block database.
|
||||||
|
type blockNode struct {
|
||||||
|
// parent is the parent block for this node.
|
||||||
|
parent *blockNode
|
||||||
|
|
||||||
|
// children contains the child nodes for this node. Typically there
|
||||||
|
// will only be one, but sometimes there can be more than one and that
|
||||||
|
// is when the best chain selection algorithm is used.
|
||||||
|
children []*blockNode
|
||||||
|
|
||||||
|
// hash is the double sha 256 of the block.
|
||||||
|
hash *chainhash.Hash
|
||||||
|
|
||||||
|
// parentHash is the double sha 256 of the parent block. This is kept
|
||||||
|
// here over simply relying on parent.hash directly since block nodes
|
||||||
|
// are sparse and the parent node might not be in memory when its hash
|
||||||
|
// is needed.
|
||||||
|
parentHash *chainhash.Hash
|
||||||
|
|
||||||
|
// height is the position in the block chain.
|
||||||
|
height int32
|
||||||
|
|
||||||
|
// workSum is the total amount of work in the chain up to and including
|
||||||
|
// this node.
|
||||||
|
workSum *big.Int
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// platforms.
|
||||||
|
version int32
|
||||||
|
bits uint32
|
||||||
|
nonce uint32
|
||||||
|
timestamp int64
|
||||||
|
merkleRoot chainhash.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// newBlockNode returns a new block node for the given block header. It is
|
||||||
|
// completely disconnected from the chain and the workSum value is just the work
|
||||||
|
// for the passed block. The work sum is updated accordingly when the node is
|
||||||
|
// inserted into a chain.
|
||||||
|
func newBlockNode(blockHeader *wire.BlockHeader, blockHash *chainhash.Hash, height int32) *blockNode {
|
||||||
|
// Make a copy of the hash so the node doesn't keep a reference to part
|
||||||
|
// of the full block/block header preventing it from being garbage
|
||||||
|
// collected.
|
||||||
|
prevHash := blockHeader.PrevBlock
|
||||||
|
node := blockNode{
|
||||||
|
hash: blockHash,
|
||||||
|
parentHash: &prevHash,
|
||||||
|
workSum: CalcWork(blockHeader.Bits),
|
||||||
|
height: height,
|
||||||
|
version: blockHeader.Version,
|
||||||
|
bits: blockHeader.Bits,
|
||||||
|
nonce: blockHeader.Nonce,
|
||||||
|
timestamp: blockHeader.Timestamp.Unix(),
|
||||||
|
merkleRoot: blockHeader.MerkleRoot,
|
||||||
|
}
|
||||||
|
return &node
|
||||||
|
}
|
||||||
|
|
||||||
|
// Header constructs a block header from the node and returns it.
|
||||||
|
//
|
||||||
|
// This function is safe for concurrent access.
|
||||||
|
func (node *blockNode) Header() wire.BlockHeader {
|
||||||
|
// No lock is needed because all accessed fields are immutable.
|
||||||
|
return wire.BlockHeader{
|
||||||
|
Version: node.version,
|
||||||
|
PrevBlock: *node.parentHash,
|
||||||
|
MerkleRoot: node.merkleRoot,
|
||||||
|
Timestamp: time.Unix(node.timestamp, 0),
|
||||||
|
Bits: node.bits,
|
||||||
|
Nonce: node.nonce,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeChildNode deletes node from the provided slice of child block
|
||||||
|
// nodes. It ensures the final pointer reference is set to nil to prevent
|
||||||
|
// potential memory leaks. The original slice is returned unmodified if node
|
||||||
|
// is invalid or not in the slice.
|
||||||
|
//
|
||||||
|
// This function MUST be called with the block index lock held (for writes).
|
||||||
|
func removeChildNode(children []*blockNode, node *blockNode) []*blockNode {
|
||||||
|
if node == nil {
|
||||||
|
return children
|
||||||
|
}
|
||||||
|
|
||||||
|
// An indexing for loop is intentionally used over a range here as range
|
||||||
|
// does not reevaluate the slice on each iteration nor does it adjust
|
||||||
|
// the index for the modified slice.
|
||||||
|
for i := 0; i < len(children); i++ {
|
||||||
|
if children[i].hash.IsEqual(node.hash) {
|
||||||
|
copy(children[i:], children[i+1:])
|
||||||
|
children[len(children)-1] = nil
|
||||||
|
return children[:len(children)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return children
|
||||||
|
}
|
||||||
|
|
||||||
|
// blockIndex provides facilities for keeping track of an in-memory index of the
|
||||||
|
// block chain. Although the name block chain suggest a single chain of blocks,
|
||||||
|
// it is actually a tree-shaped structure where any node can have multiple
|
||||||
|
// children. However, there can only be one active branch which does indeed
|
||||||
|
// form a chain from the tip all the way back to the genesis block.
|
||||||
|
type blockIndex struct {
|
||||||
|
// The following fields are set when the instance is created and can't
|
||||||
|
// be changed afterwards, so there is no need to protect them with a
|
||||||
|
// separate mutex.
|
||||||
|
db database.DB
|
||||||
|
chainParams *chaincfg.Params
|
||||||
|
|
||||||
|
sync.RWMutex
|
||||||
|
index map[chainhash.Hash]*blockNode
|
||||||
|
depNodes map[chainhash.Hash][]*blockNode
|
||||||
|
}
|
||||||
|
|
||||||
|
// newBlockIndex returns a new empty instance of a block index. The index will
|
||||||
|
// be dynamically populated as block nodes are loaded from the database and
|
||||||
|
// manually added.
|
||||||
|
func newBlockIndex(db database.DB, chainParams *chaincfg.Params) *blockIndex {
|
||||||
|
return &blockIndex{
|
||||||
|
db: db,
|
||||||
|
chainParams: chainParams,
|
||||||
|
index: make(map[chainhash.Hash]*blockNode),
|
||||||
|
depNodes: make(map[chainhash.Hash][]*blockNode),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HaveBlock returns whether or not the block index contains the provided hash.
|
||||||
|
//
|
||||||
|
// This function is safe for concurrent access.
|
||||||
|
func (bi *blockIndex) HaveBlock(hash *chainhash.Hash) bool {
|
||||||
|
bi.RLock()
|
||||||
|
_, hasBlock := bi.index[*hash]
|
||||||
|
bi.RUnlock()
|
||||||
|
return hasBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadBlockNode loads the block identified by hash from the block database,
|
||||||
|
// creates a block node from it, and updates the block index accordingly. It is
|
||||||
|
// used mainly to dynamically load previous blocks from the database as they are
|
||||||
|
// needed to avoid needing to put the entire block index in memory.
|
||||||
|
//
|
||||||
|
// This function MUST be called with the block index lock held (for writes).
|
||||||
|
// The database transaction may be read-only.
|
||||||
|
func (bi *blockIndex) loadBlockNode(dbTx database.Tx, hash *chainhash.Hash) (*blockNode, error) {
|
||||||
|
// Load the block header and height from the db.
|
||||||
|
blockHeader, err := dbFetchHeaderByHash(dbTx, hash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
blockHeight, err := dbFetchHeightByHash(dbTx, hash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the new block node for the block and set the work.
|
||||||
|
node := newBlockNode(blockHeader, hash, blockHeight)
|
||||||
|
node.inMainChain = true
|
||||||
|
|
||||||
|
// Add the node to the chain.
|
||||||
|
// There are a few possibilities here:
|
||||||
|
// 1) This node is a child of an existing block node
|
||||||
|
// 2) This node is the parent of one or more nodes
|
||||||
|
// 3) Neither 1 or 2 is true which implies it's an orphan block and
|
||||||
|
// therefore is an error to insert into the chain
|
||||||
|
prevHash := &blockHeader.PrevBlock
|
||||||
|
if parentNode, ok := bi.index[*prevHash]; ok {
|
||||||
|
// Case 1 -- This node is a child of an existing block node.
|
||||||
|
// Update the node's work sum with the sum of the parent node's
|
||||||
|
// work sum and this node's work, append the node as a child of
|
||||||
|
// the parent node and set this node's parent to the parent
|
||||||
|
// node.
|
||||||
|
node.workSum = node.workSum.Add(parentNode.workSum, node.workSum)
|
||||||
|
parentNode.children = append(parentNode.children, node)
|
||||||
|
node.parent = parentNode
|
||||||
|
|
||||||
|
} else if childNodes, ok := bi.depNodes[*hash]; ok {
|
||||||
|
// Case 2 -- This node is the parent of one or more nodes.
|
||||||
|
// Update the node's work sum by subtracting this node's work
|
||||||
|
// from the sum of its first child, and connect the node to all
|
||||||
|
// of its children.
|
||||||
|
node.workSum.Sub(childNodes[0].workSum, node.workSum)
|
||||||
|
for _, childNode := range childNodes {
|
||||||
|
childNode.parent = node
|
||||||
|
node.children = append(node.children, childNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Case 3 -- The node doesn't have a parent and is not the
|
||||||
|
// parent of another node. This means an arbitrary orphan block
|
||||||
|
// is trying to be loaded which is not allowed.
|
||||||
|
str := "loadBlockNode: attempt to insert orphan block %v"
|
||||||
|
return nil, AssertError(fmt.Sprintf(str, hash))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the new node to the indices for faster lookups.
|
||||||
|
bi.index[*hash] = node
|
||||||
|
bi.depNodes[*prevHash] = append(bi.depNodes[*prevHash], node)
|
||||||
|
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrevNodeFromBlock returns a block node for the block previous to the passed
|
||||||
|
// block (the passed block's parent). When it is already in the memory block
|
||||||
|
// chain, it simply returns it. Otherwise, it loads the previous block header
|
||||||
|
// from the block database, creates a new block node from it, and returns it.
|
||||||
|
// The returned node will be nil if the genesis block is passed.
|
||||||
|
//
|
||||||
|
// This function is safe for concurrent access.
|
||||||
|
func (bi *blockIndex) PrevNodeFromBlock(block *btcutil.Block) (*blockNode, error) {
|
||||||
|
// Genesis block.
|
||||||
|
prevHash := &block.MsgBlock().Header.PrevBlock
|
||||||
|
if prevHash.IsEqual(zeroHash) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bi.Lock()
|
||||||
|
defer bi.Unlock()
|
||||||
|
|
||||||
|
// Return the existing previous block node if it's already there.
|
||||||
|
if bn, ok := bi.index[*prevHash]; ok {
|
||||||
|
return bn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dynamically load the previous block from the block database, create
|
||||||
|
// a new block node for it, and update the memory chain accordingly.
|
||||||
|
var prevBlockNode *blockNode
|
||||||
|
err := bi.db.View(func(dbTx database.Tx) error {
|
||||||
|
var err error
|
||||||
|
prevBlockNode, err = bi.loadBlockNode(dbTx, prevHash)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return prevBlockNode, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// prevNodeFromNode returns a block node for the block previous to the
|
||||||
|
// passed block node (the passed block node's parent). When the node is already
|
||||||
|
// connected to a parent, it simply returns it. Otherwise, it loads the
|
||||||
|
// associated block from the database to obtain the previous hash and uses that
|
||||||
|
// to dynamically create a new block node and return it. The memory block
|
||||||
|
// chain is updated accordingly. The returned node will be nil if the genesis
|
||||||
|
// block is passed.
|
||||||
|
//
|
||||||
|
// This function MUST be called with the block index lock held (for writes).
|
||||||
|
func (bi *blockIndex) prevNodeFromNode(node *blockNode) (*blockNode, error) {
|
||||||
|
// Return the existing previous block node if it's already there.
|
||||||
|
if node.parent != nil {
|
||||||
|
return node.parent, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Genesis block.
|
||||||
|
if node.hash.IsEqual(bi.chainParams.GenesisHash) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dynamically load the previous block from the block database, create
|
||||||
|
// a new block node for it, and update the memory chain accordingly.
|
||||||
|
var prevBlockNode *blockNode
|
||||||
|
err := bi.db.View(func(dbTx database.Tx) error {
|
||||||
|
var err error
|
||||||
|
prevBlockNode, err = bi.loadBlockNode(dbTx, node.parentHash)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return prevBlockNode, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrevNodeFromNode returns a block node for the block previous to the
|
||||||
|
// passed block node (the passed block node's parent). When the node is already
|
||||||
|
// connected to a parent, it simply returns it. Otherwise, it loads the
|
||||||
|
// associated block from the database to obtain the previous hash and uses that
|
||||||
|
// to dynamically create a new block node and return it. The memory block
|
||||||
|
// chain is updated accordingly. The returned node will be nil if the genesis
|
||||||
|
// block is passed.
|
||||||
|
//
|
||||||
|
// This function is safe for concurrent access.
|
||||||
|
func (bi *blockIndex) PrevNodeFromNode(node *blockNode) (*blockNode, error) {
|
||||||
|
bi.Lock()
|
||||||
|
node, err := bi.prevNodeFromNode(node)
|
||||||
|
bi.Unlock()
|
||||||
|
return node, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RelativeNode returns the ancestor block a relative 'distance' blocks before
|
||||||
|
// the passed anchor block. While iterating backwards through the chain, any
|
||||||
|
// block nodes which aren't in the memory chain are loaded in dynamically.
|
||||||
|
//
|
||||||
|
// This function is safe for concurrent access.
|
||||||
|
func (bi *blockIndex) RelativeNode(anchor *blockNode, distance uint32) (*blockNode, error) {
|
||||||
|
bi.Lock()
|
||||||
|
defer bi.Unlock()
|
||||||
|
|
||||||
|
iterNode := anchor
|
||||||
|
err := bi.db.View(func(dbTx database.Tx) error {
|
||||||
|
// Walk backwards in the chian until we've gone 'distance'
|
||||||
|
// steps back.
|
||||||
|
var err error
|
||||||
|
for i := distance; i > 0; i-- {
|
||||||
|
switch {
|
||||||
|
// If the parent of this node has already been loaded
|
||||||
|
// into memory, then we can follow the link without
|
||||||
|
// hitting the database.
|
||||||
|
case iterNode.parent != nil:
|
||||||
|
iterNode = iterNode.parent
|
||||||
|
|
||||||
|
// If this node is the genesis block, then we can't go
|
||||||
|
// back any further, so we exit immediately.
|
||||||
|
case iterNode.hash.IsEqual(bi.chainParams.GenesisHash):
|
||||||
|
return nil
|
||||||
|
|
||||||
|
// Otherwise, load the block node from the database,
|
||||||
|
// pulling it into the memory cache in the processes.
|
||||||
|
default:
|
||||||
|
iterNode, err = bi.loadBlockNode(dbTx,
|
||||||
|
iterNode.parentHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return iterNode, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AncestorNode returns the ancestor block node at the provided height by
|
||||||
|
// following the chain backwards from the given node while dynamically loading
|
||||||
|
// any pruned nodes from the database and updating the memory block chain as
|
||||||
|
// needed. The returned block will be nil when a height is requested that is
|
||||||
|
// after the height of the passed node or is less than zero.
|
||||||
|
//
|
||||||
|
// This function is safe for concurrent access.
|
||||||
|
func (bi *blockIndex) AncestorNode(node *blockNode, height int32) (*blockNode, error) {
|
||||||
|
// Nothing to do if the requested height is outside of the valid range.
|
||||||
|
if height > node.height || height < 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate backwards until the requested height is reached.
|
||||||
|
bi.Lock()
|
||||||
|
iterNode := node
|
||||||
|
for iterNode != nil && iterNode.height > height {
|
||||||
|
var err error
|
||||||
|
iterNode, err = bi.prevNodeFromNode(iterNode)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bi.Unlock()
|
||||||
|
|
||||||
|
return iterNode, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddNode adds the provided node to the block index. Duplicate entries are not
|
||||||
|
// checked so it is up to caller to avoid adding them.
|
||||||
|
//
|
||||||
|
// This function is safe for concurrent access.
|
||||||
|
func (bi *blockIndex) AddNode(node *blockNode) {
|
||||||
|
bi.Lock()
|
||||||
|
bi.index[*node.hash] = node
|
||||||
|
if prevHash := node.parentHash; prevHash != nil && *prevHash != *zeroHash {
|
||||||
|
bi.depNodes[*prevHash] = append(bi.depNodes[*prevHash], node)
|
||||||
|
}
|
||||||
|
bi.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupNode returns the block node identified by the provided hash. It will
|
||||||
|
// return nil if there is no entry for the hash.
|
||||||
|
//
|
||||||
|
// This function is safe for concurrent access.
|
||||||
|
func (bi *blockIndex) LookupNode(hash *chainhash.Hash) *blockNode {
|
||||||
|
bi.RLock()
|
||||||
|
node := bi.index[*hash]
|
||||||
|
bi.RUnlock()
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
// CalcPastMedianTime calculates the median time of the previous few blocks
|
||||||
|
// prior to, and including, the passed block node.
|
||||||
|
//
|
||||||
|
// This function is safe for concurrent access.
|
||||||
|
func (bi *blockIndex) CalcPastMedianTime(startNode *blockNode) (time.Time, error) {
|
||||||
|
// Genesis block.
|
||||||
|
if startNode == nil {
|
||||||
|
return bi.chainParams.GenesisBlock.Header.Timestamp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a slice of the previous few block timestamps used to calculate
|
||||||
|
// the median per the number defined by the constant medianTimeBlocks.
|
||||||
|
timestamps := make([]int64, medianTimeBlocks)
|
||||||
|
numNodes := 0
|
||||||
|
iterNode := startNode
|
||||||
|
bi.Lock()
|
||||||
|
for i := 0; i < medianTimeBlocks && iterNode != nil; i++ {
|
||||||
|
timestamps[i] = iterNode.timestamp
|
||||||
|
numNodes++
|
||||||
|
|
||||||
|
// Get the previous block node. This function is used over
|
||||||
|
// simply accessing iterNode.parent directly as it will
|
||||||
|
// dynamically create previous block nodes as needed. This
|
||||||
|
// helps allow only the pieces of the chain that are needed
|
||||||
|
// to remain in memory.
|
||||||
|
var err error
|
||||||
|
iterNode, err = bi.prevNodeFromNode(iterNode)
|
||||||
|
if err != nil {
|
||||||
|
bi.Unlock()
|
||||||
|
log.Errorf("prevNodeFromNode: %v", err)
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bi.Unlock()
|
||||||
|
|
||||||
|
// Prune the slice to the actual number of available timestamps which
|
||||||
|
// will be fewer than desired near the beginning of the block chain
|
||||||
|
// and sort them.
|
||||||
|
timestamps = timestamps[:numNodes]
|
||||||
|
sort.Sort(timeSorter(timestamps))
|
||||||
|
|
||||||
|
// NOTE: The consensus rules incorrectly calculate the median for even
|
||||||
|
// numbers of blocks. A true median averages the middle two elements
|
||||||
|
// for a set with an even number of elements in it. Since the constant
|
||||||
|
// for the previous number of blocks to be used is odd, this is only an
|
||||||
|
// issue for a few blocks near the beginning of the chain. I suspect
|
||||||
|
// this is an optimization even though the result is slightly wrong for
|
||||||
|
// a few of the first blocks since after the first few blocks, there
|
||||||
|
// will always be an odd number of blocks in the set per the constant.
|
||||||
|
//
|
||||||
|
// This code follows suit to ensure the same rules are used, however, be
|
||||||
|
// aware that should the medianTimeBlocks constant ever be changed to an
|
||||||
|
// even number, this code will be wrong.
|
||||||
|
medianTimestamp := timestamps[numNodes/2]
|
||||||
|
return time.Unix(medianTimestamp, 0), nil
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2013-2016 The btcsuite developers
|
// Copyright (c) 2013-2017 The btcsuite developers
|
||||||
// Use of this source code is governed by an ISC
|
// Use of this source code is governed by an ISC
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -38,14 +38,14 @@ type BlockLocator []*chainhash.Hash
|
||||||
// - If the passed hash is not currently known, the block locator will only
|
// - If the passed hash is not currently known, the block locator will only
|
||||||
// consist of the passed hash
|
// consist of the passed hash
|
||||||
//
|
//
|
||||||
// This function MUST be called with the chain state lock held (for reads).
|
// This function MUST be called with the block index lock held (for reads).
|
||||||
func (b *BlockChain) blockLocatorFromHash(hash *chainhash.Hash) BlockLocator {
|
func (bi *blockIndex) blockLocatorFromHash(hash *chainhash.Hash) BlockLocator {
|
||||||
// The locator contains the requested hash at the very least.
|
// The locator contains the requested hash at the very least.
|
||||||
locator := make(BlockLocator, 0, wire.MaxBlockLocatorsPerMsg)
|
locator := make(BlockLocator, 0, wire.MaxBlockLocatorsPerMsg)
|
||||||
locator = append(locator, hash)
|
locator = append(locator, hash)
|
||||||
|
|
||||||
// Nothing more to do if a locator for the genesis hash was requested.
|
// Nothing more to do if a locator for the genesis hash was requested.
|
||||||
if hash.IsEqual(b.chainParams.GenesisHash) {
|
if hash.IsEqual(bi.chainParams.GenesisHash) {
|
||||||
return locator
|
return locator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,13 +54,13 @@ func (b *BlockChain) blockLocatorFromHash(hash *chainhash.Hash) BlockLocator {
|
||||||
// which it forks from the main chain.
|
// which it forks from the main chain.
|
||||||
blockHeight := int32(-1)
|
blockHeight := int32(-1)
|
||||||
forkHeight := int32(-1)
|
forkHeight := int32(-1)
|
||||||
node, exists := b.index[*hash]
|
node, exists := bi.index[*hash]
|
||||||
if !exists {
|
if !exists {
|
||||||
// Try to look up the height for passed block hash. Assume an
|
// Try to look up the height for passed block hash. Assume an
|
||||||
// error means it doesn't exist and just return the locator for
|
// error means it doesn't exist and just return the locator for
|
||||||
// the block itself.
|
// the block itself.
|
||||||
var height int32
|
var height int32
|
||||||
err := b.db.View(func(dbTx database.Tx) error {
|
err := bi.db.View(func(dbTx database.Tx) error {
|
||||||
var err error
|
var err error
|
||||||
height, err = dbFetchHeightByHash(dbTx, hash)
|
height, err = dbFetchHeightByHash(dbTx, hash)
|
||||||
return err
|
return err
|
||||||
|
@ -93,7 +93,7 @@ func (b *BlockChain) blockLocatorFromHash(hash *chainhash.Hash) BlockLocator {
|
||||||
// could fail is if there is something wrong with the database which
|
// could fail is if there is something wrong with the database which
|
||||||
// will be caught in short order anyways and it's also safe to ignore
|
// will be caught in short order anyways and it's also safe to ignore
|
||||||
// block locators.
|
// block locators.
|
||||||
_ = b.db.View(func(dbTx database.Tx) error {
|
_ = bi.db.View(func(dbTx database.Tx) error {
|
||||||
iterNode := node
|
iterNode := node
|
||||||
increment := int32(1)
|
increment := int32(1)
|
||||||
for len(locator) < wire.MaxBlockLocatorsPerMsg-1 {
|
for len(locator) < wire.MaxBlockLocatorsPerMsg-1 {
|
||||||
|
@ -112,7 +112,7 @@ func (b *BlockChain) blockLocatorFromHash(hash *chainhash.Hash) BlockLocator {
|
||||||
// height.
|
// height.
|
||||||
if forkHeight != -1 && blockHeight > forkHeight {
|
if forkHeight != -1 && blockHeight > forkHeight {
|
||||||
// Intentionally use parent field instead of the
|
// Intentionally use parent field instead of the
|
||||||
// getPrevNodeFromNode function since we don't
|
// PrevNodeFromNode function since we don't
|
||||||
// want to dynamically load nodes when building
|
// want to dynamically load nodes when building
|
||||||
// block locators. Side chain blocks should
|
// block locators. Side chain blocks should
|
||||||
// always be in memory already, and if they
|
// always be in memory already, and if they
|
||||||
|
@ -144,7 +144,7 @@ func (b *BlockChain) blockLocatorFromHash(hash *chainhash.Hash) BlockLocator {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Append the appropriate genesis block.
|
// Append the appropriate genesis block.
|
||||||
locator = append(locator, b.chainParams.GenesisHash)
|
locator = append(locator, bi.chainParams.GenesisHash)
|
||||||
return locator
|
return locator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,7 +162,9 @@ func (b *BlockChain) blockLocatorFromHash(hash *chainhash.Hash) BlockLocator {
|
||||||
// This function is safe for concurrent access.
|
// This function is safe for concurrent access.
|
||||||
func (b *BlockChain) BlockLocatorFromHash(hash *chainhash.Hash) BlockLocator {
|
func (b *BlockChain) BlockLocatorFromHash(hash *chainhash.Hash) BlockLocator {
|
||||||
b.chainLock.RLock()
|
b.chainLock.RLock()
|
||||||
locator := b.blockLocatorFromHash(hash)
|
b.index.RLock()
|
||||||
|
locator := b.index.blockLocatorFromHash(hash)
|
||||||
|
b.index.RUnlock()
|
||||||
b.chainLock.RUnlock()
|
b.chainLock.RUnlock()
|
||||||
return locator
|
return locator
|
||||||
}
|
}
|
||||||
|
@ -173,7 +175,9 @@ func (b *BlockChain) BlockLocatorFromHash(hash *chainhash.Hash) BlockLocator {
|
||||||
// This function is safe for concurrent access.
|
// This function is safe for concurrent access.
|
||||||
func (b *BlockChain) LatestBlockLocator() (BlockLocator, error) {
|
func (b *BlockChain) LatestBlockLocator() (BlockLocator, error) {
|
||||||
b.chainLock.RLock()
|
b.chainLock.RLock()
|
||||||
locator := b.blockLocatorFromHash(b.bestNode.hash)
|
b.index.RLock()
|
||||||
|
locator := b.index.blockLocatorFromHash(b.bestNode.hash)
|
||||||
|
b.index.RUnlock()
|
||||||
b.chainLock.RUnlock()
|
b.chainLock.RUnlock()
|
||||||
return locator, nil
|
return locator, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,6 @@ package blockchain
|
||||||
import (
|
import (
|
||||||
"container/list"
|
"container/list"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
|
||||||
"sort"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -26,88 +24,6 @@ const (
|
||||||
maxOrphanBlocks = 100
|
maxOrphanBlocks = 100
|
||||||
)
|
)
|
||||||
|
|
||||||
// blockNode represents a block within the block chain and is primarily used to
|
|
||||||
// aid in selecting the best chain to be the main chain. The main chain is
|
|
||||||
// stored into the block database.
|
|
||||||
type blockNode struct {
|
|
||||||
// parent is the parent block for this node.
|
|
||||||
parent *blockNode
|
|
||||||
|
|
||||||
// children contains the child nodes for this node. Typically there
|
|
||||||
// will only be one, but sometimes there can be more than one and that
|
|
||||||
// is when the best chain selection algorithm is used.
|
|
||||||
children []*blockNode
|
|
||||||
|
|
||||||
// hash is the double sha 256 of the block.
|
|
||||||
hash *chainhash.Hash
|
|
||||||
|
|
||||||
// parentHash is the double sha 256 of the parent block. This is kept
|
|
||||||
// here over simply relying on parent.hash directly since block nodes
|
|
||||||
// are sparse and the parent node might not be in memory when its hash
|
|
||||||
// is needed.
|
|
||||||
parentHash *chainhash.Hash
|
|
||||||
|
|
||||||
// height is the position in the block chain.
|
|
||||||
height int32
|
|
||||||
|
|
||||||
// workSum is the total amount of work in the chain up to and including
|
|
||||||
// this node.
|
|
||||||
workSum *big.Int
|
|
||||||
|
|
||||||
// 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
|
|
||||||
// platforms.
|
|
||||||
version int32
|
|
||||||
bits uint32
|
|
||||||
nonce uint32
|
|
||||||
timestamp int64
|
|
||||||
merkleRoot chainhash.Hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// newBlockNode returns a new block node for the given block header. It is
|
|
||||||
// completely disconnected from the chain and the workSum value is just the work
|
|
||||||
// for the passed block. The work sum is updated accordingly when the node is
|
|
||||||
// inserted into a chain.
|
|
||||||
func newBlockNode(blockHeader *wire.BlockHeader, blockHash *chainhash.Hash, height int32) *blockNode {
|
|
||||||
// Make a copy of the hash so the node doesn't keep a reference to part
|
|
||||||
// of the full block/block header preventing it from being garbage
|
|
||||||
// collected.
|
|
||||||
prevHash := blockHeader.PrevBlock
|
|
||||||
node := blockNode{
|
|
||||||
hash: blockHash,
|
|
||||||
parentHash: &prevHash,
|
|
||||||
workSum: CalcWork(blockHeader.Bits),
|
|
||||||
height: height,
|
|
||||||
version: blockHeader.Version,
|
|
||||||
bits: blockHeader.Bits,
|
|
||||||
nonce: blockHeader.Nonce,
|
|
||||||
timestamp: blockHeader.Timestamp.Unix(),
|
|
||||||
merkleRoot: blockHeader.MerkleRoot,
|
|
||||||
}
|
|
||||||
return &node
|
|
||||||
}
|
|
||||||
|
|
||||||
// Header constructs a block header from the node and returns it.
|
|
||||||
//
|
|
||||||
// This function is safe for concurrent access.
|
|
||||||
func (node *blockNode) Header() wire.BlockHeader {
|
|
||||||
// No lock is needed because all accessed fields are immutable.
|
|
||||||
return wire.BlockHeader{
|
|
||||||
Version: node.version,
|
|
||||||
PrevBlock: *node.parentHash,
|
|
||||||
MerkleRoot: node.merkleRoot,
|
|
||||||
Timestamp: time.Unix(node.timestamp, 0),
|
|
||||||
Bits: node.bits,
|
|
||||||
Nonce: node.nonce,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// orphanBlock represents a block that we don't yet have the parent for. It
|
// 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
|
// is a normal block plus an expiration time to prevent caching the orphan
|
||||||
// forever.
|
// forever.
|
||||||
|
@ -116,30 +32,6 @@ type orphanBlock struct {
|
||||||
expiration time.Time
|
expiration time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeChildNode deletes node from the provided slice of child block
|
|
||||||
// nodes. It ensures the final pointer reference is set to nil to prevent
|
|
||||||
// potential memory leaks. The original slice is returned unmodified if node
|
|
||||||
// is invalid or not in the slice.
|
|
||||||
//
|
|
||||||
// This function MUST be called with the chain state lock held (for writes).
|
|
||||||
func removeChildNode(children []*blockNode, node *blockNode) []*blockNode {
|
|
||||||
if node == nil {
|
|
||||||
return children
|
|
||||||
}
|
|
||||||
|
|
||||||
// An indexing for loop is intentionally used over a range here as range
|
|
||||||
// does not reevaluate the slice on each iteration nor does it adjust
|
|
||||||
// the index for the modified slice.
|
|
||||||
for i := 0; i < len(children); i++ {
|
|
||||||
if children[i].hash.IsEqual(node.hash) {
|
|
||||||
copy(children[i:], children[i+1:])
|
|
||||||
children[len(children)-1] = nil
|
|
||||||
return children[:len(children)-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return children
|
|
||||||
}
|
|
||||||
|
|
||||||
// BestState houses information about the current best block and other info
|
// BestState houses information about the current best block and other info
|
||||||
// related to the state of the main chain as it exists from the point of view of
|
// related to the state of the main chain as it exists from the point of view of
|
||||||
// the current best block.
|
// the current best block.
|
||||||
|
@ -156,7 +48,7 @@ type BestState struct {
|
||||||
BlockSize uint64 // The size of the block.
|
BlockSize uint64 // The size of the block.
|
||||||
NumTxns uint64 // The number of txns in the block.
|
NumTxns uint64 // The number of txns in the block.
|
||||||
TotalTxns uint64 // The total number of txns in the chain.
|
TotalTxns uint64 // The total number of txns in the chain.
|
||||||
MedianTime time.Time // Median time as per calcPastMedianTime.
|
MedianTime time.Time // Median time as per CalcPastMedianTime.
|
||||||
}
|
}
|
||||||
|
|
||||||
// newBestState returns a new best stats instance for the given parameters.
|
// newBestState returns a new best stats instance for the given parameters.
|
||||||
|
@ -213,11 +105,10 @@ type BlockChain struct {
|
||||||
// runtime. They are protected by the chain lock.
|
// runtime. They are protected by the chain lock.
|
||||||
noVerify bool
|
noVerify bool
|
||||||
|
|
||||||
// These fields are related to the memory block index. They are
|
// These fields are related to the memory block index. The best node
|
||||||
// protected by the chain lock.
|
// is protected by the chain lock and the index has its own locks.
|
||||||
bestNode *blockNode
|
bestNode *blockNode
|
||||||
index map[chainhash.Hash]*blockNode
|
index *blockIndex
|
||||||
depNodes map[chainhash.Hash][]*blockNode
|
|
||||||
|
|
||||||
// These fields are related to handling of orphan blocks. They are
|
// These fields are related to handling of orphan blocks. They are
|
||||||
// protected by a combination of the chain lock and the orphan lock.
|
// protected by a combination of the chain lock and the orphan lock.
|
||||||
|
@ -434,264 +325,6 @@ func (b *BlockChain) addOrphanBlock(block *btcutil.Block) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadBlockNode loads the block identified by hash from the block database,
|
|
||||||
// creates a block node from it, and updates the memory block chain accordingly.
|
|
||||||
// It is used mainly to dynamically load previous blocks from the database as
|
|
||||||
// they are needed to avoid needing to put the entire block chain in memory.
|
|
||||||
//
|
|
||||||
// This function MUST be called with the chain state lock held (for writes).
|
|
||||||
// The database transaction may be read-only.
|
|
||||||
func (b *BlockChain) loadBlockNode(dbTx database.Tx, hash *chainhash.Hash) (*blockNode, error) {
|
|
||||||
// Load the block header and height from the db.
|
|
||||||
blockHeader, err := dbFetchHeaderByHash(dbTx, hash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
blockHeight, err := dbFetchHeightByHash(dbTx, hash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the new block node for the block and set the work.
|
|
||||||
node := newBlockNode(blockHeader, hash, blockHeight)
|
|
||||||
node.inMainChain = true
|
|
||||||
|
|
||||||
// Add the node to the chain.
|
|
||||||
// There are a few possibilities here:
|
|
||||||
// 1) This node is a child of an existing block node
|
|
||||||
// 2) This node is the parent of one or more nodes
|
|
||||||
// 3) Neither 1 or 2 is true which implies it's an orphan block and
|
|
||||||
// therefore is an error to insert into the chain
|
|
||||||
prevHash := &blockHeader.PrevBlock
|
|
||||||
if parentNode, ok := b.index[*prevHash]; ok {
|
|
||||||
// Case 1 -- This node is a child of an existing block node.
|
|
||||||
// Update the node's work sum with the sum of the parent node's
|
|
||||||
// work sum and this node's work, append the node as a child of
|
|
||||||
// the parent node and set this node's parent to the parent
|
|
||||||
// node.
|
|
||||||
node.workSum = node.workSum.Add(parentNode.workSum, node.workSum)
|
|
||||||
parentNode.children = append(parentNode.children, node)
|
|
||||||
node.parent = parentNode
|
|
||||||
|
|
||||||
} else if childNodes, ok := b.depNodes[*hash]; ok {
|
|
||||||
// Case 2 -- This node is the parent of one or more nodes.
|
|
||||||
// Update the node's work sum by subtracting this node's work
|
|
||||||
// from the sum of its first child, and connect the node to all
|
|
||||||
// of its children.
|
|
||||||
node.workSum.Sub(childNodes[0].workSum, node.workSum)
|
|
||||||
for _, childNode := range childNodes {
|
|
||||||
childNode.parent = node
|
|
||||||
node.children = append(node.children, childNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Case 3 -- The node doesn't have a parent and is not the
|
|
||||||
// parent of another node. This means an arbitrary orphan block
|
|
||||||
// is trying to be loaded which is not allowed.
|
|
||||||
str := "loadBlockNode: attempt to insert orphan block %v"
|
|
||||||
return nil, AssertError(fmt.Sprintf(str, hash))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the new node to the indices for faster lookups.
|
|
||||||
b.index[*hash] = node
|
|
||||||
b.depNodes[*prevHash] = append(b.depNodes[*prevHash], node)
|
|
||||||
|
|
||||||
return node, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getPrevNodeFromBlock returns a block node for the block previous to the
|
|
||||||
// passed block (the passed block's parent). When it is already in the memory
|
|
||||||
// block chain, it simply returns it. Otherwise, it loads the previous block
|
|
||||||
// header from the block database, creates a new block node from it, and returns
|
|
||||||
// it. The returned node will be nil if the genesis block is passed.
|
|
||||||
//
|
|
||||||
// This function MUST be called with the chain state lock held (for writes).
|
|
||||||
func (b *BlockChain) getPrevNodeFromBlock(block *btcutil.Block) (*blockNode, error) {
|
|
||||||
// Genesis block.
|
|
||||||
prevHash := &block.MsgBlock().Header.PrevBlock
|
|
||||||
if prevHash.IsEqual(zeroHash) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the existing previous block node if it's already there.
|
|
||||||
if bn, ok := b.index[*prevHash]; ok {
|
|
||||||
return bn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dynamically load the previous block from the block database, create
|
|
||||||
// a new block node for it, and update the memory chain accordingly.
|
|
||||||
var prevBlockNode *blockNode
|
|
||||||
err := b.db.View(func(dbTx database.Tx) error {
|
|
||||||
var err error
|
|
||||||
prevBlockNode, err = b.loadBlockNode(dbTx, prevHash)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
return prevBlockNode, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// getPrevNodeFromNode returns a block node for the block previous to the
|
|
||||||
// passed block node (the passed block node's parent). When the node is already
|
|
||||||
// connected to a parent, it simply returns it. Otherwise, it loads the
|
|
||||||
// associated block from the database to obtain the previous hash and uses that
|
|
||||||
// to dynamically create a new block node and return it. The memory block
|
|
||||||
// chain is updated accordingly. The returned node will be nil if the genesis
|
|
||||||
// block is passed.
|
|
||||||
//
|
|
||||||
// This function MUST be called with the chain state lock held (for writes).
|
|
||||||
func (b *BlockChain) getPrevNodeFromNode(node *blockNode) (*blockNode, error) {
|
|
||||||
// Return the existing previous block node if it's already there.
|
|
||||||
if node.parent != nil {
|
|
||||||
return node.parent, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Genesis block.
|
|
||||||
if node.hash.IsEqual(b.chainParams.GenesisHash) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dynamically load the previous block from the block database, create
|
|
||||||
// a new block node for it, and update the memory chain accordingly.
|
|
||||||
var prevBlockNode *blockNode
|
|
||||||
err := b.db.View(func(dbTx database.Tx) error {
|
|
||||||
var err error
|
|
||||||
prevBlockNode, err = b.loadBlockNode(dbTx, node.parentHash)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
return prevBlockNode, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// relativeNode returns the ancestor block a relative 'distance' blocks before
|
|
||||||
// the passed anchor block. While iterating backwards through the chain, any
|
|
||||||
// block nodes which aren't in the memory chain are loaded in dynamically.
|
|
||||||
//
|
|
||||||
// This function MUST be called with the chain state lock held (for writes).
|
|
||||||
func (b *BlockChain) relativeNode(anchor *blockNode, distance uint32) (*blockNode, error) {
|
|
||||||
var err error
|
|
||||||
iterNode := anchor
|
|
||||||
|
|
||||||
err = b.db.View(func(dbTx database.Tx) error {
|
|
||||||
// Walk backwards in the chian until we've gone 'distance'
|
|
||||||
// steps back.
|
|
||||||
for i := distance; i > 0; i-- {
|
|
||||||
switch {
|
|
||||||
// If the parent of this node has already been loaded
|
|
||||||
// into memory, then we can follow the link without
|
|
||||||
// hitting the database.
|
|
||||||
case iterNode.parent != nil:
|
|
||||||
iterNode = iterNode.parent
|
|
||||||
|
|
||||||
// If this node is the genesis block, then we can't go
|
|
||||||
// back any further, so we exit immediately.
|
|
||||||
case iterNode.hash.IsEqual(b.chainParams.GenesisHash):
|
|
||||||
return nil
|
|
||||||
|
|
||||||
// Otherwise, load the block node from the database,
|
|
||||||
// pulling it into the memory cache in the processes.
|
|
||||||
default:
|
|
||||||
iterNode, err = b.loadBlockNode(dbTx,
|
|
||||||
iterNode.parentHash)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return iterNode, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ancestorNode returns the ancestor block node at the provided height by
|
|
||||||
// following the chain backwards from the given node while dynamically loading
|
|
||||||
// any pruned nodes from the database and updating the memory block chain as
|
|
||||||
// needed. The returned block will be nil when a height is requested that is
|
|
||||||
// after the height of the passed node or is less than zero.
|
|
||||||
//
|
|
||||||
// This function MUST be called with the chain state lock held (for writes).
|
|
||||||
func (b *BlockChain) ancestorNode(node *blockNode, height int32) (*blockNode, error) {
|
|
||||||
// Nothing to do if the requested height is outside of the valid range.
|
|
||||||
if height > node.height || height < 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate backwards until the requested height is reached.
|
|
||||||
iterNode := node
|
|
||||||
for iterNode != nil && iterNode.height > height {
|
|
||||||
// Get the previous block node. This function is used over
|
|
||||||
// simply accessing iterNode.parent directly as it will
|
|
||||||
// dynamically create previous block nodes as needed. This
|
|
||||||
// helps allow only the pieces of the chain that are needed
|
|
||||||
// to remain in memory.
|
|
||||||
var err error
|
|
||||||
iterNode, err = b.getPrevNodeFromNode(iterNode)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("getPrevNodeFromNode: %v", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return iterNode, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// calcPastMedianTime calculates the median time of the previous few blocks
|
|
||||||
// prior to, and including, the passed block node. It is primarily used to
|
|
||||||
// validate new blocks have sane timestamps.
|
|
||||||
//
|
|
||||||
// This function MUST be called with the chain state lock held (for writes).
|
|
||||||
func (b *BlockChain) calcPastMedianTime(startNode *blockNode) (time.Time, error) {
|
|
||||||
// Genesis block.
|
|
||||||
if startNode == nil {
|
|
||||||
return b.chainParams.GenesisBlock.Header.Timestamp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a slice of the previous few block timestamps used to calculate
|
|
||||||
// the median per the number defined by the constant medianTimeBlocks.
|
|
||||||
timestamps := make([]int64, medianTimeBlocks)
|
|
||||||
numNodes := 0
|
|
||||||
iterNode := startNode
|
|
||||||
for i := 0; i < medianTimeBlocks && iterNode != nil; i++ {
|
|
||||||
timestamps[i] = iterNode.timestamp
|
|
||||||
numNodes++
|
|
||||||
|
|
||||||
// Get the previous block node. This function is used over
|
|
||||||
// simply accessing iterNode.parent directly as it will
|
|
||||||
// dynamically create previous block nodes as needed. This
|
|
||||||
// helps allow only the pieces of the chain that are needed
|
|
||||||
// to remain in memory.
|
|
||||||
var err error
|
|
||||||
iterNode, err = b.getPrevNodeFromNode(iterNode)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("getPrevNodeFromNode: %v", err)
|
|
||||||
return time.Time{}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prune the slice to the actual number of available timestamps which
|
|
||||||
// will be fewer than desired near the beginning of the block chain
|
|
||||||
// and sort them.
|
|
||||||
timestamps = timestamps[:numNodes]
|
|
||||||
sort.Sort(timeSorter(timestamps))
|
|
||||||
|
|
||||||
// NOTE: bitcoind incorrectly calculates the median for even numbers of
|
|
||||||
// blocks. A true median averages the middle two elements for a set
|
|
||||||
// with an even number of elements in it. Since the constant for the
|
|
||||||
// previous number of blocks to be used is odd, this is only an issue
|
|
||||||
// for a few blocks near the beginning of the chain. I suspect this is
|
|
||||||
// an optimization even though the result is slightly wrong for a few
|
|
||||||
// of the first blocks since after the first few blocks, there will
|
|
||||||
// always be an odd number of blocks in the set per the constant.
|
|
||||||
//
|
|
||||||
// This code follows suit to ensure the same rules are used as bitcoind
|
|
||||||
// however, be aware that should the medianTimeBlocks constant ever be
|
|
||||||
// changed to an even number, this code will be wrong.
|
|
||||||
medianTimestamp := timestamps[numNodes/2]
|
|
||||||
return time.Unix(medianTimestamp, 0), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SequenceLock represents the converted relative lock-time in seconds, and
|
// SequenceLock represents the converted relative lock-time in seconds, and
|
||||||
// absolute block-height for a transaction input's relative lock-times.
|
// absolute block-height for a transaction input's relative lock-times.
|
||||||
// According to SequenceLock, after the referenced input has been confirmed
|
// According to SequenceLock, after the referenced input has been confirmed
|
||||||
|
@ -789,7 +422,8 @@ func (b *BlockChain) calcSequenceLock(tx *btcutil.Tx, utxoView *UtxoViewpoint,
|
||||||
// the one which included this referenced output.
|
// the one which included this referenced output.
|
||||||
// TODO: caching should be added to keep this speedy
|
// TODO: caching should be added to keep this speedy
|
||||||
inputDepth := uint32(b.bestNode.height-inputHeight) + 1
|
inputDepth := uint32(b.bestNode.height-inputHeight) + 1
|
||||||
blockNode, err := b.relativeNode(b.bestNode, inputDepth)
|
blockNode, err := b.index.RelativeNode(b.bestNode,
|
||||||
|
inputDepth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return sequenceLock, err
|
return sequenceLock, err
|
||||||
}
|
}
|
||||||
|
@ -798,7 +432,7 @@ func (b *BlockChain) calcSequenceLock(tx *btcutil.Tx, utxoView *UtxoViewpoint,
|
||||||
// memory, we can now finally calculate the MTP of the
|
// memory, we can now finally calculate the MTP of the
|
||||||
// block prior to the one which included the output
|
// block prior to the one which included the output
|
||||||
// being spent.
|
// being spent.
|
||||||
medianTime, err := b.calcPastMedianTime(blockNode)
|
medianTime, err := b.index.CalcPastMedianTime(blockNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return sequenceLock, err
|
return sequenceLock, err
|
||||||
}
|
}
|
||||||
|
@ -953,7 +587,7 @@ func (b *BlockChain) connectBlock(node *blockNode, block *btcutil.Block, view *U
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the median time for the block.
|
// Calculate the median time for the block.
|
||||||
medianTime, err := b.calcPastMedianTime(node)
|
medianTime, err := b.index.CalcPastMedianTime(node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1023,11 +657,9 @@ func (b *BlockChain) connectBlock(node *blockNode, block *btcutil.Block, view *U
|
||||||
// now that the modifications have been committed to the database.
|
// now that the modifications have been committed to the database.
|
||||||
view.commit()
|
view.commit()
|
||||||
|
|
||||||
// Add the new node to the memory main chain indices for faster
|
// Add the new node to the memory main chain indices for faster lookups.
|
||||||
// lookups.
|
|
||||||
node.inMainChain = true
|
node.inMainChain = true
|
||||||
b.index[*node.hash] = node
|
b.index.AddNode(node)
|
||||||
b.depNodes[*prevHash] = append(b.depNodes[*prevHash], node)
|
|
||||||
|
|
||||||
// This node is now the end of the best chain.
|
// This node is now the end of the best chain.
|
||||||
b.bestNode = node
|
b.bestNode = node
|
||||||
|
@ -1066,13 +698,13 @@ func (b *BlockChain) disconnectBlock(node *blockNode, block *btcutil.Block, view
|
||||||
// accessing node.parent directly as it will dynamically create previous
|
// accessing node.parent directly as it will dynamically create previous
|
||||||
// block nodes as needed. This helps allow only the pieces of the chain
|
// block nodes as needed. This helps allow only the pieces of the chain
|
||||||
// that are needed to remain in memory.
|
// that are needed to remain in memory.
|
||||||
prevNode, err := b.getPrevNodeFromNode(node)
|
prevNode, err := b.index.PrevNodeFromNode(node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the median time for the previous block.
|
// Calculate the median time for the previous block.
|
||||||
medianTime, err := b.calcPastMedianTime(prevNode)
|
medianTime, err := b.index.CalcPastMedianTime(prevNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1371,7 +1003,7 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List, flags
|
||||||
|
|
||||||
// Log the point where the chain forked.
|
// Log the point where the chain forked.
|
||||||
firstAttachNode := attachNodes.Front().Value.(*blockNode)
|
firstAttachNode := attachNodes.Front().Value.(*blockNode)
|
||||||
forkNode, err := b.getPrevNodeFromNode(firstAttachNode)
|
forkNode, err := b.index.PrevNodeFromNode(firstAttachNode)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
log.Infof("REORGANIZE: Chain forks at %v", forkNode.hash)
|
log.Infof("REORGANIZE: Chain forks at %v", forkNode.hash)
|
||||||
}
|
}
|
||||||
|
@ -1463,7 +1095,9 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla
|
||||||
// We're extending (or creating) a side chain which may or may not
|
// We're extending (or creating) a side chain which may or may not
|
||||||
// become the main chain, but in either case the entry is needed in the
|
// become the main chain, but in either case the entry is needed in the
|
||||||
// index for future processing.
|
// index for future processing.
|
||||||
b.index[*node.hash] = node
|
b.index.Lock()
|
||||||
|
b.index.index[*node.hash] = node
|
||||||
|
b.index.Unlock()
|
||||||
|
|
||||||
// Connect the parent node to this node.
|
// Connect the parent node to this node.
|
||||||
node.inMainChain = false
|
node.inMainChain = false
|
||||||
|
@ -1477,7 +1111,9 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla
|
||||||
children = removeChildNode(children, node)
|
children = removeChildNode(children, node)
|
||||||
node.parent.children = children
|
node.parent.children = children
|
||||||
|
|
||||||
delete(b.index, *node.hash)
|
b.index.Lock()
|
||||||
|
delete(b.index.index, *node.hash)
|
||||||
|
b.index.Unlock()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1587,10 +1223,7 @@ func (b *BlockChain) BestSnapshot() *BestState {
|
||||||
// if it doesn't exist.
|
// if it doesn't exist.
|
||||||
func (b *BlockChain) FetchHeader(hash *chainhash.Hash) (wire.BlockHeader, error) {
|
func (b *BlockChain) FetchHeader(hash *chainhash.Hash) (wire.BlockHeader, error) {
|
||||||
// Reconstruct the header from the block index if possible.
|
// Reconstruct the header from the block index if possible.
|
||||||
b.chainLock.RLock()
|
if node := b.index.LookupNode(hash); node != nil {
|
||||||
node, ok := b.index[*hash]
|
|
||||||
b.chainLock.RUnlock()
|
|
||||||
if ok {
|
|
||||||
return node.Header(), nil
|
return node.Header(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1727,10 +1360,7 @@ func New(config *Config) (*BlockChain, error) {
|
||||||
minRetargetTimespan: targetTimespan / adjustmentFactor,
|
minRetargetTimespan: targetTimespan / adjustmentFactor,
|
||||||
maxRetargetTimespan: targetTimespan * adjustmentFactor,
|
maxRetargetTimespan: targetTimespan * adjustmentFactor,
|
||||||
blocksPerRetarget: int32(targetTimespan / targetTimePerBlock),
|
blocksPerRetarget: int32(targetTimespan / targetTimePerBlock),
|
||||||
minMemoryNodes: int32(targetTimespan / targetTimePerBlock),
|
index: newBlockIndex(config.DB, params),
|
||||||
bestNode: nil,
|
|
||||||
index: make(map[chainhash.Hash]*blockNode),
|
|
||||||
depNodes: make(map[chainhash.Hash][]*blockNode),
|
|
||||||
orphans: make(map[chainhash.Hash]*orphanBlock),
|
orphans: make(map[chainhash.Hash]*orphanBlock),
|
||||||
prevOrphans: make(map[chainhash.Hash][]*orphanBlock),
|
prevOrphans: make(map[chainhash.Hash][]*orphanBlock),
|
||||||
warningCaches: newThresholdCaches(vbNumBits),
|
warningCaches: newThresholdCaches(vbNumBits),
|
||||||
|
|
|
@ -1086,7 +1086,7 @@ func (b *BlockChain) createChainState() error {
|
||||||
b.bestNode = node
|
b.bestNode = node
|
||||||
|
|
||||||
// Add the new node to the index which is used for faster lookups.
|
// Add the new node to the index which is used for faster lookups.
|
||||||
b.index[*node.hash] = node
|
b.index.AddNode(node)
|
||||||
|
|
||||||
// Initialize the state related to the best block. Since it is the
|
// Initialize the state related to the best block. Since it is the
|
||||||
// genesis block, use its timestamp for the median time.
|
// genesis block, use its timestamp for the median time.
|
||||||
|
@ -1186,13 +1186,11 @@ func (b *BlockChain) initChainState() error {
|
||||||
node.workSum = state.workSum
|
node.workSum = state.workSum
|
||||||
b.bestNode = node
|
b.bestNode = node
|
||||||
|
|
||||||
// Add the new node to the indices for faster lookups.
|
// Add the new node to the block index.
|
||||||
prevHash := node.parentHash
|
b.index.AddNode(node)
|
||||||
b.index[*node.hash] = node
|
|
||||||
b.depNodes[*prevHash] = append(b.depNodes[*prevHash], node)
|
|
||||||
|
|
||||||
// Calculate the median time for the block.
|
// Calculate the median time for the block.
|
||||||
medianTime, err := b.calcPastMedianTime(node)
|
medianTime, err := b.index.CalcPastMedianTime(node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1910,7 +1908,7 @@ func (b *BlockChain) initThresholdCaches() error {
|
||||||
// accessing b.bestNode.parent directly as it will dynamically create
|
// accessing b.bestNode.parent directly as it will dynamically create
|
||||||
// previous block nodes as needed. This helps allow only the pieces of
|
// previous block nodes as needed. This helps allow only the pieces of
|
||||||
// the chain that are needed to remain in memory.
|
// the chain that are needed to remain in memory.
|
||||||
prevNode, err := b.getPrevNodeFromNode(b.bestNode)
|
prevNode, err := b.index.PrevNodeFromNode(b.bestNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -207,9 +207,9 @@ func (b *BlockChain) findPrevTestNetDifficulty(startNode *blockNode) (uint32, er
|
||||||
// helps allow only the pieces of the chain that are needed
|
// helps allow only the pieces of the chain that are needed
|
||||||
// to remain in memory.
|
// to remain in memory.
|
||||||
var err error
|
var err error
|
||||||
iterNode, err = b.getPrevNodeFromNode(iterNode)
|
iterNode, err = b.index.PrevNodeFromNode(iterNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("getPrevNodeFromNode: %v", err)
|
log.Errorf("PrevNodeFromNode: %v", err)
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -277,7 +277,7 @@ func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode, newBlockTim
|
||||||
// helps allow only the pieces of the chain that are needed
|
// helps allow only the pieces of the chain that are needed
|
||||||
// to remain in memory.
|
// to remain in memory.
|
||||||
var err error
|
var err error
|
||||||
firstNode, err = b.getPrevNodeFromNode(firstNode)
|
firstNode, err = b.index.PrevNodeFromNode(firstNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2013-2016 The btcsuite developers
|
// Copyright (c) 2013-2017 The btcsuite developers
|
||||||
// Use of this source code is governed by an ISC
|
// Use of this source code is governed by an ISC
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -42,8 +42,8 @@ const (
|
||||||
//
|
//
|
||||||
// This function MUST be called with the chain state lock held (for reads).
|
// This function MUST be called with the chain state lock held (for reads).
|
||||||
func (b *BlockChain) blockExists(hash *chainhash.Hash) (bool, error) {
|
func (b *BlockChain) blockExists(hash *chainhash.Hash) (bool, error) {
|
||||||
// Check memory chain first (could be main chain or side chain blocks).
|
// Check block index first (could be main chain or side chain blocks).
|
||||||
if _, ok := b.index[*hash]; ok {
|
if b.index.HaveBlock(hash) {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2016 The btcsuite developers
|
// Copyright (c) 2016-2017 The btcsuite developers
|
||||||
// Use of this source code is governed by an ISC
|
// Use of this source code is governed by an ISC
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ func (b *BlockChain) thresholdState(prevNode *blockNode, checker thresholdCondit
|
||||||
// window in order to get its threshold state. This can be done because
|
// window in order to get its threshold state. This can be done because
|
||||||
// the state is the same for all blocks within a given window.
|
// the state is the same for all blocks within a given window.
|
||||||
var err error
|
var err error
|
||||||
prevNode, err = b.ancestorNode(prevNode, prevNode.height-
|
prevNode, err = b.index.AncestorNode(prevNode, prevNode.height-
|
||||||
(prevNode.height+1)%confirmationWindow)
|
(prevNode.height+1)%confirmationWindow)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ThresholdFailed, err
|
return ThresholdFailed, err
|
||||||
|
@ -176,7 +176,7 @@ func (b *BlockChain) thresholdState(prevNode *blockNode, checker thresholdCondit
|
||||||
|
|
||||||
// The start and expiration times are based on the median block
|
// The start and expiration times are based on the median block
|
||||||
// time, so calculate it now.
|
// time, so calculate it now.
|
||||||
medianTime, err := b.calcPastMedianTime(prevNode)
|
medianTime, err := b.index.CalcPastMedianTime(prevNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ThresholdFailed, err
|
return ThresholdFailed, err
|
||||||
}
|
}
|
||||||
|
@ -194,7 +194,7 @@ func (b *BlockChain) thresholdState(prevNode *blockNode, checker thresholdCondit
|
||||||
|
|
||||||
// Get the ancestor that is the last block of the previous
|
// Get the ancestor that is the last block of the previous
|
||||||
// confirmation window.
|
// confirmation window.
|
||||||
prevNode, err = b.ancestorNode(prevNode, prevNode.height-
|
prevNode, err = b.index.AncestorNode(prevNode, prevNode.height-
|
||||||
confirmationWindow)
|
confirmationWindow)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ThresholdFailed, err
|
return ThresholdFailed, err
|
||||||
|
@ -223,7 +223,7 @@ func (b *BlockChain) thresholdState(prevNode *blockNode, checker thresholdCondit
|
||||||
case ThresholdDefined:
|
case ThresholdDefined:
|
||||||
// The deployment of the rule change fails if it expires
|
// The deployment of the rule change fails if it expires
|
||||||
// before it is accepted and locked in.
|
// before it is accepted and locked in.
|
||||||
medianTime, err := b.calcPastMedianTime(prevNode)
|
medianTime, err := b.index.CalcPastMedianTime(prevNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ThresholdFailed, err
|
return ThresholdFailed, err
|
||||||
}
|
}
|
||||||
|
@ -243,7 +243,7 @@ func (b *BlockChain) thresholdState(prevNode *blockNode, checker thresholdCondit
|
||||||
case ThresholdStarted:
|
case ThresholdStarted:
|
||||||
// The deployment of the rule change fails if it expires
|
// The deployment of the rule change fails if it expires
|
||||||
// before it is accepted and locked in.
|
// before it is accepted and locked in.
|
||||||
medianTime, err := b.calcPastMedianTime(prevNode)
|
medianTime, err := b.index.CalcPastMedianTime(prevNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ThresholdFailed, err
|
return ThresholdFailed, err
|
||||||
}
|
}
|
||||||
|
@ -272,7 +272,7 @@ func (b *BlockChain) thresholdState(prevNode *blockNode, checker thresholdCondit
|
||||||
// previous block nodes as needed. This helps
|
// previous block nodes as needed. This helps
|
||||||
// allow only the pieces of the chain that are
|
// allow only the pieces of the chain that are
|
||||||
// needed to remain in memory.
|
// needed to remain in memory.
|
||||||
countNode, err = b.getPrevNodeFromNode(countNode)
|
countNode, err = b.index.PrevNodeFromNode(countNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ThresholdFailed, err
|
return ThresholdFailed, err
|
||||||
}
|
}
|
||||||
|
@ -305,7 +305,7 @@ func (b *BlockChain) thresholdState(prevNode *blockNode, checker thresholdCondit
|
||||||
}
|
}
|
||||||
|
|
||||||
// ThresholdState returns the current rule change threshold state of the given
|
// ThresholdState returns the current rule change threshold state of the given
|
||||||
// deployment ID for the block AFTER then end of the current best chain.
|
// deployment ID for the block AFTER the end of the current best chain.
|
||||||
//
|
//
|
||||||
// This function is safe for concurrent access.
|
// This function is safe for concurrent access.
|
||||||
func (b *BlockChain) ThresholdState(deploymentID uint32) (ThresholdState, error) {
|
func (b *BlockChain) ThresholdState(deploymentID uint32) (ThresholdState, error) {
|
||||||
|
|
|
@ -671,9 +671,9 @@ func (b *BlockChain) checkBlockHeaderContext(header *wire.BlockHeader, prevNode
|
||||||
|
|
||||||
// Ensure the timestamp for the block header is after the
|
// Ensure the timestamp for the block header is after the
|
||||||
// median time of the last several blocks (medianTimeBlocks).
|
// median time of the last several blocks (medianTimeBlocks).
|
||||||
medianTime, err := b.calcPastMedianTime(prevNode)
|
medianTime, err := b.index.CalcPastMedianTime(prevNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("calcPastMedianTime: %v", err)
|
log.Errorf("CalcPastMedianTime: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !header.Timestamp.After(medianTime) {
|
if !header.Timestamp.After(medianTime) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2016 The btcsuite developers
|
// Copyright (c) 2016-2017 The btcsuite developers
|
||||||
// Use of this source code is governed by an ISC
|
// Use of this source code is governed by an ISC
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ func (c bitConditionChecker) Condition(node *blockNode) (bool, error) {
|
||||||
// accessing node.parent directly as it will dynamically create previous
|
// accessing node.parent directly as it will dynamically create previous
|
||||||
// block nodes as needed. This helps allow only the pieces of the chain
|
// block nodes as needed. This helps allow only the pieces of the chain
|
||||||
// that are needed to remain in memory.
|
// that are needed to remain in memory.
|
||||||
prevNode, err := c.chain.getPrevNodeFromNode(node)
|
prevNode, err := c.chain.index.PrevNodeFromNode(node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -248,7 +248,7 @@ func (b *BlockChain) warnUnknownRuleActivations(node *blockNode) error {
|
||||||
// accessing node.parent directly as it will dynamically create previous
|
// accessing node.parent directly as it will dynamically create previous
|
||||||
// block nodes as needed. This helps allow only the pieces of the chain
|
// block nodes as needed. This helps allow only the pieces of the chain
|
||||||
// that are needed to remain in memory.
|
// that are needed to remain in memory.
|
||||||
prevNode, err := b.getPrevNodeFromNode(node)
|
prevNode, err := b.index.PrevNodeFromNode(node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -309,7 +309,7 @@ func (b *BlockChain) warnUnknownVersions(node *blockNode) error {
|
||||||
// simply accessing node.parent directly as it will dynamically
|
// simply accessing node.parent directly as it will dynamically
|
||||||
// create previous block nodes as needed. This helps allow only
|
// create previous block nodes as needed. This helps allow only
|
||||||
// the pieces of the chain that are needed to remain in memory.
|
// the pieces of the chain that are needed to remain in memory.
|
||||||
node, err = b.getPrevNodeFromNode(node)
|
node, err = b.index.PrevNodeFromNode(node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue