replace state_db with accounts_cache

This commit is contained in:
jangko 2020-05-30 10:14:59 +07:00
parent 50fb5e5bc6
commit 71514a0a66
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
11 changed files with 63 additions and 39 deletions

View File

@ -23,12 +23,14 @@ type
originalStorage: TableRef[UInt256, UInt256]
overlayStorage: Table[UInt256, UInt256]
AccountsCache* = object
AccountsCache* = ref object
db: TrieDatabaseRef
trie: SecureHexaryTrie
savePoint: SavePoint
unrevertablyTouched: HashSet[EthAddress]
ReadOnlyStateDB* = distinct AccountsCache
TransactionState = enum
Pending
Committed
@ -46,6 +48,7 @@ proc beginSavepoint*(ac: var AccountsCache): SavePoint {.gcsafe.}
# The AccountsCache is modeled after TrieDatabase for it's transaction style
proc init*(x: typedesc[AccountsCache], db: TrieDatabaseRef,
root: KeccakHash, pruneTrie: bool = true): AccountsCache =
new result
result.db = db
result.trie = initSecureHexaryTrie(db, root, pruneTrie)
result.unrevertablyTouched = initHashSet[EthAddress]()
@ -390,6 +393,12 @@ proc removeEmptyAccounts*(ac: var AccountsCache) =
if acc.isEmpty:
acc.kill()
proc deleteAccount*(ac: var AccountsCache, address: EthAddress) =
# make sure all savepoints already committed
doAssert(ac.savePoint.parentSavePoint.isNil)
let acc = ac.getAccount(address)
acc.kill()
proc persist*(ac: var AccountsCache) =
# make sure all savepoint already committed
doAssert(ac.savePoint.parentSavePoint.isNil)
@ -419,3 +428,22 @@ iterator storage*(ac: AccountsCache, address: EthAddress): (UInt256, UInt256) =
if slot.len != 0:
var keyData = ac.db.get(slotHashToSlotKey(slot).toOpenArray)
yield (rlp.decode(keyData, UInt256), rlp.decode(value, UInt256))
proc getStorageRoot*(ac: AccountsCache, address: EthAddress): Hash256 =
# beware that if the account not persisted,
# the storage root will not be updated
result = ac.getAccount(address).account.storageRoot
proc rootHash*(db: ReadOnlyStateDB): KeccakHash {.borrow.}
proc getCodeHash*(db: ReadOnlyStateDB, address: EthAddress): Hash256 {.borrow.}
proc getStorageRoot*(db: ReadOnlyStateDB, address: EthAddress): Hash256 {.borrow.}
proc getBalance*(db: ReadOnlyStateDB, address: EthAddress): UInt256 {.borrow.}
proc getStorage*(db: ReadOnlyStateDB, address: EthAddress, slot: UInt256): UInt256 {.borrow.}
proc getNonce*(db: ReadOnlyStateDB, address: EthAddress): AccountNonce {.borrow.}
proc getCode*(db: ReadOnlyStateDB, address: EthAddress): seq[byte] {.borrow.}
proc getCodeSize*(db: ReadOnlyStateDB, address: EthAddress): int {.borrow.}
proc hasCodeOrNonce*(db: ReadOnlyStateDB, address: EthAddress): bool {.borrow.}
proc accountExists*(db: ReadOnlyStateDB, address: EthAddress): bool {.borrow.}
proc isDeadAccount*(db: ReadOnlyStateDB, address: EthAddress): bool {.borrow.}
proc isEmptyAccount*(db: ReadOnlyStateDB, address: EthAddress): bool {.borrow.}
proc getCommittedStorage*(db: ReadOnlyStateDB, address: EthAddress, slot: UInt256): UInt256 {.borrow.}

View File

@ -1,4 +1,4 @@
import eth/common, stew/byteutils, ../db/state_db
import eth/common, stew/byteutils, ../db/accounts_cache
const
# DAOForkBlockExtra is the block header extra-data field to set for the DAO fork
@ -135,7 +135,7 @@ const
# ApplyDAOHardFork modifies the state database according to the DAO hard-fork
# rules, transferring all balances of a set of DAO accounts to a single refund
# contract.
proc applyDAOHardFork*(statedb: var AccountStateDB) =
proc applyDAOHardFork*(statedb: var AccountsCache) =
const zero = 0.u256
# Move every DAO account and extra-balance account funds into the refund contract
for address in DAODrainList:

View File

@ -1,6 +1,6 @@
import options, sets,
eth/[common, bloom, trie/db], chronicles, nimcrypto,
../db/[db_chain, state_db],
../db/[db_chain, accounts_cache],
../utils, ../constants, ../transaction,
../vm_state, ../vm_types, ../vm_state_transactions,
../vm/[computation, message],
@ -42,7 +42,8 @@ proc processTransaction*(tx: Transaction, sender: EthAddress, vmState: BaseVMSta
debug "state clearing", account
db.deleteAccount(account)
vmState.accountDb.updateOriginalRoot()
#vmState.accountDb.updateOriginalRoot()
vmState.accountDb.persist()
type
# TODO: these types need to be removed

View File

@ -10,7 +10,7 @@ import
sets, eth/[common, keys], eth/trie/db as triedb,
../constants, ../errors, ../vm_state, ../vm_types,
./interpreter/[opcode_values, gas_meter, gas_costs, vm_forks],
./code_stream, ./memory, ./message, ./stack, ../db/[state_db, db_chain],
./code_stream, ./memory, ./message, ./stack, ../db/[accounts_cache, db_chain],
../utils/header, stew/[byteutils, ranges/ptr_arith], precompiles,
transaction_tracer, ../utils
@ -90,7 +90,7 @@ template getStorage*(c: Computation, slot: Uint256): Uint256 =
when evmc_enabled:
c.host.getStorage(c.msg.contractAddress, slot)
else:
c.vmState.readOnlyStateDB.getStorage(c.msg.contractAddress, slot)[0]
c.vmState.readOnlyStateDB.getStorage(c.msg.contractAddress, slot)
template getBalance*(c: Computation, address: EthAddress): Uint256 =
when evmc_enabled:
@ -102,7 +102,7 @@ template getCodeSize*(c: Computation, address: EthAddress): uint =
when evmc_enabled:
c.host.getCodeSize(address)
else:
uint(c.vmState.readOnlyStateDB.getCode(address).len)
uint(c.vmState.readOnlyStateDB.getCodeSize(address))
template getCodeHash*(c: Computation, address: EthAddress): Hash256 =
when evmc_enabled:
@ -179,18 +179,16 @@ proc isSuicided*(c: Computation, address: EthAddress): bool =
result = address in c.suicides
proc snapshot*(c: Computation) =
c.dbsnapshot.transaction = c.vmState.chaindb.db.beginTransaction()
c.dbsnapshot.intermediateRoot = c.vmState.accountDb.rootHash
c.savePoint = c.vmState.accountDb.beginSavePoint()
proc commit*(c: Computation) =
c.dbsnapshot.transaction.commit()
c.vmState.accountDb.commit(c.savePoint)
proc dispose*(c: Computation) {.inline.} =
c.dbsnapshot.transaction.dispose()
c.vmState.accountDb.dispose(c.savePoint)
proc rollback*(c: Computation) =
c.dbsnapshot.transaction.rollback()
c.vmState.accountDb.rootHash = c.dbsnapshot.intermediateRoot
c.vmState.accountDb.rollback(c.savePoint)
proc setError*(c: Computation, msg: string, burnsGas = false) {.inline.} =
c.error = Error(info: msg, burnsGas: burnsGas)

View File

@ -12,7 +12,7 @@ import
./gas_meter, ./gas_costs, ./opcode_values, ./vm_forks,
../memory, ../stack, ../code_stream, ../computation,
../../vm_state, ../../errors, ../../constants, ../../vm_types,
../../db/[db_chain, state_db]
../../db/[db_chain, accounts_cache]
when defined(evmc_enabled):
import ../evmc_api, ../evmc_helpers, evmc/evmc

View File

@ -1,7 +1,7 @@
import
json, strutils, sets, hashes,
chronicles, nimcrypto, eth/common, stint,
../vm_types, memory, stack, ../db/state_db,
../vm_types, memory, stack, ../db/accounts_cache,
eth/trie/hexary,
./interpreter/opcode_values
@ -100,7 +100,7 @@ proc traceOpCodeEnded*(tracer: var TransactionTracer, c: Computation, op: Op, la
if c.msg.depth < tracer.storageKeys.len:
var stateDB = c.vmState.accountDb
for key in tracer.storage(c.msg.depth):
let (value, _) = stateDB.getStorage(c.msg.contractAddress, key)
let value = stateDB.getStorage(c.msg.contractAddress, key)
storage[key.dumpHex] = %(value.dumpHex)
j["storage"] = storage

View File

@ -9,7 +9,7 @@ import
macros, strformat, tables, sets, options,
eth/common,
vm/interpreter/[vm_forks, gas_costs],
./constants, ./db/[db_chain, state_db],
./constants, ./db/[db_chain, accounts_cache],
./utils, json, vm_types, vm/transaction_tracer,
./config
@ -36,7 +36,7 @@ proc init*(self: BaseVMState, prevStateRoot: Hash256, header: BlockHeader,
self.tracer.initTracer(tracerFlags)
self.tracingEnabled = TracerFlags.EnableTracing in tracerFlags
self.logEntries = @[]
self.accountDb = newAccountStateDB(chainDB.db, prevStateRoot, chainDB.pruneTrie)
self.accountDb = AccountsCache.init(chainDB.db, prevStateRoot, chainDB.pruneTrie)
self.touchedAccounts = initHashSet[EthAddress]()
proc newBaseVMState*(prevStateRoot: Hash256, header: BlockHeader,

View File

@ -7,12 +7,13 @@
import
options, sets,
eth/common, chronicles, ./db/state_db,
eth/common, chronicles, ./db/accounts_cache,
transaction, vm_types, vm_state,
./vm/[computation, interpreter]
proc validateTransaction*(vmState: BaseVMState, tx: Transaction, sender: EthAddress, fork: Fork): bool =
let account = vmState.readOnlyStateDB.getAccount(sender)
let balance = vmState.readOnlyStateDB.getBalance(sender)
let nonce = vmState.readOnlyStateDB.getNonce(sender)
if vmState.cumulativeGasUsed + tx.gasLimit > vmState.blockHeader.gasLimit:
debug "invalid tx: block header gasLimit reached",
@ -22,9 +23,9 @@ proc validateTransaction*(vmState: BaseVMState, tx: Transaction, sender: EthAddr
return
let totalCost = tx.gasLimit.u256 * tx.gasPrice.u256 + tx.value
if totalCost > account.balance:
if totalCost > balance:
debug "invalid tx: not enough cash",
available=account.balance,
available=balance,
require=totalCost
return
@ -34,10 +35,10 @@ proc validateTransaction*(vmState: BaseVMState, tx: Transaction, sender: EthAddr
require=tx.intrinsicGas(fork)
return
if tx.accountNonce != account.nonce:
if tx.accountNonce != nonce:
debug "invalid tx: account nonce mismatch",
txNonce=tx.accountnonce,
accountNonce=account.nonce
accountNonce=nonce
return
result = true

View File

@ -10,7 +10,7 @@ import
options, json, sets,
./vm/[memory, stack, code_stream],
./vm/interpreter/[gas_costs, opcode_values, vm_forks], # TODO - will be hidden at a lower layer
./db/[db_chain, state_db]
./db/[db_chain, accounts_cache]
when defined(evmc_enabled):
import ./vm/evmc_api
@ -26,7 +26,7 @@ type
tracer* : TransactionTracer
logEntries* : seq[Log]
receipts* : seq[Receipt]
accountDb* : AccountStateDB
accountDb* : AccountsCache
cumulativeGasUsed*: GasInt
touchedAccounts*: HashSet[EthAddress]
suicides* : HashSet[EthAddress]
@ -55,10 +55,6 @@ type
accounts*: HashSet[EthAddress]
storageKeys*: seq[HashSet[Uint256]]
Snapshot* = object
transaction*: DbTransaction
intermediateRoot*: Hash256
Computation* = ref object
# The execution computation
vmState*: BaseVMState
@ -75,7 +71,7 @@ type
touchedAccounts*: HashSet[EthAddress]
suicides*: HashSet[EthAddress]
logEntries*: seq[Log]
dbsnapshot*: Snapshot
savePoint*: SavePoint
instr*: Op
opIndex*: int

View File

@ -14,7 +14,7 @@ import
../nimbus/transaction,
../nimbus/[vm_state, vm_types, utils],
../nimbus/vm/interpreter,
../nimbus/db/[db_chain, state_db]
../nimbus/db/[db_chain, accounts_cache]
type
Tester = object
@ -96,7 +96,7 @@ proc testFixtureIndexes(tester: Tester, testStatusIMPL: var TestStatus) =
vmState.mutateStateDB:
setupStateDB(tester.pre, db)
vmState.accountDB.updateOriginalRoot()
#vmState.accountDB.updateOriginalRoot()
defer:
let obtainedHash = "0x" & `$`(vmState.readOnlyStateDB.rootHash).toLowerAscii

View File

@ -11,7 +11,7 @@ import
testutils/markdown_reports,
../nimbus/[config, transaction, utils, errors],
../nimbus/vm/interpreter/vm_forks,
../nimbus/db/state_db
../nimbus/db/accounts_cache
func revmap(x: Table[Fork, string]): Table[string, Fork] =
result = initTable[string, Fork]()
@ -135,7 +135,7 @@ func getHexadecimalInt*(j: JsonNode): int64 =
data = fromHex(StUInt[64], j.getStr)
result = cast[int64](data)
proc setupStateDB*(wantedState: JsonNode, stateDB: var AccountStateDB) =
proc setupStateDB*(wantedState: JsonNode, stateDB: var AccountsCache) =
for ac, accountData in wantedState:
let account = ethAddressFromHex(ac)
for slot, value in accountData{"storage"}:
@ -157,9 +157,9 @@ proc verifyStateDB*(wantedState: JsonNode, stateDB: ReadOnlyStateDB) =
slotId = UInt256.fromHex slot
wantedValue = UInt256.fromHex value.getStr
let (actualValue, found) = stateDB.getStorage(account, slotId)
if not found:
raise newException(ValidationError, "account not found: " & ac)
let actualValue = stateDB.getStorage(account, slotId)
#if not found:
# raise newException(ValidationError, "account not found: " & ac)
if actualValue != wantedValue:
raise newException(ValidationError, &"{ac} storageDiff: [{slot}] {actualValue.toHex} != {wantedValue.toHex}")