mirror of
https://github.com/btcsuite/btcd.git
synced 2024-11-19 18:00:11 +01:00
1914200080
This commit introduces the concept of “sequence locks” borrowed from Bitcoin Core for converting an input’s relative time-locks to an absolute value based on a particular block for input maturity evaluation. A sequence lock is computed as the most distant maturity height/time amongst all the referenced outputs within a particular transaction. A transaction with sequence locks activated within any of its inputs can *only* be included within a block if from the point-of-view of that block either the time-based or height-based maturity for all referenced inputs has been met. A transaction with sequence locks can only be accepted to the mempool iff from the point-of-view of the *next* (yet to be found block) all referenced inputs within the transaction are mature.
1663 lines
58 KiB
Go
1663 lines
58 KiB
Go
// Copyright (c) 2013-2016 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 (
|
|
"container/list"
|
|
"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/txscript"
|
|
"github.com/btcsuite/btcd/wire"
|
|
"github.com/btcsuite/btcutil"
|
|
)
|
|
|
|
const (
|
|
// maxOrphanBlocks is the maximum number of orphan blocks that can be
|
|
// queued.
|
|
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.
|
|
version int32
|
|
bits uint32
|
|
timestamp time.Time
|
|
}
|
|
|
|
// 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,
|
|
timestamp: blockHeader.Timestamp,
|
|
}
|
|
return &node
|
|
}
|
|
|
|
// 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.
|
|
type orphanBlock struct {
|
|
block *btcutil.Block
|
|
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
|
|
// related to the state of the main chain as it exists from the point of view of
|
|
// the current best block.
|
|
//
|
|
// The BestSnapshot method can be used to obtain access to this information
|
|
// in a concurrent safe manner and the data will not be changed out from under
|
|
// the caller when chain state changes occur as the function name implies.
|
|
// However, the returned snapshot must be treated as immutable since it is
|
|
// shared by all callers.
|
|
type BestState struct {
|
|
Hash *chainhash.Hash // The hash of the block.
|
|
Height int32 // The height of the block.
|
|
Bits uint32 // The difficulty bits of the block.
|
|
BlockSize uint64 // The size of the block.
|
|
NumTxns uint64 // The number of txns in the block.
|
|
TotalTxns uint64 // The total number of txns in the chain.
|
|
MedianTime time.Time // Median time as per calcPastMedianTime.
|
|
}
|
|
|
|
// newBestState returns a new best stats instance for the given parameters.
|
|
func newBestState(node *blockNode, blockSize, numTxns, totalTxns uint64, medianTime time.Time) *BestState {
|
|
return &BestState{
|
|
Hash: node.hash,
|
|
Height: node.height,
|
|
Bits: node.bits,
|
|
BlockSize: blockSize,
|
|
NumTxns: numTxns,
|
|
TotalTxns: totalTxns,
|
|
MedianTime: medianTime,
|
|
}
|
|
}
|
|
|
|
// BlockChain provides functions for working with the bitcoin block chain.
|
|
// It includes functionality such as rejecting duplicate blocks, ensuring blocks
|
|
// follow all rules, orphan handling, checkpoint handling, and best chain
|
|
// selection with reorganization.
|
|
type BlockChain 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.
|
|
checkpointsByHeight map[int32]*chaincfg.Checkpoint
|
|
db database.DB
|
|
chainParams *chaincfg.Params
|
|
timeSource MedianTimeSource
|
|
notifications NotificationCallback
|
|
sigCache *txscript.SigCache
|
|
indexManager IndexManager
|
|
|
|
// The following fields are calculated based upon the provided chain
|
|
// parameters. They are also set when the instance is created and
|
|
// can't be changed afterwards, so there is no need to protect them with
|
|
// a separate mutex.
|
|
//
|
|
// minMemoryNodes is the minimum number of consecutive nodes needed
|
|
// in memory in order to perform all necessary validation. It is used
|
|
// to determine when it's safe to prune nodes from memory without
|
|
// causing constant dynamic reloading. This is typically the same value
|
|
// as blocksPerRetarget, but it is separated here for tweakability and
|
|
// testability.
|
|
minRetargetTimespan int64 // target timespan / adjustment factor
|
|
maxRetargetTimespan int64 // target timespan * adjustment factor
|
|
blocksPerRetarget int32 // target timespan / target time per block
|
|
minMemoryNodes int32
|
|
|
|
// chainLock protects concurrent access to the vast majority of the
|
|
// fields in this struct below this point.
|
|
chainLock sync.RWMutex
|
|
|
|
// These fields are configuration parameters that can be toggled at
|
|
// runtime. They are protected by the chain lock.
|
|
noVerify bool
|
|
noCheckpoints bool
|
|
|
|
// These fields are related to the memory block index. They are
|
|
// protected by the chain lock.
|
|
bestNode *blockNode
|
|
index map[chainhash.Hash]*blockNode
|
|
depNodes map[chainhash.Hash][]*blockNode
|
|
|
|
// These fields are related to handling of orphan blocks. They are
|
|
// protected by a combination of the chain lock and the orphan lock.
|
|
orphanLock sync.RWMutex
|
|
orphans map[chainhash.Hash]*orphanBlock
|
|
prevOrphans map[chainhash.Hash][]*orphanBlock
|
|
oldestOrphan *orphanBlock
|
|
blockCache map[chainhash.Hash]*btcutil.Block
|
|
|
|
// These fields are related to checkpoint handling. They are protected
|
|
// by the chain lock.
|
|
nextCheckpoint *chaincfg.Checkpoint
|
|
checkpointBlock *btcutil.Block
|
|
|
|
// The state is used as a fairly efficient way to cache information
|
|
// about the current best chain state that is returned to callers when
|
|
// requested. It operates on the principle of MVCC such that any time a
|
|
// new block becomes the best block, the state pointer is replaced with
|
|
// a new struct and the old state is left untouched. In this way,
|
|
// multiple callers can be pointing to different best chain states.
|
|
// This is acceptable for most callers because the state is only being
|
|
// queried at a specific point in time.
|
|
//
|
|
// In addition, some of the fields are stored in the database so the
|
|
// chain state can be quickly reconstructed on load.
|
|
stateLock sync.RWMutex
|
|
stateSnapshot *BestState
|
|
}
|
|
|
|
// DisableVerify provides a mechanism to disable transaction script validation
|
|
// which you DO NOT want to do in production as it could allow double spends
|
|
// and other undesirable things. It is provided only for debug purposes since
|
|
// script validation is extremely intensive and when debugging it is sometimes
|
|
// nice to quickly get the chain.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (b *BlockChain) DisableVerify(disable bool) {
|
|
b.chainLock.Lock()
|
|
b.noVerify = disable
|
|
b.chainLock.Unlock()
|
|
}
|
|
|
|
// HaveBlock returns whether or not the chain instance has the block represented
|
|
// by the passed hash. This includes checking the various places a block can
|
|
// be like part of the main chain, on a side chain, or in the orphan pool.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (b *BlockChain) HaveBlock(hash *chainhash.Hash) (bool, error) {
|
|
b.chainLock.RLock()
|
|
exists, err := b.blockExists(hash)
|
|
b.chainLock.RUnlock()
|
|
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return exists || b.IsKnownOrphan(hash), nil
|
|
}
|
|
|
|
// IsKnownOrphan returns whether the passed hash is currently a known orphan.
|
|
// Keep in mind that only a limited number of orphans are held onto for a
|
|
// limited amount of time, so this function must not be used as an absolute
|
|
// way to test if a block is an orphan block. A full block (as opposed to just
|
|
// its hash) must be passed to ProcessBlock for that purpose. However, calling
|
|
// ProcessBlock with an orphan that already exists results in an error, so this
|
|
// function provides a mechanism for a caller to intelligently detect *recent*
|
|
// duplicate orphans and react accordingly.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (b *BlockChain) IsKnownOrphan(hash *chainhash.Hash) bool {
|
|
// Protect concurrent access. Using a read lock only so multiple
|
|
// readers can query without blocking each other.
|
|
b.orphanLock.RLock()
|
|
_, exists := b.orphans[*hash]
|
|
b.orphanLock.RUnlock()
|
|
|
|
return exists
|
|
}
|
|
|
|
// GetOrphanRoot returns the head of the chain for the provided hash from the
|
|
// map of orphan blocks.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (b *BlockChain) GetOrphanRoot(hash *chainhash.Hash) *chainhash.Hash {
|
|
// Protect concurrent access. Using a read lock only so multiple
|
|
// readers can query without blocking each other.
|
|
b.orphanLock.RLock()
|
|
defer b.orphanLock.RUnlock()
|
|
|
|
// Keep looping while the parent of each orphaned block is
|
|
// known and is an orphan itself.
|
|
orphanRoot := hash
|
|
prevHash := hash
|
|
for {
|
|
orphan, exists := b.orphans[*prevHash]
|
|
if !exists {
|
|
break
|
|
}
|
|
orphanRoot = prevHash
|
|
prevHash = &orphan.block.MsgBlock().Header.PrevBlock
|
|
}
|
|
|
|
return orphanRoot
|
|
}
|
|
|
|
// removeOrphanBlock removes the passed orphan block from the orphan pool and
|
|
// previous orphan index.
|
|
func (b *BlockChain) removeOrphanBlock(orphan *orphanBlock) {
|
|
// Protect concurrent access.
|
|
b.orphanLock.Lock()
|
|
defer b.orphanLock.Unlock()
|
|
|
|
// Remove the orphan block from the orphan pool.
|
|
orphanHash := orphan.block.Hash()
|
|
delete(b.orphans, *orphanHash)
|
|
|
|
// Remove the reference from the previous orphan index too. 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.
|
|
prevHash := &orphan.block.MsgBlock().Header.PrevBlock
|
|
orphans := b.prevOrphans[*prevHash]
|
|
for i := 0; i < len(orphans); i++ {
|
|
hash := orphans[i].block.Hash()
|
|
if hash.IsEqual(orphanHash) {
|
|
copy(orphans[i:], orphans[i+1:])
|
|
orphans[len(orphans)-1] = nil
|
|
orphans = orphans[:len(orphans)-1]
|
|
i--
|
|
}
|
|
}
|
|
b.prevOrphans[*prevHash] = orphans
|
|
|
|
// Remove the map entry altogether if there are no longer any orphans
|
|
// which depend on the parent hash.
|
|
if len(b.prevOrphans[*prevHash]) == 0 {
|
|
delete(b.prevOrphans, *prevHash)
|
|
}
|
|
}
|
|
|
|
// addOrphanBlock adds the passed block (which is already determined to be
|
|
// an orphan prior calling this function) to the orphan pool. It lazily cleans
|
|
// up any expired blocks so a separate cleanup poller doesn't need to be run.
|
|
// It also imposes a maximum limit on the number of outstanding orphan
|
|
// blocks and will remove the oldest received orphan block if the limit is
|
|
// exceeded.
|
|
func (b *BlockChain) addOrphanBlock(block *btcutil.Block) {
|
|
// Remove expired orphan blocks.
|
|
for _, oBlock := range b.orphans {
|
|
if time.Now().After(oBlock.expiration) {
|
|
b.removeOrphanBlock(oBlock)
|
|
continue
|
|
}
|
|
|
|
// Update the oldest orphan block pointer so it can be discarded
|
|
// in case the orphan pool fills up.
|
|
if b.oldestOrphan == nil || oBlock.expiration.Before(b.oldestOrphan.expiration) {
|
|
b.oldestOrphan = oBlock
|
|
}
|
|
}
|
|
|
|
// Limit orphan blocks to prevent memory exhaustion.
|
|
if len(b.orphans)+1 > maxOrphanBlocks {
|
|
// Remove the oldest orphan to make room for the new one.
|
|
b.removeOrphanBlock(b.oldestOrphan)
|
|
b.oldestOrphan = nil
|
|
}
|
|
|
|
// Protect concurrent access. This is intentionally done here instead
|
|
// of near the top since removeOrphanBlock does its own locking and
|
|
// the range iterator is not invalidated by removing map entries.
|
|
b.orphanLock.Lock()
|
|
defer b.orphanLock.Unlock()
|
|
|
|
// Insert the block into the orphan map with an expiration time
|
|
// 1 hour from now.
|
|
expiration := time.Now().Add(time.Hour)
|
|
oBlock := &orphanBlock{
|
|
block: block,
|
|
expiration: expiration,
|
|
}
|
|
b.orphans[*block.Hash()] = oBlock
|
|
|
|
// Add to previous hash lookup index for faster dependency lookups.
|
|
prevHash := &block.MsgBlock().Header.PrevBlock
|
|
b.prevOrphans[*prevHash] = append(b.prevOrphans[*prevHash], oBlock)
|
|
|
|
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
|
|
}
|
|
|
|
// removeBlockNode removes the passed block node from the memory chain by
|
|
// unlinking all of its children and removing it from the the node and
|
|
// dependency indices.
|
|
//
|
|
// This function MUST be called with the chain state lock held (for writes).
|
|
func (b *BlockChain) removeBlockNode(node *blockNode) error {
|
|
if node.parent != nil {
|
|
return AssertError(fmt.Sprintf("removeBlockNode must be "+
|
|
"called with a node at the front of the chain - node %v",
|
|
node.hash))
|
|
}
|
|
|
|
// Remove the node from the node index.
|
|
delete(b.index, *node.hash)
|
|
|
|
// Unlink all of the node's children.
|
|
for _, child := range node.children {
|
|
child.parent = nil
|
|
}
|
|
node.children = nil
|
|
|
|
// Remove the reference from the dependency index.
|
|
prevHash := node.parentHash
|
|
if children, ok := b.depNodes[*prevHash]; ok {
|
|
// Find the node amongst the children of the
|
|
// dependencies for the parent hash and remove it.
|
|
b.depNodes[*prevHash] = removeChildNode(children, node)
|
|
|
|
// Remove the map entry altogether if there are no
|
|
// longer any nodes which depend on the parent hash.
|
|
if len(b.depNodes[*prevHash]) == 0 {
|
|
delete(b.depNodes, *prevHash)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// isMajorityVersion determines if a previous number of blocks in the chain
|
|
// starting with startNode are at least the minimum passed version.
|
|
//
|
|
// This function MUST be called with the chain state lock held (for writes).
|
|
func (b *BlockChain) isMajorityVersion(minVer int32, startNode *blockNode, numRequired uint64) bool {
|
|
numFound := uint64(0)
|
|
iterNode := startNode
|
|
for i := uint64(0); i < b.chainParams.BlockUpgradeNumToCheck &&
|
|
numFound < numRequired && iterNode != nil; i++ {
|
|
// This node has a version that is at least the minimum version.
|
|
if iterNode.version >= minVer {
|
|
numFound++
|
|
}
|
|
|
|
// 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 {
|
|
break
|
|
}
|
|
}
|
|
|
|
return numFound >= numRequired
|
|
}
|
|
|
|
// 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([]time.Time, 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 medianTimestamp, nil
|
|
}
|
|
|
|
// CalcPastMedianTime calculates the median time of the previous few blocks
|
|
// prior to, and including, the end of the current best chain. It is primarily
|
|
// used to ensure new blocks have sane timestamps.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (b *BlockChain) CalcPastMedianTime() (time.Time, error) {
|
|
b.chainLock.Lock()
|
|
defer b.chainLock.Unlock()
|
|
|
|
return b.calcPastMedianTime(b.bestNode)
|
|
}
|
|
|
|
// SequenceLock represents the converted relative lock-time in seconds, and
|
|
// absolute block-height for a transaction input's relative lock-times.
|
|
// According to SequenceLock, after the referenced input has been confirmed
|
|
// within a block, a transaction spending that input can be included into a
|
|
// block either after 'seconds' (according to past median time), or once the
|
|
// 'BlockHeight' has been reached.
|
|
type SequenceLock struct {
|
|
Seconds int64
|
|
BlockHeight int32
|
|
}
|
|
|
|
// CalcSequenceLock computes a relative lock-time SequenceLock for the passed
|
|
// transaction using the passed UtxoViewpoint to obtain the past median time
|
|
// for blocks in which the referenced inputs of the transactions were included
|
|
// within. The generated SequenceLock lock can be used in conjunction with a
|
|
// block height, and adjusted median block time to determine if all the inputs
|
|
// referenced within a transaction have reached sufficient maturity allowing
|
|
// the candidate transaction to be included in a block.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (b *BlockChain) CalcSequenceLock(tx *btcutil.Tx, utxoView *UtxoViewpoint,
|
|
mempool bool) (*SequenceLock, error) {
|
|
|
|
b.chainLock.Lock()
|
|
defer b.chainLock.Unlock()
|
|
|
|
return b.calcSequenceLock(tx, utxoView, mempool)
|
|
}
|
|
|
|
// calcSequenceLock computes the relative lock-times for the passed
|
|
// transaction. See the exported version, CalcSequenceLock for further details.
|
|
//
|
|
// This function MUST be called with the chain state lock held (for writes).
|
|
func (b *BlockChain) calcSequenceLock(tx *btcutil.Tx, utxoView *UtxoViewpoint,
|
|
mempool bool) (*SequenceLock, error) {
|
|
|
|
mTx := tx.MsgTx()
|
|
|
|
// A value of -1 for each relative lock type represents a relative time
|
|
// lock value that will allow a transaction to be included in a block
|
|
// at any given height or time. This value is returned as the relative
|
|
// lock time in the case that BIP 68 is disabled, or has not yet been
|
|
// activated.
|
|
sequenceLock := &SequenceLock{Seconds: -1, BlockHeight: -1}
|
|
|
|
// If the transaction's version is less than 2, and BIP 68 has not yet
|
|
// been activated then sequence locks are disabled. Additionally,
|
|
// sequence locks don't apply to coinbase transactions Therefore, we
|
|
// return sequence lock values of -1 indicating that this transaction
|
|
// can be included within a block at any given height or time.
|
|
// TODO(roasbeef): check version bits state or pass as param
|
|
// * true should be replaced with a version bits state check
|
|
sequenceLockActive := mTx.Version >= 2 && (mempool || true)
|
|
if !sequenceLockActive || IsCoinBase(tx) {
|
|
return sequenceLock, nil
|
|
}
|
|
|
|
// Grab the next height to use for inputs present in the mempool.
|
|
nextHeight := b.BestSnapshot().Height + 1
|
|
|
|
for txInIndex, txIn := range mTx.TxIn {
|
|
utxo := utxoView.LookupEntry(&txIn.PreviousOutPoint.Hash)
|
|
if utxo == nil {
|
|
str := fmt.Sprintf("unable to find unspent output "+
|
|
"%v referenced from transaction %s:%d",
|
|
txIn.PreviousOutPoint, tx.Hash(), txInIndex)
|
|
return sequenceLock, ruleError(ErrMissingTx, str)
|
|
}
|
|
|
|
// If the input height is set to the mempool height, then we
|
|
// assume the transaction makes it into the next block when
|
|
// evaluating its sequence blocks.
|
|
inputHeight := utxo.BlockHeight()
|
|
if inputHeight == 0x7fffffff {
|
|
inputHeight = nextHeight
|
|
}
|
|
|
|
// Given a sequence number, we apply the relative time lock
|
|
// mask in order to obtain the time lock delta required before
|
|
// this input can be spent.
|
|
sequenceNum := txIn.Sequence
|
|
relativeLock := int64(sequenceNum & wire.SequenceLockTimeMask)
|
|
|
|
switch {
|
|
// Relative time locks are disabled for this input, so we can
|
|
// skip any further calculation.
|
|
case sequenceNum&wire.SequenceLockTimeDisabled == wire.SequenceLockTimeDisabled:
|
|
continue
|
|
case sequenceNum&wire.SequenceLockTimeIsSeconds == wire.SequenceLockTimeIsSeconds:
|
|
// This input requires a relative time lock expressed
|
|
// in seconds before it can be spent. Therefore, we
|
|
// need to query for the block prior to the one in
|
|
// which this input was included within so we can
|
|
// compute the past median time for the block prior to
|
|
// the one which included this referenced output.
|
|
// TODO: caching should be added to keep this speedy
|
|
inputDepth := uint32(b.bestNode.height-inputHeight) + 1
|
|
blockNode, err := b.relativeNode(b.bestNode, inputDepth)
|
|
if err != nil {
|
|
return sequenceLock, err
|
|
}
|
|
|
|
// With all the necessary block headers loaded into
|
|
// memory, we can now finally calculate the MTP of the
|
|
// block prior to the one which included the output
|
|
// being spent.
|
|
medianTime, err := b.calcPastMedianTime(blockNode)
|
|
if err != nil {
|
|
return sequenceLock, err
|
|
}
|
|
|
|
// Time based relative time-locks as defined by BIP 68
|
|
// have a time granularity of RelativeLockSeconds, so
|
|
// we shift left by this amount to convert to the
|
|
// proper relative time-lock. We also subtract one from
|
|
// the relative lock to maintain the original lockTime
|
|
// semantics.
|
|
timeLockSeconds := (relativeLock << wire.SequenceLockTimeGranularity) - 1
|
|
timeLock := medianTime.Unix() + timeLockSeconds
|
|
if timeLock > sequenceLock.Seconds {
|
|
sequenceLock.Seconds = timeLock
|
|
}
|
|
default:
|
|
// The relative lock-time for this input is expressed
|
|
// in blocks so we calculate the relative offset from
|
|
// the input's height as its converted absolute
|
|
// lock-time. We subtract one from the relative lock in
|
|
// order to maintain the original lockTime semantics.
|
|
blockHeight := inputHeight + int32(relativeLock-1)
|
|
if blockHeight > sequenceLock.BlockHeight {
|
|
sequenceLock.BlockHeight = blockHeight
|
|
}
|
|
}
|
|
}
|
|
|
|
return sequenceLock, nil
|
|
}
|
|
|
|
// getReorganizeNodes finds the fork point between the main chain and the passed
|
|
// node and returns a list of block nodes that would need to be detached from
|
|
// the main chain and a list of block nodes that would need to be attached to
|
|
// the fork point (which will be the end of the main chain after detaching the
|
|
// returned list of block nodes) in order to reorganize the chain such that the
|
|
// passed node is the new end of the main chain. The lists will be empty if the
|
|
// passed node is not on a side chain.
|
|
//
|
|
// This function MUST be called with the chain state lock held (for reads).
|
|
func (b *BlockChain) getReorganizeNodes(node *blockNode) (*list.List, *list.List) {
|
|
// Nothing to detach or attach if there is no node.
|
|
attachNodes := list.New()
|
|
detachNodes := list.New()
|
|
if node == nil {
|
|
return detachNodes, attachNodes
|
|
}
|
|
|
|
// Find the fork point (if any) adding each block to the list of nodes
|
|
// 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)
|
|
}
|
|
|
|
// TODO(davec): Use prevNodeFromNode function in case the requested
|
|
// node is further back than the what is in memory. This shouldn't
|
|
// happen in the normal course of operation, but the ability to fetch
|
|
// input transactions of arbitrary blocks will likely to be exposed at
|
|
// some point and that could lead to an issue here.
|
|
|
|
// 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
|
|
}
|
|
detachNodes.PushBack(n)
|
|
}
|
|
|
|
return detachNodes, attachNodes
|
|
}
|
|
|
|
// dbMaybeStoreBlock stores the provided block in the database if it's not
|
|
// already there.
|
|
func dbMaybeStoreBlock(dbTx database.Tx, block *btcutil.Block) error {
|
|
hasBlock, err := dbTx.HasBlock(block.Hash())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if hasBlock {
|
|
return nil
|
|
}
|
|
|
|
return dbTx.StoreBlock(block)
|
|
}
|
|
|
|
// connectBlock handles connecting the passed node/block to the end of the main
|
|
// (best) chain.
|
|
//
|
|
// This passed utxo view must have all referenced txos the block spends marked
|
|
// as spent and all of the new txos the block creates added to it. In addition,
|
|
// the passed stxos slice must be populated with all of the information for the
|
|
// spent txos. This approach is used because the connection validation that
|
|
// must happen prior to calling this function requires the same details, so
|
|
// it would be inefficient to repeat it.
|
|
//
|
|
// This function MUST be called with the chain state lock held (for writes).
|
|
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) {
|
|
return AssertError("connectBlock must be called with a block " +
|
|
"that extends the main chain")
|
|
}
|
|
|
|
// Sanity check the correct number of stxos are provided.
|
|
if len(stxos) != countSpentOutputs(block) {
|
|
return AssertError("connectBlock called with inconsistent " +
|
|
"spent transaction out information")
|
|
}
|
|
|
|
// Calculate the median time for the block.
|
|
medianTime, err := b.calcPastMedianTime(node)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Generate a new best state snapshot that will be used to update the
|
|
// database and later memory if all database updates are successful.
|
|
b.stateLock.RLock()
|
|
curTotalTxns := b.stateSnapshot.TotalTxns
|
|
b.stateLock.RUnlock()
|
|
numTxns := uint64(len(block.MsgBlock().Transactions))
|
|
blockSize := uint64(block.MsgBlock().SerializeSize())
|
|
state := newBestState(node, blockSize, numTxns, curTotalTxns+numTxns,
|
|
medianTime)
|
|
|
|
// Atomically insert info into the database.
|
|
err = b.db.Update(func(dbTx database.Tx) error {
|
|
// Update best block state.
|
|
err := dbPutBestState(dbTx, state, node.workSum)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Add the block hash and height to the block index which tracks
|
|
// the main chain.
|
|
err = dbPutBlockIndex(dbTx, block.Hash(), node.height)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Update the utxo set using the state of the utxo view. This
|
|
// entails removing all of the utxos spent and adding the new
|
|
// ones created by the block.
|
|
err = dbPutUtxoView(dbTx, view)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Update the transaction spend journal by adding a record for
|
|
// the block that contains all txos spent by it.
|
|
err = dbPutSpendJournalEntry(dbTx, block.Hash(), stxos)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Insert the block into the database if it's not already there.
|
|
err = dbMaybeStoreBlock(dbTx, block)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Allow the index manager to call each of the currently active
|
|
// optional indexes with the block being connected so they can
|
|
// update themselves accordingly.
|
|
if b.indexManager != nil {
|
|
err := b.indexManager.ConnectBlock(dbTx, block, view)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Prune fully spent entries and mark all entries in the view unmodified
|
|
// 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[*node.hash] = node
|
|
b.depNodes[*prevHash] = append(b.depNodes[*prevHash], node)
|
|
|
|
// This node is now the end of the best chain.
|
|
b.bestNode = node
|
|
|
|
// Update the state for the best block. Notice how this replaces the
|
|
// entire struct instead of updating the existing one. This effectively
|
|
// allows the old version to act as a snapshot which callers can use
|
|
// freely without needing to hold a lock for the duration. See the
|
|
// comments on the state variable for more details.
|
|
b.stateLock.Lock()
|
|
b.stateSnapshot = state
|
|
b.stateLock.Unlock()
|
|
|
|
// Notify the caller that the block was connected to the main chain.
|
|
// The caller would typically want to react with actions such as
|
|
// updating wallets.
|
|
b.chainLock.Unlock()
|
|
b.sendNotification(NTBlockConnected, block)
|
|
b.chainLock.Lock()
|
|
|
|
return nil
|
|
}
|
|
|
|
// disconnectBlock handles disconnecting the passed node/block from the end of
|
|
// the main (best) chain.
|
|
//
|
|
// 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) {
|
|
return AssertError("disconnectBlock must be called with the " +
|
|
"block at the end of the main chain")
|
|
}
|
|
|
|
// Get the previous block node. This function is used over simply
|
|
// accessing node.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.
|
|
prevNode, err := b.getPrevNodeFromNode(node)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Calculate the median time for the previous block.
|
|
medianTime, err := b.calcPastMedianTime(prevNode)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Load the previous block since some details for it are needed below.
|
|
var prevBlock *btcutil.Block
|
|
err = b.db.View(func(dbTx database.Tx) error {
|
|
var err error
|
|
prevBlock, err = dbFetchBlockByHash(dbTx, prevNode.hash)
|
|
return err
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Generate a new best state snapshot that will be used to update the
|
|
// database and later memory if all database updates are successful.
|
|
b.stateLock.RLock()
|
|
curTotalTxns := b.stateSnapshot.TotalTxns
|
|
b.stateLock.RUnlock()
|
|
numTxns := uint64(len(prevBlock.MsgBlock().Transactions))
|
|
blockSize := uint64(prevBlock.MsgBlock().SerializeSize())
|
|
newTotalTxns := curTotalTxns - uint64(len(block.MsgBlock().Transactions))
|
|
state := newBestState(prevNode, blockSize, numTxns, newTotalTxns,
|
|
medianTime)
|
|
|
|
err = b.db.Update(func(dbTx database.Tx) error {
|
|
// Update best block state.
|
|
err := dbPutBestState(dbTx, state, node.workSum)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Remove the block hash and height from the block index which
|
|
// tracks the main chain.
|
|
err = dbRemoveBlockIndex(dbTx, block.Hash(), node.height)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Update the utxo set using the state of the utxo view. This
|
|
// entails restoring all of the utxos spent and removing the new
|
|
// ones created by the block.
|
|
err = dbPutUtxoView(dbTx, view)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Update the transaction spend journal by removing the record
|
|
// that contains all txos spent by the block .
|
|
err = dbRemoveSpendJournalEntry(dbTx, block.Hash())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Allow the index manager to call each of the currently active
|
|
// optional indexes with the block being disconnected so they
|
|
// can update themselves accordingly.
|
|
if b.indexManager != nil {
|
|
err := b.indexManager.DisconnectBlock(dbTx, block, view)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Prune fully spent entries and mark all entries in the view unmodified
|
|
// now that the modifications have been committed to the database.
|
|
view.commit()
|
|
|
|
// Put block in the side chain cache.
|
|
node.inMainChain = false
|
|
b.blockCache[*node.hash] = block
|
|
|
|
// This node's parent is now the end of the best chain.
|
|
b.bestNode = node.parent
|
|
|
|
// Update the state for the best block. Notice how this replaces the
|
|
// entire struct instead of updating the existing one. This effectively
|
|
// allows the old version to act as a snapshot which callers can use
|
|
// freely without needing to hold a lock for the duration. See the
|
|
// comments on the state variable for more details.
|
|
b.stateLock.Lock()
|
|
b.stateSnapshot = state
|
|
b.stateLock.Unlock()
|
|
|
|
// Notify the caller that the block was disconnected from the main
|
|
// chain. The caller would typically want to react with actions such as
|
|
// updating wallets.
|
|
b.chainLock.Unlock()
|
|
b.sendNotification(NTBlockDisconnected, block)
|
|
b.chainLock.Lock()
|
|
|
|
return nil
|
|
}
|
|
|
|
// countSpentOutputs returns the number of utxos the passed block spends.
|
|
func countSpentOutputs(block *btcutil.Block) int {
|
|
// Exclude the coinbase transaction since it can't spend anything.
|
|
var numSpent int
|
|
for _, tx := range block.Transactions()[1:] {
|
|
numSpent += len(tx.MsgTx().TxIn)
|
|
}
|
|
return numSpent
|
|
}
|
|
|
|
// reorganizeChain reorganizes the block chain by disconnecting the nodes in the
|
|
// detachNodes list and connecting the nodes in the attach list. It expects
|
|
// that the lists are already in the correct order and are in sync with the
|
|
// end of the current best chain. Specifically, nodes that are being
|
|
// disconnected must be in reverse order (think of popping them off the end of
|
|
// the chain) and nodes the are being attached must be in forwards order
|
|
// (think pushing them onto the end of the chain).
|
|
//
|
|
// The flags modify the behavior of this function as follows:
|
|
// - BFDryRun: Only the checks which ensure the reorganize can be completed
|
|
// successfully are performed. The chain is not reorganized.
|
|
//
|
|
// This function MUST be called with the chain state lock held (for writes).
|
|
func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List, flags BehaviorFlags) error {
|
|
// Ensure all of the needed side chain blocks are in the cache.
|
|
for e := attachNodes.Front(); e != nil; e = e.Next() {
|
|
n := e.Value.(*blockNode)
|
|
if _, exists := b.blockCache[*n.hash]; !exists {
|
|
return AssertError(fmt.Sprintf("block %v is missing "+
|
|
"from the side chain block cache", n.hash))
|
|
}
|
|
}
|
|
|
|
// All of the blocks to detach and related spend journal entries needed
|
|
// to unspend transaction outputs in the blocks being disconnected must
|
|
// be loaded from the database during the reorg check phase below and
|
|
// then they are needed again when doing the actual database updates.
|
|
// Rather than doing two loads, cache the loaded data into these slices.
|
|
detachBlocks := make([]*btcutil.Block, 0, detachNodes.Len())
|
|
detachSpentTxOuts := make([][]spentTxOut, 0, detachNodes.Len())
|
|
|
|
// Disconnect all of the blocks back to the point of the fork. This
|
|
// entails loading the blocks and their associated spent txos from the
|
|
// 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)
|
|
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)
|
|
return err
|
|
})
|
|
|
|
// Load all of the utxos referenced by the block that aren't
|
|
// already in the view.
|
|
err = view.fetchInputUtxos(b.db, block)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Load all of the spent txos for the block from the spend
|
|
// journal.
|
|
var stxos []spentTxOut
|
|
err = b.db.View(func(dbTx database.Tx) error {
|
|
stxos, err = dbFetchSpendJournalEntry(dbTx, block, view)
|
|
return err
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Store the loaded block and spend journal entry for later.
|
|
detachBlocks = append(detachBlocks, block)
|
|
detachSpentTxOuts = append(detachSpentTxOuts, stxos)
|
|
|
|
err = view.disconnectTransactions(block, stxos)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Perform several checks to verify each block that needs to be attached
|
|
// to the main chain can be connected without violating any rules and
|
|
// without actually connecting the block.
|
|
//
|
|
// NOTE: These checks could be done directly when connecting a block,
|
|
// however the downside to that approach is that if any of these checks
|
|
// fail after disconnecting some blocks or attaching others, all of the
|
|
// operations have to be rolled back to get the chain back into the
|
|
// state it was before the rule violation (or other failure). There are
|
|
// at least a couple of ways accomplish that rollback, but both involve
|
|
// tweaking the chain and/or database. This approach catches these
|
|
// issues before ever modifying the chain.
|
|
for e := attachNodes.Front(); e != nil; e = e.Next() {
|
|
n := e.Value.(*blockNode)
|
|
block := b.blockCache[*n.hash]
|
|
|
|
// Notice the spent txout details are not requested here and
|
|
// thus will not be generated. This is done because the state
|
|
// is not being immediately written to the database, so it is
|
|
// not needed.
|
|
err := b.checkConnectBlock(n, block, view, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Skip disconnecting and connecting the blocks when running with the
|
|
// dry run flag set.
|
|
if flags&BFDryRun == BFDryRun {
|
|
return nil
|
|
}
|
|
|
|
// Reset the view for the actual connection code below. This is
|
|
// required because the view was previously modified when checking if
|
|
// the reorg would be successful and the connection code requires the
|
|
// view to be valid from the viewpoint of each block being connected or
|
|
// disconnected.
|
|
view = NewUtxoViewpoint()
|
|
view.SetBestHash(b.bestNode.hash)
|
|
|
|
// Disconnect blocks from the main chain.
|
|
for i, e := 0, detachNodes.Front(); e != nil; i, e = i+1, e.Next() {
|
|
n := e.Value.(*blockNode)
|
|
block := detachBlocks[i]
|
|
|
|
// Load all of the utxos referenced by the block that aren't
|
|
// already in the view.
|
|
err := view.fetchInputUtxos(b.db, block)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Update the view to unspend all of the spent txos and remove
|
|
// the utxos created by the block.
|
|
err = view.disconnectTransactions(block, detachSpentTxOuts[i])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Update the database and chain state.
|
|
err = b.disconnectBlock(n, block, view)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Connect the new best chain blocks.
|
|
for e := attachNodes.Front(); e != nil; e = e.Next() {
|
|
n := e.Value.(*blockNode)
|
|
block := b.blockCache[*n.hash]
|
|
|
|
// Load all of the utxos referenced by the block that aren't
|
|
// already in the view.
|
|
err := view.fetchInputUtxos(b.db, block)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Update the view to mark all utxos referenced by the block
|
|
// as spent and add all transactions being created by this block
|
|
// to it. Also, provide an stxo slice so the spent txout
|
|
// details are generated.
|
|
stxos := make([]spentTxOut, 0, countSpentOutputs(block))
|
|
err = view.connectTransactions(block, &stxos)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Update the database and chain state.
|
|
err = b.connectBlock(n, block, view, stxos)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
delete(b.blockCache, *n.hash)
|
|
}
|
|
|
|
// Log the point where the chain forked.
|
|
firstAttachNode := attachNodes.Front().Value.(*blockNode)
|
|
forkNode, err := b.getPrevNodeFromNode(firstAttachNode)
|
|
if err == nil {
|
|
log.Infof("REORGANIZE: Chain forks at %v", forkNode.hash)
|
|
}
|
|
|
|
// Log the old and new best chain heads.
|
|
firstDetachNode := detachNodes.Front().Value.(*blockNode)
|
|
lastAttachNode := attachNodes.Back().Value.(*blockNode)
|
|
log.Infof("REORGANIZE: Old best chain head was %v", firstDetachNode.hash)
|
|
log.Infof("REORGANIZE: New best chain head is %v", lastAttachNode.hash)
|
|
|
|
return nil
|
|
}
|
|
|
|
// connectBestChain handles connecting the passed block to the chain while
|
|
// respecting proper chain selection according to the chain with the most
|
|
// proof of work. In the typical case, the new block simply extends the main
|
|
// chain. However, it may also be extending (or creating) a side chain (fork)
|
|
// which may or may not end up becoming the main chain depending on which fork
|
|
// cumulatively has the most proof of work. It returns whether or not the block
|
|
// ended up on the main chain (either due to extending the main chain or causing
|
|
// a reorganization to become the main chain).
|
|
//
|
|
// The flags modify the behavior of this function as follows:
|
|
// - BFFastAdd: Avoids several expensive transaction validation operations.
|
|
// This is useful when using checkpoints.
|
|
// - BFDryRun: Prevents the block from being connected and avoids modifying the
|
|
// state of the memory chain index. Also, any log messages related to
|
|
// modifying the state are avoided.
|
|
//
|
|
// This function MUST be called with the chain state lock held (for writes).
|
|
func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, flags BehaviorFlags) (bool, error) {
|
|
fastAdd := flags&BFFastAdd == BFFastAdd
|
|
dryRun := flags&BFDryRun == BFDryRun
|
|
|
|
// We are extending the main (best) chain with a new block. This is the
|
|
// most common case.
|
|
if node.parentHash.IsEqual(b.bestNode.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.
|
|
view := NewUtxoViewpoint()
|
|
view.SetBestHash(node.parentHash)
|
|
stxos := make([]spentTxOut, 0, countSpentOutputs(block))
|
|
if !fastAdd {
|
|
err := b.checkConnectBlock(node, block, view, &stxos)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
}
|
|
|
|
// Don't connect the block if performing a dry run.
|
|
if dryRun {
|
|
return true, nil
|
|
}
|
|
|
|
// In the fast add case the code to check the block connection
|
|
// was skipped, so the utxo view needs to load the referenced
|
|
// utxos, spend them, and add the new utxos being created by
|
|
// this block.
|
|
if fastAdd {
|
|
err := view.fetchInputUtxos(b.db, block)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
err = view.connectTransactions(block, &stxos)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
}
|
|
|
|
// Connect the block to the main chain.
|
|
err := b.connectBlock(node, block, view, stxos)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
// Connect the parent node to this node.
|
|
if node.parent != nil {
|
|
node.parent.children = append(node.parent.children, node)
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
if fastAdd {
|
|
log.Warnf("fastAdd set in the side chain case? %v\n",
|
|
block.Hash())
|
|
}
|
|
|
|
// We're extending (or creating) a side chain which may or may not
|
|
// become the main chain, but in either case we need the block stored
|
|
// for future processing, so add the block to the side chain holding
|
|
// cache.
|
|
if !dryRun {
|
|
log.Debugf("Adding block %v to side chain cache", node.hash)
|
|
}
|
|
b.blockCache[*node.hash] = block
|
|
b.index[*node.hash] = node
|
|
|
|
// Connect the parent node to this node.
|
|
node.inMainChain = false
|
|
node.parent.children = append(node.parent.children, node)
|
|
|
|
// Remove the block from the side chain cache and disconnect it from the
|
|
// parent node when the function returns when running in dry run mode.
|
|
if dryRun {
|
|
defer func() {
|
|
children := node.parent.children
|
|
children = removeChildNode(children, node)
|
|
node.parent.children = children
|
|
|
|
delete(b.index, *node.hash)
|
|
delete(b.blockCache, *node.hash)
|
|
}()
|
|
}
|
|
|
|
// 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 {
|
|
// 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.
|
|
if fork.hash.IsEqual(node.parent.hash) {
|
|
log.Infof("FORK: Block %v forks the chain at height %d"+
|
|
"/block %v, but does not cause a reorganize",
|
|
node.hash, fork.height, fork.hash)
|
|
} else {
|
|
log.Infof("EXTEND FORK: Block %v extends a side chain "+
|
|
"which forks the chain at height %d/block %v",
|
|
node.hash, fork.height, fork.hash)
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
// We're extending (or creating) a side chain and the cumulative work
|
|
// for this new side chain is more than the old best chain, so this side
|
|
// chain needs to become the main chain. In order to accomplish that,
|
|
// find the common ancestor of both sides of the fork, disconnect the
|
|
// blocks that form the (now) old fork from the main chain, and attach
|
|
// the blocks that form the new chain to the main chain starting at the
|
|
// common ancenstor (the point where the chain forked).
|
|
detachNodes, attachNodes := b.getReorganizeNodes(node)
|
|
|
|
// Reorganize the chain.
|
|
if !dryRun {
|
|
log.Infof("REORGANIZE: Block %v is causing a reorganize.",
|
|
node.hash)
|
|
}
|
|
err := b.reorganizeChain(detachNodes, attachNodes, flags)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
// IsCurrent returns whether or not the chain believes it is current. Several
|
|
// factors are used to guess, but the key factors that allow the chain to
|
|
// believe it is current are:
|
|
// - Latest block height is after the latest checkpoint (if enabled)
|
|
// - Latest block has a timestamp newer than 24 hours ago
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (b *BlockChain) IsCurrent() bool {
|
|
b.chainLock.RLock()
|
|
defer b.chainLock.RUnlock()
|
|
|
|
// 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 {
|
|
return false
|
|
}
|
|
|
|
// Not current if the latest best block has a timestamp before 24 hours
|
|
// ago.
|
|
minus24Hours := b.timeSource.AdjustedTime().Add(-24 * time.Hour)
|
|
if b.bestNode.timestamp.Before(minus24Hours) {
|
|
return false
|
|
}
|
|
|
|
// The chain appears to be current if the above checks did not report
|
|
// otherwise.
|
|
return true
|
|
}
|
|
|
|
// BestSnapshot returns information about the current best chain block and
|
|
// related state as of the current point in time. The returned instance must be
|
|
// treated as immutable since it is shared by all callers.
|
|
//
|
|
// This function is safe for concurrent access.
|
|
func (b *BlockChain) BestSnapshot() *BestState {
|
|
b.stateLock.RLock()
|
|
snapshot := b.stateSnapshot
|
|
b.stateLock.RUnlock()
|
|
return snapshot
|
|
}
|
|
|
|
// 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.
|
|
type IndexManager interface {
|
|
// Init is invoked during chain initialize in order to allow the index
|
|
// manager to initialize itself and any indexes it is managing.
|
|
Init(*BlockChain) error
|
|
|
|
// ConnectBlock is invoked when a new block has been connected to the
|
|
// main chain.
|
|
ConnectBlock(database.Tx, *btcutil.Block, *UtxoViewpoint) error
|
|
|
|
// DisconnectBlock is invoked when a block has been disconnected from
|
|
// the main chain.
|
|
DisconnectBlock(database.Tx, *btcutil.Block, *UtxoViewpoint) error
|
|
}
|
|
|
|
// Config is a descriptor which specifies the blockchain instance configuration.
|
|
type Config struct {
|
|
// DB defines the database which houses the blocks and will be used to
|
|
// store all metadata created by this package such as the utxo set.
|
|
//
|
|
// This field is required.
|
|
DB database.DB
|
|
|
|
// ChainParams identifies which chain parameters the chain is associated
|
|
// with.
|
|
//
|
|
// This field is required.
|
|
ChainParams *chaincfg.Params
|
|
|
|
// TimeSource defines the median time source to use for things such as
|
|
// block processing and determining whether or not the chain is current.
|
|
//
|
|
// The caller is expected to keep a reference to the time source as well
|
|
// and add time samples from other peers on the network so the local
|
|
// time is adjusted to be in agreement with other peers.
|
|
TimeSource MedianTimeSource
|
|
|
|
// Notifications defines a callback to which notifications will be sent
|
|
// when various events take place. See the documentation for
|
|
// Notification and NotificationType for details on the types and
|
|
// contents of notifications.
|
|
//
|
|
// This field can be nil if the caller is not interested in receiving
|
|
// notifications.
|
|
Notifications NotificationCallback
|
|
|
|
// SigCache defines a signature cache to use when when validating
|
|
// signatures. This is typically most useful when individual
|
|
// transactions are already being validated prior to their inclusion in
|
|
// a block such as what is usually done via a transaction memory pool.
|
|
//
|
|
// This field can be nil if the caller is not interested in using a
|
|
// signature cache.
|
|
SigCache *txscript.SigCache
|
|
|
|
// IndexManager defines an index manager to use when initializing the
|
|
// chain and connecting and disconnecting blocks.
|
|
//
|
|
// This field can be nil if the caller does not wish to make use of an
|
|
// index manager.
|
|
IndexManager IndexManager
|
|
}
|
|
|
|
// New returns a BlockChain instance using the provided configuration details.
|
|
func New(config *Config) (*BlockChain, error) {
|
|
// Enforce required config fields.
|
|
if config.DB == nil {
|
|
return nil, AssertError("blockchain.New database is nil")
|
|
}
|
|
if config.ChainParams == nil {
|
|
return nil, AssertError("blockchain.New chain parameters nil")
|
|
}
|
|
|
|
// Generate a checkpoint by height map from the provided checkpoints.
|
|
params := config.ChainParams
|
|
var checkpointsByHeight map[int32]*chaincfg.Checkpoint
|
|
if len(params.Checkpoints) > 0 {
|
|
checkpointsByHeight = make(map[int32]*chaincfg.Checkpoint)
|
|
for i := range params.Checkpoints {
|
|
checkpoint := ¶ms.Checkpoints[i]
|
|
checkpointsByHeight[checkpoint.Height] = checkpoint
|
|
}
|
|
}
|
|
|
|
targetTimespan := int64(params.TargetTimespan)
|
|
targetTimePerBlock := int64(params.TargetTimePerBlock)
|
|
adjustmentFactor := params.RetargetAdjustmentFactor
|
|
b := BlockChain{
|
|
checkpointsByHeight: checkpointsByHeight,
|
|
db: config.DB,
|
|
chainParams: params,
|
|
timeSource: config.TimeSource,
|
|
notifications: config.Notifications,
|
|
sigCache: config.SigCache,
|
|
indexManager: config.IndexManager,
|
|
minRetargetTimespan: targetTimespan / adjustmentFactor,
|
|
maxRetargetTimespan: targetTimespan * adjustmentFactor,
|
|
blocksPerRetarget: int32(targetTimespan / targetTimePerBlock),
|
|
minMemoryNodes: int32(targetTimespan / targetTimePerBlock),
|
|
bestNode: nil,
|
|
index: make(map[chainhash.Hash]*blockNode),
|
|
depNodes: make(map[chainhash.Hash][]*blockNode),
|
|
orphans: make(map[chainhash.Hash]*orphanBlock),
|
|
prevOrphans: make(map[chainhash.Hash][]*orphanBlock),
|
|
blockCache: make(map[chainhash.Hash]*btcutil.Block),
|
|
}
|
|
|
|
// Initialize the chain state from the passed database. When the db
|
|
// does not yet contain any chain state, both it and the chain state
|
|
// will be initialized to contain only the genesis block.
|
|
if err := b.initChainState(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Initialize and catch up all of the currently active optional indexes
|
|
// as needed.
|
|
if config.IndexManager != nil {
|
|
if err := config.IndexManager.Init(&b); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
log.Infof("Chain state (height %d, hash %v, totaltx %d, work %v)",
|
|
b.bestNode.height, b.bestNode.hash, b.stateSnapshot.TotalTxns,
|
|
b.bestNode.workSum)
|
|
|
|
return &b, nil
|
|
}
|