lbcd/blockchain/chain.go
Olaoluwa Osuntokun 75569f599f
blockchain: enforce CSV soft-fork validation based on versionbits state
This commit modifies the existing block validation logic to examine the
current version bits state of the CSV soft-fork, enforcing the new
validation rules (BIPs 68, 112, and 113) accordingly based on the
current `ThesholdState`.
2017-05-10 15:37:11 -07:00

1418 lines
50 KiB
Go

// Copyright (c) 2013-2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain
import (
"container/list"
"fmt"
"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
)
// 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
}
// 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.
checkpoints []chaincfg.Checkpoint
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
// These fields are related to the memory block index. The best node
// is protected by the chain lock and the index has its own locks.
bestNode *blockNode
index *blockIndex
// These fields are related to 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
// 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
// The following caches are used to efficiently keep track of the
// current deployment threshold state of each rule change deployment.
//
// This information is stored in the database so it can be quickly
// reconstructed on load.
//
// warningCaches caches the current deployment threshold state for blocks
// in each of the **possible** deployments. This is used in order to
// detect when new unrecognized rule changes are being voted on and/or
// have been activated such as will be the case when older versions of
// the software are being used
//
// deploymentCaches caches the current deployment threshold state for
// blocks in each of the actively defined deployments.
warningCaches []thresholdStateCache
deploymentCaches []thresholdStateCache
// The following fields are used to determine if certain warnings have
// already been shown.
//
// unknownRulesWarned refers to warnings due to unknown rules being
// activated.
//
// unknownVersionsWarned refers to warnings due to unknown versions
// being mined.
unknownRulesWarned bool
unknownVersionsWarned bool
}
// 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
}
// 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(b.bestNode, 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(node *blockNode, tx *btcutil.Tx,
utxoView *UtxoViewpoint, mempool bool) (*SequenceLock, error) {
// 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}
// The sequence locks semantics are always active for transactions
// within the mempool.
csvSoftforkActive := mempool
// If we're performing block validation, then we need to query the BIP9
// state.
if !csvSoftforkActive {
prevNode, err := b.index.PrevNodeFromNode(node)
if err != nil {
return nil, err
}
// Obtain the latest BIP9 version bits state for the
// CSV-package soft-fork deployment. The adherence of sequence
// locks depends on the current soft-fork state.
csvState, err := b.deploymentState(prevNode, chaincfg.DeploymentCSV)
if err != nil {
return nil, err
}
csvSoftforkActive = csvState == ThresholdActive
}
// 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.
mTx := tx.MsgTx()
sequenceLockActive := mTx.Version >= 2 && csvSoftforkActive
if !sequenceLockActive || IsCoinBase(tx) {
return sequenceLock, nil
}
// Grab the next height from the PoV of the passed blockNode to use for
// inputs present in the mempool.
nextHeight := node.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(node.height-inputHeight) + 1
blockNode, err := b.index.RelativeNode(node, 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.index.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
}
// LockTimeToSequence converts the passed relative locktime to a sequence
// number in accordance to BIP-68.
// See: https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki
// * (Compatibility)
func LockTimeToSequence(isSeconds bool, locktime uint32) uint32 {
// If we're expressing the relative lock time in blocks, then the
// corresponding sequence number is simply the desired input age.
if !isSeconds {
return locktime
}
// Set the 22nd bit which indicates the lock time is in seconds, then
// shift the locktime over by 9 since the time granularity is in
// 512-second intervals (2^9). This results in a max lock-time of
// 33,553,920 seconds, or 1.1 years.
return wire.SequenceLockTimeIsSeconds |
locktime>>wire.SequenceLockTimeGranularity
}
// 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")
}
// No warnings about unknown rules or versions until the chain is
// current.
if b.isCurrent() {
// Warn if any unknown new rules are either about to activate or
// have already been activated.
if err := b.warnUnknownRuleActivations(node); err != nil {
return err
}
// Warn if a high enough percentage of the last blocks have
// unexpected versions.
if err := b.warnUnknownVersions(node); err != nil {
return err
}
}
// Calculate the median time for the block.
medianTime, err := b.index.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
}
// 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
}
}
// Update the cached threshold states in the database as needed.
return b.putThresholdCaches(dbTx)
})
if err != nil {
return err
}
// Mark all modified entries in the threshold caches as flushed now that
// they have been committed to the database.
b.markThresholdCachesFlushed()
// 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.AddNode(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.index.PrevNodeFromNode(node)
if err != nil {
return err
}
// Calculate the median time for the previous block.
medianTime, err := b.index.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()
// Mark block as being in a side chain.
node.inMainChain = false
// This node's parent is now the end of the best chain.
b.bestNode = node.parent
// 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 {
// 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())
attachBlocks := make([]*btcutil.Block, 0, attachNodes.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
})
if err != nil {
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)
var block *btcutil.Block
err := b.db.View(func(dbTx database.Tx) error {
// NOTE: This block is not in the main chain, so the
// block has to be loaded directly from the database
// instead of using the dbFetchBlockByHash function.
blockBytes, err := dbTx.FetchBlock(&n.hash)
if err != nil {
return err
}
block, err = btcutil.NewBlockFromBytes(blockBytes)
if err != nil {
return err
}
block.SetHeight(n.height)
return nil
})
if err != nil {
return err
}
// Store the loaded block for later.
attachBlocks = append(attachBlocks, block)
// 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 i, e := 0, attachNodes.Front(); e != nil; i, e = i+1, e.Next() {
n := e.Value.(*blockNode)
block := attachBlocks[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 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
}
}
// Log the point where the chain forked.
firstAttachNode := attachNodes.Front().Value.(*blockNode)
forkNode, err := b.index.PrevNodeFromNode(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 the entry is needed in the
// index for future processing.
b.index.Lock()
b.index.index[node.hash] = node
b.index.Unlock()
// Connect the parent node to this node.
node.inMainChain = false
node.parent.children = append(node.parent.children, node)
// 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
b.index.Lock()
delete(b.index.index, node.hash)
b.index.Unlock()
}()
}
// 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 MUST be called with the chain state lock held (for reads).
func (b *BlockChain) isCurrent() bool {
// Not current if the latest main (best) chain height is before the
// latest known good checkpoint (when checkpoints are enabled).
checkpoint := b.LatestCheckpoint()
if checkpoint != nil && b.bestNode.height < checkpoint.Height {
return false
}
// Not current if the latest best block has a timestamp before 24 hours
// ago.
//
// The chain appears to be current if none of the checks reported
// otherwise.
minus24Hours := b.timeSource.AdjustedTime().Add(-24 * time.Hour).Unix()
return b.bestNode.timestamp >= minus24Hours
}
// 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()
return b.isCurrent()
}
// 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
}
// FetchHeader returns the block header identified by the given hash or an error
// if it doesn't exist.
func (b *BlockChain) FetchHeader(hash *chainhash.Hash) (wire.BlockHeader, error) {
// Reconstruct the header from the block index if possible.
if node := b.index.LookupNode(hash); node != nil {
return node.Header(), nil
}
// Fall back to loading it from the database.
var header *wire.BlockHeader
err := b.db.View(func(dbTx database.Tx) error {
var err error
header, err = dbFetchHeaderByHash(dbTx, hash)
return err
})
if err != nil {
return wire.BlockHeader{}, err
}
return *header, nil
}
// IndexManager provides a generic interface that the is called when blocks are
// connected and disconnected to and from the tip of the main chain for the
// purpose of supporting optional indexes.
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
// Checkpoints hold caller-defined checkpoints that should be added to
// the default checkpoints in ChainParams. Checkpoints must be sorted
// by height.
//
// This field can be nil if the caller does not wish to specify any
// checkpoints.
Checkpoints []chaincfg.Checkpoint
// 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")
}
if config.TimeSource == nil {
return nil, AssertError("blockchain.New timesource is nil")
}
// Generate a checkpoint by height map from the provided checkpoints
// and assert the provided checkpoints are sorted by height as required.
var checkpointsByHeight map[int32]*chaincfg.Checkpoint
var prevCheckpointHeight int32
if len(config.Checkpoints) > 0 {
checkpointsByHeight = make(map[int32]*chaincfg.Checkpoint)
for i := range config.Checkpoints {
checkpoint := &config.Checkpoints[i]
if checkpoint.Height <= prevCheckpointHeight {
return nil, AssertError("blockchain.New " +
"checkpoints are not sorted by height")
}
checkpointsByHeight[checkpoint.Height] = checkpoint
prevCheckpointHeight = checkpoint.Height
}
}
params := config.ChainParams
targetTimespan := int64(params.TargetTimespan / time.Second)
targetTimePerBlock := int64(params.TargetTimePerBlock / time.Second)
adjustmentFactor := params.RetargetAdjustmentFactor
b := BlockChain{
checkpoints: config.Checkpoints,
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),
index: newBlockIndex(config.DB, params),
orphans: make(map[chainhash.Hash]*orphanBlock),
prevOrphans: make(map[chainhash.Hash][]*orphanBlock),
warningCaches: newThresholdCaches(vbNumBits),
deploymentCaches: newThresholdCaches(chaincfg.DefinedDeployments),
}
// 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
}
}
// Initialize rule change threshold state caches from the passed
// database. When the db does not yet contains any cached information
// for a given threshold cache, the threshold states will be calculated
// using the chain state.
if err := b.initThresholdCaches(); 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
}