nimbus-eth1/tests/test_state_db.nim
Jordan Hrycaj 221e6c9e2f
Unified database frontend integration (#1670)
* Nimbus folder environment update

details:
* Integrated `CoreDbRef` for the sources in the `nimbus` sub-folder.
* The `nimbus` program does not compile yet as it needs the updates
  in the parallel `stateless` sub-folder.

* Stateless environment update

details:
* Integrated `CoreDbRef` for the sources in the `stateless` sub-folder.
* The `nimbus` program compiles now.

* Premix environment update

details:
* Integrated `CoreDbRef` for the sources in the `premix` sub-folder.

* Fluffy environment update

details:
* Integrated `CoreDbRef` for the sources in the `fluffy` sub-folder.

* Tools environment update

details:
* Integrated `CoreDbRef` for the sources in the `tools` sub-folder.

* Nodocker environment update

details:
* Integrated `CoreDbRef` for the sources in the
  `hive_integration/nodocker` sub-folder.

* Tests environment update

details:
* Integrated `CoreDbRef` for the sources in the `tests` sub-folder.
* The unit tests compile and run cleanly now.

* Generalise `CoreDbRef` to any `select_backend` supported database

why:
  Generalisation was just missed due to overcoming some compiler oddity
  which was tied to rocksdb for testing.

* Suppress compiler warning for `newChainDB()`

why:
  Warning was added to this function which must be wrapped so that
  any `CatchableError` is re-raised as `Defect`.

* Split off persistent `CoreDbRef` constructor into separate file

why:
  This allows to compile a memory only database version without linking
  the backend library.

* Use memory `CoreDbRef` database by default

detail:
 Persistent DB constructor needs to import `db/core_db/persistent

why:
 Most tests use memory DB anyway. This avoids linking `-lrocksdb` or
 any other backend by default.

* fix `toLegacyBackend()` availability check

why:
  got garbled after memory/persistent split.

* Clarify raw access to MPT for snap sync handler

why:
  Logically, `kvt` is not the raw access for the hexary trie (although
  this holds for the legacy database)
2023-08-04 12:10:09 +01:00

288 lines
8.8 KiB
Nim

# Nimbus
# Copyright (c) 2018 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/trie/trie_defs,
stew/[byteutils, endians2],
unittest2,
../nimbus/db/state_db
include ../nimbus/db/accounts_cache
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 LegacyDbMemory
acDB {.used.} = newCoreDbRef LegacyDbMemory
trie = memDB.mptPrune()
stateDB {.used.} = newAccountStateDB(memDB, trie.rootHash, true)
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
var acc = stateDB.getAccount(address)
acc.balance = 1000.u256
stateDB.setAccount(address, acc)
check stateDB.accountExists(address) == true
check stateDB.isDeadAccount(address) == false
acc.balance = 0.u256
acc.nonce = 1
stateDB.setAccount(address, acc)
check stateDB.isDeadAccount(address) == false
stateDB.setCode(address, code)
stateDB.setNonce(address, 0)
check stateDB.isDeadAccount(address) == false
stateDB.setCode(address, [])
check stateDB.isDeadAccount(address) == true
check stateDB.accountExists(address) == true
test "clone storage":
var x = RefAccount(
overlayStorage: initTable[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 = init(AccountsCache, acDB, emptyRlpHash, true)
var addr1 = initAddr(1)
check ac.isDeadAccount(addr1) == true
check ac.accountExists(addr1) == false
check ac.hasCodeOrNonce(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.hasCodeOrNonce(addr1) == true
check ac.getCodeSize(addr1) == code.len
ac.persist()
rootHash = ac.rootHash
var db = newAccountStateDB(memDB, emptyRlpHash, true)
db.setBalance(addr1, 1100.u256)
db.setNonce(addr1, 2)
db.setCode(addr1, code)
db.setStorage(addr1, 1.u256, 10.u256)
check rootHash == db.rootHash
test "accounts cache readonly operations":
# use previous hash
var ac = init(AccountsCache, acDB, rootHash, true)
var addr2 = initAddr(2)
check ac.getCodeHash(addr2) == emptyAcc.codeHash
check ac.getBalance(addr2) == emptyAcc.balance
check ac.getNonce(addr2) == emptyAcc.nonce
check ac.getCode(addr2) == []
check ac.getCodeSize(addr2) == 0
check ac.getCommittedStorage(addr2, 1.u256) == 0.u256
check ac.getStorage(addr2, 1.u256) == 0.u256
check ac.hasCodeOrNonce(addr2) == false
check ac.accountExists(addr2) == false
check ac.isDeadAccount(addr2) == true
ac.persist()
# readonly operations should not modify
# state trie at all
check ac.rootHash == rootHash
test "accounts cache code retrieval after persist called":
var ac = init(AccountsCache, acDB)
var addr2 = initAddr(2)
ac.setCode(addr2, code)
ac.persist()
check ac.getCode(addr2) == code
let key = contractHashKey(keccakHash(code))
check acDB.kvt.get(key.toOpenArray) == code
test "accessList operations":
proc verifyAddrs(ac: AccountsCache, addrs: varargs[int]): bool =
for c in addrs:
if not ac.inAccessList(c.initAddr):
return false
true
proc verifySlots(ac: AccountsCache, 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: var AccountsCache, address: int) {.inline.} =
ac.accessList(address.initAddr)
proc accessList(ac: var AccountsCache, address, slot: int) {.inline.} =
ac.accessList(address.initAddr, slot.u256)
var ac = init(AccountsCache, acDB)
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 = init(AccountsCache, acDB)
proc tStore(ac: AccountsCache, address, slot, val: int) =
ac.setTransientStorage(address.initAddr, slot.u256, val.u256)
proc tLoad(ac: AccountsCache, address, slot: int): UInt256 =
ac.getTransientStorage(address.initAddr, slot.u256)
proc vts(ac: AccountsCache, 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
when isMainModule:
stateDBMain()