Skip to content

Commit 6331269

Browse files
authored
Merge pull request #5 from ParallaxProtocol/daa_asert_migration
[PIP-0002]: Migration from BTC-Style Difficulty Adjustment Algorithm to ASERT
2 parents 3b43ad8 + 389b069 commit 6331269

File tree

17 files changed

+14708
-15
lines changed

17 files changed

+14708
-15
lines changed

consensus/xhash/asert.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package xhash
2+
3+
import (
4+
"math/big"
5+
)
6+
7+
// ASERT params
8+
const (
9+
asertIdealBlockTime = int64(600) // seconds
10+
asertHalflife = int64(172800) // 2 days in seconds
11+
asertRadix = int64(1 << 16) // fixed-point radix (2^16)
12+
)
13+
14+
// Polynomial coefficients for 2^x cubic approximation (from BCH spec)
15+
const (
16+
asertPolyA = uint64(195766423245049)
17+
asertPolyB = uint64(971821376)
18+
asertPolyC = uint64(5127)
19+
)
20+
21+
// ASERTNextTarget computes the next target using the aserti3-2d algorithm.
22+
// All math here is integer-only and matches the BCH specification.
23+
//
24+
// anchorHeight = height of anchor block
25+
// anchorParentTime = timestamp (Unix seconds) of parent of anchor block
26+
// anchorTarget = integer target value of anchor block
27+
// evalHeight = height of evaluation block
28+
// evalTime = timestamp of evaluation block
29+
// maxTarget = maximum allowed target (easiest difficulty)
30+
//
31+
// Returns the target for the next block after the evaluation block.
32+
func ASERTNextTarget(
33+
anchorHeight int64,
34+
anchorParentTime int64,
35+
anchorTarget *big.Int,
36+
evalHeight int64,
37+
evalTime int64,
38+
maxTarget *big.Int,
39+
) *big.Int {
40+
if anchorHeight <= 0 {
41+
panic("ASERTNextTarget: anchorHeight must be > 0")
42+
}
43+
if anchorTarget.Sign() <= 0 {
44+
panic("ASERTNextTarget: anchorTarget must be > 0")
45+
}
46+
if maxTarget.Sign() <= 0 {
47+
panic("ASERTNextTarget: maxTarget must be > 0")
48+
}
49+
50+
timeDelta := evalTime - anchorParentTime
51+
heightDelta := evalHeight - anchorHeight
52+
53+
// Use truncating integer division (Go's / on ints already truncates toward zero)
54+
numBlocks := heightDelta + 1
55+
exponent := ((timeDelta - asertIdealBlockTime*numBlocks) * asertRadix) / asertHalflife
56+
57+
numShifts := exponent >> 16
58+
59+
// Keep 16-bit fractional part
60+
exponent -= numShifts * asertRadix
61+
62+
// Now compute the cubic approximation factor in 16.16 fixed point.
63+
// We interpret exponent as a signed 64-bit, but pass it to the poly as uint64
64+
// so we get the same 2's-complement behavior as the BCH reference.
65+
ux := uint64(exponent)
66+
67+
// factor = ((A*x + B*x^2 + C*x^3 + 2^47) >> 48) + 2^16
68+
// This yields a multiplier in 16.16 fixed point.
69+
x2 := ux * ux
70+
x3 := x2 * ux
71+
72+
poly := asertPolyA*ux + asertPolyB*x2 + asertPolyC*x3 + (uint64(1) << 47)
73+
factor := (poly >> 48) + uint64(asertRadix) // + 2^16
74+
75+
next := new(big.Int).Mul(anchorTarget, new(big.Int).SetUint64(factor))
76+
77+
// Apply the 2^numShifts factor:
78+
if numShifts < 0 {
79+
// Right-shift by -numShifts
80+
next.Rsh(next, uint(-numShifts))
81+
} else if numShifts > 0 {
82+
// Left-shift by numShifts
83+
next.Lsh(next, uint(numShifts))
84+
}
85+
86+
// Divide by 2^16 to remove fixed-point scaling
87+
next.Rsh(next, 16)
88+
89+
// Clamp to valid range
90+
if next.Sign() <= 0 {
91+
next.SetInt64(1)
92+
return next
93+
}
94+
if next.Cmp(maxTarget) > 0 {
95+
next = new(big.Int).Set(maxTarget)
96+
}
97+
return next
98+
}
99+
100+
// difficultyToTarget: target = floor((2^256-1) / difficulty)
101+
func difficultyToTarget(d *big.Int) *big.Int {
102+
if d.Sign() <= 0 {
103+
// avoid div by zero; treat as max difficulty → min target
104+
return new(big.Int).SetInt64(1)
105+
}
106+
t := new(big.Int).Div(new(big.Int).Set(two256m1), d)
107+
if t.Sign() <= 0 {
108+
t.SetInt64(1)
109+
}
110+
return t
111+
}
112+
113+
// targetToDifficulty: difficulty = floor((2^256-1) / target)
114+
func targetToDifficulty(t *big.Int) *big.Int {
115+
if t.Sign() <= 0 {
116+
// avoid div by zero; treat as easiest target → difficulty = 1
117+
return big.NewInt(1)
118+
}
119+
d := new(big.Int).Div(new(big.Int).Set(two256m1), t)
120+
if d.Sign() <= 0 {
121+
d.SetInt64(1)
122+
}
123+
return d
124+
}

0 commit comments

Comments
 (0)