+++ /dev/null
-// 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
-)
-
-// BlockLocator is used to help locate a specific block. The algorithm for
-// building the block locator is to add the hashes in reverse order until
-// the genesis block is reached. In order to keep the list of locator hashes
-// to a reasonable number of entries, first the most recent previous 12 block
-// hashes are added, then the step is doubled each loop iteration to
-// exponentially decrease the number of hashes as a function of the distance
-// from the block being located.
-//
-// For example, assume a block chain with a side chain as depicted below:
-// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
-// \-> 16a -> 17a
-//
-// The block locator for block 17a would be the hashes of blocks:
-// [17a 16a 15 14 13 12 11 10 9 8 7 6 4 genesis]
-type BlockLocator []*chainhash.Hash
-
-// orphanBlock represents a block that we don't yet have the parent for. It
-// is a normal block plus an expiration time to prevent caching the orphan
-// forever.
-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.
- BlockWeight uint64 // The weight 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, blockWeight, numTxns,
- totalTxns uint64, medianTime time.Time) *BestState {
-
- return &BestState{
- Hash: node.hash,
- Height: node.height,
- Bits: node.bits,
- BlockSize: blockSize,
- BlockWeight: blockWeight,
- 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
- sigCache *txscript.SigCache
- indexManager IndexManager
- hashCache *txscript.HashCache
-
- // 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.
- minRetargetTimespan int64 // target timespan / adjustment factor
- maxRetargetTimespan int64 // target timespan * adjustment factor
- blocksPerRetarget int32 // target timespan / target time per block
-
- // chainLock protects concurrent access to the vast majority of the
- // fields in this struct below this point.
- chainLock sync.RWMutex
-
- // These fields are related to the memory block index. They both have
- // their own locks, however they are often also protected by the chain
- // lock to help prevent logic races when blocks are being processed.
- //
- // index houses the entire block index in memory. The block index is
- // a tree-shaped structure.
- //
- // bestChain tracks the current active chain by making use of an
- // efficient chain view into the block index.
- index *blockIndex
- bestChain *chainView
-
- // These fields are related to handling of orphan blocks. They are
- // protected by a combination of the chain lock and the orphan lock.
- 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
- checkpointNode *blockNode
-
- // 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
-
- // The notifications field stores a slice of callbacks to be executed on
- // certain blockchain events.
- notificationsLock sync.RWMutex
- notifications []NotificationCallback
-}
-
-// 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) {
- exists, err := b.blockExists(hash)
- 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)
-}
-
-// 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.bestChain.Tip(), 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 {
- // 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(node.parent, 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("output %v referenced from "+
- "transaction %s:%d either does not exist or "+
- "has already been spent", txIn.PreviousOutPoint,
- tx.Hash(), txInIndex)
- return sequenceLock, ruleError(ErrMissingTxOut, 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.
- prevInputHeight := inputHeight - 1
- if prevInputHeight < 0 {
- prevInputHeight = 0
- }
- blockNode := node.Ancestor(prevInputHeight)
- medianTime := blockNode.CalcPastMedianTime()
-
- // 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.
- forkNode := b.bestChain.FindFork(node)
- for n := node; n != nil && n != forkNode; n = n.parent {
- attachNodes.PushFront(n)
- }
-
- // Start from the end of the main chain and work backwards until the
- // common ancestor adding each block to the list of nodes to detach from
- // the main chain.
- for n := b.bestChain.Tip(); n != nil && n != forkNode; n = n.parent {
- 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.bestChain.Tip().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
- }
- }
-
- // 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())
- blockWeight := uint64(GetBlockWeight(block))
- state := newBestState(node, blockSize, blockWeight, numTxns,
- curTotalTxns+numTxns, node.CalcPastMedianTime())
-
- // 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
- }
- }
-
- 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()
-
- // This node is now the end of the best chain.
- b.bestChain.SetTip(node)
-
- // Update the state for the best block. Notice how this replaces the
- // entire struct instead of updating the existing one. This effectively
- // 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.bestChain.Tip().hash) {
- return AssertError("disconnectBlock must be called with the " +
- "block at the end of the main chain")
- }
-
- // Load the previous block since some details for it are needed below.
- prevNode := node.parent
- var prevBlock *btcutil.Block
- err := b.db.View(func(dbTx database.Tx) error {
- var err error
- prevBlock, err = dbFetchBlockByNode(dbTx, prevNode)
- 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())
- blockWeight := uint64(GetBlockWeight(prevBlock))
- newTotalTxns := curTotalTxns - uint64(len(block.MsgBlock().Transactions))
- state := newBestState(prevNode, blockSize, blockWeight, numTxns,
- newTotalTxns, prevNode.CalcPastMedianTime())
-
- 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()
-
- // This node's parent is now the end of the best chain.
- b.bestChain.SetTip(node.parent)
-
- // Update the state for the best block. Notice how this replaces the
- // entire struct instead of updating the existing one. This effectively
- // 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.bestChain.Tip().hash)
- for e := detachNodes.Front(); e != nil; e = e.Next() {
- n := e.Value.(*blockNode)
- var block *btcutil.Block
- err := b.db.View(func(dbTx database.Tx) error {
- var err error
- block, err = dbFetchBlockByNode(dbTx, n)
- 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 {
- var err error
- block, err = dbFetchBlockByNode(dbTx, n)
- return err
- })
- 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.bestChain.Tip().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 and old and new best chain
- // heads.
- firstAttachNode := attachNodes.Front().Value.(*blockNode)
- firstDetachNode := detachNodes.Front().Value.(*blockNode)
- lastAttachNode := attachNodes.Back().Value.(*blockNode)
- log.Infof("REORGANIZE: Chain forks at %v", firstAttachNode.parent.hash)
- 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 actually being connected and avoids any
-// log messages related to modifying the state.
-//
-// 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.
- parentHash := &block.MsgBlock().Header.PrevBlock
- if parentHash.IsEqual(&b.bestChain.Tip().hash) {
- // Perform several checks to verify the block can be connected
- // to the main chain without violating any rules and without
- // actually connecting the block.
- view := NewUtxoViewpoint()
- view.SetBestHash(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
- }
-
- 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, but the cumulative
- // work for this new side chain is not enough to make it the new chain.
- if node.workSum.Cmp(b.bestChain.Tip().workSum) <= 0 {
- // Skip Logging info when the dry run flag is set.
- if dryRun {
- return false, nil
- }
-
- // Log information about how the block is forking the chain.
- fork := b.bestChain.FindFork(node)
- if fork.hash.IsEqual(parentHash) {
- log.Infof("FORK: Block %v forks the chain at height %d"+
- "/block %v, but does not cause a reorganize",
- 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.bestChain.Tip().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.bestChain.Tip().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
-}
-
-// MainChainHasBlock returns whether or not the block with the given hash is in
-// the main chain.
-//
-// This function is safe for concurrent access.
-func (b *BlockChain) MainChainHasBlock(hash *chainhash.Hash) bool {
- node := b.index.LookupNode(hash)
- return node != nil && b.bestChain.Contains(node)
-}
-
-// BlockLocatorFromHash returns a block locator for the passed block hash.
-// See BlockLocator for details on the algorithm used to create a block locator.
-//
-// In addition to the general algorithm referenced above, this function will
-// return the block locator for the latest known tip of the main (best) chain if
-// the passed hash is not currently known.
-//
-// This function is safe for concurrent access.
-func (b *BlockChain) BlockLocatorFromHash(hash *chainhash.Hash) BlockLocator {
- b.chainLock.RLock()
- node := b.index.LookupNode(hash)
- locator := b.bestChain.blockLocator(node)
- b.chainLock.RUnlock()
- return locator
-}
-
-// LatestBlockLocator returns a block locator for the latest known tip of the
-// main (best) chain.
-//
-// This function is safe for concurrent access.
-func (b *BlockChain) LatestBlockLocator() (BlockLocator, error) {
- b.chainLock.RLock()
- locator := b.bestChain.BlockLocator(nil)
- b.chainLock.RUnlock()
- return locator, nil
-}
-
-// BlockHeightByHash returns the height of the block with the given hash in the
-// main chain.
-//
-// This function is safe for concurrent access.
-func (b *BlockChain) BlockHeightByHash(hash *chainhash.Hash) (int32, error) {
- node := b.index.LookupNode(hash)
- if node == nil || !b.bestChain.Contains(node) {
- str := fmt.Sprintf("block %s is not in the main chain", hash)
- return 0, errNotInMainChain(str)
- }
-
- return node.height, nil
-}
-
-// BlockHashByHeight returns the hash of the block at the given height in the
-// main chain.
-//
-// This function is safe for concurrent access.
-func (b *BlockChain) BlockHashByHeight(blockHeight int32) (*chainhash.Hash, error) {
- node := b.bestChain.NodeByHeight(blockHeight)
- if node == nil {
- str := fmt.Sprintf("no block at height %d exists", blockHeight)
- return nil, errNotInMainChain(str)
-
- }
-
- return &node.hash, nil
-}
-
-// HeightRange returns a range of block hashes for the given start and end
-// heights. It is inclusive of the start height and exclusive of the end
-// height. The end height will be limited to the current main chain height.
-//
-// This function is safe for concurrent access.
-func (b *BlockChain) HeightRange(startHeight, endHeight int32) ([]chainhash.Hash, error) {
- // Ensure requested heights are sane.
- if startHeight < 0 {
- return nil, fmt.Errorf("start height of fetch range must not "+
- "be less than zero - got %d", startHeight)
- }
- if endHeight < startHeight {
- return nil, fmt.Errorf("end height of fetch range must not "+
- "be less than the start height - got start %d, end %d",
- startHeight, endHeight)
- }
-
- // There is nothing to do when the start and end heights are the same,
- // so return now to avoid the chain view lock.
- if startHeight == endHeight {
- return nil, nil
- }
-
- // Grab a lock on the chain view to prevent it from changing due to a
- // reorg while building the hashes.
- b.bestChain.mtx.Lock()
- defer b.bestChain.mtx.Unlock()
-
- // When the requested start height is after the most recent best chain
- // height, there is nothing to do.
- latestHeight := b.bestChain.tip().height
- if startHeight > latestHeight {
- return nil, nil
- }
-
- // Limit the ending height to the latest height of the chain.
- if endHeight > latestHeight+1 {
- endHeight = latestHeight + 1
- }
-
- // Fetch as many as are available within the specified range.
- hashes := make([]chainhash.Hash, 0, endHeight-startHeight)
- for i := startHeight; i < endHeight; i++ {
- hashes = append(hashes, b.bestChain.nodeByHeight(i).hash)
- }
- return hashes, nil
-}
-
-// locateInventory returns the node of the block after the first known block in
-// the locator along with the number of subsequent nodes needed to either reach
-// the provided stop hash or the provided max number of entries.
-//
-// In addition, there are two special cases:
-//
-// - When no locators are provided, the stop hash is treated as a request for
-// that block, so it will either return the node associated with the stop hash
-// if it is known, or nil if it is unknown
-// - When locators are provided, but none of them are known, nodes starting
-// after the genesis block will be returned
-//
-// This is primarily a helper function for the locateBlocks and locateHeaders
-// functions.
-//
-// This function MUST be called with the chain state lock held (for reads).
-func (b *BlockChain) locateInventory(locator BlockLocator, hashStop *chainhash.Hash, maxEntries uint32) (*blockNode, uint32) {
- // There are no block locators so a specific block is being requested
- // as identified by the stop hash.
- stopNode := b.index.LookupNode(hashStop)
- if len(locator) == 0 {
- if stopNode == nil {
- // No blocks with the stop hash were found so there is
- // nothing to do.
- return nil, 0
- }
- return stopNode, 1
- }
-
- // Find the most recent locator block hash in the main chain. In the
- // case none of the hashes in the locator are in the main chain, fall
- // back to the genesis block.
- startNode := b.bestChain.Genesis()
- for _, hash := range locator {
- node := b.index.LookupNode(hash)
- if node != nil && b.bestChain.Contains(node) {
- startNode = node
- break
- }
- }
-
- // Start at the block after the most recently known block. When there
- // is no next block it means the most recently known block is the tip of
- // the best chain, so there is nothing more to do.
- startNode = b.bestChain.Next(startNode)
- if startNode == nil {
- return nil, 0
- }
-
- // Calculate how many entries are needed.
- total := uint32((b.bestChain.Tip().height - startNode.height) + 1)
- if stopNode != nil && b.bestChain.Contains(stopNode) &&
- stopNode.height >= startNode.height {
-
- total = uint32((stopNode.height - startNode.height) + 1)
- }
- if total > maxEntries {
- total = maxEntries
- }
-
- return startNode, total
-}
-
-// locateBlocks returns the hashes of the blocks after the first known block in
-// the locator until the provided stop hash is reached, or up to the provided
-// max number of block hashes.
-//
-// See the comment on the exported function for more details on special cases.
-//
-// This function MUST be called with the chain state lock held (for reads).
-func (b *BlockChain) locateBlocks(locator BlockLocator, hashStop *chainhash.Hash, maxHashes uint32) []chainhash.Hash {
- // Find the node after the first known block in the locator and the
- // total number of nodes after it needed while respecting the stop hash
- // and max entries.
- node, total := b.locateInventory(locator, hashStop, maxHashes)
- if total == 0 {
- return nil
- }
-
- // Populate and return the found hashes.
- hashes := make([]chainhash.Hash, 0, total)
- for i := uint32(0); i < total; i++ {
- hashes = append(hashes, node.hash)
- node = b.bestChain.Next(node)
- }
- return hashes
-}
-
-// LocateBlocks returns the hashes of the blocks after the first known block in
-// the locator until the provided stop hash is reached, or up to the provided
-// max number of block hashes.
-//
-// In addition, there are two special cases:
-//
-// - When no locators are provided, the stop hash is treated as a request for
-// that block, so it will either return the stop hash itself if it is known,
-// or nil if it is unknown
-// - When locators are provided, but none of them are known, hashes starting
-// after the genesis block will be returned
-//
-// This function is safe for concurrent access.
-func (b *BlockChain) LocateBlocks(locator BlockLocator, hashStop *chainhash.Hash, maxHashes uint32) []chainhash.Hash {
- b.chainLock.RLock()
- hashes := b.locateBlocks(locator, hashStop, maxHashes)
- b.chainLock.RUnlock()
- return hashes
-}
-
-// locateHeaders returns the headers of the blocks after the first known block
-// in the locator until the provided stop hash is reached, or up to the provided
-// max number of block headers.
-//
-// See the comment on the exported function for more details on special cases.
-//
-// This function MUST be called with the chain state lock held (for reads).
-func (b *BlockChain) locateHeaders(locator BlockLocator, hashStop *chainhash.Hash, maxHeaders uint32) []wire.BlockHeader {
- // Find the node after the first known block in the locator and the
- // total number of nodes after it needed while respecting the stop hash
- // and max entries.
- node, total := b.locateInventory(locator, hashStop, maxHeaders)
- if total == 0 {
- return nil
- }
-
- // Populate and return the found headers.
- headers := make([]wire.BlockHeader, 0, total)
- for i := uint32(0); i < total; i++ {
- headers = append(headers, node.Header())
- node = b.bestChain.Next(node)
- }
- return headers
-}
-
-// LocateHeaders returns the headers of the blocks after the first known block
-// in the locator until the provided stop hash is reached, or up to a max of
-// wire.MaxBlockHeadersPerMsg headers.
-//
-// In addition, there are two special cases:
-//
-// - When no locators are provided, the stop hash is treated as a request for
-// that header, so it will either return the header for the stop hash itself
-// if it is known, or nil if it is unknown
-// - When locators are provided, but none of them are known, headers starting
-// after the genesis block will be returned
-//
-// This function is safe for concurrent access.
-func (b *BlockChain) LocateHeaders(locator BlockLocator, hashStop *chainhash.Hash) []wire.BlockHeader {
- b.chainLock.RLock()
- headers := b.locateHeaders(locator, hashStop, wire.MaxBlockHeadersPerMsg)
- b.chainLock.RUnlock()
- return headers
-}
-
-// 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
-
- // 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
-
- // HashCache defines a transaction hash mid-state cache to use when
- // validating transactions. This cache has the potential to greatly
- // speed up transaction validation as re-using the pre-calculated
- // mid-state eliminates the O(N^2) validation complexity due to the
- // SigHashAll flag.
- //
- // This field can be nil if the caller is not interested in using a
- // signature cache.
- HashCache *txscript.HashCache
-}
-
-// 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,
- sigCache: config.SigCache,
- indexManager: config.IndexManager,
- minRetargetTimespan: targetTimespan / adjustmentFactor,
- maxRetargetTimespan: targetTimespan * adjustmentFactor,
- blocksPerRetarget: int32(targetTimespan / targetTimePerBlock),
- index: newBlockIndex(config.DB, params),
- hashCache: config.HashCache,
- bestChain: newChainView(nil),
- orphans: make(map[chainhash.Hash]*orphanBlock),
- prevOrphans: make(map[chainhash.Hash][]*orphanBlock),
- warningCaches: newThresholdCaches(vbNumBits),
- 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.
- if err := b.initThresholdCaches(); err != nil {
- return nil, err
- }
-
- bestNode := b.bestChain.Tip()
- log.Infof("Chain state (height %d, hash %v, totaltx %d, work %v)",
- bestNode.height, bestNode.hash, b.stateSnapshot.TotalTxns,
- bestNode.workSum)
-
- return &b, nil
-}