256 lines
7.5 KiB
Nim
256 lines
7.5 KiB
Nim
# Nimbus
|
|
# Copyright (c) 2024 Status Research & Development GmbH
|
|
# Licensed under either of
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
|
# http://www.apache.org/licenses/LICENSE-2.0)
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
|
# http://opensource.org/licenses/MIT)
|
|
# at your option. This file may not be copied, modified, or distributed except
|
|
# according to those terms.
|
|
|
|
import
|
|
std/osproc,
|
|
eth/rlp,
|
|
eth/common/eth_types_rlp,
|
|
web3/conversions,
|
|
web3/engine_api_types,
|
|
../../nimbus/beacon/web3_eth_conv
|
|
|
|
const
|
|
testFile = "tests/fixtures/eth_tests/BlockchainTests/GeneralStateTests/Pyspecs/cancun/eip4844_blobs/fork_transition_excess_blob_gas.json"
|
|
#testFile = "tests/fixtures/eth_tests/BlockchainTests/ValidBlocks/bcRandomBlockhashTest/randomStatetest224BC.json"
|
|
#testFile = "tests/fixtures/eth_tests/BlockchainTests/ValidBlocks/bcRandomBlockhashTest/randomStatetest631BC.json"
|
|
#testFile = "tests/fixtures/eth_tests/BlockchainTests/ValidBlocks/bcStateTests/blockhashTests.json"
|
|
|
|
type
|
|
BCTConv* = JrpcConv
|
|
|
|
BCTBlock* = object
|
|
rlp*: seq[byte]
|
|
|
|
BCTData* = object
|
|
blocks*: seq[BCTBlock]
|
|
genesisRLP*: seq[byte]
|
|
network*: string
|
|
pre*: JsonString
|
|
postState*: JsonString
|
|
|
|
BCTCase* = object
|
|
name*: string
|
|
data*: BCTData
|
|
|
|
BCTFile* = object
|
|
cases*: seq[BCTCase]
|
|
|
|
BCTHash* = object
|
|
number*: Quantity
|
|
hash: Hash32
|
|
|
|
BCTHashes* = object
|
|
hashes: seq[BCTHash]
|
|
|
|
BCTEnv* = object
|
|
currentCoinbase*: primitives.Address
|
|
currentDifficulty*: UInt256
|
|
currentRandom*: Opt[Bytes32]
|
|
parentDifficulty*: Opt[UInt256]
|
|
currentGasLimit*: Quantity
|
|
currentNumber*: Quantity
|
|
currentTimestamp*: Opt[Quantity]
|
|
parentTimestamp*: Opt[Quantity]
|
|
blockHashes*: BCTHashes
|
|
# ommers
|
|
currentBaseFee*: Opt[UInt256]
|
|
parentUncleHash*: Opt[Hash32]
|
|
parentBaseFee*: Opt[UInt256]
|
|
parentGasUsed*: Opt[Quantity]
|
|
parentGasLimit*: Opt[Quantity]
|
|
withdrawals*: Opt[seq[WithdrawalV1]]
|
|
currentBlobGasUsed*: Opt[Quantity]
|
|
currentExcessBlobGas*: Opt[Quantity]
|
|
parentBlobGasUsed*: Opt[Quantity]
|
|
parentExcessBlobGas*: Opt[Quantity]
|
|
parentBeaconBlockRoot*: Opt[Hash32]
|
|
|
|
BCTInput* = object
|
|
alloc: JsonString
|
|
env: BCTEnv
|
|
txsRlp: seq[byte]
|
|
|
|
BCTResult* = object
|
|
stateRoot*: Hash32
|
|
|
|
BCTOutput* = object
|
|
result*: BCTResult
|
|
alloc*: JsonString
|
|
|
|
BCTData.useDefaultReaderIn BCTConv
|
|
BCTBlock.useDefaultReaderIn BCTConv
|
|
BCTOutput.useDefaultReaderIn BCTConv
|
|
BCTResult.useDefaultReaderIn BCTConv
|
|
|
|
BCTEnv.useDefaultWriterIn BCTConv
|
|
BCTInput.useDefaultWriterIn BCTConv
|
|
|
|
proc readValue*(r: var JsonReader[BCTConv], val: var BCTFile)
|
|
{.gcsafe, raises: [IOError, SerializationError].} =
|
|
r.parseObject(key):
|
|
val.cases.add BCTCase(
|
|
name: key,
|
|
data: r.readValue(BCTData)
|
|
)
|
|
|
|
proc writeValue*(w: var JsonWriter[BCTConv], v: BCTHashes)
|
|
{.gcsafe, raises: [IOError].} =
|
|
w.writeObject():
|
|
for x in v.hashes:
|
|
w.writeField($x.number, x.hash)
|
|
|
|
func toBlocks(list: openArray[BCTBlock]): seq[Block] =
|
|
result = newSeqOfCap[Block](list.len)
|
|
for x in list:
|
|
result.add rlp.decode(x.rlp, Block)
|
|
|
|
proc toBctEnv(parentBlock, currentBlock: Block, hashes: BCTHashes): BCTEnv =
|
|
let
|
|
parent = parentBlock.header
|
|
current = currentBlock.header
|
|
|
|
result.currentCoinbase = current.coinbase
|
|
result.currentGasLimit = w3Qty(current.gasLimit)
|
|
result.currentNumber = w3Qty(current.number)
|
|
result.currentTimestamp = Opt.some w3Qty(current.timestamp)
|
|
result.currentDifficulty = current.difficulty
|
|
result.currentRandom = Opt.some current.mixHash
|
|
|
|
# t8n should able to calculate these values itself if not supplied
|
|
#result.currentBaseFee = current.baseFeePerGas
|
|
#result.currentBlobGasUsed = w3Qty(current.blobGasUsed)
|
|
#result.currentExcessBlobGas = w3Qty(current.excessBlobGas)
|
|
|
|
result.parentBeaconBlockRoot = current.parentBeaconBlockRoot
|
|
result.parentDifficulty = Opt.some parent.difficulty
|
|
result.parentTimestamp = Opt.some w3Qty(parent.timestamp)
|
|
result.parentUncleHash = Opt.some parent.ommersHash
|
|
|
|
result.parentBaseFee = parent.baseFeePerGas
|
|
result.parentGasUsed = Opt.some w3Qty(parent.gasUsed)
|
|
result.parentGasLimit = Opt.some w3Qty(parent.gasLimit)
|
|
result.parentBlobGasUsed = w3Qty(parent.blobGasUsed)
|
|
result.parentExcessBlobGas = w3Qty(parent.excessBlobGas)
|
|
result.withdrawals = w3Withdrawals(currentBlock.withdrawals)
|
|
result.blockHashes = hashes
|
|
|
|
func toInput(prevAlloc: JsonString,
|
|
prevBlock, currBlock: Block,
|
|
hashes: BCTHashes): string =
|
|
let input = BCTInput(
|
|
alloc: prevAlloc,
|
|
env: toBctEnv(prevBlock, currBlock, hashes),
|
|
txsRlp: rlp.encode(currBlock.transactions),
|
|
)
|
|
BCTConv.encode(input)
|
|
|
|
func collectHashes(genesis: Block, blocks: openArray[Block]): BCTHashes =
|
|
result.hashes.add BCTHash(
|
|
number: w3Qty(genesis.header.number),
|
|
hash: rlpHash(genesis.header),
|
|
)
|
|
|
|
for blk in blocks:
|
|
result.hashes.add BCTHash(
|
|
number: w3Qty(blk.header.number),
|
|
hash: rlpHash(blk.header),
|
|
)
|
|
|
|
func eth(n: int): UInt256 {.compileTime.} =
|
|
n.u256 * pow(10.u256, 18)
|
|
|
|
const
|
|
eth5 = 5.eth
|
|
eth3 = 3.eth
|
|
eth2 = 2.eth
|
|
eth0 = 0.u256
|
|
|
|
const
|
|
BlockRewards = [
|
|
(eth5, "Frontier"),
|
|
(eth5, "Homestead"),
|
|
(eth5, "DAOFork"),
|
|
(eth5, "Tangerine"),
|
|
(eth5, "Spurious"),
|
|
(eth3, "Byzantium"),
|
|
(eth2, "Constantinople"),
|
|
(eth2, "Petersburg"),
|
|
(eth2, "Istanbul"),
|
|
(eth2, "MuirGlacier"),
|
|
(eth2, "Berlin"),
|
|
(eth2, "London"),
|
|
(eth2, "ArrowGlacier"),
|
|
(eth2, "GrayGlacier"),
|
|
(eth0, "MergeFork"),
|
|
(eth0, "Shanghai"),
|
|
(eth0, "Cancun"),
|
|
(eth0, "Prague"),
|
|
]
|
|
|
|
func blockReward(network: string): string =
|
|
for z in BlockRewards:
|
|
if network == z[1]:
|
|
return $z[0]
|
|
|
|
proc BCTMain() =
|
|
try:
|
|
let bctFile = BCTConv.loadFile(testFile, BCTFile, allowUnknownFields = true)
|
|
let cmd = "t8n --output.alloc stdout --output.result stdout --input.alloc stdin --input.env stdin --input.txs stdin --state.fork "
|
|
|
|
for c in bctFile.cases:
|
|
var
|
|
prevAlloc = c.data.pre
|
|
prevBlock = rlp.decode(c.data.genesisRLP, Block)
|
|
|
|
let
|
|
blocks = toBlocks(c.data.blocks)
|
|
hashes = collectHashes(prevBlock, blocks)
|
|
|
|
debugEcho "NAME: ", c.name
|
|
for currBlock in blocks:
|
|
let input = toInput(prevAlloc, prevBlock, currBlock, hashes)
|
|
|
|
var cmdLine = cmd & c.data.network
|
|
let reward = blockReward(c.data.network)
|
|
if reward.len > 0:
|
|
cmdLine.add " --state.reward " & reward
|
|
|
|
let (output, exitCode) = execCmdEx(cmdLine, input = input)
|
|
prevBlock = currBlock
|
|
|
|
if exitCode != QuitSuccess:
|
|
debugEcho output
|
|
break
|
|
|
|
let
|
|
parsedOutput = BCTConv.decode(output, BCTOutput, allowUnknownFields = true)
|
|
stateRoot = parsedOutput.result.stateRoot
|
|
|
|
debugEcho "BlockNumber, EXITCODE, resultStateRoot, expectedStateRoot, status: ",
|
|
currBlock.header.number, ", ",
|
|
exitCode, ", ",
|
|
stateRoot, ", ",
|
|
currBlock.header.stateRoot, ", ",
|
|
if stateRoot != currBlock.header.stateRoot: "FAILED" else: "OK"
|
|
|
|
if stateRoot != currBlock.header.stateRoot:
|
|
debugEcho "STATE ROOT MISMATCH: ", currBlock.header.number
|
|
debugEcho "WANT: ", currBlock.header.stateRoot
|
|
debugEcho "GET: ", stateRoot
|
|
quit(QuitFailure)
|
|
|
|
prevAlloc = parsedOutput.alloc
|
|
|
|
except SerializationError as exc:
|
|
debugEcho "Something error"
|
|
debugEcho exc.formatMsg(testFile)
|
|
|
|
BCTMain()
|