diff --git a/newBlockchainTests.md b/newBlockchainTests.md index 2bcadaa9b..0634d5d2e 100644 --- a/newBlockchainTests.md +++ b/newBlockchainTests.md @@ -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 diff --git a/nimbus/evm/interpreter/op_handlers/oph_blockdata.nim b/nimbus/evm/interpreter/op_handlers/oph_blockdata.nim index 0abf3cb6f..a61ec8aae 100644 --- a/nimbus/evm/interpreter/op_handlers/oph_blockdata.nim +++ b/nimbus/evm/interpreter/op_handlers/oph_blockdata.nim @@ -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. diff --git a/tests/test_allowed_to_fail.nim b/tests/test_allowed_to_fail.nim index 27f609450..683d80115 100644 --- a/tests/test_allowed_to_fail.nim +++ b/tests/test_allowed_to_fail.nim @@ -136,9 +136,6 @@ const "ChainAtoChainBtoChainAtoChainB.json", "UncleFromSideChain.json", "lotsOfLeafs.json", - "randomStatetest224BC.json", - "randomStatetest631BC.json", - "blockhashTests.json", "lotsOfBranchesOverrideAtTheEnd.json", "lotsOfBranchesOverrideAtTheMiddle.json", "newChainFrom4Block.json", @@ -158,7 +155,7 @@ func skipNewBCTests*(folder: string, name: string): bool = # TODO: fix this if name in problematicCases: return true - + # the new BC tests also contains these slow tests # for Istanbul fork if slowGSTTests(folder, name): diff --git a/tests/test_tools_build.nim b/tests/test_tools_build.nim index a10f33a14..fcaf014e7 100644 --- a/tests/test_tools_build.nim +++ b/tests/test_tools_build.nim @@ -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 diff --git a/tools/t8n/t8n_debug.nim b/tools/t8n/t8n_debug.nim new file mode 100644 index 000000000..ce47d92c6 --- /dev/null +++ b/tools/t8n/t8n_debug.nim @@ -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() diff --git a/tools/t8n/transition.nim b/tools/t8n/transition.nim index 239727675..8422afd13 100644 --- a/tools/t8n/transition.nim +++ b/tools/t8n/transition.nim @@ -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) =