fixes #188
This commit is contained in:
parent
041ed689aa
commit
e78fb72ef6
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.}
|
||||
|
|
|
@ -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] =
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Reference in New Issue