Skip to content

Commit f3a501d

Browse files
committed
ethash: tidy up and add Parallax specific test cases
1 parent bcee2d0 commit f3a501d

File tree

8 files changed

+399
-242
lines changed

8 files changed

+399
-242
lines changed

consensus/ethash/api.go

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package ethash
1818

1919
import (
2020
"errors"
21+
"math/big"
2122

2223
"github.com/microstack-tech/parallax/common"
2324
"github.com/microstack-tech/parallax/common/hexutil"
@@ -34,10 +35,11 @@ type API struct {
3435
// GetWork returns a work package for external miner.
3536
//
3637
// The work package consists of 3 strings:
37-
// result[0] - 32 bytes hex encoded current block header pow-hash
38-
// result[1] - 32 bytes hex encoded seed hash used for DAG
39-
// result[2] - 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty
40-
// result[3] - hex encoded block number
38+
//
39+
// result[0] - 32 bytes hex encoded current block header pow-hash
40+
// result[1] - 32 bytes hex encoded seed hash used for DAG
41+
// result[2] - 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty
42+
// result[3] - hex encoded block number
4143
func (api *API) GetWork() ([4]string, error) {
4244
if api.ethash.remote == nil {
4345
return [4]string{}, errors.New("not supported")
@@ -68,7 +70,7 @@ func (api *API) SubmitWork(nonce types.BlockNonce, hash, digest common.Hash) boo
6870
return false
6971
}
7072

71-
var errc = make(chan error, 1)
73+
errc := make(chan error, 1)
7274
select {
7375
case api.ethash.remote.submitWorkCh <- &mineResult{
7476
nonce: nonce,
@@ -94,7 +96,7 @@ func (api *API) SubmitHashrate(rate hexutil.Uint64, id common.Hash) bool {
9496
return false
9597
}
9698

97-
var done = make(chan struct{}, 1)
99+
done := make(chan struct{}, 1)
98100
select {
99101
case api.ethash.remote.submitRateCh <- &hashrate{done: done, rate: uint64(rate), id: id}:
100102
case <-api.ethash.remote.exitCh:
@@ -110,3 +112,14 @@ func (api *API) SubmitHashrate(rate hexutil.Uint64, id common.Hash) bool {
110112
func (api *API) GetHashrate() uint64 {
111113
return uint64(api.ethash.Hashrate())
112114
}
115+
116+
func (api *API) GetCumulativeEmissions(blockNumber hexutil.Uint64) *big.Int {
117+
emissions := big.NewInt(0)
118+
number := uint64(blockNumber)
119+
120+
for i := uint64(0); i < number; i++ {
121+
reward := calcBlockReward(number)
122+
emissions = emissions.Add(emissions, reward)
123+
}
124+
return emissions
125+
}

consensus/ethash/consensus.go

Lines changed: 3 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ import (
4040
// Ethash proof-of-work protocol constants.
4141
var (
4242
allowedFutureBlockTimeSeconds = int64(5 * 60)
43-
// Target block spacing in seconds
44-
BlockTargetSpacingSeconds = uint64(600)
4543
// Reward halving interval in number of blocks
4644
HalvingIntervalBlocks = uint64(210000)
4745
// Initial block reward in atomic units
@@ -203,10 +201,6 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainHeaderReader, header, pa
203201
return fmt.Errorf("invalid difficulty: have %v, want %v, height %v", header.Difficulty, expected, header.Number.Uint64())
204202
}
205203

206-
if chain.Config().MinDifficulty != nil && header.Difficulty.Cmp(chain.Config().MinDifficulty) < 0 {
207-
return fmt.Errorf("difficulty below powLimit/min: have %v, min %v", header.Difficulty, chain.Config().MinDifficulty)
208-
}
209-
210204
// Gas limits
211205
if header.GasLimit > params.MaxGasLimit {
212206
return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, params.MaxGasLimit)
@@ -249,67 +243,14 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainHeaderReader, header, pa
249243
// the difficulty that a new block should have when created at time
250244
// given the parent block's time and difficulty.
251245
func (ethash *Ethash) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
252-
difficulty := calcDifficulty(chain.Config(), parent)
253-
if chain.Config().MinDifficulty != nil && difficulty.Cmp(chain.Config().MinDifficulty) < 0 {
254-
difficulty.Set(chain.Config().MinDifficulty)
255-
}
256-
return difficulty
246+
return CalcNakamotoDifficulty(chain.Config(), parent)
257247
}
258248

259249
// CalcDifficulty is the difficulty adjustment algorithm. It returns
260250
// the difficulty that a new block should have when created at time
261251
// given the parent block's time and difficulty.
262252
func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int {
263-
difficulty := calcDifficulty(config, parent)
264-
if config.MinDifficulty != nil && difficulty.Cmp(config.MinDifficulty) < 0 {
265-
difficulty.Set(config.MinDifficulty)
266-
}
267-
return difficulty
268-
}
269-
270-
// calcDifficulty computes the next difficulty using Bitcoin’s rule:
271-
//
272-
// - Keep difficulty constant within each retarget interval
273-
// - On boundary: new = old * targetTimespan / actualTimespan
274-
// - Clamp actualTimespan into [minTimespan, maxTimespan]
275-
// - Ensure result >= 1
276-
//
277-
// parent.Time is the last block’s timestamp; firstHeaderTime is the timestamp
278-
// of the first block in the previous interval.
279-
func calcDifficulty(config *params.ChainConfig, parent *types.Header) *big.Int {
280-
nextHeight := new(big.Int).Add(parent.Number, big1).Uint64()
281-
var r uint64
282-
283-
if config.Ethash == nil {
284-
// If no ethash config is given, fall back to Parallax's original difficulty
285-
// adjustment scheme (which is basically Bitcoin's with a 10-minute target).
286-
r = 2016
287-
} else {
288-
r = config.Ethash.RetargetIntervalBlocks
289-
}
290-
291-
if r == 0 || (nextHeight%r) != 0 {
292-
return new(big.Int).Set(parent.Difficulty)
293-
}
294-
295-
target := BlockTargetSpacingSeconds * r
296-
minT := target / 4
297-
maxT := target * 4
298-
299-
actual := parent.Time - parent.EpochStartTime
300-
if actual < minT {
301-
actual = minT
302-
} else if actual > maxT {
303-
actual = maxT
304-
}
305-
old := new(big.Int).Set(parent.Difficulty)
306-
num := new(big.Int).Mul(old, new(big.Int).SetUint64(target))
307-
den := new(big.Int).SetUint64(actual)
308-
out := num.Div(num, den)
309-
if out.Sign() <= 0 {
310-
out.SetUint64(1)
311-
}
312-
return out
253+
return CalcNakamotoDifficulty(config, parent)
313254
}
314255

315256
// Some weird constants to avoid constant memory allocs for them.
@@ -422,12 +363,6 @@ func (ethash *Ethash) Prepare(chain consensus.ChainHeaderReader, header *types.H
422363
// Finalize implements consensus.Engine, accumulating the block and uncle rewards,
423364
// setting the final state on the header
424365
func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
425-
// if chain.Config().Ethash != nil || chain.Config().Ethash.CoinbaseMaturityBlocks == 0 {
426-
// state.AddBalance(header.Coinbase, calcBlockReward(header.Number.Uint64()))
427-
// header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
428-
// return
429-
// }
430-
431366
// 1) Schedule THIS block’s coinbase for future maturity
432367
height := header.Number.Uint64()
433368
reward := calcBlockReward(header.Number.Uint64())
@@ -485,9 +420,7 @@ var (
485420
big32 = big.NewInt(32)
486421
)
487422

488-
// AccumulateRewards credits the coinbase of the given block with the mining
489-
// reward. The total reward consists of the static block reward and rewards for
490-
// included uncles. The coinbase of each uncle block is also rewarded.
423+
// calcBlockReward calculates the block reward for a given block number
491424
func calcBlockReward(blockNumber uint64) *big.Int {
492425
// No spendable subsidy for genesis
493426
if blockNumber == 0 {
@@ -514,9 +447,6 @@ func medianTimePast(chain consensus.ChainHeaderReader, parent *types.Header) uin
514447
times = append(times, h.Time)
515448
h = chain.GetHeader(h.ParentHash, h.Number.Uint64()-1)
516449
}
517-
if len(times) == 0 {
518-
return parent.Time
519-
}
520450
slices.Sort(times)
521451
return times[len(times)/2]
522452
}

consensus/ethash/difficulty.go

Lines changed: 36 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -20,169 +20,55 @@ import (
2020
"math/big"
2121

2222
"github.com/microstack-tech/parallax/core/types"
23-
"github.com/holiman/uint256"
23+
"github.com/microstack-tech/parallax/params"
2424
)
2525

2626
const (
27-
// frontierDurationLimit is for Frontier:
28-
// The decision boundary on the blocktime duration used to determine
29-
// whether difficulty should go up or down.
30-
frontierDurationLimit = 13
31-
// minimumDifficulty The minimum that the difficulty may ever be.
32-
minimumDifficulty = 131072
33-
// expDiffPeriod is the exponential difficulty period
34-
expDiffPeriodUint = 100000
35-
// difficultyBoundDivisorBitShift is the bound divisor of the difficulty (2048),
36-
// This constant is the right-shifts to use for the division.
37-
difficultyBoundDivisor = 11
27+
// Target block spacing in seconds
28+
BlockTargetSpacingSeconds = uint64(600)
3829
)
3930

40-
// CalcDifficultyFrontierU256 is the difficulty adjustment algorithm. It returns the
41-
// difficulty that a new block should have when created at time given the parent
42-
// block's time and difficulty. The calculation uses the Frontier rules.
43-
func CalcDifficultyFrontierU256(time uint64, parent *types.Header) *big.Int {
44-
/*
45-
Algorithm
46-
block_diff = pdiff + pdiff / 2048 * (1 if time - ptime < 13 else -1) + int(2^((num // 100000) - 2))
47-
48-
Where:
49-
- pdiff = parent.difficulty
50-
- ptime = parent.time
51-
- time = block.timestamp
52-
- num = block.number
53-
*/
54-
55-
pDiff, _ := uint256.FromBig(parent.Difficulty) // pDiff: pdiff
56-
adjust := pDiff.Clone()
57-
adjust.Rsh(adjust, difficultyBoundDivisor) // adjust: pDiff / 2048
58-
59-
if time-parent.Time < frontierDurationLimit {
60-
pDiff.Add(pDiff, adjust)
31+
// CalcNakamotoDifficulty computes the next difficulty using Bitcoin’s rule:
32+
//
33+
// - Keep difficulty constant within each retarget interval
34+
// - On boundary: new = old * targetTimespan / actualTimespan
35+
// - Clamp actualTimespan into [minTimespan, maxTimespan]
36+
// - Ensure result >= 1
37+
func CalcNakamotoDifficulty(config *params.ChainConfig, parent *types.Header) *big.Int {
38+
nextHeight := new(big.Int).Add(parent.Number, big1).Uint64()
39+
var r uint64
40+
41+
if config.Ethash == nil {
42+
// If no ethash config is given, fall back to Parallax's original difficulty
43+
// adjustment scheme (which is basically Bitcoin's with a 10-minute target).
44+
r = 2016
6145
} else {
62-
pDiff.Sub(pDiff, adjust)
46+
r = config.Ethash.RetargetIntervalBlocks
6347
}
64-
if pDiff.LtUint64(minimumDifficulty) {
65-
pDiff.SetUint64(minimumDifficulty)
66-
}
67-
// 'pdiff' now contains:
68-
// pdiff + pdiff / 2048 * (1 if time - ptime < 13 else -1)
6948

70-
if periodCount := (parent.Number.Uint64() + 1) / expDiffPeriodUint; periodCount > 1 {
71-
// diff = diff + 2^(periodCount - 2)
72-
expDiff := adjust.SetOne()
73-
expDiff.Lsh(expDiff, uint(periodCount-2)) // expdiff: 2 ^ (periodCount -2)
74-
pDiff.Add(pDiff, expDiff)
49+
if r == 0 || (nextHeight%r) != 0 {
50+
return new(big.Int).Set(parent.Difficulty)
7551
}
76-
return pDiff.ToBig()
77-
}
78-
79-
// CalcDifficultyHomesteadU256 is the difficulty adjustment algorithm. It returns
80-
// the difficulty that a new block should have when created at time given the
81-
// parent block's time and difficulty. The calculation uses the Homestead rules.
82-
func CalcDifficultyHomesteadU256(time uint64, parent *types.Header) *big.Int {
83-
/*
84-
https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.md
85-
Algorithm:
86-
block_diff = pdiff + pdiff / 2048 * max(1 - (time - ptime) / 10, -99) + 2 ^ int((num / 100000) - 2))
87-
88-
Our modification, to use unsigned ints:
89-
block_diff = pdiff - pdiff / 2048 * max((time - ptime) / 10 - 1, 99) + 2 ^ int((num / 100000) - 2))
9052

91-
Where:
92-
- pdiff = parent.difficulty
93-
- ptime = parent.time
94-
- time = block.timestamp
95-
- num = block.number
96-
*/
53+
target := BlockTargetSpacingSeconds * r
54+
minT := target / 4
55+
maxT := target * 4
9756

98-
pDiff, _ := uint256.FromBig(parent.Difficulty) // pDiff: pdiff
99-
adjust := pDiff.Clone()
100-
adjust.Rsh(adjust, difficultyBoundDivisor) // adjust: pDiff / 2048
101-
102-
x := (time - parent.Time) / 10 // (time - ptime) / 10)
103-
neg := true
104-
if x == 0 {
105-
x = 1
106-
neg = false
107-
} else if x >= 100 {
108-
x = 99
109-
} else {
110-
x = x - 1
111-
}
112-
z := new(uint256.Int).SetUint64(x)
113-
adjust.Mul(adjust, z) // adjust: (pdiff / 2048) * max((time - ptime) / 10 - 1, 99)
114-
if neg {
115-
pDiff.Sub(pDiff, adjust) // pdiff - pdiff / 2048 * max((time - ptime) / 10 - 1, 99)
116-
} else {
117-
pDiff.Add(pDiff, adjust) // pdiff + pdiff / 2048 * max((time - ptime) / 10 - 1, 99)
57+
actual := parent.Time - parent.EpochStartTime
58+
if actual < minT {
59+
actual = minT
60+
} else if actual > maxT {
61+
actual = maxT
11862
}
119-
if pDiff.LtUint64(minimumDifficulty) {
120-
pDiff.SetUint64(minimumDifficulty)
121-
}
122-
// for the exponential factor, a.k.a "the bomb"
123-
// diff = diff + 2^(periodCount - 2)
124-
if periodCount := (1 + parent.Number.Uint64()) / expDiffPeriodUint; periodCount > 1 {
125-
expFactor := adjust.Lsh(adjust.SetOne(), uint(periodCount-2))
126-
pDiff.Add(pDiff, expFactor)
127-
}
128-
return pDiff.ToBig()
129-
}
13063

131-
// MakeDifficultyCalculatorU256 creates a difficultyCalculator with the given bomb-delay.
132-
// the difficulty is calculated with Byzantium rules, which differs from Homestead in
133-
// how uncles affect the calculation
134-
func MakeDifficultyCalculatorU256(bombDelay *big.Int) func(time uint64, parent *types.Header) *big.Int {
135-
// Note, the calculations below looks at the parent number, which is 1 below
136-
// the block number. Thus we remove one from the delay given
137-
bombDelayFromParent := bombDelay.Uint64() - 1
138-
return func(time uint64, parent *types.Header) *big.Int {
139-
/*
140-
https://github.com/ethereum/EIPs/issues/100
141-
pDiff = parent.difficulty
142-
BLOCK_DIFF_FACTOR = 9
143-
a = pDiff + (pDiff // BLOCK_DIFF_FACTOR) * adj_factor
144-
b = min(parent.difficulty, MIN_DIFF)
145-
child_diff = max(a,b )
146-
*/
147-
x := (time - parent.Time) / 9 // (block_timestamp - parent_timestamp) // 9
148-
c := uint64(1) // if parent.unclehash == emptyUncleHashHash
149-
xNeg := x >= c
150-
if xNeg {
151-
// x is now _negative_ adjustment factor
152-
x = x - c // - ( (t-p)/p -( 2 or 1) )
153-
} else {
154-
x = c - x // (2 or 1) - (t-p)/9
155-
}
156-
if x > 99 {
157-
x = 99 // max(x, 99)
158-
}
159-
// parent_diff + (parent_diff / 2048 * max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -99))
160-
y := new(uint256.Int)
161-
y.SetFromBig(parent.Difficulty) // y: p_diff
162-
pDiff := y.Clone() // pdiff: p_diff
163-
z := new(uint256.Int).SetUint64(x) // z : +-adj_factor (either pos or negative)
164-
y.Rsh(y, difficultyBoundDivisor) // y: p__diff / 2048
165-
z.Mul(y, z) // z: (p_diff / 2048 ) * (+- adj_factor)
64+
old := new(big.Int).Set(parent.Difficulty)
65+
num := new(big.Int).Mul(old, new(big.Int).SetUint64(target))
66+
den := new(big.Int).SetUint64(actual)
67+
out := num.Div(num, den)
16668

167-
if xNeg {
168-
y.Sub(pDiff, z) // y: parent_diff + parent_diff/2048 * adjustment_factor
169-
} else {
170-
y.Add(pDiff, z) // y: parent_diff + parent_diff/2048 * adjustment_factor
171-
}
172-
// minimum difficulty can ever be (before exponential factor)
173-
if y.LtUint64(minimumDifficulty) {
174-
y.SetUint64(minimumDifficulty)
175-
}
176-
// calculate a fake block number for the ice-age delay
177-
// Specification: https://eips.ethereum.org/EIPS/eip-1234
178-
pNum := parent.Number.Uint64()
179-
if pNum >= bombDelayFromParent {
180-
if fakeBlockNumber := pNum - bombDelayFromParent; fakeBlockNumber >= 2*expDiffPeriodUint {
181-
z.SetOne()
182-
z.Lsh(z, uint(fakeBlockNumber/expDiffPeriodUint-2))
183-
y.Add(z, y)
184-
}
185-
}
186-
return y.ToBig()
69+
if out.Sign() <= 0 {
70+
out.SetUint64(1)
18771
}
72+
73+
return out
18874
}

0 commit comments

Comments
 (0)