add diffficulty test

This commit is contained in:
andri lim 2019-08-23 22:54:25 +07:00
parent 4c7a0d4e6c
commit 0bad132387
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
2 changed files with 212 additions and 0 deletions

151
nimbus/utils/difficulty.nim Normal file
View 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
View 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)