2019-04-18 01:27:55 +00:00
import options , sets ,
2020-04-20 18:12:44 +00:00
eth / [ common , bloom , trie / db ] , chronicles , nimcrypto ,
2020-05-30 03:14:59 +00:00
.. / db / [ db_chain , accounts_cache ] ,
2018-12-14 07:32:45 +00:00
.. / utils , .. / constants , .. / transaction ,
.. / vm_state , .. / vm_types , .. / vm_state_transactions ,
2021-03-31 13:29:33 +00:00
.. / vm_computation , .. / vm_message , .. / vm_precompiles ,
2021-04-01 11:53:22 +00:00
.. / vm_types2 ,
2020-04-12 10:33:17 +00:00
. / dao , .. / config
2018-12-04 11:42:55 +00:00
2021-04-07 14:13:28 +00:00
proc validateTransaction * ( vmState : BaseVMState , tx : Transaction ,
sender : EthAddress , fork : Fork ) : bool =
let balance = vmState . readOnlyStateDB . getBalance ( sender )
let nonce = vmState . readOnlyStateDB . getNonce ( sender )
if vmState . cumulativeGasUsed + tx . gasLimit > vmState . blockHeader . gasLimit :
debug " invalid tx: block header gasLimit reached " ,
maxLimit = vmState . blockHeader . gasLimit ,
gasUsed = vmState . cumulativeGasUsed ,
addition = tx . gasLimit
return
let totalCost = tx . gasLimit . u256 * tx . gasPrice . u256 + tx . value
if totalCost > balance :
debug " invalid tx: not enough cash " ,
available = balance ,
require = totalCost
return
if tx . gasLimit < tx . intrinsicGas ( fork ) :
debug " invalid tx: not enough gas to perform calculation " ,
available = tx . gasLimit ,
require = tx . intrinsicGas ( fork )
return
if tx . accountNonce ! = nonce :
debug " invalid tx: account nonce mismatch " ,
txNonce = tx . accountnonce ,
accountNonce = nonce
return
result = true
2019-04-23 12:50:45 +00:00
proc processTransaction * ( tx : Transaction , sender : EthAddress , vmState : BaseVMState , fork : Fork ) : GasInt =
2018-12-04 11:42:55 +00:00
## Process the transaction, write the results to db.
## Returns amount of ETH to be rewarded to miner
2018-12-06 23:16:34 +00:00
trace " Sender " , sender
2019-02-27 06:11:31 +00:00
trace " txHash " , rlpHash = tx . rlpHash
2020-12-11 10:51:17 +00:00
# EIP2929
if fork > = FkBerlin :
vmState . mutateStateDB :
db . accessList ( sender )
if not tx . isContractCreation :
#If it's a create-tx, the destination will be added inside evm.create
db . accessList ( tx . getRecipient )
for c in activePrecompiles ( ) :
db . accessList ( c )
2020-01-24 12:52:55 +00:00
if validateTransaction ( vmState , tx , sender , fork ) :
2020-01-20 11:59:46 +00:00
var c = setupComputation ( vmState , tx , sender , fork )
2019-04-18 00:56:57 +00:00
vmState . mutateStateDB :
2020-01-30 15:22:38 +00:00
db . subBalance ( sender , tx . gasLimit . u256 * tx . gasPrice . u256 )
2020-01-10 11:18:36 +00:00
execComputation ( c )
2020-01-30 15:54:29 +00:00
result = tx . gasLimit
2020-01-10 11:18:36 +00:00
if not c . shouldBurnGas :
2020-01-30 15:54:29 +00:00
c . refundGas ( tx , sender )
result - = c . gasMeter . gasRemaining
2019-04-18 00:56:57 +00:00
2020-01-30 15:54:29 +00:00
vmState . cumulativeGasUsed + = result
2019-04-18 00:56:57 +00:00
2020-06-22 00:48:23 +00:00
let miner = vmState . coinbase ( )
2020-06-19 13:24:09 +00:00
2019-04-18 00:56:57 +00:00
vmState . mutateStateDB :
2020-01-10 11:18:36 +00:00
# miner fee
2020-01-30 15:54:29 +00:00
let txFee = result . u256 * tx . gasPrice . u256
2020-06-19 13:24:09 +00:00
db . addBalance ( miner , txFee )
2020-01-10 11:18:36 +00:00
for deletedAccount in vmState . suicides :
db . deleteAccount deletedAccount
2018-12-12 00:32:33 +00:00
2020-01-09 06:51:54 +00:00
if fork > = FkSpurious :
2020-06-19 13:24:09 +00:00
vmState . touchedAccounts . incl ( miner )
2020-01-09 06:51:54 +00:00
# EIP158/161 state clearing
for account in vmState . touchedAccounts :
if db . accountExists ( account ) and db . isEmptyAccount ( account ) :
debug " state clearing " , account
db . deleteAccount ( account )
2019-04-18 01:27:55 +00:00
2020-06-06 03:05:11 +00:00
if vmState . generateWitness :
vmState . accountDb . collectWitnessData ( )
2020-06-15 06:28:08 +00:00
vmState . accountDb . persist ( clearCache = false )
2019-03-11 15:32:25 +00:00
2018-12-14 07:32:45 +00:00
type
# TODO: these types need to be removed
2019-02-05 19:15:50 +00:00
# once eth/bloom and eth/common sync'ed
Bloom = common . BloomFilter
LogsBloom = bloom . BloomFilter
2018-12-14 07:32:45 +00:00
# TODO: move these three receipt procs below somewhere else more appropriate
func logsBloom ( logs : openArray [ Log ] ) : LogsBloom =
for log in logs :
result . incl log . address
for topic in log . topics :
result . incl topic
func createBloom * ( receipts : openArray [ Receipt ] ) : Bloom =
var bloom : LogsBloom
for receipt in receipts :
bloom . value = bloom . value or logsBloom ( receipt . logs ) . value
result = bloom . value . toByteArrayBE
2019-09-07 08:38:44 +00:00
proc makeReceipt * ( vmState : BaseVMState , fork = FkFrontier ) : Receipt =
2018-12-14 07:32:45 +00:00
if fork < FkByzantium :
2018-12-31 03:27:02 +00:00
result . stateRootOrStatus = hashOrStatus ( vmState . accountDb . rootHash )
2018-12-14 07:32:45 +00:00
else :
2019-04-22 09:37:41 +00:00
result . stateRootOrStatus = hashOrStatus ( vmState . status )
2018-12-14 07:32:45 +00:00
2019-04-18 00:56:57 +00:00
result . cumulativeGasUsed = vmState . cumulativeGasUsed
2018-12-14 07:32:45 +00:00
result . logs = vmState . getAndClearLogEntries ( )
result . bloom = logsBloom ( result . logs ) . value . toByteArrayBE
2018-12-12 00:32:33 +00:00
2019-05-14 04:31:30 +00:00
func eth ( n : int ) : Uint256 {. compileTime . } =
n . u256 * pow ( 10 . u256 , 18 )
const
eth5 = 5 . eth
eth3 = 3 . eth
eth2 = 2 . eth
2019-09-07 08:38:44 +00:00
blockRewards * : array [ Fork , Uint256 ] = [
2019-05-14 04:31:30 +00:00
eth5 , # FkFrontier
eth5 , # FkHomestead
eth5 , # FkTangerine
eth5 , # FkSpurious
eth3 , # FkByzantium
2019-11-11 05:20:21 +00:00
eth2 , # FkConstantinople
2020-03-04 09:32:30 +00:00
eth2 , # FkPetersburg
2020-11-19 04:59:53 +00:00
eth2 , # FkIstanbul
eth2 # FkBerlin
2019-05-14 04:31:30 +00:00
]
2020-06-19 13:24:09 +00:00
proc calculateReward ( fork : Fork , header : BlockHeader , body : BlockBody , vmState : BaseVMState ) =
# PoA consensus engine have no reward for miner
if vmState . consensusEnginePoA : return
let blockReward = blockRewards [ fork ]
var mainReward = blockReward
for uncle in body . uncles :
var uncleReward = uncle . blockNumber . u256 + 8 . u256
uncleReward - = header . blockNumber . u256
uncleReward = uncleReward * blockReward
uncleReward = uncleReward div 8 . u256
vmState . mutateStateDB :
db . addBalance ( uncle . coinbase , uncleReward )
mainReward + = blockReward div 32 . u256
vmState . mutateStateDB :
db . addBalance ( header . coinbase , mainReward )
2019-03-20 03:22:37 +00:00
proc processBlock * ( chainDB : BaseChainDB , header : BlockHeader , body : BlockBody , vmState : BaseVMState ) : ValidationResult =
2019-11-28 10:02:11 +00:00
var dbTx = chainDB . db . beginTransaction ( )
defer : dbTx . dispose ( )
2019-04-06 14:50:43 +00:00
if chainDB . config . daoForkSupport and header . blockNumber = = chainDB . config . daoForkBlock :
vmState . mutateStateDB :
db . applyDAOHardFork ( )
2018-12-25 05:58:55 +00:00
if body . transactions . calcTxRoot ! = header . txRoot :
debug " Mismatched txRoot " , blockNumber = header . blockNumber
2018-12-12 00:32:33 +00:00
return ValidationResult . Error
2020-04-12 10:33:17 +00:00
let fork = chainDB . config . toFork ( vmState . blockNumber )
2019-04-22 09:52:18 +00:00
2018-12-25 05:58:55 +00:00
if header . txRoot ! = BLANK_ROOT_HASH :
if body . transactions . len = = 0 :
debug " No transactions in body " , blockNumber = header . blockNumber
2018-12-12 00:32:33 +00:00
return ValidationResult . Error
else :
2018-12-25 05:58:55 +00:00
trace " Has transactions " , blockNumber = header . blockNumber , blockHash = header . blockHash
2018-12-12 00:32:33 +00:00
2018-12-25 12:10:04 +00:00
vmState . receipts = newSeq [ Receipt ] ( body . transactions . len )
2019-04-18 00:56:57 +00:00
vmState . cumulativeGasUsed = 0
2018-12-25 05:58:55 +00:00
for txIndex , tx in body . transactions :
2019-04-18 00:56:57 +00:00
var sender : EthAddress
if tx . getSender ( sender ) :
2020-04-15 11:09:49 +00:00
discard processTransaction ( tx , sender , vmState , fork )
2018-12-12 00:32:33 +00:00
else :
2019-04-18 00:56:57 +00:00
debug " Could not get sender " , txIndex , tx
return ValidationResult . Error
2019-04-22 09:52:18 +00:00
vmState . receipts [ txIndex ] = makeReceipt ( vmState , fork )
2018-12-25 05:58:55 +00:00
if header . ommersHash ! = EMPTY_UNCLE_HASH :
let h = chainDB . persistUncles ( body . uncles )
if h ! = header . ommersHash :
2018-12-12 00:32:33 +00:00
debug " Uncle hash mismatch "
return ValidationResult . Error
2020-06-19 13:24:09 +00:00
calculateReward ( fork , header , body , vmState )
2018-12-12 00:32:33 +00:00
# Reward beneficiary
2019-03-07 08:04:32 +00:00
vmState . mutateStateDB :
2020-06-06 03:05:11 +00:00
if vmState . generateWitness :
db . collectWitnessData ( )
2020-06-15 06:28:08 +00:00
db . persist ( ClearCache in vmState . flags )
2018-12-12 00:32:33 +00:00
2019-03-07 08:04:32 +00:00
let stateDb = vmState . accountDb
2018-12-25 05:58:55 +00:00
if header . stateRoot ! = stateDb . rootHash :
2020-06-10 05:54:15 +00:00
when defined ( geth ) :
error " Wrong state root in block " , blockNumber = header . blockNumber , expected = header . stateRoot , actual = stateDb . rootHash
else :
error " Wrong state root in block " , blockNumber = header . blockNumber , expected = header . stateRoot , actual = stateDb . rootHash , arrivedFrom = chainDB . getCanonicalHead ( ) . stateRoot
2018-12-12 00:32:33 +00:00
# this one is a show stopper until we are confident in our VM's
# compatibility with the main chain
2018-12-25 10:31:51 +00:00
return ValidationResult . Error
2018-12-12 00:32:33 +00:00
2018-12-25 05:58:55 +00:00
let bloom = createBloom ( vmState . receipts )
if header . bloom ! = bloom :
debug " wrong bloom in block " , blockNumber = header . blockNumber
return ValidationResult . Error
2018-12-12 00:32:33 +00:00
2018-12-25 05:58:55 +00:00
let receiptRoot = calcReceiptRoot ( vmState . receipts )
if header . receiptRoot ! = receiptRoot :
debug " wrong receiptRoot in block " , blockNumber = header . blockNumber , actual = receiptRoot , expected = header . receiptRoot
return ValidationResult . Error
2019-11-28 10:02:11 +00:00
# `applyDeletes = false`
2019-12-06 05:27:29 +00:00
# If the trie pruning activated, each of the block will have its own state trie keep intact,
# rather than destroyed by trie pruning. But the current block will still get a pruned trie.
# If trie pruning deactivated, `applyDeletes` have no effects.
2019-11-28 10:02:11 +00:00
dbTx . commit ( applyDeletes = false )
2021-04-07 14:13:28 +00:00
#[
method executeTransaction ( vmState : BaseVMState , transaction : Transaction ) : ( Computation , BlockHeader ) {. base . } =
# Execute the transaction in the vm
# 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
raise newException ( ValueError , " Must be implemented by subclasses " )
method addTransaction * ( vmState : BaseVMState , transaction : Transaction , c : Computation , b : Block ) : ( Block , Table [ string , string ] ) =
# 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
result = ( b , initTable [ string , string ] ( ) )
method applyTransaction * (
vmState : BaseVMState ,
transaction : Transaction ,
b : Block ,
isStateless : bool ) : ( Computation , Block , Table [ string , string ] ) =
# 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
if isStateless :
var ourBlock = b # deepcopy
vmState . blockHeader = b . header
var ( computation , blockHeader ) = vmState . executeTransaction ( transaction )
ourBlock . header = blockHeader
var trieData : Table [ string , string ]
( ourBlock , trieData ) = vmState . addTransaction ( transaction , computation , ourBlock )
result = ( computation , ourBlock , trieData )
else :
var ( computation , blockHeader ) = vmState . executeTransaction ( transaction )
return ( computation , nil , initTable [ string , string ] ( ) )
] #