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] originalStorage: TableRef[UInt256, UInt256]
overlayStorage: Table[UInt256, UInt256] overlayStorage: Table[UInt256, UInt256]
AccountsCache* = object AccountsCache* = ref object
db: TrieDatabaseRef db: TrieDatabaseRef
trie: SecureHexaryTrie trie: SecureHexaryTrie
savePoint: SavePoint savePoint: SavePoint
unrevertablyTouched: HashSet[EthAddress] unrevertablyTouched: HashSet[EthAddress]
ReadOnlyStateDB* = distinct AccountsCache
TransactionState = enum TransactionState = enum
Pending Pending
Committed Committed
@ -46,6 +48,7 @@ proc beginSavepoint*(ac: var AccountsCache): SavePoint {.gcsafe.}
# The AccountsCache is modeled after TrieDatabase for it's transaction style # The AccountsCache is modeled after TrieDatabase for it's transaction style
proc init*(x: typedesc[AccountsCache], db: TrieDatabaseRef, proc init*(x: typedesc[AccountsCache], db: TrieDatabaseRef,
root: KeccakHash, pruneTrie: bool = true): AccountsCache = root: KeccakHash, pruneTrie: bool = true): AccountsCache =
new result
result.db = db result.db = db
result.trie = initSecureHexaryTrie(db, root, pruneTrie) result.trie = initSecureHexaryTrie(db, root, pruneTrie)
result.unrevertablyTouched = initHashSet[EthAddress]() result.unrevertablyTouched = initHashSet[EthAddress]()
@ -390,6 +393,12 @@ proc removeEmptyAccounts*(ac: var AccountsCache) =
if acc.isEmpty: if acc.isEmpty:
acc.kill() 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) = proc persist*(ac: var AccountsCache) =
# make sure all savepoint already committed # make sure all savepoint already committed
doAssert(ac.savePoint.parentSavePoint.isNil) doAssert(ac.savePoint.parentSavePoint.isNil)
@ -419,3 +428,22 @@ iterator storage*(ac: AccountsCache, address: EthAddress): (UInt256, UInt256) =
if slot.len != 0: if slot.len != 0:
var keyData = ac.db.get(slotHashToSlotKey(slot).toOpenArray) var keyData = ac.db.get(slotHashToSlotKey(slot).toOpenArray)
yield (rlp.decode(keyData, UInt256), rlp.decode(value, UInt256)) 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 const
# DAOForkBlockExtra is the block header extra-data field to set for the DAO fork # 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 # 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 # rules, transferring all balances of a set of DAO accounts to a single refund
# contract. # contract.
proc applyDAOHardFork*(statedb: var AccountStateDB) = proc applyDAOHardFork*(statedb: var AccountsCache) =
const zero = 0.u256 const zero = 0.u256
# Move every DAO account and extra-balance account funds into the refund contract # Move every DAO account and extra-balance account funds into the refund contract
for address in DAODrainList: for address in DAODrainList:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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