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 ,
2021-05-18 11:18:15 +00:00
.. / vm_state , .. / vm_types ,
2021-06-01 10:54:13 +00:00
. / dao , . / validate , .. / config , .. / forks ,
2021-05-03 16:50:47 +00:00
.. / transaction / call_evm
2018-12-04 11:42:55 +00:00
2021-04-07 14:13:28 +00:00
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-01-24 12:52:55 +00:00
if validateTransaction ( vmState , tx , sender , fork ) :
2021-05-03 18:14:54 +00:00
result = txCallEvm ( tx , sender , vmState , fork )
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
2021-05-11 09:05:58 +00:00
for deletedAccount in vmState . selfDestructs :
2020-01-10 11:18:36 +00:00
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
2021-05-15 06:37:40 +00:00
for rec in receipts :
bloom . value = bloom . value or logsBloom ( rec . logs ) . value
2018-12-14 07:32:45 +00:00
result = bloom . value . toByteArrayBE
2021-05-15 06:37:40 +00:00
proc makeReceipt * ( vmState : BaseVMState , fork : Fork , txType : TxType ) : Receipt =
2021-06-27 04:19:43 +00:00
var rec : Receipt
2018-12-14 07:32:45 +00:00
if fork < FkByzantium :
2021-06-27 04:19:43 +00:00
rec . isHash = true
rec . hash = vmState . accountDb . rootHash
2018-12-14 07:32:45 +00:00
else :
2021-06-27 04:19:43 +00:00
rec . isHash = false
rec . status = vmState . status
2018-12-14 07:32:45 +00:00
2021-06-27 04:19:43 +00:00
rec . receiptType = txType
2021-05-15 06:37:40 +00:00
rec . cumulativeGasUsed = vmState . cumulativeGasUsed
rec . logs = vmState . getAndClearLogEntries ( )
rec . bloom = logsBloom ( rec . logs ) . value . toByteArrayBE
2021-06-27 04:19:43 +00:00
rec
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
2021-05-15 06:37:40 +00:00
vmState . receipts [ txIndex ] = makeReceipt ( vmState , fork , tx . txType )
2019-04-22 09:52:18 +00:00
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 :
Transaction: Use same log level for all block rejection causes
Block validation failure isn't an error, it's correct rejection of a bad block
from the network. All conditions that lead to block rejection return a simple
boolean.
When a block is rejected, most reasons log at `debug` level. Only `stateRoot`
mismatch shouts a loud, highlighted, multi-line error message with big red
`error` alert.
Historically this was to assist EVM development, because it was more likely to
be a Nimbus EVM bug than a real bad block. But now the EVM is in good shape,
has a large and thorough testsuite, and `stateRoot` mismatch is more likely to
be a real bad block that should be rejected with less fuss.
If there's a genuine EVM bug, we'll still get an alert: Consensus failure will
quickly become obvious, and the block where it happens is easily fetched.
So a big, loud error is no longer useful, and it became a problem during tests.
Recently a few hundred tests were added that trigger it, and now successful
test output is filled with attention-grabbing errors which aren't really errors
or particularly useful.
Since it's not really an error, the original motivation is now backwards, and
other reasons warn at `debug` level, make this like the others.
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-05-26 22:17:16 +00:00
debug " wrong state root in block " , blockNumber = header . blockNumber , expected = header . stateRoot , actual = stateDb . rootHash , arrivedFrom = chainDB . getCanonicalHead ( ) . stateRoot
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 ] ( ) )
] #