2018-04-06 14:52:10 +00:00
|
|
|
# Nimbus
|
|
|
|
# Copyright (c) 2018 Status Research & Development GmbH
|
|
|
|
# Licensed under either of
|
|
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
|
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
|
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
|
2018-01-17 12:57:50 +00:00
|
|
|
import
|
2018-11-28 19:02:21 +00:00
|
|
|
ranges/typedranges, sequtils, strformat, tables, options,
|
2019-02-26 07:04:12 +00:00
|
|
|
eth/common, chronicles, ./db/[db_chain, state_db],
|
|
|
|
constants, errors, transaction, vm_types, vm_state, utils,
|
|
|
|
./vm/[computation, interpreter], ./vm/interpreter/gas_costs
|
2018-01-17 12:57:50 +00:00
|
|
|
|
2018-09-27 19:09:26 +00:00
|
|
|
proc validateTransaction*(vmState: BaseVMState, transaction: Transaction, sender: EthAddress): bool =
|
|
|
|
# XXX: https://github.com/status-im/nimbus/issues/35#issuecomment-391726518
|
|
|
|
# XXX: lots of avoidable u256 construction
|
|
|
|
var readOnlyDB = vmState.readOnlyStateDB
|
|
|
|
let limitAndValue = transaction.gasLimit.u256 + transaction.value
|
2019-02-27 05:09:46 +00:00
|
|
|
let gasCost = transaction.gasLimit.u256 * transaction.gasPrice.u256
|
2018-09-27 19:09:26 +00:00
|
|
|
|
2019-02-27 05:09:46 +00:00
|
|
|
transaction.gasLimit >= transaction.intrinsicGas and
|
2018-09-27 19:09:26 +00:00
|
|
|
transaction.gasPrice <= (1 shl 34) and
|
|
|
|
limitAndValue <= readOnlyDB.getBalance(sender) and
|
|
|
|
transaction.accountNonce == readOnlyDB.getNonce(sender) and
|
2019-02-27 05:09:46 +00:00
|
|
|
readOnlyDB.getBalance(sender) >= gasCost
|
2018-09-27 19:09:26 +00:00
|
|
|
|
2019-02-27 02:28:02 +00:00
|
|
|
proc setupComputation*(vmState: BaseVMState, tx: Transaction, sender: EthAddress, forkOverride=none(Fork)) : BaseComputation =
|
|
|
|
let fork =
|
|
|
|
if forkOverride.isSome:
|
|
|
|
forkOverride.get
|
|
|
|
else:
|
|
|
|
vmState.blockNumber.toFork
|
|
|
|
|
|
|
|
var recipient: EthAddress
|
|
|
|
var gas = tx.gasLimit - tx.intrinsicGas
|
|
|
|
|
|
|
|
# TODO: refactor message to use byterange
|
|
|
|
# instead of seq[byte]
|
|
|
|
var data, code: seq[byte]
|
|
|
|
|
|
|
|
if tx.isContractCreation:
|
|
|
|
recipient = generateAddress(sender, tx.accountNonce)
|
|
|
|
gas = gas - gasFees[fork][GasTXCreate]
|
|
|
|
data = @[]
|
|
|
|
code = tx.payload
|
|
|
|
else:
|
|
|
|
recipient = tx.to
|
|
|
|
data = tx.payload
|
|
|
|
code = vmState.readOnlyStateDB.getCode(tx.to).toSeq
|
|
|
|
|
|
|
|
let msg = newMessage(
|
|
|
|
gas = gas,
|
|
|
|
gasPrice = tx.gasPrice,
|
|
|
|
to = tx.to,
|
|
|
|
sender = sender,
|
|
|
|
value = tx.value,
|
|
|
|
data = data,
|
|
|
|
code = code,
|
|
|
|
options = newMessageOptions(origin = sender,
|
|
|
|
createAddress = recipient))
|
|
|
|
|
|
|
|
result = newBaseComputation(vmState, vmState.blockNumber, msg, forkOverride)
|
2018-09-27 19:09:26 +00:00
|
|
|
doAssert result.isOriginComputation
|
|
|
|
|
2018-10-01 19:12:14 +00:00
|
|
|
proc execComputation*(computation: var BaseComputation): bool =
|
2019-02-17 00:30:02 +00:00
|
|
|
var snapshot = computation.snapshot()
|
2019-02-18 11:45:18 +00:00
|
|
|
defer: snapshot.dispose()
|
2018-12-31 03:27:02 +00:00
|
|
|
|
2019-02-27 05:09:46 +00:00
|
|
|
computation.vmState.mutateStateDB:
|
2019-02-27 05:42:26 +00:00
|
|
|
db.subBalance(computation.msg.origin, computation.msg.value)
|
2019-02-27 05:09:46 +00:00
|
|
|
db.addBalance(computation.msg.storageAddress, computation.msg.value)
|
|
|
|
|
2018-09-27 19:09:26 +00:00
|
|
|
try:
|
|
|
|
computation.executeOpcodes()
|
2018-10-01 19:12:14 +00:00
|
|
|
computation.vmState.mutateStateDB:
|
2018-09-27 19:09:26 +00:00
|
|
|
for deletedAccount in computation.getAccountsForDeletion:
|
2018-10-01 19:12:14 +00:00
|
|
|
db.deleteAccount deletedAccount
|
2018-09-27 19:09:26 +00:00
|
|
|
result = not computation.isError
|
|
|
|
except ValueError:
|
|
|
|
result = false
|
2019-02-27 05:09:46 +00:00
|
|
|
debug "execComputation() error", msg = getCurrentExceptionMsg()
|
2018-09-27 19:09:26 +00:00
|
|
|
|
2018-12-31 03:27:02 +00:00
|
|
|
if result:
|
2019-02-17 00:30:02 +00:00
|
|
|
snapshot.commit()
|
2019-02-27 14:04:42 +00:00
|
|
|
computation.vmState.addLogs(computation.logEntries)
|
2018-12-31 03:27:02 +00:00
|
|
|
else:
|
2019-02-17 00:30:02 +00:00
|
|
|
snapshot.revert()
|
2019-02-28 04:31:25 +00:00
|
|
|
if computation.tracingEnabled: computation.traceError()
|
2018-12-31 03:27:02 +00:00
|
|
|
|
2019-03-07 07:10:05 +00:00
|
|
|
proc refundGas*(computation: BaseComputation, tx: Transaction, sender: EthAddress): GasInt =
|
|
|
|
let
|
|
|
|
gasRemaining = computation.gasMeter.gasRemaining
|
|
|
|
gasRefunded = computation.getGasRefund()
|
|
|
|
gasUsed = tx.gasLimit - gasRemaining
|
|
|
|
gasRefund = min(gasRefunded, gasUsed div 2)
|
2019-02-23 13:08:59 +00:00
|
|
|
|
2019-03-07 07:10:05 +00:00
|
|
|
computation.vmState.mutateStateDB:
|
|
|
|
db.addBalance(sender, (gasRemaining + gasRefund).u256 * tx.gasPrice.u256)
|
|
|
|
|
|
|
|
result = gasUsed - gasRefund
|
|
|
|
|
2019-03-11 15:32:25 +00:00
|
|
|
proc writeContract*(computation: var BaseComputation): bool =
|
|
|
|
result = true
|
2019-03-12 01:41:51 +00:00
|
|
|
let contractAddress = computation.msg.storageAddress
|
|
|
|
if computation.isSuicided(contractAddress): return
|
|
|
|
|
|
|
|
let codeCost = computation.gasCosts[Create].m_handler(0, 0, computation.output.len)
|
|
|
|
if computation.gasMeter.gasRemaining >= codeCost:
|
|
|
|
computation.gasMeter.consumeGas(codeCost, reason = "Write contract code for CREATE")
|
|
|
|
computation.vmState.mutateStateDB:
|
|
|
|
db.setCode(contractAddress, computation.output.toRange)
|
|
|
|
result = true
|
|
|
|
else:
|
|
|
|
computation.vmState.mutateStateDB:
|
|
|
|
db.setCode(contractAddress, ByteRange())
|
|
|
|
result = false
|
2018-09-29 15:36:42 +00:00
|
|
|
|
2019-02-26 07:04:12 +00:00
|
|
|
#[
|
2018-12-03 10:54:19 +00:00
|
|
|
method executeTransaction(vmState: BaseVMState, transaction: Transaction): (BaseComputation, BlockHeader) {.base.}=
|
2018-01-17 12:57:50 +00:00
|
|
|
# Execute the transaction in the vm
|
2018-04-14 10:40:41 +00:00
|
|
|
# TODO: introduced here: https://github.com/ethereum/py-evm/commit/21c57f2d56ab91bb62723c3f9ebe291d0b132dde
|
|
|
|
# Refactored/Removed here: https://github.com/ethereum/py-evm/commit/cc991bf
|
|
|
|
# Deleted here: https://github.com/ethereum/py-evm/commit/746defb6f8e83cee2c352a0ab8690e1281c4227c
|
2018-01-17 12:57:50 +00:00
|
|
|
raise newException(ValueError, "Must be implemented by subclasses")
|
|
|
|
|
|
|
|
|
2018-12-03 10:54:19 +00:00
|
|
|
method addTransaction*(vmState: BaseVMState, transaction: Transaction, computation: BaseComputation, b: Block): (Block, Table[string, string]) =
|
2018-01-17 12:57:50 +00:00
|
|
|
# Add a transaction to the given block and
|
|
|
|
# return `trieData` to store the transaction data in chaindb in VM layer
|
|
|
|
# Update the bloomFilter, transaction trie and receipt trie roots, bloom_filter,
|
|
|
|
# bloom, and usedGas of the block
|
|
|
|
# transaction: the executed transaction
|
|
|
|
# computation: the Computation object with executed result
|
|
|
|
# block: the Block which the transaction is added in
|
|
|
|
# var receipt = vmState.makeReceipt(transaction, computation)
|
|
|
|
# vmState.add_receipt(receipt)
|
|
|
|
|
|
|
|
# block.transactions.append(transaction)
|
|
|
|
|
|
|
|
# # Get trie roots and changed key-values.
|
|
|
|
# tx_root_hash, tx_kv_nodes = make_trie_root_and_nodes(block.transactions)
|
|
|
|
# receipt_root_hash, receipt_kv_nodes = make_trie_root_and_nodes(self.receipts)
|
|
|
|
|
|
|
|
# trie_data = merge(tx_kv_nodes, receipt_kv_nodes)
|
|
|
|
|
|
|
|
# block.bloom_filter |= receipt.bloom
|
|
|
|
|
|
|
|
# block.header.transaction_root = tx_root_hash
|
|
|
|
# block.header.receipt_root = receipt_root_hash
|
|
|
|
# block.header.bloom = int(block.bloom_filter)
|
|
|
|
# block.header.gas_used = receipt.gas_used
|
|
|
|
|
|
|
|
# return block, trie_data
|
2018-01-31 12:57:05 +00:00
|
|
|
result = (b, initTable[string, string]())
|
2018-01-17 12:57:50 +00:00
|
|
|
|
|
|
|
method applyTransaction*(
|
2018-12-03 10:54:19 +00:00
|
|
|
vmState: BaseVMState,
|
2018-09-27 19:09:26 +00:00
|
|
|
transaction: Transaction,
|
2018-01-17 12:57:50 +00:00
|
|
|
b: Block,
|
2018-01-31 12:57:05 +00:00
|
|
|
isStateless: bool): (BaseComputation, Block, Table[string, string]) =
|
2018-01-17 12:57:50 +00:00
|
|
|
# Apply transaction to the given block
|
|
|
|
# transaction: the transaction need to be applied
|
|
|
|
# b: the block which the transaction applies on
|
|
|
|
# isStateless: if isStateless, call vmState.addTransaction to set block
|
2018-04-06 14:25:01 +00:00
|
|
|
|
2018-01-17 12:57:50 +00:00
|
|
|
if isStateless:
|
|
|
|
var ourBlock = b # deepcopy
|
|
|
|
vmState.blockHeader = b.header
|
|
|
|
var (computation, blockHeader) = vmState.executeTransaction(transaction)
|
|
|
|
|
|
|
|
ourBlock.header = blockHeader
|
2018-01-31 12:57:05 +00:00
|
|
|
var trieData: Table[string, string]
|
2018-01-17 12:57:50 +00:00
|
|
|
(ourBlock, trieData) = vmState.addTransaction(transaction, computation, ourBlock)
|
|
|
|
|
|
|
|
result = (computation, ourBlock, trieData)
|
|
|
|
else:
|
|
|
|
var (computation, blockHeader) = vmState.executeTransaction(transaction)
|
2018-01-31 12:57:05 +00:00
|
|
|
return (computation, nil, initTable[string, string]())
|
2019-02-26 07:04:12 +00:00
|
|
|
]#
|