/
difficulty.go
592 lines (521 loc) · 21.4 KB
/
difficulty.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
// Copyright (c) 2013-2016 The btcsuite developers
// Copyright (c) 2015-2019 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wallet
// This code was copied from dcrd/blockchain/difficulty.go and modified for
// dcrwallet's header storage.
import (
"context"
"math/big"
"time"
"github.com/EXCCoin/exccwallet/v2/deployments"
"github.com/EXCCoin/exccwallet/v2/errors"
"github.com/EXCCoin/exccwallet/v2/wallet/walletdb"
blockchain "github.com/EXCCoin/exccd/blockchain/standalone/v2"
"github.com/EXCCoin/exccd/chaincfg/chainhash"
"github.com/EXCCoin/exccd/chaincfg/v3"
"github.com/EXCCoin/exccd/dcrutil/v4"
"github.com/EXCCoin/exccd/wire"
)
func (w *Wallet) isTestNet3() bool {
return w.chainParams.Net == wire.TestNet3
}
const testNet3MaxDiffActivationHeight = 962928
var (
// bigZero is 0 represented as a big.Int. It is defined here to avoid
// the overhead of creating it multiple times.
bigZero = big.NewInt(0)
)
// findPrevTestNetDifficulty returns the difficulty of the previous block which
// did not have the special testnet minimum difficulty rule applied.
func (w *Wallet) findPrevTestNetDifficulty(dbtx walletdb.ReadTx, h *wire.BlockHeader, chain []*BlockNode) (uint32, error) {
// Search backwards through the chain for the last block without
// the special rule applied.
blocksPerRetarget := w.chainParams.WorkDiffWindowSize * w.chainParams.WorkDiffWindows
for int64(h.Height)%blocksPerRetarget != 0 && h.Bits == w.chainParams.PowLimitBits {
if h.PrevBlock == (chainhash.Hash{}) {
h = nil
break
}
if len(chain) > 0 && int32(h.Height)-int32(chain[0].Header.Height) > 0 {
h = chain[h.Height-chain[0].Header.Height-1].Header
} else {
var err error
h, err = w.txStore.GetBlockHeader(dbtx, &h.PrevBlock)
if err != nil {
return 0, err
}
}
}
// Return the found difficulty or the minimum difficulty if no
// appropriate block was found.
lastBits := w.chainParams.PowLimitBits
if h != nil {
lastBits = h.Bits
}
return lastBits, nil
}
// nextRequiredPoWDifficulty calculates the required proof-of-work difficulty
// for the block that references header as a parent.
func (w *Wallet) nextRequiredPoWDifficulty(dbtx walletdb.ReadTx, header *wire.BlockHeader, chain []*BlockNode, newBlockTime time.Time) (uint32, error) {
nextHeight := int64(header.Height) + 1
spec := w.chainParams.Algorithm(uint32(nextHeight))
if uint32(nextHeight) == spec.Height {
return spec.Bits, nil
}
// Get the old difficulty; if we aren't at a block height where it changes,
// just return this.
oldDiff := header.Bits
oldDiffBig := blockchain.CompactToBig(header.Bits)
// We're not at a retarget point, return the oldDiff.
params := w.chainParams
if nextHeight%params.WorkDiffWindowSize != 0 {
// For networks that support it, allow special reduction of the
// required difficulty once too much time has elapsed without
// mining a block.
//
// Note that this behavior is deprecated and thus is only supported on
// testnet v3 prior to the max diff activation height. It will be
// removed in future version of testnet.
if params.ReduceMinDifficulty && (!w.isTestNet3() || nextHeight <
testNet3MaxDiffActivationHeight) {
// Return minimum difficulty when more than the desired
// amount of time has elapsed without mining a block.
reductionTime := int64(params.MinDiffReductionTime /
time.Second)
allowMinTime := header.Timestamp.Unix() + reductionTime
if newBlockTime.Unix() > allowMinTime {
return params.PowLimitBits, nil
}
// The block was mined within the desired timeframe, so
// return the difficulty for the last block which did
// not have the special minimum difficulty rule applied.
return w.findPrevTestNetDifficulty(dbtx, header, chain)
}
return oldDiff, nil
}
// Declare some useful variables.
RAFBig := big.NewInt(w.chainParams.RetargetAdjustmentFactor)
nextDiffBigMin := blockchain.CompactToBig(header.Bits)
nextDiffBigMin.Div(nextDiffBigMin, RAFBig)
nextDiffBigMax := blockchain.CompactToBig(header.Bits)
nextDiffBigMax.Mul(nextDiffBigMax, RAFBig)
alpha := params.WorkDiffAlpha
// Number of nodes to traverse while calculating difficulty.
nodesToTraverse := (params.WorkDiffWindowSize * params.WorkDiffWindows)
// Initialize bigInt slice for the percentage changes for each window period
// above or below the target.
windowChanges := make([]*big.Int, params.WorkDiffWindows)
// Regress through all of the previous blocks and store the percent changes
// per window period; use bigInts to emulate 64.32 bit fixed point.
var olderTime, windowPeriod int64
var weights uint64
oldHeader := header
recentTime := header.Timestamp.Unix()
for i := int64(0); ; i++ {
// Store and reset after reaching the end of every window period.
if i%params.WorkDiffWindowSize == 0 && i != 0 {
olderTime = oldHeader.Timestamp.Unix()
timeDifference := recentTime - olderTime
// Just assume we're at the target (no change) if we've
// gone all the way back to the genesis block.
if oldHeader.Height == 0 || oldHeader.Height == spec.Height {
timeDifference = int64(params.TargetTimespan /
time.Second)
}
timeDifBig := big.NewInt(timeDifference)
timeDifBig.Lsh(timeDifBig, 32) // Add padding
targetTemp := big.NewInt(int64(params.TargetTimespan /
time.Second))
windowAdjusted := targetTemp.Div(timeDifBig, targetTemp)
// Weight it exponentially. Be aware that this could at some point
// overflow if alpha or the number of blocks used is really large.
windowAdjusted = windowAdjusted.Lsh(windowAdjusted,
uint((params.WorkDiffWindows-windowPeriod)*alpha))
// Sum up all the different weights incrementally.
weights += 1 << uint64((params.WorkDiffWindows-windowPeriod)*
alpha)
// Store it in the slice.
windowChanges[windowPeriod] = windowAdjusted
windowPeriod++
recentTime = olderTime
}
if i == nodesToTraverse {
break // Exit for loop when we hit the end.
}
// Get the previous node while staying at the genesis block as needed.
// Query the header from the provided chain instead of database if
// present. The parent of chain[0] is guaranteed to be in stored in the
// database.
if oldHeader.Height != 0 && oldHeader.Height != spec.Height {
if len(chain) > 0 && int32(oldHeader.Height)-int32(chain[0].Header.Height) > 0 {
oldHeader = chain[oldHeader.Height-chain[0].Header.Height-1].Header
} else {
var err error
oldHeader, err = w.txStore.GetBlockHeader(dbtx, &oldHeader.PrevBlock)
if err != nil {
return 0, err
}
}
}
}
// Sum up the weighted window periods.
weightedSum := big.NewInt(0)
for i := int64(0); i < params.WorkDiffWindows; i++ {
weightedSum.Add(weightedSum, windowChanges[i])
}
// Divide by the sum of all weights.
weightsBig := big.NewInt(int64(weights))
weightedSumDiv := weightedSum.Div(weightedSum, weightsBig)
// Multiply by the old diff.
nextDiffBig := weightedSumDiv.Mul(weightedSumDiv, oldDiffBig)
// Right shift to restore the original padding (restore non-fixed point).
nextDiffBig = nextDiffBig.Rsh(nextDiffBig, 32)
// Check to see if we're over the limits for the maximum allowable retarget;
// if we are, return the maximum or minimum except in the case that oldDiff
// is zero.
if oldDiffBig.Cmp(bigZero) == 0 { // This should never really happen,
nextDiffBig.Set(nextDiffBig) // but in case it does...
} else if nextDiffBig.Cmp(bigZero) == 0 {
nextDiffBig.Set(params.PowLimit)
} else if nextDiffBig.Cmp(nextDiffBigMax) == 1 {
nextDiffBig.Set(nextDiffBigMax)
} else if nextDiffBig.Cmp(nextDiffBigMin) == -1 {
nextDiffBig.Set(nextDiffBigMin)
}
// Prevent the difficulty from going lower than the minimum allowed
// difficulty.
//
// Larger numbers result in a lower difficulty, so imposing a minimum
// difficulty equates to limiting the maximum target value.
if nextDiffBig.Cmp(params.PowLimit) > 0 {
nextDiffBig.Set(params.PowLimit)
}
// Prevent the difficulty from going higher than a maximum allowed
// difficulty on the test network. This is to prevent runaway difficulty on
// testnet by ASICs and GPUs since it's not reasonable to require
// high-powered hardware to keep the test network running smoothly.
//
// Smaller numbers result in a higher difficulty, so imposing a maximum
// difficulty equates to limiting the minimum target value.
//
// This rule is only active on the version 3 test network once the max diff
// activation height has been reached.
if w.minTestNetTarget != nil && nextDiffBig.Cmp(w.minTestNetTarget) < 0 &&
(!w.isTestNet3() || nextHeight >= testNet3MaxDiffActivationHeight) {
nextDiffBig = w.minTestNetTarget
}
// Convert the difficulty to the compact representation and return it.
nextDiffBits := blockchain.BigToCompact(nextDiffBig)
return nextDiffBits, nil
}
// estimateSupply returns an estimate of the coin supply for the provided block
// height. This is primarily used in the stake difficulty algorithm and relies
// on an estimate to simplify the necessary calculations. The actual total
// coin supply as of a given block height depends on many factors such as the
// number of votes included in every prior block (not including all votes
// reduces the subsidy) and whether or not any of the prior blocks have been
// invalidated by stakeholders thereby removing the PoW subsidy for them.
func estimateSupply(params *chaincfg.Params, height int64) int64 {
if height <= 0 {
return 0
}
// Estimate the supply by calculating the full block subsidy for each
// reduction interval and multiplying it the number of blocks in the
// interval then adding the subsidy produced by number of blocks in the
// current interval.
supply := params.BlockOneSubsidy()
reductions := height / params.SubsidyReductionInterval
subsidy := params.BaseSubsidy
for i := int64(0); i < reductions; i++ {
supply += params.SubsidyReductionInterval * subsidy
subsidy *= params.MulSubsidy
subsidy /= params.DivSubsidy
}
supply += (1 + height%params.SubsidyReductionInterval) * subsidy
// Blocks 0 and 1 have special subsidy amounts that have already been
// added above, so remove what their subsidies would have normally been
// which were also added above.
supply -= params.BaseSubsidy * 2
return supply
}
// sumPurchasedTickets returns the sum of the number of tickets purchased in the
// most recent specified number of blocks from the point of view of the passed
// header.
func (w *Wallet) sumPurchasedTickets(dbtx walletdb.ReadTx, startHeader *wire.BlockHeader, chain []*BlockNode, numToSum int64) (int64, error) {
var numPurchased int64
for h, numTraversed := startHeader, int64(0); h != nil && numTraversed < numToSum; numTraversed++ {
numPurchased += int64(h.FreshStake)
if h.PrevBlock == (chainhash.Hash{}) {
break
}
if len(chain) > 0 && int32(h.Height)-int32(chain[0].Header.Height) > 0 {
h = chain[h.Height-chain[0].Header.Height-1].Header
continue
}
var err error
h, err = w.txStore.GetBlockHeader(dbtx, &h.PrevBlock)
if err != nil {
return 0, err
}
}
return numPurchased, nil
}
// calcNextStakeDiffV2 calculates the next stake difficulty for the given set
// of parameters using the algorithm defined in DCP0001.
//
// This function contains the heart of the algorithm and thus is separated for
// use in both the actual stake difficulty calculation as well as estimation.
//
// The caller must perform all of the necessary chain traversal in order to
// get the current difficulty, previous retarget interval's pool size plus
// its immature tickets, as well as the current pool size plus immature tickets.
func calcNextStakeDiffV2(params *chaincfg.Params, nextHeight, curDiff, prevPoolSizeAll, curPoolSizeAll int64) int64 {
// Shorter version of various parameter for convenience.
votesPerBlock := int64(params.TicketsPerBlock)
ticketPoolSize := int64(params.TicketPoolSize)
ticketMaturity := int64(params.TicketMaturity)
// Calculate the difficulty by multiplying the old stake difficulty
// with two ratios that represent a force to counteract the relative
// change in the pool size (Fc) and a restorative force to push the pool
// size towards the target value (Fr).
//
// Per DCP0001, the generalized equation is:
//
// nextDiff = min(max(curDiff * Fc * Fr, Slb), Sub)
//
// The detailed form expands to:
//
// curPoolSizeAll curPoolSizeAll
// nextDiff = curDiff * --------------- * -----------------
// prevPoolSizeAll targetPoolSizeAll
//
// Slb = w.chainParams.MinimumStakeDiff
//
// estimatedTotalSupply
// Sub = -------------------------------
// targetPoolSize / votesPerBlock
//
// In order to avoid the need to perform floating point math which could
// be problematic across languages due to uncertainty in floating point
// math libs, this is further simplified to integer math as follows:
//
// curDiff * curPoolSizeAll^2
// nextDiff = -----------------------------------
// prevPoolSizeAll * targetPoolSizeAll
//
// Further, the Sub parameter must calculate the denomitor first using
// integer math.
targetPoolSizeAll := votesPerBlock * (ticketPoolSize + ticketMaturity)
curPoolSizeAllBig := big.NewInt(curPoolSizeAll)
nextDiffBig := big.NewInt(curDiff)
nextDiffBig.Mul(nextDiffBig, curPoolSizeAllBig)
nextDiffBig.Mul(nextDiffBig, curPoolSizeAllBig)
nextDiffBig.Div(nextDiffBig, big.NewInt(prevPoolSizeAll))
nextDiffBig.Div(nextDiffBig, big.NewInt(targetPoolSizeAll))
// Limit the new stake difficulty between the minimum allowed stake
// difficulty and a maximum value that is relative to the total supply.
//
// NOTE: This is intentionally using integer math to prevent any
// potential issues due to uncertainty in floating point math libs. The
// ticketPoolSize parameter already contains the result of
// (targetPoolSize / votesPerBlock).
nextDiff := nextDiffBig.Int64()
estimatedSupply := estimateSupply(params, nextHeight)
maximumStakeDiff := estimatedSupply / ticketPoolSize
if nextDiff > maximumStakeDiff {
nextDiff = maximumStakeDiff
}
if nextDiff < params.MinimumStakeDiff {
nextDiff = params.MinimumStakeDiff
}
return nextDiff
}
func (w *Wallet) ancestorHeaderAtHeight(dbtx walletdb.ReadTx, h *wire.BlockHeader, chain []*BlockNode, height int32) (*wire.BlockHeader, error) {
switch {
case height == int32(h.Height):
return h, nil
case height > int32(h.Height), height < 0:
return nil, nil // dcrd's blockNode.Ancestor returns nil for child heights
}
if len(chain) > 0 && height-int32(chain[0].Header.Height) >= 0 {
return chain[height-int32(chain[0].Header.Height)].Header, nil
}
// Because the parent of chain[0] must be in the main chain, the header can
// be queried by its main chain height.
ns := dbtx.ReadBucket(wtxmgrNamespaceKey)
hash, err := w.txStore.GetMainChainBlockHashForHeight(ns, height)
if err != nil {
return nil, err
}
return w.txStore.GetBlockHeader(dbtx, &hash)
}
// nextRequiredDCP0001PoSDifficulty calculates the required stake difficulty for
// the block after the passed previous block node based on the algorithm defined
// in DCP0001.
func (w *Wallet) nextRequiredDCP0001PoSDifficulty(dbtx walletdb.ReadTx, curHeader *wire.BlockHeader, chain []*BlockNode) (dcrutil.Amount, error) {
// Stake difficulty before any tickets could possibly be purchased is
// the minimum value.
nextHeight := int64(0)
if curHeader != nil {
nextHeight = int64(curHeader.Height) + 1
}
stakeDiffStartHeight := int64(w.chainParams.CoinbaseMaturity) + 1
if nextHeight < stakeDiffStartHeight {
return dcrutil.Amount(w.chainParams.MinimumStakeDiff), nil
}
// Return the previous block's difficulty requirements if the next block
// is not at a difficulty retarget interval.
intervalSize := w.chainParams.StakeDiffWindowSize
curDiff := curHeader.SBits
if nextHeight%intervalSize != 0 {
return dcrutil.Amount(curDiff), nil
}
// Get the pool size and number of tickets that were immature at the
// previous retarget interval.
//
// NOTE: Since the stake difficulty must be calculated based on existing
// blocks, it is always calculated for the block after a given block, so
// the information for the previous retarget interval must be retrieved
// relative to the block just before it to coincide with how it was
// originally calculated.
var prevPoolSize int64
prevRetargetHeight := nextHeight - intervalSize - 1
prevRetargetHeader, err := w.ancestorHeaderAtHeight(dbtx, curHeader, chain, int32(prevRetargetHeight))
if err != nil {
return 0, err
}
if prevRetargetHeader != nil {
prevPoolSize = int64(prevRetargetHeader.PoolSize)
}
ticketMaturity := int64(w.chainParams.TicketMaturity)
prevImmatureTickets, err := w.sumPurchasedTickets(dbtx, prevRetargetHeader, chain, ticketMaturity)
if err != nil {
return 0, err
}
// Return the existing ticket price for the first few intervals to avoid
// division by zero and encourage initial pool population.
prevPoolSizeAll := prevPoolSize + prevImmatureTickets
if prevPoolSizeAll == 0 {
return dcrutil.Amount(curDiff), nil
}
// Count the number of currently immature tickets.
immatureTickets, err := w.sumPurchasedTickets(dbtx, curHeader, chain, ticketMaturity)
if err != nil {
return 0, err
}
// Calculate and return the final next required difficulty.
curPoolSizeAll := int64(curHeader.PoolSize) + immatureTickets
sdiff := calcNextStakeDiffV2(w.chainParams, nextHeight, curDiff, prevPoolSizeAll, curPoolSizeAll)
return dcrutil.Amount(sdiff), nil
}
// NextStakeDifficulty returns the ticket price for the next block after the
// current main chain tip block. This function only succeeds when DCP0001 is
// known to be active. As a fallback, the StakeDifficulty method of
// wallet.NetworkBackend may be used to query the next ticket price from a
// trusted full node.
func (w *Wallet) NextStakeDifficulty(ctx context.Context) (dcrutil.Amount, error) {
const op errors.Op = "wallet.NextStakeDifficulty"
var sdiff dcrutil.Amount
err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
tipHash, tipHeight := w.txStore.MainChainTip(dbtx)
if !deployments.DCP0001.Active(tipHeight, w.chainParams.Net) {
return errors.E(errors.Deployment, "DCP0001 is not known to be active")
}
tipHeader, err := w.txStore.GetBlockHeader(dbtx, &tipHash)
if err != nil {
return err
}
sdiff, err = w.nextRequiredDCP0001PoSDifficulty(dbtx, tipHeader, nil)
return err
})
if err != nil {
return 0, errors.E(op, err)
}
return sdiff, nil
}
// NextStakeDifficultyAfterHeader returns the ticket price for the child of h.
// All headers of ancestor blocks of h must be recorded by the wallet. This
// function only succeeds when DCP0001 is known to be active.
func (w *Wallet) NextStakeDifficultyAfterHeader(ctx context.Context, h *wire.BlockHeader) (dcrutil.Amount, error) {
const op errors.Op = "wallet.NextStakeDifficultyAfterHeader"
if !deployments.DCP0001.Active(int32(h.Height), w.chainParams.Net) {
return 0, errors.E(op, errors.Deployment, "DCP0001 is not known to be active")
}
var sdiff dcrutil.Amount
err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
var err error
sdiff, err = w.nextRequiredDCP0001PoSDifficulty(dbtx, h, nil)
return err
})
if err != nil {
return 0, errors.E(op, err)
}
return sdiff, nil
}
// ValidateHeaderChainDifficulties validates the PoW and PoS difficulties of all
// blocks in chain[idx:]. The parent of chain[0] must be recorded as wallet
// main chain block. If a consensus violation is caught, a subslice of chain
// beginning with the invalid block is returned.
func (w *Wallet) ValidateHeaderChainDifficulties(ctx context.Context, chain []*BlockNode, idx int) ([]*BlockNode, error) {
var invalid []*BlockNode
err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
var err error
invalid, err = w.validateHeaderChainDifficulties(dbtx, chain, idx)
return err
})
return invalid, err
}
func (w *Wallet) validateHeaderChainDifficulties(dbtx walletdb.ReadTx, chain []*BlockNode, idx int) ([]*BlockNode, error) {
const op errors.Op = "wallet.validateHeaderChainDifficulties"
inMainChain, _ := w.txStore.BlockInMainChain(dbtx, &chain[0].Header.PrevBlock)
if !inMainChain {
return nil, errors.E(op, errors.Bug, "parent of chain[0] is not in main chain")
}
var parent *wire.BlockHeader
for ; idx < len(chain); idx++ {
n := chain[idx]
h := n.Header
hash := n.Hash
if parent == nil && h.Height != 0 {
if idx == 0 {
var err error
parent, err = w.txStore.GetBlockHeader(dbtx, &h.PrevBlock)
if err != nil {
return nil, err
}
} else {
parent = chain[idx-1].Header
}
}
// Validate advertised and performed work
bits, err := w.nextRequiredPoWDifficulty(dbtx, parent, chain, h.Timestamp)
if err != nil {
return nil, errors.E(op, err)
}
if h.Bits != bits {
err := errors.Errorf("%v has invalid PoW difficulty, got %x, want %x",
hash, h.Bits, bits)
return chain[idx:], errors.E(op, errors.Consensus, err)
}
err = blockchain.CheckProofOfWork(h, h.Bits, w.chainParams)
if err != nil {
return chain[idx:], errors.E(op, errors.Consensus, err)
}
// Validate ticket price
if deployments.DCP0001.Active(int32(h.Height), w.chainParams.Net) {
sdiff, err := w.nextRequiredDCP0001PoSDifficulty(dbtx, parent, chain)
if err != nil {
return nil, errors.E(op, err)
}
if dcrutil.Amount(h.SBits) != sdiff {
err := errors.Errorf("%v has invalid PoS difficulty, got %v, want %v",
hash, dcrutil.Amount(h.SBits), sdiff)
return chain[idx:], errors.E(op, errors.Consensus, err)
}
}
parent = h
}
return nil, nil
}