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