mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-01-30 05:55:30 +00:00
db8c5b90bd
* Cleanup unneeded stateless and block witness code. Keeping MultiKeys which is used in the eth_getProofsByBlockNumber RPC endpoint which is needed for the Fluffy state network bridge. * Rename generateWitness flag to collectWitnessData to better describe what the flag does. We only collect the keys of the touched accounts and storage slots but no block witness generation is supported for now. * Move remaining stateless code into nimbus directory. * Add vmstate parameter to ChainRef to fix test. * Exclude *.in from check copyright year --------- Co-authored-by: jangko <jangko128@gmail.com>
181 lines
6.6 KiB
Nim
181 lines
6.6 KiB
Nim
# Nimbus
|
|
# Copyright (c) 2018-2024 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.
|
|
|
|
{.push raises: [].}
|
|
|
|
import
|
|
std/strutils,
|
|
results,
|
|
../../common/common,
|
|
../../db/ledger,
|
|
../../transaction/call_evm,
|
|
../../transaction/call_common,
|
|
../../transaction,
|
|
../../vm_state,
|
|
../../vm_types,
|
|
../../constants,
|
|
../validate
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Private functions
|
|
# ------------------------------------------------------------------------------
|
|
|
|
proc eip1559BaseFee(header: BlockHeader; fork: EVMFork): UInt256 =
|
|
## 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
|
|
|
|
proc commitOrRollbackDependingOnGasUsed(
|
|
vmState: BaseVMState;
|
|
accTx: LedgerSpRef;
|
|
header: BlockHeader;
|
|
tx: Transaction;
|
|
gasBurned: GasInt;
|
|
priorityFee: GasInt;
|
|
): Result[GasInt, string] =
|
|
# 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.
|
|
if header.gasLimit < vmState.cumulativeGasUsed + gasBurned:
|
|
try:
|
|
vmState.stateDB.rollback(accTx)
|
|
return err("invalid tx: block header gasLimit reached. gasLimit=$1, gasUsed=$2, addition=$3" % [
|
|
$header.gasLimit, $vmState.cumulativeGasUsed, $gasBurned])
|
|
except ValueError as ex:
|
|
return err(ex.msg)
|
|
else:
|
|
# Accept transaction and collect mining fee.
|
|
vmState.stateDB.commit(accTx)
|
|
vmState.stateDB.addBalance(vmState.coinbase(), gasBurned.u256 * priorityFee.u256)
|
|
vmState.cumulativeGasUsed += gasBurned
|
|
|
|
# Return remaining gas to the block gas counter so it is
|
|
# available for the next transaction.
|
|
vmState.gasPool += tx.gasLimit - gasBurned
|
|
return ok(gasBurned)
|
|
|
|
proc processTransactionImpl(
|
|
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;
|
|
): Result[GasInt, string] =
|
|
## Modelled after `https://eips.ethereum.org/EIPS/eip-1559#specification`_
|
|
## which provides a backward compatible framwork for EIP1559.
|
|
|
|
let
|
|
roDB = vmState.readOnlyStateDB
|
|
baseFee256 = header.eip1559BaseFee(fork)
|
|
baseFee = baseFee256.truncate(GasInt)
|
|
tx = eip1559TxNormalization(tx, baseFee)
|
|
priorityFee = min(tx.maxPriorityFee, tx.maxFee - baseFee)
|
|
excessBlobGas = header.excessBlobGas.get(0'u64)
|
|
|
|
# Return failure unless explicitely set `ok()`
|
|
var res: Result[GasInt, string] = err("")
|
|
|
|
# buy gas, then the gas goes into gasMeter
|
|
if vmState.gasPool < tx.gasLimit:
|
|
return err("gas limit reached. gasLimit=" & $vmState.gasPool &
|
|
", gasNeeded=" & $tx.gasLimit)
|
|
|
|
vmState.gasPool -= tx.gasLimit
|
|
|
|
# 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.
|
|
let txRes = roDB.validateTransaction(tx, sender, header.gasLimit, baseFee256, excessBlobGas, fork)
|
|
if txRes.isOk:
|
|
|
|
# EIP-1153
|
|
vmState.stateDB.clearTransientStorage()
|
|
|
|
# Execute the transaction.
|
|
vmState.captureTxStart(tx.gasLimit)
|
|
let
|
|
accTx = vmState.stateDB.beginSavepoint
|
|
gasBurned = tx.txCallEvm(sender, vmState, fork)
|
|
vmState.captureTxEnd(tx.gasLimit - gasBurned)
|
|
|
|
res = commitOrRollbackDependingOnGasUsed(vmState, accTx, header, tx, gasBurned, priorityFee)
|
|
else:
|
|
res = err(txRes.error)
|
|
|
|
if vmState.collectWitnessData:
|
|
vmState.stateDB.collectWitnessData()
|
|
vmState.stateDB.persist(clearEmptyAccount = fork >= FkSpurious)
|
|
|
|
return res
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public functions
|
|
# ------------------------------------------------------------------------------
|
|
|
|
proc processBeaconBlockRoot*(vmState: BaseVMState, beaconRoot: Hash256):
|
|
Result[void, string] =
|
|
## processBeaconBlockRoot applies the EIP-4788 system call to the
|
|
## beacon block root contract. This method is exported to be used in tests.
|
|
## If EIP-4788 is enabled, we need to invoke the beaconroot storage
|
|
## contract with the new root.
|
|
let
|
|
statedb = vmState.stateDB
|
|
call = CallParams(
|
|
vmState : vmState,
|
|
sender : SYSTEM_ADDRESS,
|
|
gasLimit : 30_000_000.GasInt,
|
|
gasPrice : 0.GasInt,
|
|
to : BEACON_ROOTS_ADDRESS,
|
|
input : @(beaconRoot.data),
|
|
|
|
# It's a systemCall, no need for other knicks knacks
|
|
sysCall : true,
|
|
noAccessList: true,
|
|
noIntrinsic : true,
|
|
noGasCharge : true,
|
|
noRefund : true,
|
|
)
|
|
|
|
# runComputation a.k.a syscall/evm.call
|
|
let res = call.runComputation()
|
|
if res.isError:
|
|
return err("processBeaconBlockRoot: " & res.error)
|
|
|
|
statedb.persist(clearEmptyAccount = true)
|
|
ok()
|
|
|
|
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
|
|
fork: EVMFork;
|
|
): Result[GasInt,string] =
|
|
vmState.processTransactionImpl(tx, sender, header, fork)
|
|
|
|
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,string] =
|
|
let fork = vmState.com.toEVMFork(header.forkDeterminationInfo)
|
|
vmState.processTransaction(tx, sender, header, fork)
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# End
|
|
# ------------------------------------------------------------------------------
|