accept.go raw

   1  package blockchain
   2  
   3  import (
   4  	"fmt"
   5  	"github.com/p9c/p9/pkg/block"
   6  	
   7  	"github.com/p9c/p9/pkg/database"
   8  	"github.com/p9c/p9/pkg/hardfork"
   9  )
  10  
  11  // maybeAcceptBlock potentially accepts a block into the block chain
  12  // and, if accepted, returns whether or not it is on the main chain.
  13  // It performs several validation checks which depend on its position within
  14  // the block chain before adding it.
  15  // The block is expected to have already gone through ProcessBlock before
  16  // calling this function with it.
  17  // The flags are also passed to checkBlockContext and connectBestChain.
  18  // See their documentation for how the flags modify their behavior.
  19  // This function MUST be called with the chain state lock held (for writes).
  20  func (b *BlockChain) maybeAcceptBlock(workerNumber uint32, block *block.Block, flags BehaviorFlags) (bool, error) {
  21  	T.Ln("maybeAcceptBlock starting")
  22  	// The height of this block is one more than the referenced previous block.
  23  	prevHash := &block.WireBlock().Header.PrevBlock
  24  	prevNode := b.Index.LookupNode(prevHash)
  25  	if prevNode == nil {
  26  		str := fmt.Sprintf("previous block %s is unknown", prevHash)
  27  		E.Ln(str)
  28  		return false, ruleError(ErrPreviousBlockUnknown, str)
  29  	} else if b.Index.NodeStatus(prevNode).KnownInvalid() {
  30  		str := fmt.Sprintf("previous block %s is known to be invalid", prevHash)
  31  		E.Ln(str)
  32  		return false, ruleError(ErrInvalidAncestorBlock, str)
  33  	}
  34  	blockHeight := prevNode.height + 1
  35  	T.Ln("block not found, good, setting height", blockHeight)
  36  	block.SetHeight(blockHeight)
  37  	// // To deal with multiple mining algorithms, we must check first the block header version. Rather than pass the
  38  	// // direct previous by height, we look for the previous of the same algorithm and pass that.
  39  	// if blockHeight < b.params.BIP0034Height {
  40  	//
  41  	// }
  42  	T.Ln("sanitizing header versions for legacy")
  43  	var DoNotCheckPow bool
  44  	var pn *BlockNode
  45  	var a int32 = 2
  46  	if block.WireBlock().Header.Version == 514 {
  47  		a = 514
  48  	}
  49  	var aa int32 = 2
  50  	if prevNode.version == 514 {
  51  		aa = 514
  52  	}
  53  	if a != aa {
  54  		var i int64
  55  		pn = prevNode
  56  		for ; i < b.params.AveragingInterval-1; i++ {
  57  			pn = pn.GetLastWithAlgo(a)
  58  			if pn == nil {
  59  				break
  60  			}
  61  		}
  62  	}
  63  	T.Ln("check for blacklisted addresses")
  64  	txs := block.Transactions()
  65  	for i := range txs {
  66  		if ContainsBlacklisted(b, txs[i], hardfork.Blacklist) {
  67  			return false, ruleError(ErrBlacklisted, "block contains a blacklisted address ")
  68  		}
  69  	}
  70  	T.Ln("found no blacklisted addresses")
  71  	var e error
  72  	if pn != nil {
  73  		// The block must pass all of the validation rules which depend on the position
  74  		// of the block within the block chain.
  75  		if e = b.checkBlockContext(block, prevNode, flags, DoNotCheckPow); E.Chk(e) {
  76  			return false, e
  77  		}
  78  	}
  79  	// Insert the block into the database if it's not already there. Even though it
  80  	// is possible the block will ultimately fail to connect, it has already passed
  81  	// all proof-of-work and validity tests which means it would be prohibitively
  82  	// expensive for an attacker to fill up the disk with a bunch of blocks that
  83  	// fail to connect. This is necessary since it allows block download to be
  84  	// decoupled from the much more expensive connection logic. It also has some
  85  	// other nice properties such as making blocks that never become part of the
  86  	// main chain or blocks that fail to connect available for further analysis.
  87  	T.Ln("inserting block into database")
  88  	if e = b.db.Update(func(dbTx database.Tx) (e error) {
  89  		return dbStoreBlock(dbTx, block)
  90  	},
  91  	); E.Chk(e) {
  92  		return false, e
  93  	}
  94  	// Create a new block node for the block and add it to the node index. Even if the block ultimately gets connected
  95  	// to the main chain, it starts out on a side chain.
  96  	blockHeader := &block.WireBlock().Header
  97  	newNode := NewBlockNode(blockHeader, prevNode)
  98  	newNode.status = statusDataStored
  99  	b.Index.AddNode(newNode)
 100  	T.Ln("flushing db")
 101  	if e = b.Index.flushToDB(); E.Chk(e) {
 102  		return false, e
 103  	}
 104  	
 105  	// Connect the passed block to the chain while respecting proper chain selection
 106  	// according to the chain with the most proof of work. This also handles
 107  	// validation of the transaction scripts.
 108  	T.Ln("connecting to best chain")
 109  	var isMainChain bool
 110  	if isMainChain, e = b.connectBestChain(newNode, block, flags); E.Chk(e) {
 111  		return false, e
 112  	}
 113  	// Notify the caller that the new block was accepted into the block chain. The caller would typically want to react
 114  	// by relaying the inventory to other peers.
 115  	T.Ln("sending out block notifications for block accepted")
 116  	b.ChainLock.Unlock()
 117  	b.sendNotification(NTBlockAccepted, block)
 118  	b.ChainLock.Lock()
 119  	return isMainChain, nil
 120  }
 121