github.com/lbryio/lbcd@v0.22.119/blockchain/accept.go (about)

     1  // Copyright (c) 2013-2017 The btcsuite developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package blockchain
     6  
     7  import (
     8  	"fmt"
     9  
    10  	"github.com/lbryio/lbcd/database"
    11  	btcutil "github.com/lbryio/lbcutil"
    12  )
    13  
    14  // maybeAcceptBlock potentially accepts a block into the block chain and, if
    15  // accepted, returns whether or not it is on the main chain.  It performs
    16  // several validation checks which depend on its position within the block chain
    17  // before adding it.  The block is expected to have already gone through
    18  // ProcessBlock before calling this function with it.
    19  //
    20  // The flags are also passed to checkBlockContext and connectBestChain.  See
    21  // their documentation for how the flags modify their behavior.
    22  //
    23  // This function MUST be called with the chain state lock held (for writes).
    24  func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) (bool, error) {
    25  	// The height of this block is one more than the referenced previous
    26  	// block.
    27  	prevHash := &block.MsgBlock().Header.PrevBlock
    28  	prevNode := b.index.LookupNode(prevHash)
    29  	if prevNode == nil {
    30  		str := fmt.Sprintf("previous block %s is unknown", prevHash)
    31  		return false, ruleError(ErrPreviousBlockUnknown, str)
    32  	} else if b.index.NodeStatus(prevNode).KnownInvalid() {
    33  		str := fmt.Sprintf("previous block %s is known to be invalid", prevHash)
    34  		return false, ruleError(ErrInvalidAncestorBlock, str)
    35  	}
    36  
    37  	blockHeight := prevNode.height + 1
    38  	block.SetHeight(blockHeight)
    39  
    40  	// The block must pass all of the validation rules which depend on the
    41  	// position of the block within the block chain.
    42  	err := b.checkBlockContext(block, prevNode, flags)
    43  	if err != nil {
    44  		return false, err
    45  	}
    46  
    47  	// Insert the block into the database if it's not already there.  Even
    48  	// though it is possible the block will ultimately fail to connect, it
    49  	// has already passed all proof-of-work and validity tests which means
    50  	// it would be prohibitively expensive for an attacker to fill up the
    51  	// disk with a bunch of blocks that fail to connect.  This is necessary
    52  	// since it allows block download to be decoupled from the much more
    53  	// expensive connection logic.  It also has some other nice properties
    54  	// such as making blocks that never become part of the main chain or
    55  	// blocks that fail to connect available for further analysis.
    56  	err = b.db.Update(func(dbTx database.Tx) error {
    57  		return dbStoreBlock(dbTx, block)
    58  	})
    59  	if err != nil {
    60  		return false, err
    61  	}
    62  
    63  	// Create a new block node for the block and add it to the node index. Even
    64  	// if the block ultimately gets connected to the main chain, it starts out
    65  	// on a side chain.
    66  	blockHeader := &block.MsgBlock().Header
    67  	newNode := newBlockNode(blockHeader, prevNode)
    68  	newNode.status = statusDataStored
    69  
    70  	b.index.AddNode(newNode)
    71  	err = b.index.flushToDB()
    72  	if err != nil {
    73  		return false, err
    74  	}
    75  
    76  	// Connect the passed block to the chain while respecting proper chain
    77  	// selection according to the chain with the most proof of work.  This
    78  	// also handles validation of the transaction scripts.
    79  	isMainChain, err := b.connectBestChain(newNode, block, flags)
    80  	if err != nil {
    81  		return false, err
    82  	}
    83  
    84  	// Notify the caller that the new block was accepted into the block
    85  	// chain.  The caller would typically want to react by relaying the
    86  	// inventory to other peers.
    87  	b.notificationSendLock.Lock()
    88  	defer b.notificationSendLock.Unlock()
    89  	b.chainLock.Unlock()
    90  	defer b.chainLock.Lock()
    91  	b.sendNotification(NTBlockAccepted, block)
    92  
    93  	return isMainChain, nil
    94  }