From 71514a0a66e5b1d5318d3097d2552e3941a3170d Mon Sep 17 00:00:00 2001 From: jangko Date: Sat, 30 May 2020 10:14:59 +0700 Subject: [PATCH] replace state_db with accounts_cache --- nimbus/db/accounts_cache.nim | 30 +++++++++++++++++++++++++- nimbus/p2p/dao.nim | 4 ++-- nimbus/p2p/executor.nim | 5 +++-- nimbus/vm/computation.nim | 16 ++++++-------- nimbus/vm/interpreter/opcodes_impl.nim | 2 +- nimbus/vm/transaction_tracer.nim | 4 ++-- nimbus/vm_state.nim | 4 ++-- nimbus/vm_state_transactions.nim | 13 +++++------ nimbus/vm_types.nim | 10 +++------ tests/test_generalstate_json.nim | 4 ++-- tests/test_helpers.nim | 10 ++++----- 11 files changed, 63 insertions(+), 39 deletions(-) diff --git a/nimbus/db/accounts_cache.nim b/nimbus/db/accounts_cache.nim index 7d104cb94..5fcaa2ca3 100644 --- a/nimbus/db/accounts_cache.nim +++ b/nimbus/db/accounts_cache.nim @@ -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.} diff --git a/nimbus/p2p/dao.nim b/nimbus/p2p/dao.nim index 45ffeab7e..5068f2c41 100644 --- a/nimbus/p2p/dao.nim +++ b/nimbus/p2p/dao.nim @@ -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: diff --git a/nimbus/p2p/executor.nim b/nimbus/p2p/executor.nim index 98f64f274..10652ddd5 100644 --- a/nimbus/p2p/executor.nim +++ b/nimbus/p2p/executor.nim @@ -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 diff --git a/nimbus/vm/computation.nim b/nimbus/vm/computation.nim index 363a1d127..e1507ae9c 100644 --- a/nimbus/vm/computation.nim +++ b/nimbus/vm/computation.nim @@ -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) diff --git a/nimbus/vm/interpreter/opcodes_impl.nim b/nimbus/vm/interpreter/opcodes_impl.nim index d2a02385b..ae15f2e8a 100644 --- a/nimbus/vm/interpreter/opcodes_impl.nim +++ b/nimbus/vm/interpreter/opcodes_impl.nim @@ -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 diff --git a/nimbus/vm/transaction_tracer.nim b/nimbus/vm/transaction_tracer.nim index e35bf16fa..a813d21a6 100644 --- a/nimbus/vm/transaction_tracer.nim +++ b/nimbus/vm/transaction_tracer.nim @@ -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 diff --git a/nimbus/vm_state.nim b/nimbus/vm_state.nim index f75723c26..ddb765c3d 100644 --- a/nimbus/vm_state.nim +++ b/nimbus/vm_state.nim @@ -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, diff --git a/nimbus/vm_state_transactions.nim b/nimbus/vm_state_transactions.nim index 809adc38b..40a4869aa 100644 --- a/nimbus/vm_state_transactions.nim +++ b/nimbus/vm_state_transactions.nim @@ -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 diff --git a/nimbus/vm_types.nim b/nimbus/vm_types.nim index 5c65dd4a0..2233421c4 100644 --- a/nimbus/vm_types.nim +++ b/nimbus/vm_types.nim @@ -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 diff --git a/tests/test_generalstate_json.nim b/tests/test_generalstate_json.nim index e5f4cd9ec..267d246b7 100644 --- a/tests/test_generalstate_json.nim +++ b/tests/test_generalstate_json.nim @@ -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 diff --git a/tests/test_helpers.nim b/tests/test_helpers.nim index 5a58d8aee..05d38677b 100644 --- a/tests/test_helpers.nim +++ b/tests/test_helpers.nim @@ -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}")