Skip to content

Commit e6ca2c5

Browse files
authored
Merge pull request #399 from gcash/asert
Implement November 2020 hardfork ASERT DAA
2 parents c7d087a + b7eb1ab commit e6ca2c5

File tree

5 files changed

+14314
-21
lines changed

5 files changed

+14314
-21
lines changed

blockchain/difficulty.go

Lines changed: 123 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package blockchain
66

77
import (
8+
"errors"
89
"math/big"
910
"time"
1011

@@ -19,14 +20,31 @@ var (
1920
// oneLsh256 is 1 shifted left 256 bits. It is defined here to avoid
2021
// the overhead of creating it multiple times.
2122
oneLsh256 = new(big.Int).Lsh(bigOne, 256)
23+
24+
// anchorNode is the node used for the asert difficult algorithm. This is the
25+
// block just prior to the fork. After the fork this will be hardcoded, but for
26+
// now it will be set at runtime.
27+
anchorNode *blockNode
2228
)
2329

24-
// DifficultyAdjustmentWindow is the size of the window used by the DAA adjustment
25-
// algorithm when calculating the current difficulty. The algorithm requires fetching
26-
// a 'suitable' block out of blocks n-144, n-145, and n-146. We set this value equal
27-
// to n-144 as that is the first of the three candidate blocks and we will use it
28-
// to fetch the previous two.
29-
const DifficultyAdjustmentWindow = 144
30+
const (
31+
// difficultyAdjustmentWindow is the size of the window used by the DAA adjustment
32+
// algorithm when calculating the current difficulty. The algorithm requires fetching
33+
// a 'suitable' block out of blocks n-144, n-145, and n-146. We set this value equal
34+
// to n-144 as that is the first of the three candidate blocks and we will use it
35+
// to fetch the previous two.
36+
difficultyAdjustmentWindow = 144
37+
38+
// idealBlockTime is used by the Asert difficulty adjustment algorithm. It equals
39+
// 10 minutes between blocks.
40+
idealBlockTime = 600
41+
42+
// radix is used by the Asert difficulty adjustment algorithm.
43+
radix = 65536
44+
45+
// rbits is the number of bits after the radix for fixed-point math
46+
rbits = 16
47+
)
3048

3149
// The DifficultyAlgorithm specifies which algorithm to use and is passed into
3250
// the calcNextRequiredDifficulty function.
@@ -44,15 +62,21 @@ const (
4462
// right after the August 1st, 2017 hardfork and lasted until November 15th, 2017.
4563
DifficultyEDA DifficultyAlgorithm = 1
4664

47-
// DifficultyDAA (Difficulty Adjustment Algorithm) is the current Bitcoin Cash
48-
// difficulty algorithm in effect since Novemeber 15th, 2017.
65+
// DifficultyDAA (Difficulty Adjustment Algorithm) is the Bitcoin Cash difficulty
66+
// algorithm in effect between November 15th, 2017 and November 15, 2020.
4967
DifficultyDAA DifficultyAlgorithm = 2
68+
69+
// DifficultyAsert is the aserti3-2d algorithm in effect as of November 15, 2020.
70+
DifficultyAsert DifficultyAlgorithm = 3
5071
)
5172

5273
// SelectDifficultyAdjustmentAlgorithm returns the difficulty adjustment algorithm that
5374
// should be used when validating a block at the given height.
54-
func (b *BlockChain) SelectDifficultyAdjustmentAlgorithm(height int32) DifficultyAlgorithm {
55-
if height > b.chainParams.UahfForkHeight && height <= b.chainParams.DaaForkHeight {
75+
func (b *BlockChain) SelectDifficultyAdjustmentAlgorithm(prevNode *blockNode) DifficultyAlgorithm {
76+
height := prevNode.height + 1
77+
if uint64(prevNode.CalcPastMedianTime().Unix()) >= b.chainParams.AxionActivationTime {
78+
return DifficultyAsert
79+
} else if height > b.chainParams.UahfForkHeight && height <= b.chainParams.DaaForkHeight {
5680
return DifficultyEDA
5781
} else if height > b.chainParams.DaaForkHeight {
5882
return DifficultyDAA
@@ -254,11 +278,96 @@ func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode, newBlockTim
254278
return lastNode.bits, nil
255279
}
256280

257-
// If we're still using a legacy algorithm
258-
if algorithm != DifficultyDAA {
281+
switch algorithm {
282+
case DifficultyLegacy, DifficultyEDA:
259283
return b.calcLegacyRequiredDifficulty(lastNode, newBlockTime, algorithm)
284+
case DifficultyDAA:
285+
return b.calcDAARequiredDifficulty(lastNode, newBlockTime)
286+
case DifficultyAsert:
287+
if anchorNode == nil {
288+
// If block 1 has a median timestamp less than the activation time and..
289+
// block 2 has a median timestamp greater than or equal to the activation time,
290+
// then block 3 is the first block to contain the new rules and block 2 is the
291+
// "reference block".
292+
node := b.bestChain.Tip()
293+
for {
294+
if uint64(node.CalcPastMedianTime().Unix()) < b.chainParams.AxionActivationTime {
295+
anchorNode = b.bestChain.next(node)
296+
break
297+
}
298+
node = node.parent
299+
}
300+
}
301+
return b.calcAsertRequiredDifficulty(lastNode, anchorNode.height, anchorNode.parent.timestamp, anchorNode.bits, newBlockTime)
260302
}
303+
return 0, errors.New("unknown difficulty algorithm")
304+
}
305+
306+
func (b *BlockChain) calcAsertRequiredDifficulty(lastNode *blockNode, anchorBlockHeight int32, anchorBlockTime int64, anchorBlockBits uint32, evalTimestamp time.Time) (uint32, error) {
307+
// For networks that support it, allow special reduction of the
308+
// required difficulty once too much time has elapsed without
309+
// mining a block.
310+
if b.chainParams.ReduceMinDifficulty {
311+
// Return minimum difficulty when more than the desired
312+
// amount of time has elapsed without mining a block.
313+
reductionTime := int64(b.chainParams.MinDiffReductionTime /
314+
time.Second)
315+
allowMinTime := lastNode.timestamp + reductionTime
316+
if evalTimestamp.Unix() > allowMinTime {
317+
return b.chainParams.PowLimitBits, nil
318+
}
319+
}
320+
321+
target := CompactToBig(anchorBlockBits)
322+
323+
tDelta := lastNode.timestamp - anchorBlockTime
324+
hDelta := lastNode.height - anchorBlockHeight
325+
bigRadix := big.NewInt(radix)
326+
327+
// exponent = int(((time_diff - IDEAL_BLOCK_TIME * (height_diff + 1)) * RADIX) / HALFLIFE)
328+
exponent := new(big.Int).Sub(big.NewInt(tDelta), new(big.Int).Mul(big.NewInt(int64(idealBlockTime)), new(big.Int).Add(big.NewInt(int64(hDelta)), big.NewInt(1))))
329+
exponent.Mul(exponent, bigRadix)
330+
exponent.Quo(exponent, big.NewInt(b.chainParams.AsertDifficultyHalflife))
331+
332+
// shifts = exponent >> RBITS
333+
shifts := new(big.Int).Rsh(exponent, rbits)
334+
335+
// exponent -= shifts * RADIX
336+
exponent.Sub(exponent, new(big.Int).Mul(shifts, bigRadix))
337+
338+
// target *= RADIX + ((195766423245049 * exponent + 971821376 * exponent**2 + 5127 * exponent**3 + 2**47) >> (RBITS * 3))
339+
factor := new(big.Int).Mul(big.NewInt(195766423245049), exponent)
340+
factor.Add(factor, new(big.Int).Mul(big.NewInt(971821376), new(big.Int).Exp(exponent, big.NewInt(2), nil)))
341+
factor.Add(factor, new(big.Int).Mul(big.NewInt(5127), new(big.Int).Exp(exponent, big.NewInt(3), nil)))
342+
factor.Add(factor, new(big.Int).Exp(big.NewInt(2), big.NewInt(47), nil))
343+
factor.Rsh(factor, rbits*3)
344+
345+
target.Mul(target, new(big.Int).Add(bigRadix, factor))
346+
347+
// if shifts < 0: target >>= -shifts else: target <<= shifts
348+
if shifts.Cmp(big.NewInt(0)) < 0 {
349+
target = target.Rsh(target, uint(-shifts.Int64()))
350+
} else {
351+
target = target.Lsh(target, uint(shifts.Int64()))
352+
}
353+
354+
// target >>= RBITS
355+
target.Rsh(target, rbits)
356+
357+
// If target is zero
358+
if target.Cmp(big.NewInt(0)) == 0 {
359+
return BigToCompact(big.NewInt(1)), nil
360+
}
361+
362+
if target.Cmp(b.chainParams.PowLimit) > 0 {
363+
// Return softest target
364+
return b.chainParams.PowLimitBits, nil
365+
}
366+
367+
return BigToCompact(target), nil
368+
}
261369

370+
func (b *BlockChain) calcDAARequiredDifficulty(lastNode *blockNode, newBlockTime time.Time) (uint32, error) {
262371
// For networks that support it, allow special reduction of the
263372
// required difficulty once too much time has elapsed without
264373
// mining a block.
@@ -274,7 +383,7 @@ func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode, newBlockTim
274383
}
275384

276385
// Get the block node at the beginning of the window (n-144)
277-
firstNode := lastNode.RelativeAncestor(DifficultyAdjustmentWindow)
386+
firstNode := lastNode.RelativeAncestor(difficultyAdjustmentWindow)
278387
if firstNode == nil {
279388
return 0, AssertError("unable to obtain previous retarget block")
280389
}
@@ -441,7 +550,7 @@ func (b *BlockChain) CalcNextRequiredDifficulty(timestamp time.Time) (uint32, er
441550
b.chainLock.Lock()
442551
tip := b.bestChain.Tip()
443552
difficulty, err := b.calcNextRequiredDifficulty(tip, timestamp,
444-
b.SelectDifficultyAdjustmentAlgorithm(tip.height))
553+
b.SelectDifficultyAdjustmentAlgorithm(tip))
445554
b.chainLock.Unlock()
446555
return difficulty, err
447556
}

0 commit comments

Comments
 (0)