2016-04-25 11:12:09 +02:00
|
|
|
// Copyright (c) 2013-2017 The btcsuite developers
|
2013-07-18 16:49:28 +02:00
|
|
|
// Use of this source code is governed by an ISC
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
2015-01-30 21:54:30 +01:00
|
|
|
package blockchain
|
2013-07-18 16:49:28 +02:00
|
|
|
|
2017-01-29 18:20:10 +01:00
|
|
|
import (
|
2017-09-06 05:42:54 +02:00
|
|
|
"fmt"
|
|
|
|
|
2021-10-15 07:45:32 +02:00
|
|
|
"github.com/lbryio/lbcd/database"
|
|
|
|
btcutil "github.com/lbryio/lbcutil"
|
2017-01-29 18:20:10 +01:00
|
|
|
)
|
2013-07-18 16:49:28 +02:00
|
|
|
|
2016-10-13 02:43:01 +02:00
|
|
|
// 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.
|
2014-06-26 22:50:13 +02:00
|
|
|
//
|
2015-04-19 20:34:42 +02:00
|
|
|
// The flags are also passed to checkBlockContext and connectBestChain. See
|
|
|
|
// their documentation for how the flags modify their behavior.
|
2015-08-26 06:03:18 +02:00
|
|
|
//
|
|
|
|
// This function MUST be called with the chain state lock held (for writes).
|
2016-10-13 02:43:01 +02:00
|
|
|
func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) (bool, error) {
|
2013-10-11 17:24:13 +02:00
|
|
|
// The height of this block is one more than the referenced previous
|
|
|
|
// block.
|
2017-09-06 05:42:54 +02:00
|
|
|
prevHash := &block.MsgBlock().Header.PrevBlock
|
|
|
|
prevNode := b.index.LookupNode(prevHash)
|
|
|
|
if prevNode == nil {
|
|
|
|
str := fmt.Sprintf("previous block %s is unknown", prevHash)
|
|
|
|
return false, ruleError(ErrPreviousBlockUnknown, str)
|
2017-10-13 23:36:40 +02:00
|
|
|
} else if b.index.NodeStatus(prevNode).KnownInvalid() {
|
2017-09-06 05:42:54 +02:00
|
|
|
str := fmt.Sprintf("previous block %s is known to be invalid", prevHash)
|
|
|
|
return false, ruleError(ErrInvalidAncestorBlock, str)
|
2013-07-18 16:49:28 +02:00
|
|
|
}
|
2017-09-06 05:42:54 +02:00
|
|
|
|
|
|
|
blockHeight := prevNode.height + 1
|
2013-07-29 23:58:48 +02:00
|
|
|
block.SetHeight(blockHeight)
|
2013-07-18 16:49:28 +02:00
|
|
|
|
2015-04-19 20:34:42 +02:00
|
|
|
// The block must pass all of the validation rules which depend on the
|
|
|
|
// position of the block within the block chain.
|
2017-02-03 19:13:53 +01:00
|
|
|
err := b.checkBlockContext(block, prevNode, flags)
|
2014-02-21 20:02:59 +01:00
|
|
|
if err != nil {
|
2017-01-29 18:20:10 +01:00
|
|
|
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 {
|
2017-09-06 01:43:50 +02:00
|
|
|
return dbStoreBlock(dbTx, block)
|
2017-01-29 18:20:10 +01:00
|
|
|
})
|
|
|
|
if err != nil {
|
2016-10-13 02:43:01 +02:00
|
|
|
return false, err
|
2014-02-21 20:02:59 +01:00
|
|
|
}
|
2013-07-18 16:49:28 +02:00
|
|
|
|
2017-09-14 23:41:21 +02:00
|
|
|
// Create a new block node for the block and add it to the node index. Even
|
|
|
|
// if the block ultimately gets connected to the main chain, it starts out
|
|
|
|
// on a side chain.
|
2015-04-19 20:34:42 +02:00
|
|
|
blockHeader := &block.MsgBlock().Header
|
2017-09-15 04:32:36 +02:00
|
|
|
newNode := newBlockNode(blockHeader, prevNode)
|
2017-10-13 23:36:40 +02:00
|
|
|
newNode.status = statusDataStored
|
2017-09-14 23:41:21 +02:00
|
|
|
|
2017-08-30 20:27:02 +02:00
|
|
|
b.index.AddNode(newNode)
|
2017-09-14 23:41:21 +02:00
|
|
|
err = b.index.flushToDB()
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
2017-08-30 20:27:02 +02:00
|
|
|
|
2013-07-18 16:49:28 +02:00
|
|
|
// 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.
|
2016-10-13 02:43:01 +02:00
|
|
|
isMainChain, err := b.connectBestChain(newNode, block, flags)
|
2013-07-18 16:49:28 +02:00
|
|
|
if err != nil {
|
2016-10-13 02:43:01 +02:00
|
|
|
return false, err
|
2013-07-18 16:49:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2017-09-15 05:35:11 +02:00
|
|
|
b.chainLock.Unlock()
|
|
|
|
b.sendNotification(NTBlockAccepted, block)
|
|
|
|
b.chainLock.Lock()
|
2013-07-18 16:49:28 +02:00
|
|
|
|
2016-10-13 02:43:01 +02:00
|
|
|
return isMainChain, nil
|
2013-07-18 16:49:28 +02:00
|
|
|
}
|