// 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 ( "github.com/btcsuite/btcd/database" "github.com/btcsuite/btcutil" ) // 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 block index will not be updated 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 // The height of this block is one more than the referenced previous // block. blockHeight := int32(0) prevNode := b.index.LookupNode(&block.MsgBlock().Header.PrevBlock) 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 } // Insert the block into the database if it's not already there. Even // though it is possible the block will ultimately fail to connect, it // has already passed all proof-of-work and validity tests which means // it would be prohibitively expensive for an attacker to fill up the // disk with a bunch of blocks that fail to connect. This is necessary // since it allows block download to be decoupled from the much more // expensive connection logic. It also has some other nice properties // such as making blocks that never become part of the main chain or // blocks that fail to connect available for further analysis. err = b.db.Update(func(dbTx database.Tx) error { return dbMaybeStoreBlock(dbTx, block) }) 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, blockHeight) if prevNode != nil { newNode.parent = prevNode newNode.height = blockHeight newNode.workSum.Add(prevNode.workSum, newNode.workSum) } b.index.AddNode(newNode) // Undo changes to the block index when running in dry run mode. if dryRun { defer func() { b.index.RemoveNode(newNode) }() } // 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 }