// 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 ( "fmt" "time" "github.com/lbryio/lbcd/chaincfg/chainhash" "github.com/lbryio/lbcd/database" btcutil "github.com/lbryio/lbcutil" ) // BehaviorFlags is a bitmask defining tweaks to the normal behavior when // performing chain processing and consensus rules checks. type BehaviorFlags uint32 const ( // BFFastAdd may be set to indicate that several checks can be avoided // for the block since it is already known to fit into the chain due to // already proving it correct links into the chain up to a known // checkpoint. This is primarily used for headers-first mode. BFFastAdd BehaviorFlags = 1 << iota // BFNoPoWCheck may be set to indicate the proof of work check which // ensures a block hashes to a value less than the required target will // not be performed. BFNoPoWCheck // BFNone is a convenience value to specifically indicate no flags. BFNone BehaviorFlags = 0 ) // blockExists determines whether a block with the given hash exists either in // the main chain or any side chains. // // This function is safe for concurrent access. func (b *BlockChain) blockExists(hash *chainhash.Hash) (bool, error) { // Check block index first (could be main chain or side chain blocks). if b.index.HaveBlock(hash) { return true, nil } // Check in the database. var exists bool err := b.db.View(func(dbTx database.Tx) error { var err error exists, err = dbTx.HasBlock(hash) if err != nil || !exists { return err } // Ignore side chain blocks in the database. This is necessary // because there is not currently any record of the associated // block index data such as its block height, so it's not yet // possible to efficiently load the block and do anything useful // with it. // // Ultimately the entire block index should be serialized // instead of only the current main chain so it can be consulted // directly. _, err = dbFetchHeightByHash(dbTx, hash) if isNotInMainChainErr(err) { exists = false return nil } return err }) return exists, err } // processOrphans determines if there are any orphans which depend on the passed // block hash (they are no longer orphans if true) and potentially accepts them. // It repeats the process for the newly accepted blocks (to detect further // orphans which may no longer be orphans) until there are no more. // // The flags do not modify the behavior of this function directly, however they // are needed to pass along to maybeAcceptBlock. // // This function MUST be called with the chain state lock held (for writes). func (b *BlockChain) processOrphans(hash *chainhash.Hash, flags BehaviorFlags) error { // Start with processing at least the passed hash. Leave a little room // for additional orphan blocks that need to be processed without // needing to grow the array in the common case. processHashes := make([]*chainhash.Hash, 0, 10) processHashes = append(processHashes, hash) for len(processHashes) > 0 { // Pop the first hash to process from the slice. processHash := processHashes[0] processHashes[0] = nil // Prevent GC leak. processHashes = processHashes[1:] // Look up all orphans that are parented by the block we just // accepted. This will typically only be one, but it could // be multiple if multiple blocks are mined and broadcast // around the same time. The one with the most proof of work // will eventually win out. An indexing for loop is // intentionally used over a range here as range does not // reevaluate the slice on each iteration nor does it adjust the // index for the modified slice. for i := 0; i < len(b.prevOrphans[*processHash]); i++ { orphan := b.prevOrphans[*processHash][i] if orphan == nil { log.Warnf("Found a nil entry at index %d in the "+ "orphan dependency list for block %v", i, processHash) continue } // Remove the orphan from the orphan pool. orphanHash := orphan.block.Hash() b.removeOrphanBlock(orphan) i-- // Potentially accept the block into the block chain. _, err := b.maybeAcceptBlock(orphan.block, flags) if err != nil { return err } // Add this block to the list of blocks to process so // any orphan blocks that depend on this block are // handled too. processHashes = append(processHashes, orphanHash) } } return nil } // ProcessBlock is the main workhorse for handling insertion of new blocks into // the block chain. It includes functionality such as rejecting duplicate // blocks, ensuring blocks follow all rules, orphan handling, and insertion into // the block chain along with best chain selection and reorganization. // // When no errors occurred during processing, the first return value indicates // whether or not the block is on the main chain and the second indicates // whether or not the block is an orphan. // // This function is safe for concurrent access. func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bool, bool, error) { b.chainLock.Lock() defer b.chainLock.Unlock() fastAdd := flags&BFFastAdd == BFFastAdd blockHash := block.Hash() log.Tracef("Processing block %v", blockHash) // The block must not already exist in the main chain or side chains. exists, err := b.blockExists(blockHash) if err != nil { return false, false, err } if exists { str := fmt.Sprintf("already have block %v", blockHash) return false, false, ruleError(ErrDuplicateBlock, str) } // The block must not already exist as an orphan. if _, exists := b.orphans[*blockHash]; exists { str := fmt.Sprintf("already have block (orphan) %v", blockHash) return false, false, ruleError(ErrDuplicateBlock, str) } // Perform preliminary sanity checks on the block and its transactions. err = checkBlockSanity(block, b.chainParams.PowLimit, b.timeSource, flags) if err != nil { return false, false, err } // Find the previous checkpoint and perform some additional checks based // on the checkpoint. This provides a few nice properties such as // preventing old side chain blocks before the last checkpoint, // rejecting easy to mine, but otherwise bogus, blocks that could be // used to eat memory, and ensuring expected (versus claimed) proof of // work requirements since the previous checkpoint are met. blockHeader := &block.MsgBlock().Header checkpointNode, err := b.findPreviousCheckpoint() if err != nil { return false, false, err } if checkpointNode != nil { // Ensure the block timestamp is after the checkpoint timestamp. checkpointTime := time.Unix(checkpointNode.timestamp, 0) if blockHeader.Timestamp.Before(checkpointTime) { str := fmt.Sprintf("block %v has timestamp %v before "+ "last checkpoint timestamp %v", blockHash, blockHeader.Timestamp, checkpointTime) return false, false, ruleError(ErrCheckpointTimeTooOld, str) } if !fastAdd { // Even though the checks prior to now have already ensured the // proof of work exceeds the claimed amount, the claimed amount // is a field in the block header which could be forged. This // check ensures the proof of work is at least the minimum // expected based on elapsed time since the last checkpoint and // maximum adjustment allowed by the retarget rules. duration := blockHeader.Timestamp.Sub(checkpointTime) requiredTarget := CompactToBig(b.calcEasiestDifficulty( checkpointNode.bits, duration)) currentTarget := CompactToBig(blockHeader.Bits) if currentTarget.Cmp(requiredTarget) > 0 { str := fmt.Sprintf("block target difficulty of %064x "+ "is too low when compared to the previous "+ "checkpoint", currentTarget) return false, false, ruleError(ErrDifficultyTooLow, str) } } } // Handle orphan blocks. prevHash := &blockHeader.PrevBlock prevHashExists, err := b.blockExists(prevHash) if err != nil { return false, false, err } if !prevHashExists { log.Infof("Adding orphan block %v with parent %v", blockHash, prevHash) b.addOrphanBlock(block) return false, true, nil } // The block has passed all context independent checks and appears sane // enough to potentially accept it into the block chain. isMainChain, err := b.maybeAcceptBlock(block, flags) if err != nil { return false, false, err } // Accept any orphan blocks that depend on this block (they are // no longer orphans) and repeat for those accepted blocks until // there are no more. err = b.processOrphans(blockHash, flags) if err != nil { return false, false, err } log.Debugf("Accepted block %v", blockHash) return isMainChain, false, nil }