github.com/decred/dcrd/blockchain@v1.2.1/accept.go (about) 1 // Copyright (c) 2013-2016 The btcsuite developers 2 // Copyright (c) 2015-2018 The Decred developers 3 // Use of this source code is governed by an ISC 4 // license that can be found in the LICENSE file. 5 6 package blockchain 7 8 import ( 9 "fmt" 10 11 "github.com/decred/dcrd/blockchain/stake" 12 "github.com/decred/dcrd/database" 13 "github.com/decred/dcrd/dcrutil" 14 ) 15 16 // maybeAcceptBlock potentially accepts a block into the block chain and, if 17 // accepted, returns the length of the fork the block extended. It performs 18 // several validation checks which depend on its position within the block chain 19 // before adding it. The block is expected to have already gone through 20 // ProcessBlock before calling this function with it. In the case the block 21 // extends the best chain or is now the tip of the best chain due to causing a 22 // reorganize, the fork length will be 0. 23 // 24 // The flags are also passed to checkBlockPositional, checkBlockContext and 25 // connectBestChain. See their documentation for how the flags modify their 26 // behavior. 27 // 28 // This function MUST be called with the chain state lock held (for writes). 29 func (b *BlockChain) maybeAcceptBlock(block *dcrutil.Block, flags BehaviorFlags) (int64, error) { 30 // This function should never be called with orphan blocks or the 31 // genesis block. 32 prevHash := &block.MsgBlock().Header.PrevBlock 33 prevNode := b.index.LookupNode(prevHash) 34 if prevNode == nil { 35 str := fmt.Sprintf("previous block %s is not known", prevHash) 36 return 0, ruleError(ErrMissingParent, str) 37 } 38 39 // There is no need to validate the block if an ancestor is already 40 // known to be invalid. 41 if b.index.NodeStatus(prevNode).KnownInvalid() { 42 str := fmt.Sprintf("previous block %s is known to be invalid", 43 prevHash) 44 return 0, ruleError(ErrInvalidAncestorBlock, str) 45 } 46 47 // The block must pass all of the validation rules which depend on having 48 // the headers of all ancestors available, but do not rely on having the 49 // full block data of all ancestors available. 50 err := b.checkBlockPositional(block, prevNode, flags) 51 if err != nil { 52 return 0, err 53 } 54 55 // The block must pass all of the validation rules which depend on having 56 // the full block data for all of its ancestors available. 57 err = b.checkBlockContext(block, prevNode, flags) 58 if err != nil { 59 return 0, err 60 } 61 62 // Prune stake nodes which are no longer needed before creating a new 63 // node. 64 b.pruner.pruneChainIfNeeded() 65 66 // Insert the block into the database if it's not already there. Even 67 // though it is possible the block will ultimately fail to connect, it 68 // has already passed all proof-of-work and validity tests which means 69 // it would be prohibitively expensive for an attacker to fill up the 70 // disk with a bunch of blocks that fail to connect. This is necessary 71 // since it allows block download to be decoupled from the much more 72 // expensive connection logic. It also has some other nice properties 73 // such as making blocks that never become part of the main chain or 74 // blocks that fail to connect available for further analysis. 75 err = b.db.Update(func(dbTx database.Tx) error { 76 return dbMaybeStoreBlock(dbTx, block) 77 }) 78 if err != nil { 79 return 0, err 80 } 81 82 // Create a new block node for the block and add it to the block index. 83 // The block could either be on a side chain or the main chain, but it 84 // starts off as a side chain regardless. 85 blockHeader := &block.MsgBlock().Header 86 newNode := newBlockNode(blockHeader, prevNode) 87 newNode.populateTicketInfo(stake.FindSpentTicketsInBlock(block.MsgBlock())) 88 newNode.status = statusDataStored 89 b.index.AddNode(newNode) 90 91 // Ensure the new block index entry is written to the database. 92 err = b.flushBlockIndex() 93 if err != nil { 94 return 0, err 95 } 96 97 // Notify the caller when the block intends to extend the main chain, 98 // the chain believes it is current, and the block has passed all of the 99 // sanity and contextual checks, such as having valid proof of work, 100 // valid merkle and stake roots, and only containing allowed votes and 101 // revocations. 102 // 103 // This allows the block to be relayed before doing the more expensive 104 // connection checks, because even though the block might still fail 105 // to connect and becomes the new main chain tip, that is quite rare in 106 // practice since a lot of work was expended to create a block that 107 // satisifies the proof of work requirement. 108 // 109 // Notice that the chain lock is not released before sending the 110 // notification. This is intentional and must not be changed without 111 // understanding why! 112 if b.isCurrent() && b.bestChain.Tip() == prevNode { 113 b.sendNotification(NTNewTipBlockChecked, block) 114 } 115 116 // Fetching a stake node could enable a new DoS vector, so restrict 117 // this only to blocks that are recent in history. 118 if newNode.height < b.bestChain.Tip().height-minMemoryNodes { 119 newNode.stakeNode, err = b.fetchStakeNode(newNode) 120 if err != nil { 121 return 0, err 122 } 123 } 124 125 // Grab the parent block since it is required throughout the block 126 // connection process. 127 parent, err := b.fetchBlockByNode(newNode.parent) 128 if err != nil { 129 return 0, err 130 } 131 132 // Connect the passed block to the chain while respecting proper chain 133 // selection according to the chain with the most proof of work. This 134 // also handles validation of the transaction scripts. 135 forkLen, err := b.connectBestChain(newNode, block, parent, flags) 136 if err != nil { 137 return 0, err 138 } 139 140 // Notify the caller that the new block was accepted into the block 141 // chain. The caller would typically want to react by relaying the 142 // inventory to other peers unless it was already relayed above 143 // via NTNewTipBlockChecked. 144 bestHeight := b.bestChain.Tip().height 145 b.chainLock.Unlock() 146 b.sendNotification(NTBlockAccepted, &BlockAcceptedNtfnsData{ 147 BestHeight: bestHeight, 148 ForkLen: forkLen, 149 Block: block, 150 }) 151 b.chainLock.Lock() 152 153 return forkLen, nil 154 }