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:
andri lim 2024-06-29 08:57:30 +07:00 committed by GitHub
parent 8dd038144b
commit b751d3adee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 697 additions and 884 deletions

View File

@ -1,3 +0,0 @@
# tests
TODO: more GeneralStateTest fixtures!

View File

@ -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,

View File

@ -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

View File

@ -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
View 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()

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()