mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-01-13 05:44:40 +00:00
f6be4bd0ec
`initTable` is obsolete since nim 0.19 and can introduce significant memory overhead while providing no benefit (since the table will be grown to the default initial size on first use anyway). In particular, aristo layers will not necessarily use all tables they initialize, for exampe when many empty accounts are being created.
313 lines
9.6 KiB
Nim
313 lines
9.6 KiB
Nim
# 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()
|