Add t8n debugging tool and fix EVM regression (#2386)
- fix blockNumber overflow in blockHash op code - reenable 3 test cases of test_blockchain_json - fix t8n crash when creating invalid tracer stream
This commit is contained in:
parent
4fd2ecddec
commit
83f6f89869
|
@ -215,7 +215,7 @@ OK: 0/9 Fail: 0/9 Skip: 9/9
|
|||
+ randomStatetest213BC.json OK
|
||||
+ randomStatetest218BC.json OK
|
||||
+ randomStatetest21BC.json OK
|
||||
randomStatetest224BC.json Skip
|
||||
+ randomStatetest224BC.json OK
|
||||
+ randomStatetest234BC.json OK
|
||||
+ randomStatetest235BC.json OK
|
||||
+ randomStatetest239BC.json OK
|
||||
|
@ -286,7 +286,7 @@ OK: 0/9 Fail: 0/9 Skip: 9/9
|
|||
+ randomStatetest61BC.json OK
|
||||
+ randomStatetest622BC.json OK
|
||||
+ randomStatetest623BC.json OK
|
||||
randomStatetest631BC.json Skip
|
||||
+ randomStatetest631BC.json OK
|
||||
+ randomStatetest634BC.json OK
|
||||
+ randomStatetest65BC.json OK
|
||||
+ randomStatetest68BC.json OK
|
||||
|
@ -301,7 +301,7 @@ OK: 0/9 Fail: 0/9 Skip: 9/9
|
|||
+ randomStatetest93BC.json OK
|
||||
+ randomStatetest99BC.json OK
|
||||
```
|
||||
OK: 103/105 Fail: 0/105 Skip: 2/105
|
||||
OK: 105/105 Fail: 0/105 Skip: 0/105
|
||||
## bcStateTests
|
||||
```diff
|
||||
+ BLOCKHASH_Bounds.json OK
|
||||
|
@ -342,7 +342,7 @@ OK: 103/105 Fail: 0/105 Skip: 2/105
|
|||
+ ZeroValue_TransactionCALLwithData_ToOneStorageKey_OOGRevert_Ist OK
|
||||
+ ZeroValue_TransactionCALLwithData_ToOneStorageKey_OOGRevert_Par OK
|
||||
+ blockhashNonConstArg.json OK
|
||||
blockhashTests.json Skip
|
||||
+ blockhashTests.json OK
|
||||
+ callcodeOutput1.json OK
|
||||
+ callcodeOutput2.json OK
|
||||
+ callcodeOutput3partial.json OK
|
||||
|
@ -405,7 +405,7 @@ OK: 103/105 Fail: 0/105 Skip: 2/105
|
|||
+ transactionFromSelfDestructedContract.json OK
|
||||
+ txCost-sec73.json OK
|
||||
```
|
||||
OK: 98/100 Fail: 0/100 Skip: 2/100
|
||||
OK: 99/100 Fail: 0/100 Skip: 1/100
|
||||
## bcTotalDifficultyTest
|
||||
```diff
|
||||
lotsOfBranchesOverrideAtTheEnd.json Skip
|
||||
|
@ -3726,4 +3726,4 @@ OK: 11/11 Fail: 0/11 Skip: 0/11
|
|||
OK: 1/1 Fail: 0/1 Skip: 0/1
|
||||
|
||||
---TOTAL---
|
||||
OK: 3137/3272 Fail: 0/3272 Skip: 135/3272
|
||||
OK: 3140/3272 Fail: 0/3272 Skip: 132/3272
|
||||
|
|
|
@ -34,9 +34,11 @@ proc blockhashOp (k: var VmCtx): EvmResultVoid =
|
|||
let
|
||||
cpt = k.cpt
|
||||
blockNumber = ? cpt.stack.popInt()
|
||||
blockHash = cpt.getBlockHash(blockNumber.truncate(BlockNumber))
|
||||
|
||||
cpt.stack.push blockHash
|
||||
if blockNumber > high(BlockNumber).u256:
|
||||
cpt.stack.push Hash256()
|
||||
else:
|
||||
cpt.stack.push cpt.getBlockHash(blockNumber.truncate(BlockNumber))
|
||||
|
||||
proc coinBaseOp (k: var VmCtx): EvmResultVoid =
|
||||
## 0x41, Get the block's beneficiary address.
|
||||
|
|
|
@ -136,9 +136,6 @@ const
|
|||
"ChainAtoChainBtoChainAtoChainB.json",
|
||||
"UncleFromSideChain.json",
|
||||
"lotsOfLeafs.json",
|
||||
"randomStatetest224BC.json",
|
||||
"randomStatetest631BC.json",
|
||||
"blockhashTests.json",
|
||||
"lotsOfBranchesOverrideAtTheEnd.json",
|
||||
"lotsOfBranchesOverrideAtTheMiddle.json",
|
||||
"newChainFrom4Block.json",
|
||||
|
|
|
@ -27,6 +27,7 @@ import
|
|||
../hive_integration/nodocker/pyspec/pyspec_sim,
|
||||
../tools/t8n/t8n,
|
||||
../tools/t8n/t8n_test,
|
||||
../tools/t8n/t8n_debug,
|
||||
../tools/evmstate/evmstate,
|
||||
../tools/evmstate/evmstate_test,
|
||||
./test_rpc_getproofs_track_state_changes
|
||||
|
|
|
@ -0,0 +1,253 @@
|
|||
# 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,
|
||||
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: BlockHash
|
||||
|
||||
BCTHashes* = object
|
||||
hashes: seq[BCTHash]
|
||||
|
||||
BCTEnv* = object
|
||||
currentCoinbase*: Address
|
||||
currentDifficulty*: UInt256
|
||||
currentRandom*: Opt[BlockHash]
|
||||
parentDifficulty*: Opt[UInt256]
|
||||
currentGasLimit*: Quantity
|
||||
currentNumber*: Quantity
|
||||
currentTimestamp*: Opt[Quantity]
|
||||
parentTimestamp*: Opt[Quantity]
|
||||
blockHashes*: BCTHashes
|
||||
# ommers
|
||||
currentBaseFee*: Opt[UInt256]
|
||||
parentUncleHash*: Opt[BlockHash]
|
||||
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[BlockHash]
|
||||
|
||||
BCTInput* = object
|
||||
alloc: JsonString
|
||||
env: BCTEnv
|
||||
txsRlp: seq[byte]
|
||||
|
||||
BCTResult* = object
|
||||
stateRoot*: BlockHash
|
||||
|
||||
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[EthBlock] =
|
||||
result = newSeqOfCap[EthBlock](list.len)
|
||||
for x in list:
|
||||
result.add rlp.decode(x.rlp, EthBlock)
|
||||
|
||||
proc toBctEnv(parentBlock, currentBlock: EthBlock, hashes: BCTHashes): BCTEnv =
|
||||
let
|
||||
parent = parentBlock.header
|
||||
current = currentBlock.header
|
||||
|
||||
result.currentCoinbase = w3Addr(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 w3Hash(current.mixHash)
|
||||
result.currentBaseFee = current.baseFeePerGas
|
||||
|
||||
result.currentBlobGasUsed = w3Qty(current.blobGasUsed)
|
||||
result.currentExcessBlobGas = w3Qty(current.excessBlobGas)
|
||||
result.parentBeaconBlockRoot = w3Hash(current.parentBeaconBlockRoot)
|
||||
result.parentDifficulty = Opt.some parent.difficulty
|
||||
result.parentTimestamp = Opt.some w3Qty(parent.timestamp)
|
||||
result.parentUncleHash = Opt.some w3Hash(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: EthBlock,
|
||||
hashes: BCTHashes): string =
|
||||
let input = BCTInput(
|
||||
alloc: prevAlloc,
|
||||
env: toBctEnv(prevBlock, currBlock, hashes),
|
||||
txsRlp: rlp.encode(currBlock.transactions),
|
||||
)
|
||||
BCTConv.encode(input)
|
||||
|
||||
func collectHashes(genesis: EthBlock, blocks: openArray[EthBlock]): BCTHashes =
|
||||
result.hashes.add BCTHash(
|
||||
number: w3Qty(genesis.header.number),
|
||||
hash: w3Hash(rlpHash(genesis.header)),
|
||||
)
|
||||
|
||||
for blk in blocks:
|
||||
result.hashes.add BCTHash(
|
||||
number: w3Qty(blk.header.number),
|
||||
hash: w3Hash(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, EthBlock)
|
||||
|
||||
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 = ethHash(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()
|
|
@ -146,7 +146,9 @@ proc calcLogsHash(receipts: openArray[Receipt]): Hash256 =
|
|||
logs.add rec.logs
|
||||
rlpHash(logs)
|
||||
|
||||
proc defaultTraceStream(conf: T8NConf, txIndex: int, txHash: Hash256): Stream =
|
||||
proc defaultTraceStreamFilename(conf: T8NConf,
|
||||
txIndex: int,
|
||||
txHash: Hash256): (string, string) =
|
||||
let
|
||||
txHash = "0x" & toLowerAscii($txHash)
|
||||
baseDir = if conf.outputBaseDir.len > 0:
|
||||
|
@ -154,6 +156,11 @@ proc defaultTraceStream(conf: T8NConf, txIndex: int, txHash: Hash256): Stream =
|
|||
else:
|
||||
"."
|
||||
fName = "$1/trace-$2-$3.jsonl" % [baseDir, $txIndex, txHash]
|
||||
(baseDir, fName)
|
||||
|
||||
proc defaultTraceStream(conf: T8NConf, txIndex: int, txHash: Hash256): Stream =
|
||||
let (baseDir, fName) = defaultTraceStreamFilename(conf, txIndex, txHash)
|
||||
createDir(baseDir)
|
||||
newFileStream(fName, fmWrite)
|
||||
|
||||
proc traceToFileStream(path: string, txIndex: int): Stream =
|
||||
|
@ -161,6 +168,7 @@ proc traceToFileStream(path: string, txIndex: int): Stream =
|
|||
let
|
||||
file = path.splitFile
|
||||
fName = "$1/$2-$3.jsonl" % [file.dir, file.name, $txIndex]
|
||||
createDir(file.dir)
|
||||
newFileStream(fName, fmWrite)
|
||||
|
||||
proc setupTrace(conf: T8NConf, txIndex: int, txHash: Hash256, vmState: BaseVMState) =
|
||||
|
@ -185,6 +193,15 @@ proc setupTrace(conf: T8NConf, txIndex: int, txHash: Hash256, vmState: BaseVMSta
|
|||
traceToFileStream(traceMode, txIndex)
|
||||
else:
|
||||
defaultTraceStream(conf, txIndex, txHash)
|
||||
|
||||
if stream.isNil:
|
||||
let traceLoc =
|
||||
if traceMode.len > 0:
|
||||
traceMode
|
||||
else:
|
||||
defaultTraceStreamFilename(conf, txIndex, txHash)[1]
|
||||
raise newError(ErrorConfig, "Unable to open tracer stream: " & traceLoc)
|
||||
|
||||
vmState.tracer = newJsonTracer(stream, tracerFlags, false)
|
||||
|
||||
proc closeTrace(vmState: BaseVMState) =
|
||||
|
|
Loading…
Reference in New Issue