lbcd/blockchain/blockindex.go
Dave Collins d06c0bb181
blockchain: Use hash values in structs.
This modifies the blockNode and BestState structs in the blockchain
package to store hashes directly instead of pointers to them and updates
callers to deal with the API change in the exported BestState struct.

In general, the preferred approach for hashes moving forward is to store
hash values in complex data structures, particularly those that will be
used for cache entries, and accept pointers to hashes in arguments to
functions.

Some of the reasoning behind making this change is:

- It is generally preferred to avoid storing pointers to data in cache
  objects since doing so can easily lead to storing interior pointers
  into other structs that then can't be GC'd
- Keeping the hash values directly in the block node provides better
  cache locality
2017-02-03 11:36:33 -06:00

462 lines
16 KiB
Go

// 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 {
node := blockNode{
hash: *blockHash,
parentHash: blockHeader.PrevBlock,
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 != *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
}