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