2021-07-06 13:14:45 +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.
|
|
|
|
|
2023-02-14 20:27:17 +00:00
|
|
|
{.push raises: [].}
|
|
|
|
|
2021-07-06 13:14:45 +00:00
|
|
|
import
|
2022-12-02 04:35:41 +00:00
|
|
|
../../common/common,
|
2021-07-06 13:14:45 +00:00
|
|
|
../../db/accounts_cache,
|
|
|
|
../../transaction/call_evm,
|
2022-04-05 10:22:46 +00:00
|
|
|
../../transaction,
|
2021-07-06 13:14:45 +00:00
|
|
|
../../vm_state,
|
|
|
|
../../vm_types,
|
2023-04-12 12:39:11 +00:00
|
|
|
../../evm/async/operations,
|
2021-07-06 13:14:45 +00:00
|
|
|
../validate,
|
|
|
|
chronicles,
|
2023-04-12 12:39:11 +00:00
|
|
|
chronos,
|
2022-01-10 09:04:06 +00:00
|
|
|
stew/results
|
2021-07-06 13:14:45 +00:00
|
|
|
|
2021-07-14 15:13:27 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Private functions
|
|
|
|
# ------------------------------------------------------------------------------
|
2021-07-06 13:14:45 +00:00
|
|
|
|
2022-12-02 04:35:41 +00:00
|
|
|
proc eip1559BaseFee(header: BlockHeader; fork: EVMFork): UInt256 =
|
2022-01-10 09:04:06 +00:00
|
|
|
## Actually, `baseFee` should be 0 for pre-London headers already. But this
|
|
|
|
## function just plays safe. In particular, the `test_general_state_json.nim`
|
|
|
|
## module modifies this block header `baseFee` field unconditionally :(.
|
|
|
|
if FkLondon <= fork:
|
|
|
|
result = header.baseFee
|
|
|
|
|
2023-04-11 08:28:45 +00:00
|
|
|
proc commitOrRollbackDependingOnGasUsed(vmState: BaseVMState, accTx: SavePoint,
|
|
|
|
header: BlockHeader, tx: Transaction,
|
|
|
|
gasBurned: GasInt, priorityFee: GasInt): Result[GasInt, void] {.raises: [Defect, RlpError].} =
|
2023-04-12 12:39:11 +00:00
|
|
|
# Make sure that the tx does not exceed the maximum cumulative limit as
|
|
|
|
# set in the block header. Again, the eip-1559 reference does not mention
|
|
|
|
# an early stop. It would rather detect differing values for the block
|
|
|
|
# header `gasUsed` and the `vmState.cumulativeGasUsed` at a later stage.
|
2023-04-11 08:28:45 +00:00
|
|
|
if header.gasLimit < vmState.cumulativeGasUsed + gasBurned:
|
2023-04-12 12:39:11 +00:00
|
|
|
vmState.stateDB.rollback(accTx)
|
|
|
|
debug "invalid tx: block header gasLimit reached",
|
2023-04-11 08:28:45 +00:00
|
|
|
maxLimit = header.gasLimit,
|
2023-04-12 12:39:11 +00:00
|
|
|
gasUsed = vmState.cumulativeGasUsed,
|
|
|
|
addition = gasBurned
|
|
|
|
return err()
|
|
|
|
else:
|
|
|
|
# Accept transaction and collect mining fee.
|
|
|
|
vmState.stateDB.commit(accTx)
|
|
|
|
vmState.stateDB.addBalance(vmState.coinbase(), gasBurned.u256 * priorityFee.u256)
|
|
|
|
vmState.cumulativeGasUsed += gasBurned
|
2023-04-11 08:28:45 +00:00
|
|
|
|
|
|
|
# Return remaining gas to the block gas counter so it is
|
|
|
|
# available for the next transaction.
|
|
|
|
vmState.gasPool += tx.gasLimit - gasBurned
|
2023-04-12 12:39:11 +00:00
|
|
|
return ok(gasBurned)
|
|
|
|
|
2023-04-22 14:17:37 +00:00
|
|
|
# For stateless mode with on-demand fetching, we need to do
|
|
|
|
# this, because it's possible for deletion to result in a
|
|
|
|
# branch node with only one child, which then needs to be
|
|
|
|
# transformed into an extension node or leaf node (or
|
|
|
|
# grafted onto one), but we don't actually have that node
|
|
|
|
# yet so we have to fetch it and then retry.
|
|
|
|
proc repeatedlyTryToPersist(vmState: BaseVMState, fork: EVMFork): Future[void] {.async.} =
|
|
|
|
#info("repeatedlyTryToPersist about to get started")
|
|
|
|
var i = 0
|
|
|
|
while i < 100:
|
|
|
|
#info("repeatedlyTryToPersist making an attempt to persist", i)
|
|
|
|
try:
|
|
|
|
await vmState.stateDB.asyncPersist(
|
|
|
|
clearEmptyAccount = fork >= FkSpurious,
|
|
|
|
clearCache = false)
|
|
|
|
return
|
|
|
|
except MissingNodesError as e:
|
|
|
|
#warn("repeatedlyTryToPersist found missing paths", missingPaths=e.paths, missingNodeHashes=e.nodeHashes)
|
|
|
|
await fetchAndPopulateNodes(vmState, e.paths, e.nodeHashes)
|
|
|
|
i += 1
|
|
|
|
error("repeatedlyTryToPersist failed after 100 tries")
|
|
|
|
raise newException(CatchableError, "repeatedlyTryToPersist failed after 100 tries")
|
|
|
|
|
2023-04-12 12:39:11 +00:00
|
|
|
proc asyncProcessTransactionImpl(
|
2022-01-10 09:04:06 +00:00
|
|
|
vmState: BaseVMState; ## Parent accounts environment for transaction
|
|
|
|
tx: Transaction; ## Transaction to validate
|
|
|
|
sender: EthAddress; ## tx.getSender or tx.ecRecover
|
|
|
|
header: BlockHeader; ## Header for the block containing the current tx
|
2023-04-12 12:39:11 +00:00
|
|
|
fork: EVMFork): Future[Result[GasInt,void]]
|
2022-01-10 09:04:06 +00:00
|
|
|
# wildcard exception, wrapped below
|
2023-04-12 12:39:11 +00:00
|
|
|
{.async, gcsafe.} =
|
2022-01-10 09:04:06 +00:00
|
|
|
## Modelled after `https://eips.ethereum.org/EIPS/eip-1559#specification`_
|
|
|
|
## which provides a backward compatible framwork for EIP1559.
|
2021-07-06 13:14:45 +00:00
|
|
|
|
Tracing: Remove some trace messages that occur a lot during sync
Disable some trace messages which appeared a lot in the output and probably
aren't so useful any more, when block processing is functioning well at high
speed.
Turning on the trace level globally is useful to get a feel for what's
happening, but only if each category is kept to a reasonable amount.
As well as overwhelming the output so that it's hard to see general activity,
some of these messages happen so much they severely slow down processing. Ones
called every time an EVM opcode uses some gas are particularly extreme.
These messages have all been chosen as things which are probably not useful any
more (the relevant functionality has been debugged and is tested plenty).
These have been commented out rather than removed. It may be that turning
trace topics on/off, or other selection, is a better longer term solution, but
that will require better command line options and good defaults for sure.
(I think higher levels `tracev` and `tracevv` levels (extra verbose) would be
more useful for this sort of deep tracing on request.)
For now, enabling `--log-level:TRACE` on the command line is quite useful as
long as we keep each category reasonable, and this patch tries to keep that
balance.
- Don't show "has transactions" on virtually every block imported.
- Don't show "Sender" and "txHash" lines on every transaction processed.
- Don't show "GAS CONSUMPTION" on every opcode executed", this is way too much.
- Don't show "GAS RETURNED" and "GAS REFUND" on each contract call.
- Don't show "op: Stop" on every Stop opcode, which means every transaction.
- Don't show "Insufficient funds" whenever a contract can't call another.
- Don't show "ECRecover", "SHA256 precompile", "RIPEMD160", "Identity"
or even "Call precompile" every time a precompile is called. These are
very well tested now.
- Don't show "executeOpcodes error" whenever a contract returns an error.
(This is changed to `trace` too, it's a normal event that is well tested.)
Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-07-22 13:35:41 +00:00
|
|
|
#trace "Sender", sender
|
2022-01-10 09:04:06 +00:00
|
|
|
#trace "txHash", rlpHash = ty.rlpHash
|
2023-04-11 08:28:45 +00:00
|
|
|
|
2022-01-10 09:04:06 +00:00
|
|
|
let
|
|
|
|
roDB = vmState.readOnlyStateDB
|
|
|
|
baseFee256 = header.eip1559BaseFee(fork)
|
2022-04-05 10:22:46 +00:00
|
|
|
baseFee = baseFee256.truncate(GasInt)
|
|
|
|
tx = eip1559TxNormalization(tx, baseFee, fork)
|
2021-07-06 13:14:45 +00:00
|
|
|
priorityFee = min(tx.maxPriorityFee, tx.maxFee - baseFee)
|
|
|
|
|
2022-01-10 09:04:06 +00:00
|
|
|
# Return failure unless explicitely set `ok()`
|
2023-04-12 12:39:11 +00:00
|
|
|
var res: Result[GasInt,void] = err()
|
|
|
|
|
|
|
|
await ifNecessaryGetAccounts(vmState, @[sender, vmState.coinbase()])
|
|
|
|
if tx.to.isSome:
|
|
|
|
await ifNecessaryGetCode(vmState, tx.to.get)
|
2022-01-10 09:04:06 +00:00
|
|
|
|
2023-04-11 08:28:45 +00:00
|
|
|
# buy gas, then the gas goes into gasMeter
|
|
|
|
if vmState.gasPool < tx.gasLimit:
|
|
|
|
debug "gas limit reached",
|
|
|
|
gasLimit = vmState.gasPool,
|
|
|
|
gasNeeded = tx.gasLimit
|
|
|
|
return
|
|
|
|
vmState.gasPool -= tx.gasLimit
|
|
|
|
|
2022-01-10 09:04:06 +00:00
|
|
|
# Actually, the eip-1559 reference does not mention an early exit.
|
|
|
|
#
|
|
|
|
# Even though database was not changed yet but, a `persist()` directive
|
|
|
|
# before leaving is crucial for some unit tests that us a direct/deep call
|
|
|
|
# of the `processTransaction()` function. So there is no `return err()`
|
|
|
|
# statement, here.
|
|
|
|
if roDB.validateTransaction(tx, sender, header.gasLimit, baseFee256, fork):
|
|
|
|
|
|
|
|
# Execute the transaction.
|
|
|
|
let
|
|
|
|
accTx = vmState.stateDB.beginSavepoint
|
|
|
|
gasBurned = tx.txCallEvm(sender, vmState, fork)
|
|
|
|
|
2023-04-11 08:28:45 +00:00
|
|
|
res = commitOrRollbackDependingOnGasUsed(vmState, accTx, header, tx, gasBurned, priorityFee)
|
2021-07-06 13:14:45 +00:00
|
|
|
|
|
|
|
if vmState.generateWitness:
|
2021-10-28 09:42:39 +00:00
|
|
|
vmState.stateDB.collectWitnessData()
|
2023-04-22 14:17:37 +00:00
|
|
|
await repeatedlyTryToPersist(vmState, fork)
|
2021-07-06 13:14:45 +00:00
|
|
|
|
2023-04-12 12:39:11 +00:00
|
|
|
return res
|
|
|
|
|
2021-07-14 15:13:27 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Public functions
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
2023-04-12 12:39:11 +00:00
|
|
|
proc asyncProcessTransaction*(
|
|
|
|
vmState: BaseVMState; ## Parent accounts environment for transaction
|
|
|
|
tx: Transaction; ## Transaction to validate
|
|
|
|
sender: EthAddress; ## tx.getSender or tx.ecRecover
|
|
|
|
header: BlockHeader; ## Header for the block containing the current tx
|
|
|
|
fork: EVMFork): Future[Result[GasInt,void]]
|
|
|
|
{.async, gcsafe.} =
|
|
|
|
## Process the transaction, write the results to accounts db. The function
|
|
|
|
## returns the amount of gas burned if executed.
|
|
|
|
return await vmState.asyncProcessTransactionImpl(tx, sender, header, fork)
|
|
|
|
|
|
|
|
# FIXME-duplicatedForAsync
|
|
|
|
proc asyncProcessTransaction*(
|
|
|
|
vmState: BaseVMState; ## Parent accounts environment for transaction
|
|
|
|
tx: Transaction; ## Transaction to validate
|
|
|
|
sender: EthAddress; ## tx.getSender or tx.ecRecover
|
|
|
|
header: BlockHeader): Future[Result[GasInt,void]]
|
|
|
|
{.async, gcsafe.} =
|
|
|
|
## Variant of `asyncProcessTransaction()` with `*fork* derived
|
|
|
|
## from the `vmState` argument.
|
|
|
|
let fork = vmState.com.toEVMFork(header.forkDeterminationInfoForHeader)
|
|
|
|
return await vmState.asyncProcessTransaction(tx, sender, header, fork)
|
|
|
|
|
2022-01-10 09:04:06 +00:00
|
|
|
proc processTransaction*(
|
|
|
|
vmState: BaseVMState; ## Parent accounts environment for transaction
|
|
|
|
tx: Transaction; ## Transaction to validate
|
|
|
|
sender: EthAddress; ## tx.getSender or tx.ecRecover
|
|
|
|
header: BlockHeader; ## Header for the block containing the current tx
|
2022-12-02 04:35:41 +00:00
|
|
|
fork: EVMFork): Result[GasInt,void]
|
2023-01-30 22:10:23 +00:00
|
|
|
{.gcsafe, raises: [CatchableError].} =
|
2023-04-12 12:39:11 +00:00
|
|
|
return waitFor(vmState.asyncProcessTransaction(tx, sender, header, fork))
|
2022-01-10 09:04:06 +00:00
|
|
|
|
|
|
|
proc processTransaction*(
|
|
|
|
vmState: BaseVMState; ## Parent accounts environment for transaction
|
|
|
|
tx: Transaction; ## Transaction to validate
|
|
|
|
sender: EthAddress; ## tx.getSender or tx.ecRecover
|
|
|
|
header: BlockHeader): Result[GasInt,void]
|
2023-01-30 22:10:23 +00:00
|
|
|
{.gcsafe, raises: [CatchableError].} =
|
2023-04-12 12:39:11 +00:00
|
|
|
return waitFor(vmState.asyncProcessTransaction(tx, sender, header))
|
2022-01-10 09:04:06 +00:00
|
|
|
|
2021-07-14 15:13:27 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
2021-07-06 13:14:45 +00:00
|
|
|
# End
|
2021-07-14 15:13:27 +00:00
|
|
|
# ------------------------------------------------------------------------------
|