1 package blockchain
2 3 import (
4 "errors"
5 "fmt"
6 "github.com/p9c/p9/pkg/bits"
7 "github.com/p9c/p9/pkg/block"
8 "github.com/p9c/p9/pkg/fork"
9 "github.com/p9c/p9/pkg/log"
10 11 "time"
12 13 "github.com/p9c/p9/pkg/chainhash"
14 "github.com/p9c/p9/pkg/database"
15 )
16 17 // BehaviorFlags is a bitmask defining tweaks to the normal behavior when
18 // performing chain processing and consensus rules checks.
19 type BehaviorFlags uint32
20 21 const (
22 // BFFastAdd may be set to indicate that several checks can be avoided for the
23 // block since it is already known to fit into the chain due to already proving
24 // it correct links into the chain up to a known checkpoint. This is primarily
25 // used for headers-first mode.
26 BFFastAdd BehaviorFlags = 1 << iota
27 // BFNoPoWCheck may be set to indicate the proof of work check which ensures a
28 // block hashes to a value less than the required target will not be performed.
29 BFNoPoWCheck
30 // BFNone is a convenience value to specifically indicate no flags.
31 BFNone BehaviorFlags = 0
32 )
33 34 // ProcessBlock is the main workhorse for handling insertion of new blocks into
35 // the block chain. It includes functionality such as rejecting duplicate
36 // blocks, ensuring blocks follow all rules, orphan handling, and insertion into
37 // the block chain along with best chain selection and reorganization.
38 //
39 // When no errors occurred during processing, the first return value indicates
40 // whether or not the block is on the main chain and the second indicates
41 // whether or not the block is an orphan.
42 //
43 // This function is safe for concurrent access.
44 func (b *BlockChain) ProcessBlock(
45 workerNumber uint32, candidateBlock *block.Block,
46 flags BehaviorFlags, blockHeight int32,
47 ) (bool, bool, error,) {
48 T.Ln("blockchain.ProcessBlock", blockHeight, log.Caller("\nfrom", 1))
49 var prevBlock *block.Block
50 var e error
51 prevBlock, e = b.BlockByHash(&candidateBlock.WireBlock().Header.PrevBlock)
52 if prevBlock != nil {
53 blockHeight = prevBlock.Height() + 1
54 } else {
55 return false, false, e
56 }
57 // trc.S(prevBlock)
58 b.ChainLock.Lock()
59 defer b.ChainLock.Unlock()
60 fastAdd := flags&BFFastAdd == BFFastAdd
61 blockHash := candidateBlock.Hash()
62 hf := fork.GetCurrent(blockHeight)
63 bhwa := candidateBlock.WireBlock().BlockHashWithAlgos
64 var algo int32
65 switch hf {
66 case 0:
67 if candidateBlock.WireBlock().Header.Version != 514 {
68 algo = 2
69 } else {
70 algo = 514
71 }
72 case 1:
73 algo = candidateBlock.WireBlock().Header.Version
74 }
75 // The candidateBlock must not already exist in the main chain or side chains.
76 var exists bool
77 if exists, e = b.blockExists(blockHash); E.Chk(e) {
78 return false, false, e
79 }
80 if exists {
81 str := ruleError(ErrDuplicateBlock, fmt.Sprintf("already have candidateBlock %v", bhwa(blockHeight).String()))
82 E.Ln(str)
83 return false, false, str
84 }
85 // The candidateBlock must not already exist as an orphan.
86 if _, exists := b.orphans[*blockHash]; exists {
87 str := ruleError(ErrDuplicateBlock, fmt.Sprintf("already have candidateBlock (orphan)"))
88 E.Ln(str)
89 return false, false, str
90 }
91 // Perform preliminary sanity checks on the candidateBlock and its transactions.
92 var DoNotCheckPow bool
93 pl := fork.GetMinDiff(fork.GetAlgoName(algo, blockHeight), blockHeight)
94 T.F("powLimit %d %s %d %064x", algo, fork.GetAlgoName(algo, blockHeight), blockHeight, pl)
95 ph := &candidateBlock.WireBlock().Header.PrevBlock
96 pn := b.Index.LookupNode(ph)
97 if pn == nil {
98 return false, false, errors.New("could not find parent block of candidate block")
99 }
100 var pb *BlockNode
101 pb = pn.GetLastWithAlgo(algo)
102 if pb == nil {
103 DoNotCheckPow = true
104 }
105 T.F("checkBlockSanity powLimit %d %s %d %064x ts %v", algo, fork.GetAlgoName(algo, blockHeight), blockHeight, pl,
106 pn.Header().Timestamp,
107 )
108 if e = checkBlockSanity(
109 candidateBlock,
110 pl,
111 b.timeSource,
112 flags,
113 DoNotCheckPow,
114 blockHeight,
115 pn.Header().Timestamp,
116 ); E.Chk(e) {
117 return false, false, e
118 }
119 T.Ln("searching back to checkpoints")
120 // Find the previous checkpoint and perform some additional checks based on the
121 // checkpoint. This provides a few nice properties such as preventing old side
122 // chain blocks before the last checkpoint, rejecting easy to mine, but
123 // otherwise bogus, blocks that could be used to eat memory, and ensuring
124 // expected (versus claimed) proof of work requirements since the previous
125 // checkpoint are met.
126 blockHeader := &candidateBlock.WireBlock().Header
127 var checkpointNode *BlockNode
128 if checkpointNode, e = b.findPreviousCheckpoint(); E.Chk(e) {
129 return false, false, e
130 }
131 if checkpointNode != nil {
132 // Ensure the candidateBlock timestamp is after the checkpoint timestamp.
133 checkpointTime := time.Unix(checkpointNode.timestamp, 0)
134 if blockHeader.Timestamp.Before(checkpointTime) {
135 str := fmt.Sprintf(
136 "candidateBlock %v has timestamp %v before last checkpoint timestamp %v",
137 bhwa(blockHeight).String(), blockHeader.Timestamp, checkpointTime,
138 )
139 T.Ln(str)
140 return false, false, ruleError(ErrCheckpointTimeTooOld, str)
141 }
142 if !fastAdd {
143 // Even though the checks prior to now have already ensured the proof of work
144 // exceeds the claimed amount, the claimed amount is a field in the candidateBlock header
145 // which could be forged. This check ensures the proof of work is at least the
146 // minimum expected based on elapsed time since the last checkpoint and maximum
147 // adjustment allowed by the retarget rules.
148 duration := blockHeader.Timestamp.Sub(checkpointTime)
149 requiredTarget := bits.CompactToBig(
150 b.calcEasiestDifficulty(
151 checkpointNode.bits, duration,
152 ),
153 )
154 currentTarget := bits.CompactToBig(blockHeader.Bits)
155 if currentTarget.Cmp(requiredTarget) > 0 {
156 str := fmt.Sprintf(
157 "processing: candidateBlock target difficulty of %064x is too low when compared to the"+
158 " previous checkpoint", currentTarget,
159 )
160 E.Ln(str)
161 return false, false, ruleError(ErrDifficultyTooLow, str)
162 }
163 }
164 }
165 T.Ln("handling orphans")
166 // Handle orphan blocks.
167 prevHash := &blockHeader.PrevBlock
168 var prevHashExists bool
169 if prevHashExists, e = b.blockExists(prevHash); E.Chk(e) {
170 return false, false, e
171 }
172 if !prevHashExists {
173 D.C(
174 func() string {
175 return fmt.Sprintf(
176 "adding orphan candidateBlock %v with parent %v",
177 bhwa(blockHeight).String(),
178 prevHash,
179 )
180 },
181 )
182 b.addOrphanBlock(candidateBlock)
183 return false, true, nil
184 }
185 // The candidateBlock has passed all context independent checks and appears sane enough
186 // to potentially accept it into the candidateBlock chain.
187 T.Ln("maybe accept candidateBlock")
188 var isMainChain bool
189 if isMainChain, e = b.maybeAcceptBlock(workerNumber, candidateBlock, flags); E.Chk(e) {
190 return false, false, e
191 }
192 // Accept any orphan blocks that depend on this candidateBlock (they are no longer
193 // orphans) and repeat for those accepted blocks until there are no more.
194 if isMainChain {
195 T.Ln("new candidateBlock on main chain")
196 // Traces(candidateBlock)
197 }
198 if e = b.processOrphans(workerNumber, blockHash, flags); E.Chk(e) {
199 return false, false, e
200 }
201 T.F(
202 "accepted candidateBlock %d %v %s",
203 blockHeight, bhwa(blockHeight).String(), fork.GetAlgoName(
204 candidateBlock.WireBlock().
205 Header.Version, blockHeight,
206 ),
207 )
208 T.Ln("finished blockchain.ProcessBlock")
209 return isMainChain, false, nil
210 }
211 212 // blockExists determines whether a block with the given hash exists either in
213 // the main chain or any side chains.
214 //
215 // This function is safe for concurrent access.
216 func (b *BlockChain) blockExists(hash *chainhash.Hash) (exists bool, e error) {
217 // Chk block index first (could be main chain or side chain blocks).
218 if b.Index.HaveBlock(hash) {
219 return true, nil
220 }
221 // Check in the database.
222 e = b.db.View(
223 func(dbTx database.Tx) (e error) {
224 exists, e = dbTx.HasBlock(hash)
225 if e != nil || !exists {
226 return e
227 }
228 // Ignore side chain blocks in the database. This is necessary because there is
229 // not currently any record of the associated block index data such as its block
230 // height, so it's not yet possible to efficiently load the block and do
231 // anything useful with it. Ultimately the entire block index should be
232 // serialized instead of only the current main chain so it can be consulted
233 // directly.
234 if _, e = dbFetchHeightByHash(dbTx, hash); E.Chk(e) {
235 }
236 if isNotInMainChainErr(e) {
237 exists = false
238 return nil
239 }
240 return e
241 },
242 )
243 return exists, e
244 }
245 246 // processOrphans determines if there are any orphans which depend on the passed
247 // block hash (they are no longer orphans if true) and potentially accepts them.
248 // It repeats the process for the newly accepted blocks ( to detect further
249 // orphans which may no longer be orphans) until there are no more. The flags do
250 // not modify the behavior of this function directly, however they are needed to
251 // pass along to maybeAcceptBlock.
252 //
253 // This function MUST be called with the chain state lock held (for writes).
254 func (b *BlockChain) processOrphans(
255 workerNumber uint32, hash *chainhash.Hash,
256 flags BehaviorFlags,
257 ) (e error) {
258 // Start with processing at least the passed hash. Leave a little room for
259 // additional orphan blocks that need to be processed without needing to grow
260 // the array in the common case.
261 processHashes := make([]*chainhash.Hash, 0, 10)
262 processHashes = append(processHashes, hash)
263 for len(processHashes) > 0 {
264 // Pop the first hash to process from the slice.
265 processHash := processHashes[0]
266 processHashes[0] = nil // Prevent GC leak.
267 processHashes = processHashes[1:]
268 // Look up all orphans that are parented by the block we just accepted. This
269 // will typically only be one, but it could be multiple if multiple blocks are
270 // mined and broadcast around the same time. The one with the most proof of work
271 // will eventually win out. An indexing for loop is intentionally used over a
272 // range here as range does not reevaluate the slice on each iteration nor does
273 // it adjust the index for the modified slice.
274 for i := 0; i < len(b.prevOrphans[*processHash]); i++ {
275 orphan := b.prevOrphans[*processHash][i]
276 if orphan == nil {
277 D.F(
278 "found a nil entry at index %d in the orphan dependency list for block %v",
279 i, processHash,
280 )
281 continue
282 }
283 // Remove the orphan from the orphan pool.
284 orphanHash := orphan.block.Hash()
285 b.removeOrphanBlock(orphan)
286 i--
287 // Potentially accept the block into the block chain.
288 var e error
289 if _, e = b.maybeAcceptBlock(workerNumber, orphan.block, flags); E.Chk(e) {
290 return e
291 }
292 // Add this block to the list of blocks to process so any orphan blocks that
293 // depend on this block are handled too.
294 processHashes = append(processHashes, orphanHash)
295 }
296 }
297 return nil
298 }
299