mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-01-11 21:04:11 +00:00
Combine smaller tests into bigger one (#2425)
1. test_state_db and test_ledger -> test_ledger. They are the same thing now. 2. stack, memory, code_stream, gas_meter, misc, overflow -> test_evm_support. They are small tests and fall into the same area.
This commit is contained in:
parent
8dd038144b
commit
b751d3adee
@ -1,3 +0,0 @@
|
||||
# tests
|
||||
|
||||
TODO: more GeneralStateTest fixtures!
|
@ -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,
|
||||
|
@ -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
|
@ -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))
|
396
tests/test_evm_support.nim
Normal file
396
tests/test_evm_support.nim
Normal file
@ -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()
|
@ -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()
|
@ -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
|
||||
|
@ -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()
|
@ -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()
|
@ -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()
|
@ -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()
|
@ -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()
|
Loading…
x
Reference in New Issue
Block a user