164 lines
5.9 KiB
Nim
164 lines
5.9 KiB
Nim
import pkg/chronos
|
|
import codex/contracts
|
|
import ../asynctest
|
|
import ../ethertest
|
|
import ./time
|
|
import ./helpers/mockprovider
|
|
|
|
# to see supportive information in the test output
|
|
# use `-d:"chronicles_enabled_topics:testProvider:DEBUG` option
|
|
# when compiling the test file
|
|
logScope:
|
|
topics = "testProvider"
|
|
|
|
suite "Provider (Mock)":
|
|
test "blockNumberForEpoch returns the earliest block when its timestamp " &
|
|
"is greater than the given epoch time and the earliest block is not " &
|
|
"block number 0 (genesis block)":
|
|
let mockProvider = newMockProvider(
|
|
numberOfBlocks = 10,
|
|
earliestBlockNumber = 1,
|
|
earliestBlockTimestamp = 10,
|
|
timeIntervalBetweenBlocks = 10
|
|
)
|
|
|
|
let (earliestBlockNumber, earliestTimestamp) =
|
|
await mockProvider.blockNumberAndTimestamp(BlockTag.earliest)
|
|
|
|
let epochTime = earliestTimestamp - 1
|
|
|
|
let actual = await mockProvider.blockNumberForEpoch(
|
|
epochTime.truncate(SecondsSince1970))
|
|
|
|
check actual == earliestBlockNumber
|
|
|
|
test "blockNumberForEpoch returns the earliest block when its timestamp " &
|
|
"is equal to the given epoch time":
|
|
let mockProvider = newMockProvider(
|
|
numberOfBlocks = 10,
|
|
earliestBlockNumber = 0,
|
|
earliestBlockTimestamp = 10,
|
|
timeIntervalBetweenBlocks = 10
|
|
)
|
|
|
|
let (earliestBlockNumber, earliestTimestamp) =
|
|
await mockProvider.blockNumberAndTimestamp(BlockTag.earliest)
|
|
|
|
let epochTime = earliestTimestamp
|
|
|
|
let actual = await mockProvider.blockNumberForEpoch(
|
|
epochTime.truncate(SecondsSince1970))
|
|
|
|
check earliestBlockNumber == 0.u256
|
|
check actual == earliestBlockNumber
|
|
|
|
test "blockNumberForEpoch returns the latest block when its timestamp " &
|
|
"is equal to the given epoch time":
|
|
let mockProvider = newMockProvider(
|
|
numberOfBlocks = 10,
|
|
earliestBlockNumber = 0,
|
|
earliestBlockTimestamp = 10,
|
|
timeIntervalBetweenBlocks = 10
|
|
)
|
|
|
|
let (latestBlockNumber, latestTimestamp) =
|
|
await mockProvider.blockNumberAndTimestamp(BlockTag.latest)
|
|
|
|
let epochTime = latestTimestamp
|
|
|
|
let actual = await mockProvider.blockNumberForEpoch(
|
|
epochTime.truncate(SecondsSince1970))
|
|
|
|
check actual == latestBlockNumber
|
|
|
|
ethersuite "Provider":
|
|
proc mineNBlocks(provider: JsonRpcProvider, n: int) {.async.} =
|
|
for _ in 0..<n:
|
|
discard await provider.send("evm_mine")
|
|
|
|
test "blockNumberForEpoch finds closest blockNumber for given epoch time":
|
|
proc createBlockHistory(n: int, blockTime: int):
|
|
Future[seq[(UInt256, UInt256)]] {.async.} =
|
|
var blocks: seq[(UInt256, UInt256)] = @[]
|
|
for _ in 0..<n:
|
|
await ethProvider.advanceTime(blockTime.u256)
|
|
let (blockNumber, blockTimestamp) =
|
|
await ethProvider.blockNumberAndTimestamp(BlockTag.latest)
|
|
# collect blocknumbers and timestamps
|
|
blocks.add((blockNumber, blockTimestamp))
|
|
blocks
|
|
|
|
proc printBlockNumbersAndTimestamps(blocks: seq[(UInt256, UInt256)]) =
|
|
for (blockNumber, blockTimestamp) in blocks:
|
|
debug "Block", blockNumber = blockNumber, timestamp = blockTimestamp
|
|
|
|
type Expectations = tuple
|
|
epochTime: UInt256
|
|
expectedBlockNumber: UInt256
|
|
|
|
# We want to test that timestamps at the block boundaries, in the middle,
|
|
# and towards lower and upper part of the range are correctly mapped to
|
|
# the closest block number.
|
|
# For example: assume we have the following two blocks with
|
|
# the corresponding block numbers and timestamps:
|
|
# block1: (291, 1728436100)
|
|
# block2: (292, 1728436110)
|
|
# To test that binary search correctly finds the closest block number,
|
|
# we will test the following timestamps:
|
|
# 1728436100 => 291
|
|
# 1728436104 => 291
|
|
# 1728436105 => 292
|
|
# 1728436106 => 292
|
|
# 1728436110 => 292
|
|
proc generateExpectations(
|
|
blocks: seq[(UInt256, UInt256)]): seq[Expectations] =
|
|
var expectations: seq[Expectations] = @[]
|
|
for i in 0..<blocks.len - 1:
|
|
let (startNumber, startTimestamp) = blocks[i]
|
|
let (endNumber, endTimestamp) = blocks[i + 1]
|
|
let middleTimestamp = (startTimestamp + endTimestamp) div 2
|
|
let lowerExpectation = (middleTimestamp - 1, startNumber)
|
|
expectations.add((startTimestamp, startNumber))
|
|
expectations.add(lowerExpectation)
|
|
if middleTimestamp.truncate(int64) - startTimestamp.truncate(int64) <
|
|
endTimestamp.truncate(int64) - middleTimestamp.truncate(int64):
|
|
expectations.add((middleTimestamp, startNumber))
|
|
else:
|
|
expectations.add((middleTimestamp, endNumber))
|
|
let higherExpectation = (middleTimestamp + 1, endNumber)
|
|
expectations.add(higherExpectation)
|
|
if i == blocks.len - 2:
|
|
expectations.add((endTimestamp, endNumber))
|
|
expectations
|
|
|
|
proc printExpectations(expectations: seq[Expectations]) =
|
|
debug "Expectations", numberOfExpectations = expectations.len
|
|
for (epochTime, expectedBlockNumber) in expectations:
|
|
debug "Expectation", epochTime = epochTime,
|
|
expectedBlockNumber = expectedBlockNumber
|
|
|
|
# mark the beginning of the history for our test
|
|
await ethProvider.mineNBlocks(1)
|
|
|
|
# set average block time - 10s - we use larger block time
|
|
# then expected in Linea for more precise testing of the binary search
|
|
let averageBlockTime = 10
|
|
|
|
# create a history of N blocks
|
|
let N = 10
|
|
let blocks = await createBlockHistory(N, averageBlockTime)
|
|
|
|
printBlockNumbersAndTimestamps(blocks)
|
|
|
|
# generate expectations for block numbers
|
|
let expectations = generateExpectations(blocks)
|
|
printExpectations(expectations)
|
|
|
|
# validate expectations
|
|
for (epochTime, expectedBlockNumber) in expectations:
|
|
debug "Validating", epochTime = epochTime,
|
|
expectedBlockNumber = expectedBlockNumber
|
|
let actualBlockNumber = await ethProvider.blockNumberForEpoch(
|
|
epochTime.truncate(SecondsSince1970))
|
|
check actualBlockNumber == expectedBlockNumber
|