access list implementation

This commit is contained in:
jangko 2020-12-09 12:24:37 +07:00
parent 08c8b12821
commit f2b483d6ad
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
3 changed files with 138 additions and 3 deletions

41
nimbus/db/access_list.nim Normal file
View File

@ -0,0 +1,41 @@
import
tables, sets,
stint,
eth/common
type
SlotSet = HashSet[UInt256]
AccessList* = object
slots: Table[EthAddress, SlotSet]
proc init*(ac: var AccessList) =
ac.slots = initTable[EthAddress, SlotSet]()
proc init*(_: type AccessList): AccessList {.inline.} =
result.init()
func contains*(ac: AccessList, address: EthAddress): bool {.inline.} =
address in ac.slots
# returnValue: (addressPresent, slotPresent)
func contains*(ac: var AccessList, address: EthAddress, slot: UInt256): bool =
ac.slots.withValue(address, val):
result = slot in val[]
proc merge*(ac: var AccessList, other: AccessList) {.inline.} =
for k, v in other.slots:
ac.slots.withValue(k, val):
val[].incl v
do:
ac.slots[k] = v
proc add*(ac: var AccessList, address: EthAddress) =
if address notin ac.slots:
ac.slots[address] = initHashSet[UInt256]()
proc add*(ac: var AccessList, address: EthAddress, slot: UInt256) =
ac.slots.withValue(address, val):
val[].incl slot
do:
ac.slots[address] = toHashSet([slot])

View File

@ -2,7 +2,8 @@ import
tables, hashes, sets, tables, hashes, sets,
eth/[common, rlp], eth/trie/[hexary, db, trie_defs], eth/[common, rlp], eth/trie/[hexary, db, trie_defs],
../constants, ../utils, storage_types, ../constants, ../utils, storage_types,
../../stateless/multi_keys ../../stateless/multi_keys,
./access_list
type type
AccountFlag = enum AccountFlag = enum
@ -45,6 +46,7 @@ type
SavePoint* = ref object SavePoint* = ref object
parentSavepoint: SavePoint parentSavepoint: SavePoint
cache: Table[EthAddress, RefAccount] cache: Table[EthAddress, RefAccount]
accessList: access_list.AccessList
state: TransactionState state: TransactionState
const const
@ -83,6 +85,7 @@ proc rootHash*(ac: AccountsCache): KeccakHash =
proc beginSavepoint*(ac: var AccountsCache): SavePoint = proc beginSavepoint*(ac: var AccountsCache): SavePoint =
new result new result
result.cache = initTable[EthAddress, RefAccount]() result.cache = initTable[EthAddress, RefAccount]()
result.accessList.init()
result.state = Pending result.state = Pending
result.parentSavepoint = ac.savePoint result.parentSavepoint = ac.savePoint
ac.savePoint = result ac.savePoint = result
@ -106,6 +109,8 @@ proc commit*(ac: var AccountsCache, sp: Savepoint) =
ac.savePoint = sp.parentSavepoint ac.savePoint = sp.parentSavepoint
for k, v in sp.cache: for k, v in sp.cache:
sp.parentSavepoint.cache[k] = v sp.parentSavepoint.cache[k] = v
ac.savePoint.accessList.merge(sp.accessList)
sp.state = Committed sp.state = Committed
proc dispose*(ac: var AccountsCache, sp: Savepoint) {.inline.} = proc dispose*(ac: var AccountsCache, sp: Savepoint) {.inline.} =
@ -514,6 +519,28 @@ proc makeMultiKeys*(ac: AccountsCache): MultikeysRef =
result.add(k, v.codeTouched, multiKeys(v.storageKeys)) result.add(k, v.codeTouched, multiKeys(v.storageKeys))
result.sort() result.sort()
proc accessList*(ac: var AccountsCache, address: EthAddress) {.inline.} =
ac.savePoint.accessList.add(address)
proc accessList*(ac: var AccountsCache, address: EthAddress, slot: UInt256) {.inline.} =
ac.savePoint.accessList.add(address, slot)
func inAccessList*(ac: AccountsCache, address: EthAddress): bool =
var sp = ac.savePoint
while sp != nil:
result = sp.accessList.contains(address)
if result:
return
sp = sp.parentSavepoint
func inAccessList*(ac: AccountsCache, address: EthAddress, slot: UInt256): bool =
var sp = ac.savePoint
while sp != nil:
result = sp.accessList.contains(address, slot)
if result:
return
sp = sp.parentSavepoint
proc rootHash*(db: ReadOnlyStateDB): KeccakHash {.borrow.} proc rootHash*(db: ReadOnlyStateDB): KeccakHash {.borrow.}
proc getCodeHash*(db: ReadOnlyStateDB, address: EthAddress): Hash256 {.borrow.} proc getCodeHash*(db: ReadOnlyStateDB, address: EthAddress): Hash256 {.borrow.}
proc getStorageRoot*(db: ReadOnlyStateDB, address: EthAddress): Hash256 {.borrow.} proc getStorageRoot*(db: ReadOnlyStateDB, address: EthAddress): Hash256 {.borrow.}
@ -527,3 +554,5 @@ proc accountExists*(db: ReadOnlyStateDB, address: EthAddress): bool {.borrow.}
proc isDeadAccount*(db: ReadOnlyStateDB, address: EthAddress): bool {.borrow.} proc isDeadAccount*(db: ReadOnlyStateDB, address: EthAddress): bool {.borrow.}
proc isEmptyAccount*(db: ReadOnlyStateDB, address: EthAddress): bool {.borrow.} proc isEmptyAccount*(db: ReadOnlyStateDB, address: EthAddress): bool {.borrow.}
proc getCommittedStorage*(db: ReadOnlyStateDB, address: EthAddress, slot: UInt256): UInt256 {.borrow.} proc getCommittedStorage*(db: ReadOnlyStateDB, address: EthAddress, slot: UInt256): UInt256 {.borrow.}
func inAccessList*(ac: ReadOnlyStateDB, address: EthAddress): bool {.borrow.}
func inAccessList*(ac: ReadOnlyStateDB, address: EthAddress, slot: UInt256): bool {.borrow.}

View File

@ -6,12 +6,13 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms. # at your option. This file may not be copied, modified, or distributed except according to those terms.
import unittest2, eth/trie/[hexary, db], import unittest2, eth/trie/[hexary, db],
../nimbus/db/state_db, stew/byteutils, eth/common ../nimbus/db/state_db, stew/[byteutils, endians2], eth/common
include ../nimbus/db/accounts_cache include ../nimbus/db/accounts_cache
func initAddr(z: int): EthAddress = func initAddr(z: int): EthAddress =
result[^1] = z.byte const L = sizeof(result)
result[L-sizeof(uint32)..^1] = toBytesBE(z.uint32)
proc stateDBMain*() = proc stateDBMain*() =
suite "Account State DB": suite "Account State DB":
@ -149,5 +150,69 @@ proc stateDBMain*() =
let key = contractHashKey(hexary.keccak(code)) let key = contractHashKey(hexary.keccak(code))
check acDB.get(key.toOpenArray) == code check acDB.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)
when isMainModule: when isMainModule:
stateDBMain() stateDBMain()