// maybeAcceptBlock potentially accepts a block into the block chain and, if // accepted, returns whether or not it is on the main chain. It performs // several validation checks which depend on its position within the block chain // before adding it. The block is expected to have already gone through // ProcessBlock before calling this function with it. // // The flags modify the behavior of this function as follows: // - BFDryRun: The memory chain index will not be pruned and no accept // notification will be sent since the block is not being accepted. // // The flags are also passed to checkBlockContext and connectBestChain. See // their documentation for how the flags modify their behavior. // // This function MUST be called with the chain state lock held (for writes). func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) (bool, error) { dryRun := flags&BFDryRun == BFDryRun // Get a block node for the block previous to this one. Will be nil // if this is the genesis block. prevNode, err := b.getPrevNodeFromBlock(block) if err != nil { log.Errorf("getPrevNodeFromBlock: %v", err) return false, err } // The height of this block is one more than the referenced previous // block. blockHeight := int32(0) if prevNode != nil { blockHeight = prevNode.height + 1 } block.SetHeight(blockHeight) // The block must pass all of the validation rules which depend on the // position of the block within the block chain. err = b.checkBlockContext(block, prevNode, flags) if err != nil { return false, err } // Create a new block node for the block and add it to the in-memory // block chain (could be either a side chain or the main chain). blockHeader := &block.MsgBlock().Header newNode := newBlockNode(blockHeader, block.Hash(), blockHeight) if prevNode != nil { newNode.parent = prevNode newNode.height = blockHeight newNode.workSum.Add(prevNode.workSum, newNode.workSum) } // Connect the passed block to the chain while respecting proper chain // selection according to the chain with the most proof of work. This // also handles validation of the transaction scripts. isMainChain, err := b.connectBestChain(newNode, block, flags) if err != nil { return false, err } // Notify the caller that the new block was accepted into the block // chain. The caller would typically want to react by relaying the // inventory to other peers. if !dryRun { b.chainLock.Unlock() b.sendNotification(NTBlockAccepted, block) b.chainLock.Lock() } return isMainChain, 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. // // It returns a bool which indicates whether or not the block is an orphan and // any errors that occurred during processing. The returned bool is only valid // when the error is nil. func (b *BlockChain) ProcessBlock(block *btcutil.Block, timeSource MedianTimeSource, flags BehaviorFlags) (bool, error) { dryRun := flags&BFDryRun == BFDryRun blockHash := block.Sha() // The block must not already exist in the main chain or side chains. exists, err := b.blockExists(blockHash) if err != nil { return false, err } if exists { str := fmt.Sprintf("already have block %v", blockHash) return 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, ruleError(ErrDuplicateBlock, str) } // Perform preliminary sanity checks on the block and its transactions. err = checkBlockSanity(block, b.chainParams.PowLimit, timeSource, flags) if err != nil { return false, err } blockHeader := &block.MsgBlock().Header // Can we just go ahead and add this? latestCheckpoint := b.LatestCheckpoint() if blockHeader.PrevBlock.IsEqual(latestCheckpoint.Hash) { block.SetHeight(latestCheckpoint.Height + 1) err = b.maybeAcceptBlock(block, flags|BFFastAdd) if err != nil { log.Infof("ERROR: %v", err) return false, err } log.Infof("Checkpoint (+1) block inserted: %v", block.Sha()) return false, nil } // Handle orphan blocks. prevHash := &blockHeader.PrevBlock if !prevHash.IsEqual(zeroHash) { prevHashExists, err := b.blockExists(prevHash) if err != nil { return false, err } if !prevHashExists { if !dryRun { log.Infof("Adding orphan block %v with parent %v", blockHash, prevHash) b.addOrphanBlock(block) } return true, nil } } // The block has passed all context independent checks and appears sane // enough to potentially accept it into the block chain. err = b.maybeAcceptBlock(block, flags) if err != nil { return false, err } // Don't process any orphans or log when the dry run flag is set. if !dryRun { // 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, err } // log.Tracef("Accepted block %v", blockHash) } // log.Infof("Accepted block %v (height: %v)", blockHash, block.Height()) return false, nil }
// maybeAcceptBlock potentially accepts a block into the memory block chain. // It performs several validation checks which depend on its position within // the block chain before adding it. The block is expected to have already gone // through ProcessBlock before calling this function with it. // // The flags modify the behavior of this function as follows: // - BFFastAdd: The somewhat expensive BIP0034 validation is not performed. // - BFDryRun: The memory chain index will not be pruned and no accept // notification will be sent since the block is not being accepted. func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) error { fastAdd := flags&BFFastAdd == BFFastAdd dryRun := flags&BFDryRun == BFDryRun // Get a block node for the block previous to this one. Will be nil // if this is the genesis block. prevNode, err := b.getPrevNodeFromBlock(block) if err != nil { log.Errorf("getPrevNodeFromBlock: %v", err) return err } // The height of this block is one more than the referenced previous // block. blockHeight := int64(0) if prevNode != nil { blockHeight = prevNode.height + 1 } block.SetHeight(blockHeight) blockHeader := &block.MsgBlock().Header if !fastAdd { // Ensure the difficulty specified in the block header matches // the calculated difficulty based on the previous block and // difficulty retarget rules. expectedDifficulty, err := b.calcNextRequiredDifficulty(prevNode, block.MsgBlock().Header.Timestamp) if err != nil { return err } blockDifficulty := blockHeader.Bits if blockDifficulty != expectedDifficulty { str := "block difficulty of %d is not the expected value of %d" str = fmt.Sprintf(str, blockDifficulty, expectedDifficulty) return ruleError(ErrUnexpectedDifficulty, str) } // Ensure the timestamp for the block header is after the // median time of the last several blocks (medianTimeBlocks). medianTime, err := b.calcPastMedianTime(prevNode) if err != nil { log.Errorf("calcPastMedianTime: %v", err) return err } if !blockHeader.Timestamp.After(medianTime) { str := "block timestamp of %v is not after expected %v" str = fmt.Sprintf(str, blockHeader.Timestamp, medianTime) return ruleError(ErrTimeTooOld, str) } // Ensure all transactions in the block are finalized. for _, tx := range block.Transactions() { if !IsFinalizedTransaction(tx, blockHeight, blockHeader.Timestamp) { str := fmt.Sprintf("block contains "+ "unfinalized transaction %v", tx.Sha()) return ruleError(ErrUnfinalizedTx, str) } } } // Ensure chain matches up to predetermined checkpoints. // It's safe to ignore the error on Sha since it's already cached. blockHash, _ := block.Sha() if !b.verifyCheckpoint(blockHeight, blockHash) { str := fmt.Sprintf("block at height %d does not match "+ "checkpoint hash", blockHeight) return ruleError(ErrBadCheckpoint, str) } // Find the previous checkpoint and prevent blocks which fork the main // chain before it. This prevents storage of new, otherwise valid, // blocks which build off of old blocks that are likely at a much easier // difficulty and therefore could be used to waste cache and disk space. checkpointBlock, err := b.findPreviousCheckpoint() if err != nil { return err } if checkpointBlock != nil && blockHeight < checkpointBlock.Height() { str := fmt.Sprintf("block at height %d forks the main chain "+ "before the previous checkpoint at height %d", blockHeight, checkpointBlock.Height()) return ruleError(ErrForkTooOld, str) } if !fastAdd { // Reject version 2 blocks once a majority of the network has // upgraded. This is part of BIP0066. if blockHeader.Version < 3 && b.isMajorityVersion(3, prevNode, b.chainParams.BlockRejectNumRequired) { str := "new blocks with version %d are no longer valid" str = fmt.Sprintf(str, blockHeader.Version) return ruleError(ErrBlockVersionTooOld, str) } // Reject version 1 blocks once a majority of the network has // upgraded. This is part of BIP0034. if blockHeader.Version < 2 && b.isMajorityVersion(2, prevNode, b.chainParams.BlockRejectNumRequired) { str := "new blocks with version %d are no longer valid" str = fmt.Sprintf(str, blockHeader.Version) return ruleError(ErrBlockVersionTooOld, str) } // Ensure coinbase starts with serialized block heights for // blocks whose version is the serializedHeightVersion or // newer once a majority of the network has upgraded. This is // part of BIP0034. if blockHeader.Version >= serializedHeightVersion && b.isMajorityVersion(serializedHeightVersion, prevNode, b.chainParams.BlockEnforceNumRequired) { expectedHeight := int64(0) if prevNode != nil { expectedHeight = prevNode.height + 1 } coinbaseTx := block.Transactions()[0] err := checkSerializedHeight(coinbaseTx, expectedHeight) if err != nil { return err } } } // Prune block nodes which are no longer needed before creating // a new node. if !dryRun { err = b.pruneBlockNodes() if err != nil { return err } } // Create a new block node for the block and add it to the in-memory // block chain (could be either a side chain or the main chain). newNode := newBlockNode(blockHeader, blockHash, blockHeight) if prevNode != nil { newNode.parent = prevNode newNode.height = blockHeight newNode.workSum.Add(prevNode.workSum, newNode.workSum) } // Connect the passed block to the chain while respecting proper chain // selection according to the chain with the most proof of work. This // also handles validation of the transaction scripts. err = b.connectBestChain(newNode, block, flags) if err != nil { return err } // Notify the caller that the new block was accepted into the block // chain. The caller would typically want to react by relaying the // inventory to other peers. if !dryRun { b.sendNotification(NTBlockAccepted, block) } return nil }