nimbus-eth1/nimbus/p2p/executor.nim

272 lines
9.7 KiB
Nim
Raw Normal View History

import options, sets,
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,
./dao, ./validate, ../config, ../forks,
../transaction/call_evm
2018-12-04 11:42:55 +00:00
proc eip1559TxNormalization(tx: Transaction): Transaction =
result = tx
if tx.txType < TxEip1559:
result.maxPriorityFee = tx.gasPrice
result.maxFee = tx.gasPrice
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
trace "Sender", sender
2019-02-27 06:11:31 +00:00
trace "txHash", rlpHash = tx.rlpHash
var tx = eip1559TxNormalization(tx)
var priorityFee: GasInt
if fork >= FkLondon:
# priority fee is capped because the base fee is filled first
let baseFee = vmState.blockHeader.baseFee.truncate(GasInt)
priorityFee = min(tx.maxPriorityFee, tx.maxFee - baseFee)
# signer pays both the priority fee and the base fee
# tx.gasPrice now is the effective gasPrice
tx.gasPrice = priorityFee + baseFee
let miner = vmState.coinbase()
if validateTransaction(vmState, tx, sender, fork):
result = txCallEvm(tx, sender, vmState, fork)
2019-04-18 00:56:57 +00:00
# miner fee
if fork >= FkLondon:
# miner only receives the priority fee;
# note that the base fee is not given to anyone (it is burned)
let txFee = result.u256 * priorityFee.u256
vmState.accountDb.addBalance(miner, txFee)
else:
let txFee = result.u256 * tx.gasPrice.u256
vmState.accountDb.addBalance(miner, txFee)
2019-04-18 00:56:57 +00:00
vmState.cumulativeGasUsed += result
2019-04-18 00:56:57 +00:00
vmState.mutateStateDB:
for deletedAccount in vmState.selfDestructs:
2020-01-10 11:18:36 +00:00
db.deleteAccount deletedAccount
2018-12-12 00:32:33 +00:00
if fork >= FkSpurious:
vmState.touchedAccounts.incl(miner)
# EIP158/161 state clearing
for account in vmState.touchedAccounts:
if db.accountExists(account) and db.isEmptyAccount(account):
debug "state clearing", account
db.deleteAccount(account)
if vmState.generateWitness:
vmState.accountDb.collectWitnessData()
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 rec in receipts:
bloom.value = bloom.value or logsBloom(rec.logs).value
2018-12-14 07:32:45 +00:00
result = bloom.value.toByteArrayBE
proc makeReceipt*(vmState: BaseVMState, fork: Fork, txType: TxType): Receipt =
var rec: Receipt
2018-12-14 07:32:45 +00:00
if fork < FkByzantium:
rec.isHash = true
rec.hash = vmState.accountDb.rootHash
2018-12-14 07:32:45 +00:00
else:
rec.isHash = false
rec.status = vmState.status
2018-12-14 07:32:45 +00:00
rec.receiptType = txType
rec.cumulativeGasUsed = vmState.cumulativeGasUsed
rec.logs = vmState.getAndClearLogEntries()
rec.bloom = logsBloom(rec.logs).value.toByteArrayBE
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
eth2 # FkLondon
2019-05-14 04:31:30 +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)
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
vmState.receipts[txIndex] = makeReceipt(vmState, fork, tx.txType)
if vmState.cumulativeGasUsed != header.gasUsed:
debug "gasUsed neq cumulativeGasUsed",
gasUsed=header.gasUsed,
cumulativeGasUsed=vmState.cumulativeGasUsed
return ValidationResult.Error
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
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:
if vmState.generateWitness:
db.collectWitnessData()
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:
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)
#[
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]())
]#