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
5package blockchain
6
7import (
8	"fmt"
9
10	"github.com/btcsuite/btcd/database"
11	"github.com/btcsuite/btcutil"
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).
24func (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.chainLock.Unlock()
88	b.sendNotification(NTBlockAccepted, block)
89	b.chainLock.Lock()
90
91	return isMainChain, nil
92}
93