diff --git a/tests/README.md b/tests/README.md deleted file mode 100644 index 4195bef6e..000000000 --- a/tests/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# tests - -TODO: more GeneralStateTest fixtures! diff --git a/tests/all_tests.nim b/tests/all_tests.nim index a38c229c2..d906c96eb 100644 --- a/tests/all_tests.nim +++ b/tests/all_tests.nim @@ -10,12 +10,9 @@ import ./all_tests_macro {. warning[UnusedImport]:off .} cliBuilder: - import ./test_code_stream, - ./test_ledger, + import ./test_ledger, ./test_jwt_auth, - ./test_gas_meter, - ./test_memory, - ./test_stack, + ./test_evm_support, ./test_genesis, ./test_precompiles, ./test_generalstate_json, @@ -29,13 +26,11 @@ cliBuilder: ./test_op_memory, ./test_op_misc, ./test_op_custom, - ./test_state_db, ./test_difficulty, ./test_transaction_json, ./test_blockchain_json, ./test_forkid, ./test_multi_keys, - ./test_misc, #./test_graphql, -- fails ./test_configuration, ./test_txpool, @@ -43,7 +38,6 @@ cliBuilder: #./test_merge, -- fails ./test_eip4844, ./test_beacon/test_skeleton, - ./test_overflow, #./test_getproof_json, -- fails #./test_rpc_experimental_json, -- fails ./test_aristo, diff --git a/tests/evm_tests.nim b/tests/evm_tests.nim deleted file mode 100644 index 08fbe2296..000000000 --- a/tests/evm_tests.nim +++ /dev/null @@ -1,24 +0,0 @@ -# Nimbus -# Copyright (c) 2022-2023 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 ../test_macro - -{. warning[UnusedImport]:off .} - -# This file is just meant to gather a bunch of EVM tests in one -# place. I want to be able to gradually add to this test suite. -# --Adam - -when not defined(evmc_enabled): - cliBuilder: - import ./test_op_arith, - ./test_op_bit, - ./test_op_env, - ./test_op_memory, - ./test_op_misc, - ./test_op_custom, - ./test_tracer_json diff --git a/tests/test_code_stream.nim b/tests/test_code_stream.nim deleted file mode 100644 index 8d05b3f4e..000000000 --- a/tests/test_code_stream.nim +++ /dev/null @@ -1,118 +0,0 @@ -# Nimbus -# Copyright (c) 2018-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 unittest2, sequtils, - ../nimbus/evm/internals - -proc codeStreamMain*() = - suite "parse bytecode": - test "accepts bytes": - let codeStream = CodeStream.init("\x01") - check(codeStream.len == 1) - - - # quicktest - # @pytest.mark.parametrize("code_bytes", (1010, '1010', True, bytearray(32))) - # def test_codeStream_rejects_invalid_code_byte_values(code_bytes): - # with pytest.raises(ValidationError): - # CodeStream(code_bytes) - - test "next returns the correct opcode": - var codeStream = CodeStream.init("\x01\x02\x30") - check(codeStream.next == Op.ADD) - check(codeStream.next == Op.MUL) - check(codeStream.next == Op.ADDRESS) - - - test "peek returns next opcode without changing location": - var codeStream = CodeStream.init("\x01\x02\x30") - check(codeStream.pc == 0) - check(codeStream.peek == Op.ADD) - check(codeStream.pc == 0) - check(codeStream.next == Op.ADD) - check(codeStream.pc == 1) - check(codeStream.peek == Op.MUL) - check(codeStream.pc == 1) - - - test "stop opcode is returned when end reached": - var codeStream = CodeStream.init("\x01\x02") - discard codeStream.next - discard codeStream.next - check(codeStream.next == Op.STOP) - - # Seek has been dommented out for future deletion - # test "seek reverts to original position on exit": - # var codeStream = CodeStream.init("\x01\x02\x30") - # check(codeStream.pc == 0) - # codeStream.seek(1): - # check(codeStream.pc == 1) - # check(codeStream.next == Op.MUL) - # check(codeStream.pc == 0) - # check(codeStream.peek == Op.ADD) - - test "[] returns opcode": - let codeStream = CodeStream.init("\x01\x02\x30") - check(codeStream[0] == Op.ADD) - check(codeStream[1] == Op.MUL) - check(codeStream[2] == Op.ADDRESS) - - test "isValidOpcode invalidates after PUSHXX": - var codeStream = CodeStream.init("\x02\x60\x02\x04") - check(codeStream.isValidOpcode(0)) - check(codeStream.isValidOpcode(1)) - check(not codeStream.isValidOpcode(2)) - check(codeStream.isValidOpcode(3)) - check(not codeStream.isValidOpcode(4)) - - - test "isValidOpcode 0": - var codeStream = CodeStream.init(@[2.byte, 3.byte, 0x72.byte].concat(repeat(4.byte, 32)).concat(@[5.byte])) - # valid: 0 - 2 :: 22 - 35 - # invalid: 3-21 (PUSH19) :: 36+ (too long) - check(codeStream.isValidOpcode(0)) - check(codeStream.isValidOpcode(1)) - check(codeStream.isValidOpcode(2)) - check(not codeStream.isValidOpcode(3)) - check(not codeStream.isValidOpcode(21)) - check(codeStream.isValidOpcode(22)) - check(codeStream.isValidOpcode(35)) - check(not codeStream.isValidOpcode(36)) - - - test "isValidOpcode 1": - let test = @[2.byte, 3.byte, 0x7d.byte].concat(repeat(4.byte, 32)).concat(@[5.byte, 0x7e.byte]).concat(repeat(4.byte, 35)).concat(@[1.byte, 0x61.byte, 1.byte, 1.byte, 1.byte]) - var codeStream = CodeStream.init(test) - # valid: 0 - 2 :: 33 - 36 :: 68 - 73 :: 76 - # invalid: 3 - 32 (PUSH30) :: 37 - 67 (PUSH31) :: 74, 75 (PUSH2) :: 77+ (too long) - check(codeStream.isValidOpcode(0)) - check(codeStream.isValidOpcode(1)) - check(codeStream.isValidOpcode(2)) - check(not codeStream.isValidOpcode(3)) - check(not codeStream.isValidOpcode(32)) - check(codeStream.isValidOpcode(33)) - check(codeStream.isValidOpcode(36)) - check(not codeStream.isValidOpcode(37)) - check(not codeStream.isValidOpcode(67)) - check(codeStream.isValidOpcode(68)) - check(codeStream.isValidOpcode(71)) - check(codeStream.isValidOpcode(72)) - check(codeStream.isValidOpcode(73)) - check(not codeStream.isValidOpcode(74)) - check(not codeStream.isValidOpcode(75)) - check(codeStream.isValidOpcode(76)) - check(not codeStream.isValidOpcode(77)) - - - test "right number of bytes invalidates": - var codeStream = CodeStream.init("\x02\x03\x60\x02\x02") - check(codeStream.isValidOpcode(0)) - check(codeStream.isValidOpcode(1)) - check(codeStream.isValidOpcode(2)) - check(not codeStream.isValidOpcode(3)) - check(codeStream.isValidOpcode(4)) - check(not codeStream.isValidOpcode(5)) diff --git a/tests/test_evm_support.nim b/tests/test_evm_support.nim new file mode 100644 index 000000000..34e4340d6 --- /dev/null +++ b/tests/test_evm_support.nim @@ -0,0 +1,396 @@ +# Nimbus +# Copyright (c) 2018-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/[importutils, sequtils], + unittest2, + stew/byteutils, + eth/keys, + ../nimbus/common, + ../nimbus/transaction, + ../nimbus/evm/types, + ../nimbus/evm/state, + ../nimbus/evm/evm_errors, + ../nimbus/evm/stack, + ../nimbus/evm/memory, + ../nimbus/evm/code_stream, + ../nimbus/evm/internals, + ../nimbus/evm/types, + ../nimbus/constants, + ../nimbus/core/pow/header, + ../nimbus/db/ledger, + ../nimbus/transaction/call_evm + +template testPush(value: untyped, expected: untyped): untyped = + privateAccess(EvmStackRef) + var stack = EvmStackRef.new() + check stack.push(value).isOk + check(stack.values == @[expected]) + +func toBytes(s: string): seq[byte] = + cast[seq[byte]](s) + +func bigEndianToInt(value: openArray[byte]): UInt256 = + result.initFromBytesBE(value) + +proc runStackTests() = + suite "Stack tests": + test "push only valid": + testPush(0'u, 0.u256) + testPush(UINT_256_MAX, UINT_256_MAX) + testPush("ves".toBytes, "ves".toBytes.bigEndianToInt) + + test "push does not allow stack to exceed 1024": + var stack = EvmStackRef.new() + for z in 0 ..< 1024: + check stack.push(z.uint).isOk + check(stack.len == 1024) + check stack.push(1025).error.code == EvmErrorCode.StackFull + + test "dup does not allow stack to exceed 1024": + var stack = EvmStackRef.new() + check stack.push(1.u256).isOk + for z in 0 ..< 1023: + check stack.dup(1).isOk + check(stack.len == 1024) + check stack.dup(1).error.code == EvmErrorCode.StackFull + + test "pop returns latest stack item": + var stack = EvmStackRef.new() + for element in @[1'u, 2'u, 3'u]: + check stack.push(element).isOk + check(stack.popInt.get == 3.u256) + + test "swap correct": + privateAccess(EvmStackRef) + var stack = EvmStackRef.new() + for z in 0 ..< 5: + check stack.push(z.uint).isOk + check(stack.values == @[0.u256, 1.u256, 2.u256, 3.u256, 4.u256]) + check stack.swap(3).isOk + check(stack.values == @[0.u256, 4.u256, 2.u256, 3.u256, 1.u256]) + check stack.swap(1).isOk + check(stack.values == @[0.u256, 4.u256, 2.u256, 1.u256, 3.u256]) + + test "dup correct": + privateAccess(EvmStackRef) + var stack = EvmStackRef.new() + for z in 0 ..< 5: + check stack.push(z.uint).isOk + check(stack.values == @[0.u256, 1.u256, 2.u256, 3.u256, 4.u256]) + check stack.dup(1).isOk + check(stack.values == @[0.u256, 1.u256, 2.u256, 3.u256, 4.u256, 4.u256]) + check stack.dup(5).isOk + check(stack.values == @[0.u256, 1.u256, 2.u256, 3.u256, 4.u256, 4.u256, 1.u256]) + + test "pop raises InsufficientStack appropriately": + var stack = EvmStackRef.new() + check stack.popInt().error.code == EvmErrorCode.StackInsufficient + + test "swap raises InsufficientStack appropriately": + var stack = EvmStackRef.new() + check stack.swap(0).error.code == EvmErrorCode.StackInsufficient + + test "dup raises InsufficientStack appropriately": + var stack = EvmStackRef.new() + check stack.dup(0).error.code == EvmErrorCode.StackInsufficient + + test "binary operations raises InsufficientStack appropriately": + # https://github.com/status-im/nimbus/issues/31 + # ./tests/fixtures/VMTests/vmArithmeticTest/mulUnderFlow.json + + var stack = EvmStackRef.new() + check stack.push(123).isOk + check stack.popInt(2).error.code == EvmErrorCode.StackInsufficient + +proc memory32: EvmMemoryRef = + result = EvmMemoryRef.new(32) + +proc memory128: EvmMemoryRef = + result = EvmMemoryRef.new(123) + +proc runMemoryTests() = + suite "Memory tests": + test "write": + var mem = memory32() + # Test that write creates 32byte string == value padded with zeros + check mem.write(startPos = 0, value = @[1.byte, 0.byte, 1.byte, 0.byte]).isOk + check(mem.bytes == @[1.byte, 0.byte, 1.byte, 0.byte].concat(repeat(0.byte, 28))) + + test "write rejects values beyond memory size": + var mem = memory128() + check mem.write(startPos = 128, value = @[1.byte, 0.byte, 1.byte, 0.byte]).error.code == EvmErrorCode.MemoryFull + check mem.write(startPos = 128, value = 1.byte).error.code == EvmErrorCode.MemoryFull + + test "extends appropriately extends memory": + var mem = EvmMemoryRef.new() + # Test extends to 32 byte array: 0 < (start_position + size) <= 32 + mem.extend(startPos = 0, size = 10) + check(mem.bytes == repeat(0.byte, 32)) + # Test will extend past length if params require: 32 < (start_position + size) <= 64 + mem.extend(startPos = 28, size = 32) + check(mem.bytes == repeat(0.byte, 64)) + # Test won't extend past length unless params require: 32 < (start_position + size) <= 64 + mem.extend(startPos = 48, size = 10) + check(mem.bytes == repeat(0.byte, 64)) + + test "read returns correct bytes": + var mem = memory32() + check mem.write(startPos = 5, value = @[1.byte, 0.byte, 1.byte, 0.byte]).isOk + check(@(mem.read(startPos = 5, size = 4)) == @[1.byte, 0.byte, 1.byte, 0.byte]) + check(@(mem.read(startPos = 6, size = 4)) == @[0.byte, 1.byte, 0.byte, 0.byte]) + check(@(mem.read(startPos = 1, size = 3)) == @[0.byte, 0.byte, 0.byte]) + +proc runCodeStreamTests() = + suite "Codestream tests": + test "accepts bytes": + let codeStream = CodeStream.init("\x01") + check(codeStream.len == 1) + + test "next returns the correct opcode": + var codeStream = CodeStream.init("\x01\x02\x30") + check(codeStream.next == Op.ADD) + check(codeStream.next == Op.MUL) + check(codeStream.next == Op.ADDRESS) + + test "peek returns next opcode without changing location": + var codeStream = CodeStream.init("\x01\x02\x30") + check(codeStream.pc == 0) + check(codeStream.peek == Op.ADD) + check(codeStream.pc == 0) + check(codeStream.next == Op.ADD) + check(codeStream.pc == 1) + check(codeStream.peek == Op.MUL) + check(codeStream.pc == 1) + + test "stop opcode is returned when end reached": + var codeStream = CodeStream.init("\x01\x02") + discard codeStream.next + discard codeStream.next + check(codeStream.next == Op.STOP) + + test "[] returns opcode": + let codeStream = CodeStream.init("\x01\x02\x30") + check(codeStream[0] == Op.ADD) + check(codeStream[1] == Op.MUL) + check(codeStream[2] == Op.ADDRESS) + + test "isValidOpcode invalidates after PUSHXX": + var codeStream = CodeStream.init("\x02\x60\x02\x04") + check(codeStream.isValidOpcode(0)) + check(codeStream.isValidOpcode(1)) + check(not codeStream.isValidOpcode(2)) + check(codeStream.isValidOpcode(3)) + check(not codeStream.isValidOpcode(4)) + + test "isValidOpcode 0": + var codeStream = CodeStream.init(@[2.byte, 3.byte, 0x72.byte].concat(repeat(4.byte, 32)).concat(@[5.byte])) + # valid: 0 - 2 :: 22 - 35 + # invalid: 3-21 (PUSH19) :: 36+ (too long) + check(codeStream.isValidOpcode(0)) + check(codeStream.isValidOpcode(1)) + check(codeStream.isValidOpcode(2)) + check(not codeStream.isValidOpcode(3)) + check(not codeStream.isValidOpcode(21)) + check(codeStream.isValidOpcode(22)) + check(codeStream.isValidOpcode(35)) + check(not codeStream.isValidOpcode(36)) + + test "isValidOpcode 1": + let test = @[2.byte, 3.byte, 0x7d.byte].concat(repeat(4.byte, 32)).concat(@[5.byte, 0x7e.byte]).concat(repeat(4.byte, 35)).concat(@[1.byte, 0x61.byte, 1.byte, 1.byte, 1.byte]) + var codeStream = CodeStream.init(test) + # valid: 0 - 2 :: 33 - 36 :: 68 - 73 :: 76 + # invalid: 3 - 32 (PUSH30) :: 37 - 67 (PUSH31) :: 74, 75 (PUSH2) :: 77+ (too long) + check(codeStream.isValidOpcode(0)) + check(codeStream.isValidOpcode(1)) + check(codeStream.isValidOpcode(2)) + check(not codeStream.isValidOpcode(3)) + check(not codeStream.isValidOpcode(32)) + check(codeStream.isValidOpcode(33)) + check(codeStream.isValidOpcode(36)) + check(not codeStream.isValidOpcode(37)) + check(not codeStream.isValidOpcode(67)) + check(codeStream.isValidOpcode(68)) + check(codeStream.isValidOpcode(71)) + check(codeStream.isValidOpcode(72)) + check(codeStream.isValidOpcode(73)) + check(not codeStream.isValidOpcode(74)) + check(not codeStream.isValidOpcode(75)) + check(codeStream.isValidOpcode(76)) + check(not codeStream.isValidOpcode(77)) + + test "right number of bytes invalidates": + var codeStream = CodeStream.init("\x02\x03\x60\x02\x02") + check(codeStream.isValidOpcode(0)) + check(codeStream.isValidOpcode(1)) + check(codeStream.isValidOpcode(2)) + check(not codeStream.isValidOpcode(3)) + check(codeStream.isValidOpcode(4)) + check(not codeStream.isValidOpcode(5)) + + +proc initGasMeter(startGas: GasInt): GasMeter = result.init(startGas) + +proc gasMeters: seq[GasMeter] = + @[initGasMeter(10), initGasMeter(100), initGasMeter(999)] + +template runTest(body: untyped) = + var res = gasMeters() + for gasMeter {.inject.} in res.mitems: + let StartGas {.inject.} = gasMeter.gasRemaining + body + +proc runGasMeterTests() = + suite "GasMeter tests": + test "consume spends": + runTest: + check(gasMeter.gasRemaining == StartGas) + let consume = StartGas + check gasMeter.consumeGas(consume, "0").isOk + check(gasMeter.gasRemaining - (StartGas - consume) == 0) + + test "consume errors": + runTest: + check(gasMeter.gasRemaining == StartGas) + check gasMeter.consumeGas(StartGas + 1, "").error.code == EvmErrorCode.OutOfGas + + test "return refund works correctly": + runTest: + check(gasMeter.gasRemaining == StartGas) + check(gasMeter.gasRefunded == 0) + check gasMeter.consumeGas(5, "").isOk + check(gasMeter.gasRemaining == StartGas - 5) + gasMeter.returnGas(5) + check(gasMeter.gasRemaining == StartGas) + gasMeter.refundGas(5) + check(gasMeter.gasRefunded == 5) + +func toAddress(n: int): EthAddress = + result[19] = n.byte + +func toAddress(a, b: int): EthAddress = + result[18] = a.byte + result[19] = b.byte + +func toAddress(a, b, c: int): EthAddress = + result[17] = a.byte + result[18] = b.byte + result[19] = c.byte + +proc runMiscTests() = + suite "Misc test suite": + test "EthAddress to int": + check toAddress(0xff).toInt == 0xFF + check toAddress(0x10, 0x0).toInt == 0x1000 + check toAddress(0x10, 0x0, 0x0).toInt == 0x100000 + + test "calcGasLimitEIP1559": + type + GLT = object + limit: GasInt + max : GasInt + min : GasInt + + const testData = [ + GLT(limit: 20000000, max: 20019530, min: 19980470), + GLT(limit: 40000000, max: 40039061, min: 39960939) + ] + + for x in testData: + # Increase + var have = calcGasLimit1559(x.limit, 2*x.limit) + var want = x.max + check have == want + + # Decrease + have = calcGasLimit1559(x.limit, 0) + want = x.min + check have == want + + # Small decrease + have = calcGasLimit1559(x.limit, x.limit-1) + want = x.limit-1 + check have == want + + # Small increase + have = calcGasLimit1559(x.limit, x.limit+1) + want = x.limit+1 + check have == want + + # No change + have = calcGasLimit1559(x.limit, x.limit) + want = x.limit + check have == want + +const + data = [0x5b.uint8, 0x5a, 0x5a, 0x30, 0x30, 0x30, 0x30, 0x72, 0x00, 0x00, 0x00, 0x58, + 0x58, 0x24, 0x58, 0x58, 0x3a, 0x19, 0x75, 0x75, 0x2e, 0x2e, 0x2e, 0x2e, + 0xec, 0x9f, 0x69, 0x67, 0x7f, 0xff, 0xff, 0xff, 0xff, 0x6c, 0x5a, 0x32, + 0x07, 0xf4, 0x75, 0x75, 0xf5, 0x75, 0x75, 0x75, 0x7f, 0x5b, 0xd9, 0x32, + 0x5a, 0x07, 0x19, 0x34, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xec, + 0x9f, 0x69, 0x67, 0x7f, 0xff, 0xff, 0xff, 0xff, 0x6c, 0xfc, 0xf7, 0xfc, + 0xfc, 0xfc, 0xfc, 0xf4, 0x03, 0x03, 0x81, 0x81, 0x81, 0xfb, 0x7a, 0x30, + 0x80, 0x3d, 0x59, 0x59, 0x59, 0x59, 0x81, 0x00, 0x59, 0x2f, 0x45, 0x30, + 0x32, 0xf4, 0x5d, 0x5b, 0x37, 0x19] + + codeAddress = hexToByteArray[20]("000000000000000000000000636f6e7472616374") + coinbase = hexToByteArray[20]("4444588443C3a91288c5002483449Aba1054192b") + +proc runTestOverflow() = + test "GasCall unhandled overflow": + let header = BlockHeader( + stateRoot: EMPTY_ROOT_HASH, + number: 1150000'u64, + coinBase: coinbase, + gasLimit: 30000000, + timeStamp: EthTime(123456), + ) + + let com = CommonRef.new( + newCoreDbRef(DefaultDbMemory), + config = chainConfigForNetwork(MainNet) + ) + + let s = BaseVMState.new( + header, + header, + com, + ) + + s.stateDB.setCode(codeAddress, @data) + let unsignedTx = Transaction( + txType: TxLegacy, + nonce: 0, + chainId: MainNet.ChainId, + gasPrice: 0.GasInt, + gasLimit: 30000000, + to: Opt.some codeAddress, + value: 0.u256, + payload: @data + ) + + let privateKey = PrivateKey.fromHex("0000000000000000000000000000000000000000000000000000001000000000")[] + let tx = signTransaction(unsignedTx, privateKey, ChainId(1), false) + let res = testCallEvm(tx, tx.getSender, s, FkHomestead) + + when defined(evmc_enabled): + check res.error == "EVMC_FAILURE" + else: + check res.error == "Opcode Dispatch Error: GasIntOverflow, depth=1" + +proc evmSupportMain*() = + runStackTests() + runMemoryTests() + runCodeStreamTests() + runGasMeterTests() + runMiscTests() + runTestOverflow() + +when isMainModule: + evmSupportMain() diff --git a/tests/test_gas_meter.nim b/tests/test_gas_meter.nim deleted file mode 100644 index 3be283ce4..000000000 --- a/tests/test_gas_meter.nim +++ /dev/null @@ -1,105 +0,0 @@ -# Nimbus -# Copyright (c) 2018-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 - unittest2, macros, strformat, - eth/common/eth_types, - ../nimbus/[evm/types, evm/internals, evm/evm_errors] - -# TODO: quicktest -# PS: parametrize can be easily immitated, but still quicktests would be even more useful - -# disableLogging() - -proc initGasMeter(startGas: GasInt): GasMeter = result.init(startGas) - -proc gasMeters: seq[GasMeter] = - @[initGasMeter(10), initGasMeter(100), initGasMeter(999)] - -macro all(element: untyped, handler: untyped): untyped = - let name = ident(&"{element.repr}s") - let StartGas = ident("StartGas") - result = quote: - var res = `name`() - for `element` in res.mitems: - let `StartGas` = `element`.gasRemaining - `handler` - -# @pytest.mark.parametrize("value", (0, 10)) -# def test_start_gas_on_instantiation(value): -# meter = GasMeter(value) -# doAssert meter.start_gas == value -# doAssert meter.gas_remaining == value -# doAssert meter.gas_refunded == 0 - - -# @pytest.mark.parametrize("value", (-1, 2**256, 'a')) -# def test_instantiation_invalid_value(value): -# with pytest.raises(ValidationError): -# GasMeter(value) - - -# @pytest.mark.parametrize("amount", (0, 1, 10)) -# def test_consume_gas(gas_meter, amount): -# gas_meter.consume_gas(amount, "reason") -# doAssert gas_meter.gas_remaining == gas_meter.start_gas - amount - - -# @pytest.mark.parametrize("amount", (0, 1, 99)) -# def test_return_gas(gas_meter, amount): -# gas_meter.return_gas(amount) -# doAssert gas_meter.gas_remaining == (gas_meter.start_gas + amount) - -# @pytest.mark.parametrize("amount", (0, 1, 99)) -# def test_refund_gas(gas_meter, amount): -# gas_meter.refund_gas(amount) -# doAssert gas_meter.gas_refunded == amount - -proc gasMeterMain*() = - suite "gasMeter": - # types - # test "consume rejects negative": - # all(gasMeter): - # expect(ValidationError): - # gasMeter.consumeGas(-1.i256, "independent") - - # test "return rejects negative": - # all(gasMeter): - # expect(ValidationError): - # gasMeter.returnGas(-1.i256) - - # test "refund rejects negative": - # all(gasMeter): - # expect(ValidationError): - # gasMeter.returnGas(-1.i256) - - # TODO: -0/+0 - test "consume spends": - all(gasMeter): - check(gasMeter.gasRemaining == StartGas) - let consume = StartGas - check gasMeter.consumeGas(consume, "0").isOk - check(gasMeter.gasRemaining - (StartGas - consume) == 0) - - test "consume errors": - all(gasMeter): - check(gasMeter.gasRemaining == StartGas) - check gasMeter.consumeGas(StartGas + 1, "").error.code == EvmErrorCode.OutOfGas - - test "return refund works correctly": - all(gasMeter): - check(gasMeter.gasRemaining == StartGas) - check(gasMeter.gasRefunded == 0) - check gasMeter.consumeGas(5, "").isOk - check(gasMeter.gasRemaining == StartGas - 5) - gasMeter.returnGas(5) - check(gasMeter.gasRemaining == StartGas) - gasMeter.refundGas(5) - check(gasMeter.gasRefunded == 5) - -when isMainModule: - gasMeterMain() diff --git a/tests/test_ledger.nim b/tests/test_ledger.nim index 6e61d70d8..62fbc4f34 100644 --- a/tests/test_ledger.nim +++ b/tests/test_ledger.nim @@ -9,18 +9,20 @@ # according to those terms. import - std/[strformat, strutils], + std/[strformat, strutils, importutils], eth/keys, stew/byteutils, stew/endians2, ../nimbus/config, ../nimbus/db/ledger, + ../nimbus/db/storage_types, ../nimbus/common/common, ../nimbus/core/chain, ../nimbus/core/tx_pool, ../nimbus/core/casper, ../nimbus/transaction, ../nimbus/constants, + ../nimbus/db/ledger/accounts_ledger {.all.}, # import all private symbols unittest2 const @@ -64,7 +66,7 @@ proc pp*(tx: Transaction; ledger: LedgerRef): string = when isMainModule: import chronicles - + proc setTraceLevel = discard when defined(chronicles_runtime_filtering) and loggingEnabled: @@ -138,10 +140,9 @@ func initAddr(z: int): EthAddress = result[L-sizeof(uint32)..^1] = toBytesBE(z.uint32) proc importBlocks(env: TestEnv; blk: EthBlock) = - let res = env.chain.persistBlocks([blk]) - if res.isErr: - debugEcho res.error - raiseAssert "persistBlocks() failed at block #" & $blk.header.number + env.chain.persistBlocks([blk]).isOkOr: + raiseAssert "persistBlocks() failed at block #" & + $blk.header.number & " msg: " & error proc getLedger(com: CommonRef; header: BlockHeader): LedgerRef = LedgerRef.init(com.db, header.stateRoot) @@ -295,8 +296,8 @@ const feeRecipient = initAddr(401) prevRandao = EMPTY_UNCLE_HASH # it can be any valid hash -proc runner(noisy = true) = - suite "StateDB nesting scenarios": +proc runLedgerTransactionTests(noisy = true) = + suite "Ledger nesting scenarios": var env = initEnv() test "Create transactions and blocks": @@ -390,18 +391,306 @@ proc runner(noisy = true) = let ledger = env.com.getLedger(head) env.runTrial4(ledger, n, rollback = true) +proc runLedgerBesicOperationsTests() = + suite "Ledger basic operations tests": + setup: + const emptyAcc {.used.} = newAccount() + + var + memDB = newCoreDbRef DefaultDbMemory + stateDB {.used.} = LedgerRef.init(memDB, EMPTY_ROOT_HASH) + address {.used.} = hexToByteArray[20]("0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6") + code {.used.} = hexToSeqByte("0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6") + rootHash {.used.} : KeccakHash + + test "accountExists and isDeadAccount": + check stateDB.accountExists(address) == false + check stateDB.isDeadAccount(address) == true + + stateDB.setBalance(address, 1000.u256) + + check stateDB.accountExists(address) == true + check stateDB.isDeadAccount(address) == false + + stateDB.setBalance(address, 0.u256) + stateDB.setNonce(address, 1) + check stateDB.isDeadAccount(address) == false + + stateDB.setCode(address, code) + stateDB.setNonce(address, 0) + check stateDB.isDeadAccount(address) == false + + stateDB.setCode(address, newSeq[byte]()) + check stateDB.isDeadAccount(address) == true + check stateDB.accountExists(address) == true + + test "clone storage": + # give access to private fields of AccountRef + privateAccess(AccountRef) + var x = AccountRef( + overlayStorage: Table[UInt256, UInt256](), + originalStorage: newTable[UInt256, UInt256]() + ) + + x.overlayStorage[10.u256] = 11.u256 + x.overlayStorage[11.u256] = 12.u256 + + x.originalStorage[10.u256] = 11.u256 + x.originalStorage[11.u256] = 12.u256 + + var y = x.clone(cloneStorage = true) + y.overlayStorage[12.u256] = 13.u256 + y.originalStorage[12.u256] = 13.u256 + + check 12.u256 notin x.overlayStorage + check 12.u256 in y.overlayStorage + + check x.overlayStorage.len == 2 + check y.overlayStorage.len == 3 + + check 12.u256 in x.originalStorage + check 12.u256 in y.originalStorage + + check x.originalStorage.len == 3 + check y.originalStorage.len == 3 + + test "accounts cache": + var ac = LedgerRef.init(memDB, EMPTY_ROOT_HASH) + var addr1 = initAddr(1) + + check ac.isDeadAccount(addr1) == true + check ac.accountExists(addr1) == false + check ac.contractCollision(addr1) == false + + ac.setBalance(addr1, 1000.u256) + check ac.getBalance(addr1) == 1000.u256 + ac.subBalance(addr1, 100.u256) + check ac.getBalance(addr1) == 900.u256 + ac.addBalance(addr1, 200.u256) + check ac.getBalance(addr1) == 1100.u256 + + ac.setNonce(addr1, 1) + check ac.getNonce(addr1) == 1 + ac.incNonce(addr1) + check ac.getNonce(addr1) == 2 + + ac.setCode(addr1, code) + check ac.getCode(addr1) == code + + ac.setStorage(addr1, 1.u256, 10.u256) + check ac.getStorage(addr1, 1.u256) == 10.u256 + check ac.getCommittedStorage(addr1, 1.u256) == 0.u256 + + check ac.contractCollision(addr1) == true + check ac.getCodeSize(addr1) == code.len + + ac.persist() + rootHash = ac.rootHash + + var db = LedgerRef.init(memDB, EMPTY_ROOT_HASH) + db.setBalance(addr1, 1100.u256) + db.setNonce(addr1, 2) + db.setCode(addr1, code) + db.setStorage(addr1, 1.u256, 10.u256) + check rootHash == db.rootHash + + # accounts cache readonly operations + # use previous hash + var ac2 = LedgerRef.init(memDB, rootHash) + var addr2 = initAddr(2) + + check ac2.getCodeHash(addr2) == emptyAcc.codeHash + check ac2.getBalance(addr2) == emptyAcc.balance + check ac2.getNonce(addr2) == emptyAcc.nonce + check ac2.getCode(addr2) == [] + check ac2.getCodeSize(addr2) == 0 + check ac2.getCommittedStorage(addr2, 1.u256) == 0.u256 + check ac2.getStorage(addr2, 1.u256) == 0.u256 + check ac2.contractCollision(addr2) == false + check ac2.accountExists(addr2) == false + check ac2.isDeadAccount(addr2) == true + + ac2.persist() + # readonly operations should not modify + # state trie at all + check ac2.rootHash == rootHash + + test "accounts cache code retrieval after persist called": + var ac = LedgerRef.init(memDB, EMPTY_ROOT_HASH) + var addr2 = initAddr(2) + ac.setCode(addr2, code) + ac.persist() + check ac.getCode(addr2) == code + let + key = contractHashKey(keccakHash(code)) + val = memDB.newKvt().get(key.toOpenArray).valueOr: EmptyBlob + check val == code + + test "accessList operations": + proc verifyAddrs(ac: LedgerRef, addrs: varargs[int]): bool = + for c in addrs: + if not ac.inAccessList(c.initAddr): + return false + true + + proc verifySlots(ac: LedgerRef, address: int, slots: varargs[int]): bool = + let a = address.initAddr + if not ac.inAccessList(a): + return false + + for c in slots: + if not ac.inAccessList(a, c.u256): + return false + true + + proc accessList(ac: LedgerRef, address: int) {.inline.} = + ac.accessList(address.initAddr) + + proc accessList(ac: LedgerRef, address, slot: int) {.inline.} = + ac.accessList(address.initAddr, slot.u256) + + var ac = LedgerRef.init(memDB, EMPTY_ROOT_HASH) + + ac.accessList(0xaa) + ac.accessList(0xbb, 0x01) + ac.accessList(0xbb, 0x02) + check ac.verifyAddrs(0xaa, 0xbb) + check ac.verifySlots(0xbb, 0x01, 0x02) + check ac.verifySlots(0xaa, 0x01) == false + check ac.verifySlots(0xaa, 0x02) == false + + var sp = ac.beginSavepoint + # some new ones + ac.accessList(0xbb, 0x03) + ac.accessList(0xaa, 0x01) + ac.accessList(0xcc, 0x01) + ac.accessList(0xcc) + + check ac.verifyAddrs(0xaa, 0xbb, 0xcc) + check ac.verifySlots(0xaa, 0x01) + check ac.verifySlots(0xbb, 0x01, 0x02, 0x03) + check ac.verifySlots(0xcc, 0x01) + + ac.rollback(sp) + check ac.verifyAddrs(0xaa, 0xbb) + check ac.verifyAddrs(0xcc) == false + check ac.verifySlots(0xcc, 0x01) == false + + sp = ac.beginSavepoint + ac.accessList(0xbb, 0x03) + ac.accessList(0xaa, 0x01) + ac.accessList(0xcc, 0x01) + ac.accessList(0xcc) + ac.accessList(0xdd, 0x04) + ac.commit(sp) + + check ac.verifyAddrs(0xaa, 0xbb, 0xcc) + check ac.verifySlots(0xaa, 0x01) + check ac.verifySlots(0xbb, 0x01, 0x02, 0x03) + check ac.verifySlots(0xcc, 0x01) + check ac.verifySlots(0xdd, 0x04) + + test "transient storage operations": + var ac = LedgerRef.init(memDB, EMPTY_ROOT_HASH) + + proc tStore(ac: LedgerRef, address, slot, val: int) = + ac.setTransientStorage(address.initAddr, slot.u256, val.u256) + + proc tLoad(ac: LedgerRef, address, slot: int): UInt256 = + ac.getTransientStorage(address.initAddr, slot.u256) + + proc vts(ac: LedgerRef, address, slot, val: int): bool = + ac.tLoad(address, slot) == val.u256 + + ac.tStore(0xaa, 3, 66) + ac.tStore(0xbb, 1, 33) + ac.tStore(0xbb, 2, 99) + + check ac.vts(0xaa, 3, 66) + check ac.vts(0xbb, 1, 33) + check ac.vts(0xbb, 2, 99) + check ac.vts(0xaa, 1, 33) == false + check ac.vts(0xbb, 1, 66) == false + + var sp = ac.beginSavepoint + # some new ones + ac.tStore(0xaa, 3, 77) + ac.tStore(0xbb, 1, 55) + ac.tStore(0xcc, 7, 88) + + check ac.vts(0xaa, 3, 77) + check ac.vts(0xbb, 1, 55) + check ac.vts(0xcc, 7, 88) + + check ac.vts(0xaa, 3, 66) == false + check ac.vts(0xbb, 1, 33) == false + check ac.vts(0xbb, 2, 99) + + ac.rollback(sp) + check ac.vts(0xaa, 3, 66) + check ac.vts(0xbb, 1, 33) + check ac.vts(0xbb, 2, 99) + check ac.vts(0xcc, 7, 88) == false + + sp = ac.beginSavepoint + ac.tStore(0xaa, 3, 44) + ac.tStore(0xaa, 4, 55) + ac.tStore(0xbb, 1, 22) + ac.tStore(0xdd, 2, 66) + + ac.commit(sp) + check ac.vts(0xaa, 3, 44) + check ac.vts(0xaa, 4, 55) + check ac.vts(0xbb, 1, 22) + check ac.vts(0xbb, 1, 55) == false + check ac.vts(0xbb, 2, 99) + check ac.vts(0xcc, 7, 88) == false + check ac.vts(0xdd, 2, 66) + + ac.clearTransientStorage() + check ac.vts(0xaa, 3, 44) == false + check ac.vts(0xaa, 4, 55) == false + check ac.vts(0xbb, 1, 22) == false + check ac.vts(0xbb, 1, 55) == false + check ac.vts(0xbb, 2, 99) == false + check ac.vts(0xcc, 7, 88) == false + check ac.vts(0xdd, 2, 66) == false + + test "accounts cache contractCollision": + # use previous hash + var ac = LedgerRef.init(memDB, EMPTY_ROOT_HASH) + let addr2 = initAddr(2) + check ac.contractCollision(addr2) == false + + ac.setStorage(addr2, 1.u256, 1.u256) + check ac.contractCollision(addr2) == false + + ac.persist() + check ac.contractCollision(addr2) == true + + let addr3 = initAddr(3) + check ac.contractCollision(addr3) == false + ac.setCode(addr3, @[0xaa.byte, 0xbb]) + check ac.contractCollision(addr3) == true + + let addr4 = initAddr(4) + check ac.contractCollision(addr4) == false + ac.setNonce(addr4, 1) + check ac.contractCollision(addr4) == true + # ------------------------------------------------------------------------------ # Main function(s) # ------------------------------------------------------------------------------ proc ledgerMain*(noisy = defined(debug)) = - noisy.runner + noisy.runLedgerTransactionTests + runLedgerBesicOperationsTests() when isMainModule: var noisy = defined(debug) setErrorLevel() - noisy.runner + noisy.ledgerMain # ------------------------------------------------------------------------------ # End diff --git a/tests/test_memory.nim b/tests/test_memory.nim deleted file mode 100644 index 047b24a6b..000000000 --- a/tests/test_memory.nim +++ /dev/null @@ -1,53 +0,0 @@ -# Nimbus -# Copyright (c) 2018-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/sequtils, - unittest2, - ../nimbus/evm/evm_errors, - ../nimbus/evm/memory - -proc memory32: EvmMemoryRef = - result = EvmMemoryRef.new(32) - -proc memory128: EvmMemoryRef = - result = EvmMemoryRef.new(123) - -proc memoryMain*() = - suite "memory": - test "write": - var mem = memory32() - # Test that write creates 32byte string == value padded with zeros - check mem.write(startPos = 0, value = @[1.byte, 0.byte, 1.byte, 0.byte]).isOk - check(mem.bytes == @[1.byte, 0.byte, 1.byte, 0.byte].concat(repeat(0.byte, 28))) - - test "write rejects values beyond memory size": - var mem = memory128() - check mem.write(startPos = 128, value = @[1.byte, 0.byte, 1.byte, 0.byte]).error.code == EvmErrorCode.MemoryFull - check mem.write(startPos = 128, value = 1.byte).error.code == EvmErrorCode.MemoryFull - - test "extends appropriately extends memory": - var mem = EvmMemoryRef.new() - # Test extends to 32 byte array: 0 < (start_position + size) <= 32 - mem.extend(startPos = 0, size = 10) - check(mem.bytes == repeat(0.byte, 32)) - # Test will extend past length if params require: 32 < (start_position + size) <= 64 - mem.extend(startPos = 28, size = 32) - check(mem.bytes == repeat(0.byte, 64)) - # Test won't extend past length unless params require: 32 < (start_position + size) <= 64 - mem.extend(startPos = 48, size = 10) - check(mem.bytes == repeat(0.byte, 64)) - - test "read returns correct bytes": - var mem = memory32() - check mem.write(startPos = 5, value = @[1.byte, 0.byte, 1.byte, 0.byte]).isOk - check(@(mem.read(startPos = 5, size = 4)) == @[1.byte, 0.byte, 1.byte, 0.byte]) - check(@(mem.read(startPos = 6, size = 4)) == @[0.byte, 1.byte, 0.byte, 0.byte]) - check(@(mem.read(startPos = 1, size = 3)) == @[0.byte, 0.byte, 0.byte]) - -when isMainModule: - memoryMain() diff --git a/tests/test_misc.nim b/tests/test_misc.nim deleted file mode 100644 index 93931c09a..000000000 --- a/tests/test_misc.nim +++ /dev/null @@ -1,75 +0,0 @@ -# Nimbus -# Copyright (c) 2019-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 - unittest2, - eth/common/eth_types, - ../nimbus/evm/internals, - ../nimbus/core/pow/header - -func toAddress(n: int): EthAddress = - result[19] = n.byte - -func toAddress(a, b: int): EthAddress = - result[18] = a.byte - result[19] = b.byte - -func toAddress(a, b, c: int): EthAddress = - result[17] = a.byte - result[18] = b.byte - result[19] = c.byte - -proc miscMain*() = - suite "Misc test suite": - test "EthAddress to int": - check toAddress(0xff).toInt == 0xFF - check toAddress(0x10, 0x0).toInt == 0x1000 - check toAddress(0x10, 0x0, 0x0).toInt == 0x100000 - - test "calcGasLimitEIP1559": - type - GLT = object - limit: GasInt - max : GasInt - min : GasInt - - const testData = [ - GLT(limit: 20000000, max: 20019530, min: 19980470), - GLT(limit: 40000000, max: 40039061, min: 39960939) - ] - - for x in testData: - # Increase - var have = calcGasLimit1559(x.limit, 2*x.limit) - var want = x.max - check have == want - - # Decrease - have = calcGasLimit1559(x.limit, 0) - want = x.min - check have == want - - # Small decrease - have = calcGasLimit1559(x.limit, x.limit-1) - want = x.limit-1 - check have == want - - # Small increase - have = calcGasLimit1559(x.limit, x.limit+1) - want = x.limit+1 - check have == want - - # No change - have = calcGasLimit1559(x.limit, x.limit) - want = x.limit - check have == want - -when isMainModule: - miscMain() diff --git a/tests/test_overflow.nim b/tests/test_overflow.nim deleted file mode 100644 index f25838226..000000000 --- a/tests/test_overflow.nim +++ /dev/null @@ -1,77 +0,0 @@ -# Nimbus -# Copyright (c) 2023-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 eth/[keys, trie] -import stew/byteutils -import unittest2 -import ../nimbus/common -import ../nimbus/evm/state -import ../nimbus/evm/types -import ../nimbus/transaction -import ../nimbus/transaction/call_evm -import ../nimbus/db/core_db -import ../nimbus/db/ledger - -const - data = [0x5b.uint8, 0x5a, 0x5a, 0x30, 0x30, 0x30, 0x30, 0x72, 0x00, 0x00, 0x00, 0x58, - 0x58, 0x24, 0x58, 0x58, 0x3a, 0x19, 0x75, 0x75, 0x2e, 0x2e, 0x2e, 0x2e, - 0xec, 0x9f, 0x69, 0x67, 0x7f, 0xff, 0xff, 0xff, 0xff, 0x6c, 0x5a, 0x32, - 0x07, 0xf4, 0x75, 0x75, 0xf5, 0x75, 0x75, 0x75, 0x7f, 0x5b, 0xd9, 0x32, - 0x5a, 0x07, 0x19, 0x34, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0xec, - 0x9f, 0x69, 0x67, 0x7f, 0xff, 0xff, 0xff, 0xff, 0x6c, 0xfc, 0xf7, 0xfc, - 0xfc, 0xfc, 0xfc, 0xf4, 0x03, 0x03, 0x81, 0x81, 0x81, 0xfb, 0x7a, 0x30, - 0x80, 0x3d, 0x59, 0x59, 0x59, 0x59, 0x81, 0x00, 0x59, 0x2f, 0x45, 0x30, - 0x32, 0xf4, 0x5d, 0x5b, 0x37, 0x19] - - codeAddress = hexToByteArray[20]("000000000000000000000000636f6e7472616374") - coinbase = hexToByteArray[20]("4444588443C3a91288c5002483449Aba1054192b") - -proc overflowMain*() = - test "GasCall unhandled overflow": - let header = BlockHeader( - stateRoot: emptyRlpHash, - number: 1150000'u64, - coinBase: coinbase, - gasLimit: 30000000, - timeStamp: EthTime(123456), - ) - - let com = CommonRef.new(newCoreDbRef(DefaultDbMemory), config = chainConfigForNetwork(MainNet)) - let s = BaseVMState.new( - header, - header, - com, - ) - - s.stateDB.setCode(codeAddress, @data) - let unsignedTx = Transaction( - txType: TxLegacy, - nonce: 0, - chainId: MainNet.ChainId, - gasPrice: 0.GasInt, - gasLimit: 30000000, - to: Opt.some codeAddress, - value: 0.u256, - payload: @data - ) - - let privateKey = PrivateKey.fromHex("0000000000000000000000000000000000000000000000000000001000000000")[] - let tx = signTransaction(unsignedTx, privateKey, ChainId(1), false) - let res = testCallEvm(tx, tx.getSender, s, FkHomestead) - - when defined(evmc_enabled): - check res.error == "EVMC_FAILURE" - else: - check res.error == "Opcode Dispatch Error: GasIntOverflow, depth=1" - -when isMainModule: - overflowMain() diff --git a/tests/test_stack.nim b/tests/test_stack.nim deleted file mode 100644 index 596367bfb..000000000 --- a/tests/test_stack.nim +++ /dev/null @@ -1,99 +0,0 @@ -# Nimbus -# Copyright (c) 2018-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/importutils, - unittest2, - eth/common/eth_types, - ../nimbus/evm/evm_errors, - ../nimbus/evm/stack, - ../nimbus/constants - -template testPush(value: untyped, expected: untyped): untyped = - privateAccess(EvmStackRef) - var stack = EvmStackRef.new() - check stack.push(value).isOk - check(stack.values == @[expected]) - -func toBytes(s: string): seq[byte] = - cast[seq[byte]](s) - -func bigEndianToInt*(value: openArray[byte]): UInt256 = - result.initFromBytesBE(value) - -proc stackMain*() = - suite "stack": - test "push only valid": - testPush(0'u, 0.u256) - testPush(UINT_256_MAX, UINT_256_MAX) - testPush("ves".toBytes, "ves".toBytes.bigEndianToInt) - - test "push does not allow stack to exceed 1024": - var stack = EvmStackRef.new() - for z in 0 ..< 1024: - check stack.push(z.uint).isOk - check(stack.len == 1024) - check stack.push(1025).error.code == EvmErrorCode.StackFull - - test "dup does not allow stack to exceed 1024": - var stack = EvmStackRef.new() - check stack.push(1.u256).isOk - for z in 0 ..< 1023: - check stack.dup(1).isOk - check(stack.len == 1024) - check stack.dup(1).error.code == EvmErrorCode.StackFull - - test "pop returns latest stack item": - var stack = EvmStackRef.new() - for element in @[1'u, 2'u, 3'u]: - check stack.push(element).isOk - check(stack.popInt.get == 3.u256) - - test "swap correct": - privateAccess(EvmStackRef) - var stack = EvmStackRef.new() - for z in 0 ..< 5: - check stack.push(z.uint).isOk - check(stack.values == @[0.u256, 1.u256, 2.u256, 3.u256, 4.u256]) - check stack.swap(3).isOk - check(stack.values == @[0.u256, 4.u256, 2.u256, 3.u256, 1.u256]) - check stack.swap(1).isOk - check(stack.values == @[0.u256, 4.u256, 2.u256, 1.u256, 3.u256]) - - test "dup correct": - privateAccess(EvmStackRef) - var stack = EvmStackRef.new() - for z in 0 ..< 5: - check stack.push(z.uint).isOk - check(stack.values == @[0.u256, 1.u256, 2.u256, 3.u256, 4.u256]) - check stack.dup(1).isOk - check(stack.values == @[0.u256, 1.u256, 2.u256, 3.u256, 4.u256, 4.u256]) - check stack.dup(5).isOk - check(stack.values == @[0.u256, 1.u256, 2.u256, 3.u256, 4.u256, 4.u256, 1.u256]) - - test "pop raises InsufficientStack appropriately": - var stack = EvmStackRef.new() - check stack.popInt().error.code == EvmErrorCode.StackInsufficient - - test "swap raises InsufficientStack appropriately": - var stack = EvmStackRef.new() - check stack.swap(0).error.code == EvmErrorCode.StackInsufficient - - test "dup raises InsufficientStack appropriately": - var stack = EvmStackRef.new() - check stack.dup(0).error.code == EvmErrorCode.StackInsufficient - - test "binary operations raises InsufficientStack appropriately": - # https://github.com/status-im/nimbus/issues/31 - # ./tests/fixtures/VMTests/vmArithmeticTest/mulUnderFlow.json - - var stack = EvmStackRef.new() - check stack.push(123).isOk - check stack.popInt(2).error.code == EvmErrorCode.StackInsufficient - -when isMainModule: - stackMain() diff --git a/tests/test_state_db.nim b/tests/test_state_db.nim deleted file mode 100644 index 31a0f4dc6..000000000 --- a/tests/test_state_db.nim +++ /dev/null @@ -1,312 +0,0 @@ -# Nimbus -# Copyright (c) 2018-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/importutils, - eth/trie/trie_defs, - eth/common/eth_types, - stew/[byteutils, endians2], - results, - unittest2, - ../nimbus/db/storage_types, - ../nimbus/db/core_db, - ../nimbus/db/ledger, - ../nimbus/db/ledger/accounts_ledger {.all.} # import all private symbols - -func initAddr(z: int): EthAddress = - const L = sizeof(result) - result[L-sizeof(uint32)..^1] = toBytesBE(z.uint32) - -proc stateDBMain*() = - suite "Account State DB": - setup: - const emptyAcc {.used.} = newAccount() - - var - memDB = newCoreDbRef DefaultDbMemory - stateDB {.used.} = LedgerRef.init(memDB, emptyRlpHash) - address {.used.} = hexToByteArray[20]("0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6") - code {.used.} = hexToSeqByte("0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6") - rootHash {.used.} : KeccakHash - - test "accountExists and isDeadAccount": - check stateDB.accountExists(address) == false - check stateDB.isDeadAccount(address) == true - - stateDB.setBalance(address, 1000.u256) - - check stateDB.accountExists(address) == true - check stateDB.isDeadAccount(address) == false - - stateDB.setBalance(address, 0.u256) - stateDB.setNonce(address, 1) - check stateDB.isDeadAccount(address) == false - - stateDB.setCode(address, code) - stateDB.setNonce(address, 0) - check stateDB.isDeadAccount(address) == false - - stateDB.setCode(address, newSeq[byte]()) - check stateDB.isDeadAccount(address) == true - check stateDB.accountExists(address) == true - - test "clone storage": - # give access to private fields of AccountRef - privateAccess(AccountRef) - var x = AccountRef( - overlayStorage: Table[UInt256, UInt256](), - originalStorage: newTable[UInt256, UInt256]() - ) - - x.overlayStorage[10.u256] = 11.u256 - x.overlayStorage[11.u256] = 12.u256 - - x.originalStorage[10.u256] = 11.u256 - x.originalStorage[11.u256] = 12.u256 - - var y = x.clone(cloneStorage = true) - y.overlayStorage[12.u256] = 13.u256 - y.originalStorage[12.u256] = 13.u256 - - check 12.u256 notin x.overlayStorage - check 12.u256 in y.overlayStorage - - check x.overlayStorage.len == 2 - check y.overlayStorage.len == 3 - - check 12.u256 in x.originalStorage - check 12.u256 in y.originalStorage - - check x.originalStorage.len == 3 - check y.originalStorage.len == 3 - - test "accounts cache": - var ac = LedgerRef.init(memDB, emptyRlpHash) - var addr1 = initAddr(1) - - check ac.isDeadAccount(addr1) == true - check ac.accountExists(addr1) == false - check ac.contractCollision(addr1) == false - - ac.setBalance(addr1, 1000.u256) - check ac.getBalance(addr1) == 1000.u256 - ac.subBalance(addr1, 100.u256) - check ac.getBalance(addr1) == 900.u256 - ac.addBalance(addr1, 200.u256) - check ac.getBalance(addr1) == 1100.u256 - - ac.setNonce(addr1, 1) - check ac.getNonce(addr1) == 1 - ac.incNonce(addr1) - check ac.getNonce(addr1) == 2 - - ac.setCode(addr1, code) - check ac.getCode(addr1) == code - - ac.setStorage(addr1, 1.u256, 10.u256) - check ac.getStorage(addr1, 1.u256) == 10.u256 - check ac.getCommittedStorage(addr1, 1.u256) == 0.u256 - - check ac.contractCollision(addr1) == true - check ac.getCodeSize(addr1) == code.len - - ac.persist() - rootHash = ac.rootHash - - var db = LedgerRef.init(memDB, emptyRlpHash) - db.setBalance(addr1, 1100.u256) - db.setNonce(addr1, 2) - db.setCode(addr1, code) - db.setStorage(addr1, 1.u256, 10.u256) - check rootHash == db.rootHash - - # accounts cache readonly operations - # use previous hash - var ac2 = LedgerRef.init(memDB, rootHash) - var addr2 = initAddr(2) - - check ac2.getCodeHash(addr2) == emptyAcc.codeHash - check ac2.getBalance(addr2) == emptyAcc.balance - check ac2.getNonce(addr2) == emptyAcc.nonce - check ac2.getCode(addr2) == [] - check ac2.getCodeSize(addr2) == 0 - check ac2.getCommittedStorage(addr2, 1.u256) == 0.u256 - check ac2.getStorage(addr2, 1.u256) == 0.u256 - check ac2.contractCollision(addr2) == false - check ac2.accountExists(addr2) == false - check ac2.isDeadAccount(addr2) == true - - ac2.persist() - # readonly operations should not modify - # state trie at all - check ac2.rootHash == rootHash - - test "accounts cache code retrieval after persist called": - var ac = LedgerRef.init(memDB, emptyRlpHash) - var addr2 = initAddr(2) - ac.setCode(addr2, code) - ac.persist() - check ac.getCode(addr2) == code - let - key = contractHashKey(keccakHash(code)) - val = memDB.newKvt().get(key.toOpenArray).valueOr: EmptyBlob - check val == code - - test "accessList operations": - proc verifyAddrs(ac: LedgerRef, addrs: varargs[int]): bool = - for c in addrs: - if not ac.inAccessList(c.initAddr): - return false - true - - proc verifySlots(ac: LedgerRef, address: int, slots: varargs[int]): bool = - let a = address.initAddr - if not ac.inAccessList(a): - return false - - for c in slots: - if not ac.inAccessList(a, c.u256): - return false - true - - proc accessList(ac: LedgerRef, address: int) {.inline.} = - ac.accessList(address.initAddr) - - proc accessList(ac: LedgerRef, address, slot: int) {.inline.} = - ac.accessList(address.initAddr, slot.u256) - - var ac = LedgerRef.init(memDB, emptyRlpHash) - - ac.accessList(0xaa) - ac.accessList(0xbb, 0x01) - ac.accessList(0xbb, 0x02) - check ac.verifyAddrs(0xaa, 0xbb) - check ac.verifySlots(0xbb, 0x01, 0x02) - check ac.verifySlots(0xaa, 0x01) == false - check ac.verifySlots(0xaa, 0x02) == false - - var sp = ac.beginSavepoint - # some new ones - ac.accessList(0xbb, 0x03) - ac.accessList(0xaa, 0x01) - ac.accessList(0xcc, 0x01) - ac.accessList(0xcc) - - check ac.verifyAddrs(0xaa, 0xbb, 0xcc) - check ac.verifySlots(0xaa, 0x01) - check ac.verifySlots(0xbb, 0x01, 0x02, 0x03) - check ac.verifySlots(0xcc, 0x01) - - ac.rollback(sp) - check ac.verifyAddrs(0xaa, 0xbb) - check ac.verifyAddrs(0xcc) == false - check ac.verifySlots(0xcc, 0x01) == false - - sp = ac.beginSavepoint - ac.accessList(0xbb, 0x03) - ac.accessList(0xaa, 0x01) - ac.accessList(0xcc, 0x01) - ac.accessList(0xcc) - ac.accessList(0xdd, 0x04) - ac.commit(sp) - - check ac.verifyAddrs(0xaa, 0xbb, 0xcc) - check ac.verifySlots(0xaa, 0x01) - check ac.verifySlots(0xbb, 0x01, 0x02, 0x03) - check ac.verifySlots(0xcc, 0x01) - check ac.verifySlots(0xdd, 0x04) - - test "transient storage operations": - var ac = LedgerRef.init(memDB, emptyRlpHash) - - proc tStore(ac: LedgerRef, address, slot, val: int) = - ac.setTransientStorage(address.initAddr, slot.u256, val.u256) - - proc tLoad(ac: LedgerRef, address, slot: int): UInt256 = - ac.getTransientStorage(address.initAddr, slot.u256) - - proc vts(ac: LedgerRef, address, slot, val: int): bool = - ac.tLoad(address, slot) == val.u256 - - ac.tStore(0xaa, 3, 66) - ac.tStore(0xbb, 1, 33) - ac.tStore(0xbb, 2, 99) - - check ac.vts(0xaa, 3, 66) - check ac.vts(0xbb, 1, 33) - check ac.vts(0xbb, 2, 99) - check ac.vts(0xaa, 1, 33) == false - check ac.vts(0xbb, 1, 66) == false - - var sp = ac.beginSavepoint - # some new ones - ac.tStore(0xaa, 3, 77) - ac.tStore(0xbb, 1, 55) - ac.tStore(0xcc, 7, 88) - - check ac.vts(0xaa, 3, 77) - check ac.vts(0xbb, 1, 55) - check ac.vts(0xcc, 7, 88) - - check ac.vts(0xaa, 3, 66) == false - check ac.vts(0xbb, 1, 33) == false - check ac.vts(0xbb, 2, 99) - - ac.rollback(sp) - check ac.vts(0xaa, 3, 66) - check ac.vts(0xbb, 1, 33) - check ac.vts(0xbb, 2, 99) - check ac.vts(0xcc, 7, 88) == false - - sp = ac.beginSavepoint - ac.tStore(0xaa, 3, 44) - ac.tStore(0xaa, 4, 55) - ac.tStore(0xbb, 1, 22) - ac.tStore(0xdd, 2, 66) - - ac.commit(sp) - check ac.vts(0xaa, 3, 44) - check ac.vts(0xaa, 4, 55) - check ac.vts(0xbb, 1, 22) - check ac.vts(0xbb, 1, 55) == false - check ac.vts(0xbb, 2, 99) - check ac.vts(0xcc, 7, 88) == false - check ac.vts(0xdd, 2, 66) - - ac.clearTransientStorage() - check ac.vts(0xaa, 3, 44) == false - check ac.vts(0xaa, 4, 55) == false - check ac.vts(0xbb, 1, 22) == false - check ac.vts(0xbb, 1, 55) == false - check ac.vts(0xbb, 2, 99) == false - check ac.vts(0xcc, 7, 88) == false - check ac.vts(0xdd, 2, 66) == false - - test "accounts cache contractCollision": - # use previous hash - var ac = LedgerRef.init(memDB, emptyRlpHash) - let addr2 = initAddr(2) - check ac.contractCollision(addr2) == false - - ac.setStorage(addr2, 1.u256, 1.u256) - check ac.contractCollision(addr2) == false - - ac.persist() - check ac.contractCollision(addr2) == true - - let addr3 = initAddr(3) - check ac.contractCollision(addr3) == false - ac.setCode(addr3, @[0xaa.byte, 0xbb]) - check ac.contractCollision(addr3) == true - - let addr4 = initAddr(4) - check ac.contractCollision(addr4) == false - ac.setNonce(addr4, 1) - check ac.contractCollision(addr4) == true - -when isMainModule: - stateDBMain()