mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-02-03 07:45:18 +00:00
add diffficulty test
This commit is contained in:
parent
4c7a0d4e6c
commit
0bad132387
151
nimbus/utils/difficulty.nim
Normal file
151
nimbus/utils/difficulty.nim
Normal file
@ -0,0 +1,151 @@
|
||||
import times
|
||||
import eth/common, stint
|
||||
import ../constants, ../vm/interpreter/vm_forks
|
||||
|
||||
const
|
||||
ExpDiffPeriod = 100000.u256
|
||||
DifficultyBoundDivisorU = 2048.u256
|
||||
DifficultyBoundDivisorI = 2048.i256
|
||||
DurationLimit = 13
|
||||
MinimumDifficultyU = 131072.u256
|
||||
MinimumDifficultyI = 131072.i256
|
||||
bigOne = 1.u256
|
||||
bigTwo = 2.u256
|
||||
bigNine = 9.i256
|
||||
bigOneI = 1.i256
|
||||
bigTwoI = 2.i256
|
||||
bigTenI = 10.i256
|
||||
bigMin99 = -99.i256
|
||||
|
||||
template difficultyBomb(periodCount: Uint256) =
|
||||
periodCount = periodCount div ExpDiffPeriod
|
||||
|
||||
if periodCount > bigOne:
|
||||
# diff = diff + 2^(periodCount - 2)
|
||||
var expDiff = periodCount - bigTwo
|
||||
expDiff = bigTwo.pow(expDiff)
|
||||
|
||||
diff = diff + expDiff
|
||||
diff = max(diff, MinimumDifficultyU)
|
||||
|
||||
# calcDifficultyFrontier is the difficulty adjustment algorithm. It returns the
|
||||
# difficulty that a new block should have when created at time given the parent
|
||||
# block's time and difficulty. The calculation uses the Frontier rules.
|
||||
func calcDifficultyFrontier*(timeStamp: EthTime, parent: BlockHeader): DifficultyInt =
|
||||
var diff: DifficultyInt
|
||||
let adjust = parent.difficulty div DifficultyBoundDivisorU
|
||||
let time = timeStamp.toUnix()
|
||||
let parentTime = parent.timeStamp.toUnix()
|
||||
|
||||
if time - parentTime < DurationLimit:
|
||||
diff = parent.difficulty + adjust
|
||||
else:
|
||||
diff = parent.difficulty - adjust
|
||||
|
||||
diff = max(diff, MinimumDifficultyU)
|
||||
|
||||
var periodCount = parent.blockNumber + bigOne
|
||||
difficultyBomb(periodCount)
|
||||
result = diff
|
||||
|
||||
# calcDifficultyHomestead is the difficulty adjustment algorithm. It returns
|
||||
# the difficulty that a new block should have when created at time given the
|
||||
# parent block's time and difficulty. The calculation uses the Homestead rules.
|
||||
func calcDifficultyHomestead*(timeStamp: EthTime, parent: BlockHeader): DifficultyInt =
|
||||
# https:#github.com/ethereum/EIPs/blob/master/EIPS/eip-2.md
|
||||
# algorithm:
|
||||
# diff = (parent_diff +
|
||||
# (parent_diff / 2048 * max(1 - (block_timestamp - parent_timestamp) # 10, -99))
|
||||
# ) + 2^(periodCount - 2)
|
||||
|
||||
let time = timeStamp.toUnix()
|
||||
let parentTime = parent.timeStamp.toUnix()
|
||||
let parentDifficulty = cast[Int256](parent.difficulty)
|
||||
|
||||
# 1 - (block_timestamp - parent_timestamp) # 10
|
||||
var x = (time - parentTime).i256
|
||||
x = x div bigTenI
|
||||
x = bigOneI - x
|
||||
|
||||
# max(1 - (block_timestamp - parent_timestamp) # 10, -99)
|
||||
x = max(x, bigMin99)
|
||||
|
||||
# (parent_diff + parent_diff # 2048 * max(1 - (block_timestamp - parent_timestamp) # 10, -99))
|
||||
var y = parentDifficulty div DifficultyBoundDivisorI
|
||||
x = y * x
|
||||
x = parentDifficulty + x
|
||||
|
||||
# minimum difficulty can ever be (before exponential factor)
|
||||
var diff = cast[Uint256](max(x, MinimumDifficultyI))
|
||||
|
||||
# for the exponential factor
|
||||
var periodCount = parent.blockNumber + bigOne
|
||||
difficultyBomb(periodCount)
|
||||
|
||||
result = diff
|
||||
|
||||
# makeDifficultyCalculator creates a difficultyCalculator with the given bomb-delay.
|
||||
# the difficulty is calculated with Byzantium rules, which differs from Homestead in
|
||||
# how uncles affect the calculation
|
||||
func makeDifficultyCalculator(bombDelay: static[int], timeStamp: EthTime, parent: BlockHeader): DifficultyInt =
|
||||
# Note, the calculations below looks at the parent number, which is 1 below
|
||||
# the block number. Thus we remove one from the delay given
|
||||
const
|
||||
bombDelayFromParent = bombDelay.u256 - bigOne
|
||||
|
||||
# https:#github.com/ethereum/EIPs/issues/100.
|
||||
# algorithm:
|
||||
# diff = (parent_diff +
|
||||
# (parent_diff / 2048 * max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) # 9), -99))
|
||||
# ) + 2^(periodCount - 2)
|
||||
|
||||
let time = timeStamp.toUnix()
|
||||
let parentTime = parent.timeStamp.toUnix()
|
||||
let parentDifficulty = cast[Int256](parent.difficulty)
|
||||
|
||||
# (2 if len(parent_uncles) else 1) - (block_timestamp - parent_timestamp) # 9
|
||||
var x = (time - parentTime).i256
|
||||
x = x div bigNine
|
||||
|
||||
if parent.ommersHash == EMPTY_UNCLE_HASH:
|
||||
x = bigOneI - x
|
||||
else:
|
||||
x = bigTwoI - x
|
||||
|
||||
# max((2 if len(parent_uncles) else 1) - (block_timestamp - parent_timestamp) # 9, -99)
|
||||
x = max(x, bigMin99)
|
||||
|
||||
# parent_diff + (parent_diff / 2048 * max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) # 9), -99))
|
||||
var y = parentDifficulty div DifficultyBoundDivisorI
|
||||
x = y * x
|
||||
x = parentDifficulty + x
|
||||
|
||||
# minimum difficulty can ever be (before exponential factor)
|
||||
var diff = cast[Uint256](max(x, MinimumDifficultyI))
|
||||
|
||||
# calculate a fake block number for the ice-age delay
|
||||
# Specification: https:#eips.ethereum.org/EIPS/eip-1234
|
||||
var periodCount: Uint256
|
||||
if parent.blockNumber >= bombDelayFromParent:
|
||||
periodCount = parent.blockNumber - bombDelayFromParent
|
||||
|
||||
difficultyBomb(periodCount)
|
||||
|
||||
result = diff
|
||||
|
||||
template calcDifficultyByzantium*(timeStamp: EthTime, parent: BlockHeader): DifficultyInt =
|
||||
makeDifficultyCalculator(3_000_000, timeStamp, parent)
|
||||
|
||||
template calcDifficultyConstantinople*(timeStamp: EthTime, parent: BlockHeader): DifficultyInt =
|
||||
makeDifficultyCalculator(5_000_000, timeStamp, parent)
|
||||
|
||||
func calcDifficulty*(timeStamp: EthTime, parent: BlockHeader): DifficultyInt =
|
||||
let next = parent.blockNumber + bigOne
|
||||
if next >= forkBlocks[FkConstantinople]:
|
||||
result = makeDifficultyCalculator(5_000_000, timeStamp, parent)
|
||||
elif next >= forkBlocks[FkByzantium]:
|
||||
result = makeDifficultyCalculator(3_000_000, timeStamp, parent)
|
||||
elif next >= forkBlocks[FkHomestead]:
|
||||
result = calcDifficultyHomestead(timeStamp, parent)
|
||||
else:
|
||||
result = calcDifficultyFrontier(timeStamp, parent)
|
61
tests/test_difficulty.nim
Normal file
61
tests/test_difficulty.nim
Normal file
@ -0,0 +1,61 @@
|
||||
import unittest, strutils, tables, ospaths, json,
|
||||
../nimbus/utils/difficulty, stint, times,
|
||||
eth/common, test_helpers, stew/byteutils
|
||||
|
||||
type
|
||||
Tester = object
|
||||
parentTimestamp: int64
|
||||
parentDifficulty: Uint256
|
||||
parentUncles: Hash256
|
||||
currentTimestamp: int64
|
||||
currentBlockNumber: Uint256
|
||||
currentDifficulty: Uint256
|
||||
|
||||
Tests = Table[string, Tester]
|
||||
|
||||
proc hexOrInt64(data: JsonNode, key: string, hex: static[bool]): int64 =
|
||||
when hex:
|
||||
getHexadecimalInt data[key]
|
||||
else:
|
||||
int64(parseInt data[key].getStr)
|
||||
|
||||
proc hexOrInt256(data: JsonNode, key: string, hex: static[bool]): Uint256 =
|
||||
when hex:
|
||||
UInt256.fromHex data[key].getStr
|
||||
else:
|
||||
parse(data[key].getStr, Uint256)
|
||||
|
||||
proc parseTests(name: string, hex: static[bool]): Tests =
|
||||
let fileName = "tests" / "fixtures" / "DifficultyTests" / "difficulty" & name & ".json"
|
||||
let fixtures = parseJSON(readFile(fileName))
|
||||
|
||||
result = initTable[string, Tester]()
|
||||
var t: Tester
|
||||
for title, data in fixtures:
|
||||
t.parentTimestamp = hexOrInt64(data, "parentTimestamp", hex)
|
||||
t.parentDifficulty = hexOrInt256(data, "parentDifficulty", hex)
|
||||
hexToByteArray(data["parentUncles"].getStr, t.parentUncles.data)
|
||||
t.currentTimestamp = hexOrInt64(data, "currentTimestamp", hex)
|
||||
t.currentBlockNumber = hexOrInt256(data, "currentBlockNumber", hex)
|
||||
t.currentDifficulty = hexOrInt256(data, "currentDifficulty", hex)
|
||||
result[title] = t
|
||||
|
||||
template runTests(name: string, hex: bool, calculator: typed) =
|
||||
let data = parseTests(name, hex)
|
||||
for title, t in data:
|
||||
var p = BlockHeader(
|
||||
difficulty: t.parentDifficulty,
|
||||
timestamp: times.fromUnix(t.parentTimestamp),
|
||||
blockNumber: t.currentBlockNumber - 1,
|
||||
ommersHash: t.parentUncles)
|
||||
|
||||
let diff = calculator(times.fromUnix(t.currentTimeStamp), p)
|
||||
test title:
|
||||
check diff == t.currentDifficulty
|
||||
|
||||
suite "DifficultyTest":
|
||||
runTests("Byzantium", true, calcDifficultyByzantium)
|
||||
runTests("Constantinople", true, calcDifficultyConstantinople)
|
||||
runTests("Homestead", true, calcDifficultyHomestead)
|
||||
runTests("Frontier", true, calcDifficultyFrontier)
|
||||
runTests("", false, calcDifficulty)
|
Loading…
x
Reference in New Issue
Block a user