From e78fb72ef670e77dff552a8e7480e4d218a503b2 Mon Sep 17 00:00:00 2001 From: andri lim Date: Mon, 31 Dec 2018 10:27:02 +0700 Subject: [PATCH] fixes #188 --- nimbus/db/db_chain.nim | 36 +-------------- nimbus/db/select_backend.nim | 2 +- nimbus/db/state_db.nim | 53 +++++++++++++-------- nimbus/db/storage_types.nim | 6 +++ nimbus/p2p/executor.nim | 64 ++++++++++++++++++++------ nimbus/rpc/p2p.nim | 14 +++--- nimbus/tracer.nim | 11 ++--- nimbus/vm/computation.nim | 4 +- nimbus/vm/interpreter/opcodes_impl.nim | 4 +- nimbus/vm/transaction_tracer.nim | 2 +- nimbus/vm_state.nim | 18 ++++++-- nimbus/vm_state_transactions.nim | 19 ++++++-- nimbus/vm_types.nim | 3 +- 13 files changed, 140 insertions(+), 96 deletions(-) diff --git a/nimbus/db/db_chain.nim b/nimbus/db/db_chain.nim index 223278a58..022b012ea 100644 --- a/nimbus/db/db_chain.nim +++ b/nimbus/db/db_chain.nim @@ -12,12 +12,7 @@ import type BaseChainDB* = ref object - db*: TrieDatabaseRef - # XXX: intentionally simple stand-in for one use of full JournalDB - # Doesn't handle CREATE+revert, etc. But also creates minimal tech - # debt while setting a CI baseline from which to improve/replace. - accountCodes*: TableRef[Hash256, ByteRange] - # TODO db*: JournalDB + db* : TrieDatabaseRef pruneTrie*: bool KeyType = enum @@ -31,7 +26,6 @@ type proc newBaseChainDB*(db: TrieDatabaseRef, pruneTrie: bool = true): BaseChainDB = new(result) result.db = db - result.accountCodes = newTable[Hash256, ByteRange]() result.pruneTrie = pruneTrie proc `$`*(db: BaseChainDB): string = @@ -263,34 +257,6 @@ proc persistBlockToDb*(self: BaseChainDB; blk: Block): ValidationResult = debug "ommersHash mismatch" return ValidationResult.Error -# proc addTransaction*(self: BaseChainDB; blockHeader: BlockHeader; indexKey: cstring; -# transaction: FrontierTransaction): cstring = -# var transactionDb = HexaryTrie(self.db) -# transactionDb[indexKey] = rlp.encode(transaction) -# return transactionDb.rootHash - -# proc addReceipt*(self: BaseChainDB; blockHeader: BlockHeader; indexKey: cstring; -# receipt: Receipt): cstring = -# var receiptDb = HexaryTrie() -# receiptDb[indexKey] = rlp.encode(receipt) -# return receiptDb.rootHash - -#proc snapshot*(self: BaseChainDB): UUID = - # Snapshots are a combination of the state_root at the time of the - # snapshot and the id of the changeset from the journaled DB. - #return self.db.snapshot() - -# proc commit*(self: BaseChainDB; checkpoint: UUID): void = -# self.db.commit(checkpoint) - -# proc clear*(self: BaseChainDB): void = -# self.db.clear() - -proc getStateDb*(self: BaseChainDB; stateRoot: Hash256; readOnly: bool = false): AccountStateDB = - # TODO: readOnly is not used. - result = newAccountStateDB(self.db, stateRoot, self.pruneTrie, readOnly, self.accountCodes) - - # Deprecated: proc getBlockHeaderByHash*(self: BaseChainDB; blockHash: Hash256): BlockHeader {.deprecated.} = self.getBlockHeader(blockHash) diff --git a/nimbus/db/select_backend.nim b/nimbus/db/select_backend.nim index 29680461c..af092e4ea 100644 --- a/nimbus/db/select_backend.nim +++ b/nimbus/db/select_backend.nim @@ -1,4 +1,4 @@ -const nimbus_db_backend {.strdefine.} = "rocksdb" +const nimbus_db_backend* {.strdefine.} = "rocksdb" when nimbus_db_backend == "sqlite": import ./backends/sqlite_backend as database_backend diff --git a/nimbus/db/state_db.nim b/nimbus/db/state_db.nim index e79cf022c..2f7af710f 100644 --- a/nimbus/db/state_db.nim +++ b/nimbus/db/state_db.nim @@ -16,21 +16,20 @@ logScope: type AccountStateDB* = ref object - trie: SecureHexaryTrie - accountCodes: TableRef[Hash256, ByteRange] + trie: SecureHexaryTrie -proc rootHash*(accountDb: AccountStateDB): KeccakHash = - accountDb.trie.rootHash + ReadOnlyStateDB* = distinct AccountStateDB -# proc `rootHash=`*(db: var AccountStateDB, value: string) = -# TODO: self.Trie.rootHash = value +proc rootHash*(db: AccountStateDB): KeccakHash = + db.trie.rootHash + +proc `rootHash=`*(db: AccountStateDB, root: KeccakHash) = + db.trie = initSecureHexaryTrie(HexaryTrie(db.trie).db, root, db.trie.isPruning) proc newAccountStateDB*(backingStore: TrieDatabaseRef, - root: KeccakHash, pruneTrie: bool, readOnly: bool = false, - accountCodes = newTable[Hash256, ByteRange]()): AccountStateDB = + root: KeccakHash, pruneTrie: bool): AccountStateDB = result.new() result.trie = initSecureHexaryTrie(backingStore, root, pruneTrie) - result.accountCodes = accountCodes template createRangeFromAddress(address: EthAddress): ByteRange = ## XXX: The name of this proc is intentionally long, because it @@ -142,7 +141,7 @@ proc getStorage*(db: AccountStateDB, address: EthAddress, slot: UInt256): (UInt2 else: result = (0.u256, false) -proc setNonce*(db: var AccountStateDB, address: EthAddress, newNonce: AccountNonce) = +proc setNonce*(db: AccountStateDB, address: EthAddress, newNonce: AccountNonce) = var account = db.getAccount(address) if newNonce != account.nonce: account.nonce = newNonce @@ -152,18 +151,25 @@ proc getNonce*(db: AccountStateDB, address: EthAddress): AccountNonce = let account = db.getAccount(address) account.nonce -proc setCode*(db: var AccountStateDB, address: EthAddress, code: ByteRange) = +proc setCode*(db: AccountStateDB, address: EthAddress, code: ByteRange) = var account = db.getAccount(address) - let newCodeHash = keccak256.digest code.toOpenArray - if newCodeHash != account.codeHash: - account.codeHash = newCodeHash - db.accountCodes[newCodeHash] = code - # XXX: this uses the journaldb in py-evm - # db.trie.put(account.codeHash.toByteRange_Unnecessary, code) - db.setAccount(address, account) + # TODO: implement JournalDB to store code and storage + # also use JournalDB to revert state trie + + let + newCodeHash = keccak256.digest code.toOpenArray + triedb = HexaryTrie(db.trie).db + + if code.len != 0: + triedb.put(contractHashKey(newCodeHash).toOpenArray, code.toOpenArray) + + account.codeHash = newCodeHash + db.setAccount(address, account) proc getCode*(db: AccountStateDB, address: EthAddress): ByteRange = - db.accountCodes.getOrDefault(db.getCodeHash(address)) + let triedb = HexaryTrie(db.trie).db + let data = triedb.get(contractHashKey(db.getCodeHash(address)).toOpenArray) + data.toRange proc hasCodeOrNonce*(account: AccountStateDB, address: EthAddress): bool {.inline.} = account.getNonce(address) != 0 or account.getCodeHash(address) != EMPTY_SHA3 @@ -172,3 +178,12 @@ proc dumpAccount*(db: AccountStateDB, addressS: string): string = let address = addressS.parseAddress return fmt"{addressS}: Storage: {db.getStorage(address, 0.u256)}; getAccount: {db.getAccount address}" +proc rootHash*(db: ReadOnlyStateDB): KeccakHash {.borrow.} +proc getAccount*(db: ReadOnlyStateDB, address: EthAddress): Account {.borrow.} +proc getCodeHash*(db: ReadOnlyStateDB, address: EthAddress): Hash256 {.borrow.} +proc getBalance*(db: ReadOnlyStateDB, address: EthAddress): UInt256 {.borrow.} +proc getStorageRoot*(db: ReadOnlyStateDB, address: EthAddress): Hash256 {.borrow.} +proc getStorage*(db: ReadOnlyStateDB, address: EthAddress, slot: UInt256): (UInt256, bool) {.borrow.} +proc getNonce*(db: ReadOnlyStateDB, address: EthAddress): AccountNonce {.borrow.} +proc getCode*(db: ReadOnlyStateDB, address: EthAddress): ByteRange {.borrow.} +proc hasCodeOrNonce*(account: ReadOnlyStateDB, address: EthAddress): bool {.borrow.} diff --git a/nimbus/db/storage_types.nim b/nimbus/db/storage_types.nim index 5694a644c..faf66dbfd 100644 --- a/nimbus/db/storage_types.nim +++ b/nimbus/db/storage_types.nim @@ -9,6 +9,7 @@ type transactionHashToBlock canonicalHeadHash slotHashToSlot + contractHash DbKey* = object # The first byte stores the key type. The rest are key-specific values @@ -48,6 +49,11 @@ proc slotHashToSlotKey*(h: openArray[byte]): DbKey {.inline.} = result.data[1 .. 32] = h result.dataEndPos = uint8 32 +proc contractHashKey*(h: Hash256): DbKey {.inline.} = + result.data[0] = byte ord(contractHash) + result.data[1 .. 32] = h.data + result.dataEndPos = uint8 32 + const hashHolderKinds = {genericHash, blockHashToScore, transactionHashToBlock} template toOpenArray*(k: DbKey): openarray[byte] = diff --git a/nimbus/p2p/executor.nim b/nimbus/p2p/executor.nim index 0c698fd25..a4f668566 100644 --- a/nimbus/p2p/executor.nim +++ b/nimbus/p2p/executor.nim @@ -1,15 +1,54 @@ -import eth_common, ranges, chronicles, eth_bloom, nimcrypto, +import options, + eth_common, ranges, chronicles, eth_bloom, nimcrypto, ../db/[db_chain, state_db], ../utils, ../constants, ../transaction, ../vm_state, ../vm_types, ../vm_state_transactions, ../vm/[computation, interpreter_dispatch, message], ../vm/interpreter/vm_forks -proc processTransaction*(db: var AccountStateDB, t: Transaction, sender: EthAddress, vmState: BaseVMState): UInt256 = +proc contractCall(t: Transaction, vmState: BaseVMState, sender: EthAddress, forkOverride=none(Fork)): UInt256 = + # TODO: this function body was copied from GST with it's comments and TODOs. + # Right now it's main purpose is to produce VM tracing when syncing block with + # contract call. At later stage, this proc together with applyCreateTransaction + # and processTransaction need to be restructured. + + # TODO: replace with cachingDb or similar approach; necessary + # when calls/subcalls/etc come in, too. + var db = vmState.accountDb + let storageRoot = db.getStorageRoot(t.to) + + var computation = setupComputation(vmState, t, sender, forkOverride) + # contract creation transaction.to == 0, so ensure happens after + db.addBalance(t.to, t.value) + + let header = vmState.blockHeader + let gasCost = t.gasLimit.u256 * t.gasPrice.u256 + + if execComputation(computation): + let + gasRemaining = computation.gasMeter.gasRemaining.u256 + gasRefunded = computation.gasMeter.gasRefunded.u256 + gasUsed = t.gasLimit.u256 - gasRemaining + gasRefund = min(gasRefunded, gasUsed div 2) + gasRefundAmount = (gasRefund + gasRemaining) * t.gasPrice.u256 + + db.addBalance(sender, gasRefundAmount) + + return (t.gasLimit.u256 - gasRemaining - gasRefund) * t.gasPrice.u256 + else: + db.subBalance(t.to, t.value) + db.addBalance(sender, t.value) + db.setStorageRoot(t.to, storageRoot) + if computation.tracingEnabled: computation.traceError() + vmState.clearLogs() + return t.gasLimit.u256 * t.gasPrice.u256 + +proc processTransaction*(t: Transaction, sender: EthAddress, vmState: BaseVMState): UInt256 = ## Process the transaction, write the results to db. ## Returns amount of ETH to be rewarded to miner trace "Sender", sender trace "txHash", rlpHash = t.rlpHash + var db = vmState.accountDb # Inct nonce: db.setNonce(sender, db.getNonce(sender) + 1) var transactionFailed = false @@ -43,7 +82,7 @@ proc processTransaction*(db: var AccountStateDB, t: Transaction, sender: EthAddr else: if t.isContractCreation: # TODO: re-derive sender in callee for cleaner interface, perhaps - return applyCreateTransaction(db, t, vmState, sender) + return applyCreateTransaction(t, vmState, sender) else: let code = db.getCode(t.to) @@ -56,8 +95,9 @@ proc processTransaction*(db: var AccountStateDB, t: Transaction, sender: EthAddr # Contract call trace "Contract call" trace "Transaction", sender, to = t.to, value = t.value, hasCode = code.len != 0 - let msg = newMessage(t.gasLimit, t.gasPrice, t.to, sender, t.value, t.payload, code.toSeq) - # TODO: Run the vm + #let msg = newMessage(t.gasLimit, t.gasPrice, t.to, sender, t.value, t.payload, code.toSeq) + # TODO: Run the vm with proper fork + return contractCall(t, vmState, sender) if gasUsed > t.gasLimit: gasUsed = t.gasLimit @@ -88,12 +128,9 @@ func createBloom*(receipts: openArray[Receipt]): Bloom = bloom.value = bloom.value or logsBloom(receipt.logs).value result = bloom.value.toByteArrayBE -proc makeReceipt(vmState: BaseVMState, stateRoot: Hash256, cumulativeGasUsed: GasInt, fork = FkFrontier): Receipt = +proc makeReceipt(vmState: BaseVMState, cumulativeGasUsed: GasInt, fork = FkFrontier): Receipt = if fork < FkByzantium: - # TODO: which one: vmState.blockHeader.stateRoot or stateDb.rootHash? - # currently, vmState.blockHeader.stateRoot vs stateDb.rootHash can be different - # need to wait #188 solved - result.stateRootOrStatus = hashOrStatus(stateRoot) + result.stateRootOrStatus = hashOrStatus(vmState.accountDb.rootHash) else: # TODO: post byzantium fork use status instead of rootHash let vmStatus = true # success or failure @@ -106,12 +143,11 @@ proc makeReceipt(vmState: BaseVMState, stateRoot: Hash256, cumulativeGasUsed: Ga proc processBlock*(chainDB: BaseChainDB, head, header: BlockHeader, body: BlockBody, vmState: BaseVMState): ValidationResult = let blockReward = 5.u256 * pow(10.u256, 18) # 5 ETH - var stateDb = newAccountStateDB(chainDB.db, head.stateRoot, chainDB.pruneTrie) - if body.transactions.calcTxRoot != header.txRoot: debug "Mismatched txRoot", blockNumber=header.blockNumber return ValidationResult.Error + var stateDb = vmState.accountDb if header.txRoot != BLANK_ROOT_HASH: if body.transactions.len == 0: debug "No transactions in body", blockNumber=header.blockNumber @@ -124,7 +160,7 @@ proc processBlock*(chainDB: BaseChainDB, head, header: BlockHeader, body: BlockB for txIndex, tx in body.transactions: var sender: EthAddress if tx.getSender(sender): - let txFee = processTransaction(stateDb, tx, sender, vmState) + let txFee = processTransaction(tx, sender, vmState) # perhaps this can be altered somehow # or processTransaction return only gasUsed @@ -137,7 +173,7 @@ proc processBlock*(chainDB: BaseChainDB, head, header: BlockHeader, body: BlockB else: debug "Could not get sender", txIndex, tx return ValidationResult.Error - vmState.receipts[txIndex] = makeReceipt(vmState, stateDb.rootHash, cumulativeGasUsed) + vmState.receipts[txIndex] = makeReceipt(vmState, cumulativeGasUsed) var mainReward = blockReward if header.ommersHash != EMPTY_UNCLE_HASH: diff --git a/nimbus/rpc/p2p.nim b/nimbus/rpc/p2p.nim index ef0dc0332..afe6500fa 100644 --- a/nimbus/rpc/p2p.nim +++ b/nimbus/rpc/p2p.nim @@ -29,18 +29,18 @@ import proc `%`*(value: Time): JsonNode = result = %value.toSeconds -template balance(addressDb: AccountStateDb, address: EthAddress): GasInt = +template balance(addressDb: ReadOnlyStateDb, address: EthAddress): GasInt = # TODO: Account balance u256 but GasInt is int64? addressDb.getBalance(address).truncate(int64) proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) = - func getAccountDb(header: BlockHeader, readOnly = true): AccountStateDb = + func getAccountDb(header: BlockHeader): ReadOnlyStateDB = ## Retrieves the account db from canonical head let vmState = newBaseVMState(header, chain) - result = vmState.chaindb.getStateDb(vmState.blockHeader.hash, readOnly) + result = vmState.readOnlyStateDB() - func accountDbFromTag(tag: string, readOnly = true): AccountStateDb = + func accountDbFromTag(tag: string, readOnly = true): ReadOnlyStateDB = result = getAccountDb(chain.headerFromTag(tag)) proc getBlockBody(hash: KeccakHash): BlockBody = @@ -188,7 +188,7 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) = ## data: address. ## message: message to sign. ## Returns signature. - let accountDb = getAccountDb(chain.getCanonicalHead(), true) + let accountDb = getAccountDb(chain.getCanonicalHead()) var privateKey: PrivateKey # TODO: Get from key store result = ("0x" & sign(privateKey, message.string)).HexDataStr @@ -341,7 +341,7 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) = proc populateTransactionObject(transaction: Transaction, txIndex: int64, blockHeader: BlockHeader, blockHash: Hash256): TransactionObject = let vmState = newBaseVMState(blockHeader, chain) - accountDb = vmState.chaindb.getStateDb(blockHash, true) + accountDb = vmState.readOnlyStateDB() address = transaction.getSender() txCount = accountDb.getNonce(address) txHash = transaction.rlpHash @@ -439,7 +439,7 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) = var idx = 0 prevGasUsed = GasInt(0) - + for receipt in chain.getReceipts(header): let gasUsed = receipt.cumulativeGasUsed - prevGasUsed prevGasUsed = receipt.cumulativeGasUsed diff --git a/nimbus/tracer.nim b/nimbus/tracer.nim index 8009e95b6..29aae96c9 100644 --- a/nimbus/tracer.nim +++ b/nimbus/tracer.nim @@ -27,7 +27,7 @@ proc dumpReceipts*(chainDB: BaseChainDB, header: BlockHeader): JsonNode = for receipt in chainDB.getReceipts(header): result.add receipt.toJson -proc toJson(receipts: seq[Receipt]): JsonNode = +proc toJson*(receipts: seq[Receipt]): JsonNode = result = newJArray() for receipt in receipts: result.add receipt.toJson @@ -88,7 +88,7 @@ proc traceTransaction*(db: BaseChainDB, header: BlockHeader, captureChainDB = newBaseChainDB(captureTrieDB, false) # prune or not prune? vmState = newBaseVMState(parent, captureChainDB, tracerFlags + {EnableAccount}) - var stateDb = newAccountStateDB(captureTrieDB, parent.stateRoot, db.pruneTrie) + var stateDb = vmState.accountDb if header.txRoot == BLANK_ROOT_HASH: return newJNull() assert(body.transactions.calcTxRoot == header.txRoot) @@ -113,7 +113,7 @@ proc traceTransaction*(db: BaseChainDB, header: BlockHeader, stateDiff["beforeRoot"] = %($stateDb.rootHash) beforeRoot = stateDb.rootHash - let txFee = processTransaction(stateDb, tx, sender, vmState) + let txFee = processTransaction(tx, sender, vmState) stateDb.addBalance(header.coinbase, txFee) if idx == txIndex: @@ -203,9 +203,6 @@ proc traceBlock*(db: BaseChainDB, header: BlockHeader, body: BlockBody, tracerFl captureChainDB = newBaseChainDB(captureTrieDB, false) vmState = newBaseVMState(parent, captureChainDB, tracerFlags + {EnableTracing}) - var - stateDb = newAccountStateDB(captureTrieDB, parent.stateRoot, db.pruneTrie) - if header.txRoot == BLANK_ROOT_HASH: return newJNull() assert(body.transactions.calcTxRoot == header.txRoot) assert(body.transactions.len != 0) @@ -215,7 +212,7 @@ proc traceBlock*(db: BaseChainDB, header: BlockHeader, body: BlockBody, tracerFl for tx in body.transactions: let sender = tx.getSender - txFee = processTransaction(stateDb, tx, sender, vmState) + txFee = processTransaction(tx, sender, vmState) gasUsed = gasUsed + (txFee div tx.gasPrice.u256).truncate(GasInt) result = vmState.getTracingResult() diff --git a/nimbus/vm/computation.nim b/nimbus/vm/computation.nim index ed45c4191..ea7cfdfe3 100644 --- a/nimbus/vm/computation.nim +++ b/nimbus/vm/computation.nim @@ -99,8 +99,8 @@ proc applyMessage(computation: var BaseComputation, opCode: static[Op]) = if computation.msg.value != 0: let senderBalance = - computation.vmState.chainDb.getStateDb( - computation.vmState.blockHeader.hash, false). + computation.vmState.getStateDb( + computation.vmState.blockHeader.hash). getBalance(computation.msg.sender) var newBalance = senderBalance diff --git a/nimbus/vm/interpreter/opcodes_impl.nim b/nimbus/vm/interpreter/opcodes_impl.nim index 059c4810b..a1623f152 100644 --- a/nimbus/vm/interpreter/opcodes_impl.nim +++ b/nimbus/vm/interpreter/opcodes_impl.nim @@ -518,8 +518,8 @@ op create, inline = false, value, startPosition, size: computation.memory.extend(memPos, len) let senderBalance = - computation.vmState.chainDb.getStateDb( - computation.vmState.blockHeader.rlphash, false). + computation.vmState.getStateDb( + computation.vmState.blockHeader.rlphash). getBalance(computation.msg.sender) if senderBalance < value: diff --git a/nimbus/vm/transaction_tracer.nim b/nimbus/vm/transaction_tracer.nim index 8d6dcb578..0a1f39645 100644 --- a/nimbus/vm/transaction_tracer.nim +++ b/nimbus/vm/transaction_tracer.nim @@ -67,7 +67,7 @@ proc traceOpCodeEnded*(tracer: var TransactionTracer, c: BaseComputation, op: Op # when contract execution interrupted by exception if TracerFlags.DisableStorage notin tracer.flags: var storage = newJObject() - var stateDB = c.vmState.chaindb.getStateDb(c.vmState.blockHeader.stateRoot, readOnly = true) + var stateDB = c.vmState.accountDb for key, value in stateDB.storage(c.msg.storageAddress): storage[key.dumpHex] = %(value.dumpHex) j["storage"] = storage diff --git a/nimbus/vm_state.nim b/nimbus/vm_state.nim index dab5af93a..aa05f909a 100644 --- a/nimbus/vm_state.nim +++ b/nimbus/vm_state.nim @@ -34,6 +34,10 @@ proc newBaseVMState*(header: BlockHeader, chainDB: BaseChainDB, tracerFlags: set result.tracer.initTracer(tracerFlags) result.tracingEnabled = TracerFlags.EnableTracing in tracerFlags result.logEntries = @[] + result.accountDb = newAccountStateDB(chainDB.db, header.stateRoot, chainDB.pruneTrie) + +proc stateRoot*(vmState: BaseVMState): Hash256 = + vmState.blockHeader.stateRoot method blockhash*(vmState: BaseVMState): Hash256 = vmState.blockHeader.hash @@ -94,11 +98,20 @@ when false: # TODO `db`.db = nil # state._trie = None +proc getStateDb*(vmState: BaseVMState; stateRoot: Hash256): AccountStateDB = + # TODO: use AccountStateDB revert/commit after JournalDB implemented + vmState.accountDb.rootHash = stateRoot + vmState.accountDb + +proc readOnlyStateDB*(vmState: BaseVMState): ReadOnlyStateDB {.inline.} = + ReadOnlyStateDB(vmState.accountDb) + template mutateStateDB*(vmState: BaseVMState, body: untyped) = # This should provide more clever change handling in the future + # TODO: use AccountStateDB revert/commit after JournalDB implemented block: let initialStateRoot = vmState.blockHeader.stateRoot - var db {.inject.} = vmState.chaindb.getStateDB(initialStateRoot, false) + var db {.inject.} = vmState.getStateDB(initialStateRoot) body @@ -106,9 +119,6 @@ template mutateStateDB*(vmState: BaseVMState, body: untyped) = if finalStateRoot != initialStateRoot: vmState.blockHeader.stateRoot = finalStateRoot -proc readOnlyStateDB*(vmState: BaseVMState): AccountStateDB {.inline.}= - vmState.chaindb.getStateDb(vmState.blockHeader.stateRoot, readOnly = true) - export DbTransaction, commit, rollback, dispose, safeDispose proc beginTransaction*(vmState: BaseVMState): DbTransaction = diff --git a/nimbus/vm_state_transactions.nim b/nimbus/vm_state_transactions.nim index 48bcc158d..f809f85b3 100644 --- a/nimbus/vm_state_transactions.nim +++ b/nimbus/vm_state_transactions.nim @@ -33,7 +33,7 @@ proc validateTransaction*(vmState: BaseVMState, transaction: Transaction, sender transaction.accountNonce == readOnlyDB.getNonce(sender) and readOnlyDB.getBalance(sender) >= gas_cost -proc setupComputation*(header: BlockHeader, vmState: BaseVMState, transaction: Transaction, sender: EthAddress, forkOverride=none(Fork)) : BaseComputation = +proc setupComputation*(vmState: BaseVMState, transaction: Transaction, sender: EthAddress, forkOverride=none(Fork)) : BaseComputation = let message = newMessage( gas = transaction.gasLimit - transaction.payload.intrinsicGas, gasPrice = transaction.gasPrice, @@ -45,10 +45,15 @@ proc setupComputation*(header: BlockHeader, vmState: BaseVMState, transaction: T options = newMessageOptions(origin = sender, createAddress = transaction.to)) - result = newBaseComputation(vmState, header.blockNumber, message, forkOverride) + result = newBaseComputation(vmState, vmState.blockNumber, message, forkOverride) doAssert result.isOriginComputation proc execComputation*(computation: var BaseComputation): bool = + # TODO: use AccountStateDB revert/commit after JournalDB implemented + let stateDb = computation.vmState.accountDb + let intermediateRoot = stateDb.rootHash + computation.vmState.blockHeader.stateRoot = stateDb.rootHash + try: computation.executeOpcodes() computation.vmState.mutateStateDB: @@ -59,7 +64,12 @@ proc execComputation*(computation: var BaseComputation): bool = except ValueError: result = false -proc applyCreateTransaction*(db: var AccountStateDB, t: Transaction, vmState: BaseVMState, sender: EthAddress, forkOverride=none(Fork)): UInt256 = + if result: + stateDb.rootHash = computation.vmState.blockHeader.stateRoot + else: + stateDb.rootHash = intermediateRoot + +proc applyCreateTransaction*(t: Transaction, vmState: BaseVMState, sender: EthAddress, forkOverride=none(Fork)): UInt256 = doAssert t.isContractCreation # TODO: clean up params trace "Contract creation" @@ -76,9 +86,11 @@ proc applyCreateTransaction*(db: var AccountStateDB, t: Transaction, vmState: Ba let msg = newMessage(t.gasLimit - gasUsed, t.gasPrice, t.to, sender, t.value, @[], t.payload, options = newMessageOptions(origin = sender, createAddress = contractAddress)) + var c = newBaseComputation(vmState, vmState.blockNumber, msg, forkOverride) if execComputation(c): + var db = vmState.accountDb db.addBalance(contractAddress, t.value) # XXX: copy/pasted from GST fixture @@ -111,6 +123,7 @@ proc applyCreateTransaction*(db: var AccountStateDB, t: Transaction, vmState: Ba # FIXME: don't do this revert, but rather only subBalance correctly # the if transactionfailed at end is what is supposed to pick it up # especially when it's cross-function, it's ugly/fragile + var db = vmState.accountDb db.addBalance(sender, t.value) debug "execComputation() error", isError = c.isError if c.tracingEnabled: diff --git a/nimbus/vm_types.nim b/nimbus/vm_types.nim index 95216c6c5..cf0ad5c52 100644 --- a/nimbus/vm_types.nim +++ b/nimbus/vm_types.nim @@ -10,7 +10,7 @@ import ./constants, json, ./vm/[memory, stack, code_stream], ./vm/interpreter/[gas_costs, opcode_values, vm_forks], # TODO - will be hidden at a lower layer - ./db/db_chain + ./db/[db_chain, state_db] type BaseVMState* = ref object of RootObj @@ -23,6 +23,7 @@ type tracer* : TransactionTracer logEntries* : seq[Log] receipts* : seq[Receipt] + accountDb* : AccountStateDB AccessLogs* = ref object reads*: Table[string, string]