diff --git a/tests/test_generalstate_json.nim b/tests/test_generalstate_json.nim index 5f741c9aa..6850b2365 100644 --- a/tests/test_generalstate_json.nim +++ b/tests/test_generalstate_json.nim @@ -6,63 +6,100 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - unittest, strformat, strutils, tables, json, ospaths, times, + unittest, strformat, strutils, tables, json, ospaths, times, os, byteutils, ranges/typedranges, nimcrypto, options, - eth/[rlp, common, keys], eth/trie/db, chronicles, + eth/[rlp, common, keys], eth/trie/[db, trie_defs], chronicles, ./test_helpers, ../nimbus/p2p/executor, ../nimbus/[constants, errors, transaction], ../nimbus/[vm_state, vm_types, vm_state_transactions, utils], ../nimbus/vm/interpreter, ../nimbus/db/[db_chain, state_db] -proc hashLogEntries(logs: seq[Log]): string = - toLowerAscii("0x" & $keccakHash(rlp.encode(logs))) +type + Tester = object + name: string + header: BlockHeader + pre: JsonNode + tx: Transaction + expectedHash: string + expectedLogs: string + fork: Fork + debugMode: bool -proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) +proc dumpAccount(accountDb: ReadOnlyStateDB, address: EthAddress, name: string): JsonNode = + result = %{ + "name": %name, + "address": %($address), + "nonce": %toHex(accountDb.getNonce(address)), + "balance": %accountDb.getBalance(address).toHex(), + "codehash": %($accountDb.getCodeHash(address)), + "storageRoot": %($accountDb.getStorageRoot(address)) + } -suite "generalstate json tests": - jsonTest("GeneralStateTests", testFixture) +proc dumpDebugData(tester: Tester, vmState: BaseVMState, sender: EthAddress, gasUsed: GasInt) = + let recipient = tester.tx.getRecipient() + let miner = tester.header.coinbase + var accounts = newJObject() -proc testFixtureIndexes(prevStateRoot: Hash256, header: BlockHeader, pre: JsonNode, tx: Transaction, - expectedHash, expectedLogs: string, testStatusIMPL: var TestStatus, fork: Fork) = - when enabledLogLevel <= TRACE: - let tracerFlags = {TracerFlags.EnableTracing} - else: - let tracerFlags: set[TracerFlags] = {} - var vmState = newBaseVMState(prevStateRoot, header, newBaseChainDB(newMemoryDb()), tracerFlags) + accounts[$miner] = dumpAccount(vmState.readOnlyStateDB, miner, "miner") + accounts[$sender] = dumpAccount(vmState.readOnlyStateDB, sender, "sender") + accounts[$recipient] = dumpAccount(vmState.readOnlyStateDB, recipient, "recipient") + + let accountList = [sender, miner, recipient] + var i = 0 + for ac, _ in tester.pre: + let account = ethAddressFromHex(ac) + if account notin accountList: + accounts[$account] = dumpAccount(vmState.readOnlyStateDB, account, "pre" & $i) + inc i + + let debugData = %{ + "gasUsed": %gasUsed, + "structLogs": vmState.getTracingResult(), + "accounts": accounts + } + writeFile("debug_" & tester.name & ".json", debugData.pretty()) + +proc testFixtureIndexes(tester: Tester, testStatusIMPL: var TestStatus) = + var tracerFlags: set[TracerFlags] = if tester.debugMode: {TracerFlags.EnableTracing} else : {} + var vmState = newBaseVMState(emptyRlpHash, tester.header, newBaseChainDB(newMemoryDb()), tracerFlags) vmState.mutateStateDB: - setupStateDB(pre, db) + setupStateDB(tester.pre, db) defer: - #echo vmState.readOnlyStateDB.dumpAccount("c94f5374fce5edbc8e2a8697c15331677e6ebf0b") let obtainedHash = "0x" & `$`(vmState.readOnlyStateDB.rootHash).toLowerAscii - check obtainedHash == expectedHash + check obtainedHash == tester.expectedHash let logEntries = vmState.getAndClearLogEntries() let actualLogsHash = hashLogEntries(logEntries) - let expectedLogsHash = toLowerAscii(expectedLogs) + let expectedLogsHash = toLowerAscii(tester.expectedLogs) check(expectedLogsHash == actualLogsHash) - let sender = tx.getSender() - if not validateTransaction(vmState, tx, sender): + let sender = tester.tx.getSender() + if not validateTransaction(vmState, tester.tx, sender): vmState.mutateStateDB: # pre-EIP158 (e.g., Byzantium) should ensure currentCoinbase exists # in later forks, don't create at all - db.addBalance(header.coinbase, 0.u256) + db.addBalance(tester.header.coinbase, 0.u256) return + var gasUsed: GasInt vmState.mutateStateDB: - let gasUsed = tx.processTransaction(sender, vmState, some(fork)) - db.addBalance(header.coinbase, gasUsed.u256 * tx.gasPrice.u256) + gasUsed = tester.tx.processTransaction(sender, vmState, some(tester.fork)) + db.addBalance(tester.header.coinbase, gasUsed.u256 * tester.tx.gasPrice.u256) -proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) = + if tester.debugMode: + tester.dumpDebugData(vmState, sender, gasUsed) + +proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus, debugMode = false) = + var tester: Tester var fixture: JsonNode for label, child in fixtures: fixture = child + tester.name = label break let fenv = fixture["env"] - var emptyRlpHash = keccak256.digest(rlp.encode("")) - let header = BlockHeader( + tester.header = BlockHeader( coinbase: fenv["currentCoinbase"].getStr.ethAddressFromHex, difficulty: fromHex(UInt256, fenv{"currentDifficulty"}.getStr), blockNumber: fenv{"currentNumber"}.getHexadecimalInt.u256, @@ -71,18 +108,34 @@ proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) = stateRoot: emptyRlpHash ) + tester.debugMode = debugMode let ftrans = fixture["transaction"] for fork in supportedForks: if fixture["post"].hasKey(forkNames[fork]): # echo "[fork: ", forkNames[fork], "]" for expectation in fixture["post"][forkNames[fork]]: + tester.expectedHash = expectation["hash"].getStr + tester.expectedLogs = expectation["logs"].getStr let - expectedHash = expectation["hash"].getStr - expectedLogs = expectation["logs"].getStr indexes = expectation["indexes"] dataIndex = indexes["data"].getInt gasIndex = indexes["gas"].getInt valueIndex = indexes["value"].getInt - let transaction = ftrans.getFixtureTransaction(dataIndex, gasIndex, valueIndex) - testFixtureIndexes(emptyRlpHash, header, fixture["pre"], transaction, - expectedHash, expectedLogs, testStatusIMPL, fork) + tester.tx = ftrans.getFixtureTransaction(dataIndex, gasIndex, valueIndex) + tester.pre = fixture["pre"] + tester.fork = fork + testFixtureIndexes(tester, testStatusIMPL) + +proc main() = + if paramCount() == 0: + # run all test fixtures + suite "generalstate json tests": + jsonTest("GeneralStateTests", testFixture) + else: + # execute single test in debug mode + let path = "tests" / "fixtures" / "GeneralStateTests" + let n = json.parseFile(path / paramStr(1)) + var testStatusIMPL: TestStatus + testFixture(n, testStatusIMPL, true) + +main() diff --git a/tests/test_helpers.nim b/tests/test_helpers.nim index 1bdac79c8..e5f37a55a 100644 --- a/tests/test_helpers.nim +++ b/tests/test_helpers.nim @@ -7,10 +7,10 @@ import os, macros, json, strformat, strutils, parseutils, ospaths, tables, - byteutils, eth/[common, keys], ranges/typedranges, + byteutils, eth/[common, keys, rlp], ranges/typedranges, ../nimbus/[vm_state, constants], ../nimbus/db/[db_chain, state_db], - ../nimbus/transaction, + ../nimbus/[transaction, utils], ../nimbus/vm/interpreter/[gas_costs, vm_forks], ../tests/test_generalstate_failing @@ -290,3 +290,7 @@ proc getFixtureTransaction*(j: JsonNode, dataIndex, gasIndex, valueIndex: int): result.R = fromBytesBE(Uint256, raw[0..31]) result.S = fromBytesBE(Uint256, raw[32..63]) result.V = raw[64] + 27.byte + +proc hashLogEntries*(logs: seq[Log]): string = + toLowerAscii("0x" & $keccakHash(rlp.encode(logs))) + \ No newline at end of file diff --git a/tests/test_vm_json.nim b/tests/test_vm_json.nim index 64a666c84..3046b409d 100644 --- a/tests/test_vm_json.nim +++ b/tests/test_vm_json.nim @@ -16,15 +16,11 @@ import ../nimbus/vm/interpreter, ../nimbus/db/[db_chain, state_db] -proc hashLogEntries(logs: seq[Log]): string = - toLowerAscii("0x" & $keccakHash(rlp.encode(logs))) - proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) suite "vm json tests": jsonTest("VMTests", testFixture) - proc testFixture(fixtures: JsonNode, testStatusIMPL: var TestStatus) = var fixture: JsonNode for label, child in fixtures: