github.com/palcoin-project/palcd@v1.0.0/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/palcoin-project/palcd/database" 11 "github.com/palcoin-project/palcutil" 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 *palcutil.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 }