This commit is contained in:
andri lim 2018-12-31 10:27:02 +07:00 committed by zah
parent 041ed689aa
commit e78fb72ef6
13 changed files with 140 additions and 96 deletions

View File

@ -12,12 +12,7 @@ import
type type
BaseChainDB* = ref object BaseChainDB* = ref object
db*: TrieDatabaseRef 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
pruneTrie*: bool pruneTrie*: bool
KeyType = enum KeyType = enum
@ -31,7 +26,6 @@ type
proc newBaseChainDB*(db: TrieDatabaseRef, pruneTrie: bool = true): BaseChainDB = proc newBaseChainDB*(db: TrieDatabaseRef, pruneTrie: bool = true): BaseChainDB =
new(result) new(result)
result.db = db result.db = db
result.accountCodes = newTable[Hash256, ByteRange]()
result.pruneTrie = pruneTrie result.pruneTrie = pruneTrie
proc `$`*(db: BaseChainDB): string = proc `$`*(db: BaseChainDB): string =
@ -263,34 +257,6 @@ proc persistBlockToDb*(self: BaseChainDB; blk: Block): ValidationResult =
debug "ommersHash mismatch" debug "ommersHash mismatch"
return ValidationResult.Error 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: # Deprecated:
proc getBlockHeaderByHash*(self: BaseChainDB; blockHash: Hash256): BlockHeader {.deprecated.} = proc getBlockHeaderByHash*(self: BaseChainDB; blockHash: Hash256): BlockHeader {.deprecated.} =
self.getBlockHeader(blockHash) self.getBlockHeader(blockHash)

View File

@ -1,4 +1,4 @@
const nimbus_db_backend {.strdefine.} = "rocksdb" const nimbus_db_backend* {.strdefine.} = "rocksdb"
when nimbus_db_backend == "sqlite": when nimbus_db_backend == "sqlite":
import ./backends/sqlite_backend as database_backend import ./backends/sqlite_backend as database_backend

View File

@ -17,20 +17,19 @@ logScope:
type type
AccountStateDB* = ref object AccountStateDB* = ref object
trie: SecureHexaryTrie trie: SecureHexaryTrie
accountCodes: TableRef[Hash256, ByteRange]
proc rootHash*(accountDb: AccountStateDB): KeccakHash = ReadOnlyStateDB* = distinct AccountStateDB
accountDb.trie.rootHash
# proc `rootHash=`*(db: var AccountStateDB, value: string) = proc rootHash*(db: AccountStateDB): KeccakHash =
# TODO: self.Trie.rootHash = value db.trie.rootHash
proc `rootHash=`*(db: AccountStateDB, root: KeccakHash) =
db.trie = initSecureHexaryTrie(HexaryTrie(db.trie).db, root, db.trie.isPruning)
proc newAccountStateDB*(backingStore: TrieDatabaseRef, proc newAccountStateDB*(backingStore: TrieDatabaseRef,
root: KeccakHash, pruneTrie: bool, readOnly: bool = false, root: KeccakHash, pruneTrie: bool): AccountStateDB =
accountCodes = newTable[Hash256, ByteRange]()): AccountStateDB =
result.new() result.new()
result.trie = initSecureHexaryTrie(backingStore, root, pruneTrie) result.trie = initSecureHexaryTrie(backingStore, root, pruneTrie)
result.accountCodes = accountCodes
template createRangeFromAddress(address: EthAddress): ByteRange = template createRangeFromAddress(address: EthAddress): ByteRange =
## XXX: The name of this proc is intentionally long, because it ## 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: else:
result = (0.u256, false) 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) var account = db.getAccount(address)
if newNonce != account.nonce: if newNonce != account.nonce:
account.nonce = newNonce account.nonce = newNonce
@ -152,18 +151,25 @@ proc getNonce*(db: AccountStateDB, address: EthAddress): AccountNonce =
let account = db.getAccount(address) let account = db.getAccount(address)
account.nonce account.nonce
proc setCode*(db: var AccountStateDB, address: EthAddress, code: ByteRange) = proc setCode*(db: AccountStateDB, address: EthAddress, code: ByteRange) =
var account = db.getAccount(address) var account = db.getAccount(address)
let newCodeHash = keccak256.digest code.toOpenArray # TODO: implement JournalDB to store code and storage
if newCodeHash != account.codeHash: # 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 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) db.setAccount(address, account)
proc getCode*(db: AccountStateDB, address: EthAddress): ByteRange = 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.} = proc hasCodeOrNonce*(account: AccountStateDB, address: EthAddress): bool {.inline.} =
account.getNonce(address) != 0 or account.getCodeHash(address) != EMPTY_SHA3 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 let address = addressS.parseAddress
return fmt"{addressS}: Storage: {db.getStorage(address, 0.u256)}; getAccount: {db.getAccount address}" 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.}

View File

@ -9,6 +9,7 @@ type
transactionHashToBlock transactionHashToBlock
canonicalHeadHash canonicalHeadHash
slotHashToSlot slotHashToSlot
contractHash
DbKey* = object DbKey* = object
# The first byte stores the key type. The rest are key-specific values # 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.data[1 .. 32] = h
result.dataEndPos = uint8 32 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} const hashHolderKinds = {genericHash, blockHashToScore, transactionHashToBlock}
template toOpenArray*(k: DbKey): openarray[byte] = template toOpenArray*(k: DbKey): openarray[byte] =

View File

@ -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], ../db/[db_chain, state_db],
../utils, ../constants, ../transaction, ../utils, ../constants, ../transaction,
../vm_state, ../vm_types, ../vm_state_transactions, ../vm_state, ../vm_types, ../vm_state_transactions,
../vm/[computation, interpreter_dispatch, message], ../vm/[computation, interpreter_dispatch, message],
../vm/interpreter/vm_forks ../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. ## Process the transaction, write the results to db.
## Returns amount of ETH to be rewarded to miner ## Returns amount of ETH to be rewarded to miner
trace "Sender", sender trace "Sender", sender
trace "txHash", rlpHash = t.rlpHash trace "txHash", rlpHash = t.rlpHash
var db = vmState.accountDb
# Inct nonce: # Inct nonce:
db.setNonce(sender, db.getNonce(sender) + 1) db.setNonce(sender, db.getNonce(sender) + 1)
var transactionFailed = false var transactionFailed = false
@ -43,7 +82,7 @@ proc processTransaction*(db: var AccountStateDB, t: Transaction, sender: EthAddr
else: else:
if t.isContractCreation: if t.isContractCreation:
# TODO: re-derive sender in callee for cleaner interface, perhaps # TODO: re-derive sender in callee for cleaner interface, perhaps
return applyCreateTransaction(db, t, vmState, sender) return applyCreateTransaction(t, vmState, sender)
else: else:
let code = db.getCode(t.to) let code = db.getCode(t.to)
@ -56,8 +95,9 @@ proc processTransaction*(db: var AccountStateDB, t: Transaction, sender: EthAddr
# Contract call # Contract call
trace "Contract call" trace "Contract call"
trace "Transaction", sender, to = t.to, value = t.value, hasCode = code.len != 0 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) #let msg = newMessage(t.gasLimit, t.gasPrice, t.to, sender, t.value, t.payload, code.toSeq)
# TODO: Run the vm # TODO: Run the vm with proper fork
return contractCall(t, vmState, sender)
if gasUsed > t.gasLimit: if gasUsed > t.gasLimit:
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 bloom.value = bloom.value or logsBloom(receipt.logs).value
result = bloom.value.toByteArrayBE 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: if fork < FkByzantium:
# TODO: which one: vmState.blockHeader.stateRoot or stateDb.rootHash? result.stateRootOrStatus = hashOrStatus(vmState.accountDb.rootHash)
# currently, vmState.blockHeader.stateRoot vs stateDb.rootHash can be different
# need to wait #188 solved
result.stateRootOrStatus = hashOrStatus(stateRoot)
else: else:
# TODO: post byzantium fork use status instead of rootHash # TODO: post byzantium fork use status instead of rootHash
let vmStatus = true # success or failure 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 = proc processBlock*(chainDB: BaseChainDB, head, header: BlockHeader, body: BlockBody, vmState: BaseVMState): ValidationResult =
let blockReward = 5.u256 * pow(10.u256, 18) # 5 ETH 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: if body.transactions.calcTxRoot != header.txRoot:
debug "Mismatched txRoot", blockNumber=header.blockNumber debug "Mismatched txRoot", blockNumber=header.blockNumber
return ValidationResult.Error return ValidationResult.Error
var stateDb = vmState.accountDb
if header.txRoot != BLANK_ROOT_HASH: if header.txRoot != BLANK_ROOT_HASH:
if body.transactions.len == 0: if body.transactions.len == 0:
debug "No transactions in body", blockNumber=header.blockNumber 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: for txIndex, tx in body.transactions:
var sender: EthAddress var sender: EthAddress
if tx.getSender(sender): if tx.getSender(sender):
let txFee = processTransaction(stateDb, tx, sender, vmState) let txFee = processTransaction(tx, sender, vmState)
# perhaps this can be altered somehow # perhaps this can be altered somehow
# or processTransaction return only gasUsed # or processTransaction return only gasUsed
@ -137,7 +173,7 @@ proc processBlock*(chainDB: BaseChainDB, head, header: BlockHeader, body: BlockB
else: else:
debug "Could not get sender", txIndex, tx debug "Could not get sender", txIndex, tx
return ValidationResult.Error return ValidationResult.Error
vmState.receipts[txIndex] = makeReceipt(vmState, stateDb.rootHash, cumulativeGasUsed) vmState.receipts[txIndex] = makeReceipt(vmState, cumulativeGasUsed)
var mainReward = blockReward var mainReward = blockReward
if header.ommersHash != EMPTY_UNCLE_HASH: if header.ommersHash != EMPTY_UNCLE_HASH:

View File

@ -29,18 +29,18 @@ import
proc `%`*(value: Time): JsonNode = proc `%`*(value: Time): JsonNode =
result = %value.toSeconds 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? # TODO: Account balance u256 but GasInt is int64?
addressDb.getBalance(address).truncate(int64) addressDb.getBalance(address).truncate(int64)
proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) = 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 ## Retrieves the account db from canonical head
let vmState = newBaseVMState(header, chain) 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)) result = getAccountDb(chain.headerFromTag(tag))
proc getBlockBody(hash: KeccakHash): BlockBody = proc getBlockBody(hash: KeccakHash): BlockBody =
@ -188,7 +188,7 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB, rpcsrv: RpcServer) =
## data: address. ## data: address.
## message: message to sign. ## message: message to sign.
## Returns signature. ## Returns signature.
let accountDb = getAccountDb(chain.getCanonicalHead(), true) let accountDb = getAccountDb(chain.getCanonicalHead())
var privateKey: PrivateKey # TODO: Get from key store var privateKey: PrivateKey # TODO: Get from key store
result = ("0x" & sign(privateKey, message.string)).HexDataStr 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 = proc populateTransactionObject(transaction: Transaction, txIndex: int64, blockHeader: BlockHeader, blockHash: Hash256): TransactionObject =
let let
vmState = newBaseVMState(blockHeader, chain) vmState = newBaseVMState(blockHeader, chain)
accountDb = vmState.chaindb.getStateDb(blockHash, true) accountDb = vmState.readOnlyStateDB()
address = transaction.getSender() address = transaction.getSender()
txCount = accountDb.getNonce(address) txCount = accountDb.getNonce(address)
txHash = transaction.rlpHash txHash = transaction.rlpHash

View File

@ -27,7 +27,7 @@ proc dumpReceipts*(chainDB: BaseChainDB, header: BlockHeader): JsonNode =
for receipt in chainDB.getReceipts(header): for receipt in chainDB.getReceipts(header):
result.add receipt.toJson result.add receipt.toJson
proc toJson(receipts: seq[Receipt]): JsonNode = proc toJson*(receipts: seq[Receipt]): JsonNode =
result = newJArray() result = newJArray()
for receipt in receipts: for receipt in receipts:
result.add receipt.toJson result.add receipt.toJson
@ -88,7 +88,7 @@ proc traceTransaction*(db: BaseChainDB, header: BlockHeader,
captureChainDB = newBaseChainDB(captureTrieDB, false) # prune or not prune? captureChainDB = newBaseChainDB(captureTrieDB, false) # prune or not prune?
vmState = newBaseVMState(parent, captureChainDB, tracerFlags + {EnableAccount}) 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() if header.txRoot == BLANK_ROOT_HASH: return newJNull()
assert(body.transactions.calcTxRoot == header.txRoot) assert(body.transactions.calcTxRoot == header.txRoot)
@ -113,7 +113,7 @@ proc traceTransaction*(db: BaseChainDB, header: BlockHeader,
stateDiff["beforeRoot"] = %($stateDb.rootHash) stateDiff["beforeRoot"] = %($stateDb.rootHash)
beforeRoot = stateDb.rootHash beforeRoot = stateDb.rootHash
let txFee = processTransaction(stateDb, tx, sender, vmState) let txFee = processTransaction(tx, sender, vmState)
stateDb.addBalance(header.coinbase, txFee) stateDb.addBalance(header.coinbase, txFee)
if idx == txIndex: if idx == txIndex:
@ -203,9 +203,6 @@ proc traceBlock*(db: BaseChainDB, header: BlockHeader, body: BlockBody, tracerFl
captureChainDB = newBaseChainDB(captureTrieDB, false) captureChainDB = newBaseChainDB(captureTrieDB, false)
vmState = newBaseVMState(parent, captureChainDB, tracerFlags + {EnableTracing}) vmState = newBaseVMState(parent, captureChainDB, tracerFlags + {EnableTracing})
var
stateDb = newAccountStateDB(captureTrieDB, parent.stateRoot, db.pruneTrie)
if header.txRoot == BLANK_ROOT_HASH: return newJNull() if header.txRoot == BLANK_ROOT_HASH: return newJNull()
assert(body.transactions.calcTxRoot == header.txRoot) assert(body.transactions.calcTxRoot == header.txRoot)
assert(body.transactions.len != 0) assert(body.transactions.len != 0)
@ -215,7 +212,7 @@ proc traceBlock*(db: BaseChainDB, header: BlockHeader, body: BlockBody, tracerFl
for tx in body.transactions: for tx in body.transactions:
let let
sender = tx.getSender sender = tx.getSender
txFee = processTransaction(stateDb, tx, sender, vmState) txFee = processTransaction(tx, sender, vmState)
gasUsed = gasUsed + (txFee div tx.gasPrice.u256).truncate(GasInt) gasUsed = gasUsed + (txFee div tx.gasPrice.u256).truncate(GasInt)
result = vmState.getTracingResult() result = vmState.getTracingResult()

View File

@ -99,8 +99,8 @@ proc applyMessage(computation: var BaseComputation, opCode: static[Op]) =
if computation.msg.value != 0: if computation.msg.value != 0:
let senderBalance = let senderBalance =
computation.vmState.chainDb.getStateDb( computation.vmState.getStateDb(
computation.vmState.blockHeader.hash, false). computation.vmState.blockHeader.hash).
getBalance(computation.msg.sender) getBalance(computation.msg.sender)
var newBalance = senderBalance var newBalance = senderBalance

View File

@ -518,8 +518,8 @@ op create, inline = false, value, startPosition, size:
computation.memory.extend(memPos, len) computation.memory.extend(memPos, len)
let senderBalance = let senderBalance =
computation.vmState.chainDb.getStateDb( computation.vmState.getStateDb(
computation.vmState.blockHeader.rlphash, false). computation.vmState.blockHeader.rlphash).
getBalance(computation.msg.sender) getBalance(computation.msg.sender)
if senderBalance < value: if senderBalance < value:

View File

@ -67,7 +67,7 @@ proc traceOpCodeEnded*(tracer: var TransactionTracer, c: BaseComputation, op: Op
# when contract execution interrupted by exception # when contract execution interrupted by exception
if TracerFlags.DisableStorage notin tracer.flags: if TracerFlags.DisableStorage notin tracer.flags:
var storage = newJObject() 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): for key, value in stateDB.storage(c.msg.storageAddress):
storage[key.dumpHex] = %(value.dumpHex) storage[key.dumpHex] = %(value.dumpHex)
j["storage"] = storage j["storage"] = storage

View File

@ -34,6 +34,10 @@ proc newBaseVMState*(header: BlockHeader, chainDB: BaseChainDB, tracerFlags: set
result.tracer.initTracer(tracerFlags) result.tracer.initTracer(tracerFlags)
result.tracingEnabled = TracerFlags.EnableTracing in tracerFlags result.tracingEnabled = TracerFlags.EnableTracing in tracerFlags
result.logEntries = @[] result.logEntries = @[]
result.accountDb = newAccountStateDB(chainDB.db, header.stateRoot, chainDB.pruneTrie)
proc stateRoot*(vmState: BaseVMState): Hash256 =
vmState.blockHeader.stateRoot
method blockhash*(vmState: BaseVMState): Hash256 = method blockhash*(vmState: BaseVMState): Hash256 =
vmState.blockHeader.hash vmState.blockHeader.hash
@ -94,11 +98,20 @@ when false:
# TODO `db`.db = nil # TODO `db`.db = nil
# state._trie = None # 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) = template mutateStateDB*(vmState: BaseVMState, body: untyped) =
# This should provide more clever change handling in the future # This should provide more clever change handling in the future
# TODO: use AccountStateDB revert/commit after JournalDB implemented
block: block:
let initialStateRoot = vmState.blockHeader.stateRoot let initialStateRoot = vmState.blockHeader.stateRoot
var db {.inject.} = vmState.chaindb.getStateDB(initialStateRoot, false) var db {.inject.} = vmState.getStateDB(initialStateRoot)
body body
@ -106,9 +119,6 @@ template mutateStateDB*(vmState: BaseVMState, body: untyped) =
if finalStateRoot != initialStateRoot: if finalStateRoot != initialStateRoot:
vmState.blockHeader.stateRoot = finalStateRoot vmState.blockHeader.stateRoot = finalStateRoot
proc readOnlyStateDB*(vmState: BaseVMState): AccountStateDB {.inline.}=
vmState.chaindb.getStateDb(vmState.blockHeader.stateRoot, readOnly = true)
export DbTransaction, commit, rollback, dispose, safeDispose export DbTransaction, commit, rollback, dispose, safeDispose
proc beginTransaction*(vmState: BaseVMState): DbTransaction = proc beginTransaction*(vmState: BaseVMState): DbTransaction =

View File

@ -33,7 +33,7 @@ proc validateTransaction*(vmState: BaseVMState, transaction: Transaction, sender
transaction.accountNonce == readOnlyDB.getNonce(sender) and transaction.accountNonce == readOnlyDB.getNonce(sender) and
readOnlyDB.getBalance(sender) >= gas_cost 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( let message = newMessage(
gas = transaction.gasLimit - transaction.payload.intrinsicGas, gas = transaction.gasLimit - transaction.payload.intrinsicGas,
gasPrice = transaction.gasPrice, gasPrice = transaction.gasPrice,
@ -45,10 +45,15 @@ proc setupComputation*(header: BlockHeader, vmState: BaseVMState, transaction: T
options = newMessageOptions(origin = sender, options = newMessageOptions(origin = sender,
createAddress = transaction.to)) createAddress = transaction.to))
result = newBaseComputation(vmState, header.blockNumber, message, forkOverride) result = newBaseComputation(vmState, vmState.blockNumber, message, forkOverride)
doAssert result.isOriginComputation doAssert result.isOriginComputation
proc execComputation*(computation: var BaseComputation): bool = 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: try:
computation.executeOpcodes() computation.executeOpcodes()
computation.vmState.mutateStateDB: computation.vmState.mutateStateDB:
@ -59,7 +64,12 @@ proc execComputation*(computation: var BaseComputation): bool =
except ValueError: except ValueError:
result = false 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 doAssert t.isContractCreation
# TODO: clean up params # TODO: clean up params
trace "Contract creation" 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, let msg = newMessage(t.gasLimit - gasUsed, t.gasPrice, t.to, sender, t.value, @[], t.payload,
options = newMessageOptions(origin = sender, options = newMessageOptions(origin = sender,
createAddress = contractAddress)) createAddress = contractAddress))
var c = newBaseComputation(vmState, vmState.blockNumber, msg, forkOverride) var c = newBaseComputation(vmState, vmState.blockNumber, msg, forkOverride)
if execComputation(c): if execComputation(c):
var db = vmState.accountDb
db.addBalance(contractAddress, t.value) db.addBalance(contractAddress, t.value)
# XXX: copy/pasted from GST fixture # 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 # FIXME: don't do this revert, but rather only subBalance correctly
# the if transactionfailed at end is what is supposed to pick it up # the if transactionfailed at end is what is supposed to pick it up
# especially when it's cross-function, it's ugly/fragile # especially when it's cross-function, it's ugly/fragile
var db = vmState.accountDb
db.addBalance(sender, t.value) db.addBalance(sender, t.value)
debug "execComputation() error", isError = c.isError debug "execComputation() error", isError = c.isError
if c.tracingEnabled: if c.tracingEnabled:

View File

@ -10,7 +10,7 @@ import
./constants, json, ./constants, json,
./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 ./db/[db_chain, state_db]
type type
BaseVMState* = ref object of RootObj BaseVMState* = ref object of RootObj
@ -23,6 +23,7 @@ type
tracer* : TransactionTracer tracer* : TransactionTracer
logEntries* : seq[Log] logEntries* : seq[Log]
receipts* : seq[Receipt] receipts* : seq[Receipt]
accountDb* : AccountStateDB
AccessLogs* = ref object AccessLogs* = ref object
reads*: Table[string, string] reads*: Table[string, string]