nimbus-eth1/nimbus/core/tx_pool/tx_chain.nim

268 lines
8.9 KiB
Nim

# Nimbus
# Copyright (c) 2022-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: [].}
## Transaction Pool Block Chain Packer Environment
## ===============================================
##
import
results,
../../common/common,
../../constants,
../../db/ledger,
../../utils/utils,
../../evm/state,
../../evm/types,
../pow/header,
../eip4844,
../casper,
eth/eip1559
type
TxChainRef* = ref object ##\
## State cache of the transaction environment for creating a new\
## block. This state is typically synchrionised with the canonical\
## block chain head when updated.
com: CommonRef ## Block chain config
roAcc: ReadOnlyStateDB ## Accounts cache fixed on current sync header
prepHeader: BlockHeader ## Prepared Header from Consensus Engine
vmState: BaseVMState ## current tx/packer environment
receiptsRoot: Hash256
logsBloom: BloomFilter
txRoot: Hash256 ## `rootHash` after packing
stateRoot: Hash256 ## `stateRoot` after packing
# ------------------------------------------------------------------------------
# Private functions
# ------------------------------------------------------------------------------
proc baseFeeGet(com: CommonRef; parent: BlockHeader): Opt[UInt256] =
## Calculates the `baseFee` of the head assuming this is the parent of a
## new block header to generate.
# Note that the baseFee is calculated for the next header
if not com.isLondonOrLater(parent.number+1):
return Opt.none(UInt256)
# If the new block is the first EIP-1559 block, return initial base fee.
if not com.isLondonOrLater(parent.number):
return Opt.some(EIP1559_INITIAL_BASE_FEE)
Opt.some calcEip1599BaseFee(
parent.gasLimit,
parent.gasUsed,
parent.baseFeePerGas.get(0.u256))
proc gasLimitsGet(com: CommonRef; parent: BlockHeader): GasInt =
if com.isLondonOrLater(parent.number+1):
var parentGasLimit = parent.gasLimit
if not com.isLondonOrLater(parent.number):
# Bump by 2x
parentGasLimit = parent.gasLimit * EIP1559_ELASTICITY_MULTIPLIER
calcGasLimit1559(parentGasLimit, desiredLimit = DEFAULT_GAS_LIMIT)
else:
computeGasLimit(
parent.gasUsed,
parent.gasLimit,
gasFloor = DEFAULT_GAS_LIMIT,
gasCeil = DEFAULT_GAS_LIMIT)
func prepareHeader(dh: TxChainRef) =
dh.com.pos.prepare(dh.prepHeader)
func prepareForSeal(dh: TxChainRef; header: var BlockHeader) =
dh.com.pos.prepareForSeal(header)
func getTimestamp(dh: TxChainRef): EthTime =
dh.com.pos.timestamp
func feeRecipient*(dh: TxChainRef): EthAddress =
## Getter
dh.com.pos.feeRecipient
proc resetTxEnv(dh: TxChainRef; parent: BlockHeader) =
# do hardfork transition before
# BaseVMState querying any hardfork/consensus from CommonRef
let timestamp = dh.getTimestamp()
dh.com.hardForkTransition(
parent.blockHash, parent.number+1, Opt.some(timestamp))
dh.prepareHeader()
# we don't consider PoS difficulty here
# because that is handled in vmState
let blockCtx = BlockContext(
timestamp : dh.prepHeader.timestamp,
gasLimit : gasLimitsGet(dh.com, parent),
baseFeePerGas: baseFeeGet(dh.com, parent),
prevRandao : dh.prepHeader.prevRandao,
difficulty : dh.prepHeader.difficulty,
coinbase : dh.feeRecipient,
excessBlobGas: calcExcessBlobGas(parent),
)
dh.vmState = BaseVMState.new(
parent = parent,
blockCtx = blockCtx,
com = dh.com)
dh.txRoot = EMPTY_ROOT_HASH
dh.stateRoot = dh.vmState.parent.stateRoot
proc update(dh: TxChainRef; parent: BlockHeader)
{.gcsafe,raises: [].} =
let
db = dh.com.db
acc = LedgerRef.init(db, parent.stateRoot)
# Keep a separate accounts descriptor positioned at the sync point
dh.roAcc = ReadOnlyStateDB(acc)
dh.resetTxEnv(parent)
# ------------------------------------------------------------------------------
# Public functions, constructor
# ------------------------------------------------------------------------------
proc new*(T: type TxChainRef; com: CommonRef): T
{.gcsafe, raises: [EVMError].} =
## Constructor
new result
result.com = com
result.update(com.db.getCanonicalHead)
# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
proc getBalance*(dh: TxChainRef; account: EthAddress): UInt256 =
## Wrapper around `vmState.readOnlyStateDB.getBalance()` for a `vmState`
## descriptor positioned at the `dh.head`. This might differ from the
## `dh.vmState.readOnlyStateDB.getBalance()` which returnes the current
## balance relative to what has been accumulated by the current packing
## procedure.
dh.roAcc.getBalance(account)
proc getNonce*(dh: TxChainRef; account: EthAddress): AccountNonce =
## Wrapper around `vmState.readOnlyStateDB.getNonce()` for a `vmState`
## descriptor positioned at the `dh.head`. This might differ from the
## `dh.vmState.readOnlyStateDB.getNonce()` which returnes the current balance
## relative to what has been accumulated by the current packing procedure.
dh.roAcc.getNonce(account)
func baseFee*(dh: TxChainRef): GasInt =
## Getter, baseFee for the next bock header. This value is auto-generated
## when a new insertion point is set via `head=`.
if dh.vmState.blockCtx.baseFeePerGas.isSome:
dh.vmState.blockCtx.baseFeePerGas.get.truncate(GasInt)
else:
0.GasInt
func excessBlobGas*(dh: TxChainRef): uint64 =
## Getter, baseFee for the next bock header. This value is auto-generated
## when a new insertion point is set via `head=`.
dh.vmState.blockCtx.excessBlobGas
func blobGasUsed*(dh: TxChainRef): uint64 =
dh.vmState.blobGasUsed
func gasLimit*(dh: TxChainRef): GasInt =
dh.vmState.blockCtx.gasLimit
proc getHeader*(dh: TxChainRef): BlockHeader
{.gcsafe,raises: [].} =
## Generate a new header, a child of the cached `head`
result = BlockHeader(
parentHash: dh.vmState.parent.blockHash,
ommersHash: EMPTY_UNCLE_HASH,
coinbase: dh.prepHeader.coinbase,
stateRoot: dh.stateRoot,
txRoot: dh.txRoot,
receiptsRoot: dh.receiptsRoot,
logsBloom: dh.logsBloom,
difficulty: dh.prepHeader.difficulty,
number: dh.vmState.blockNumber,
gasLimit: dh.gasLimit,
gasUsed: dh.vmState.cumulativeGasUsed,
timestamp: dh.prepHeader.timestamp,
# extraData: Blob # signing data
# mixHash: Hash256 # mining hash for given difficulty
# nonce: BlockNonce # mining free vaiable
baseFeePerGas: dh.vmState.blockCtx.baseFeePerGas,
)
if dh.com.isShanghaiOrLater(result.timestamp):
result.withdrawalsRoot = Opt.some(calcWithdrawalsRoot(dh.com.pos.withdrawals))
if dh.com.isCancunOrLater(result.timestamp):
result.parentBeaconBlockRoot = Opt.some(dh.com.pos.parentBeaconBlockRoot)
result.blobGasUsed = Opt.some dh.blobGasUsed
result.excessBlobGas = Opt.some dh.excessBlobGas
dh.prepareForSeal(result)
proc clearAccounts*(dh: TxChainRef)
{.gcsafe,raises: [].} =
## Reset transaction environment, e.g. before packing a new block
dh.resetTxEnv(dh.vmState.parent)
# ------------------------------------------------------------------------------
# Public functions, getters
# ------------------------------------------------------------------------------
func com*(dh: TxChainRef): CommonRef =
## Getter
dh.com
func head*(dh: TxChainRef): BlockHeader =
## Getter
dh.vmState.parent
func nextFork*(dh: TxChainRef): EVMFork =
## Getter, fork of next block
dh.vmState.fork
func vmState*(dh: TxChainRef): BaseVMState =
## Getter, `BaseVmState` descriptor based on the current insertion point.
dh.vmState
# ------------------------------------------------------------------------------
# Public functions, setters
# ------------------------------------------------------------------------------
proc `head=`*(dh: TxChainRef; val: BlockHeader)
{.gcsafe,raises: [].} =
## Setter, updates descriptor. This setter re-positions the `vmState` and
## account caches to a new insertion point on the block chain database.
dh.update(val)
func `receiptsRoot=`*(dh: TxChainRef; val: Hash256) =
## Setter, implies `gasUsed`
dh.receiptsRoot = val
func `logsBloom=`*(dh: TxChainRef; val: BloomFilter) =
## Setter, implies `gasUsed`
dh.logsBloom = val
func `stateRoot=`*(dh: TxChainRef; val: Hash256) =
## Setter
dh.stateRoot = val
func `txRoot=`*(dh: TxChainRef; val: Hash256) =
## Setter
dh.txRoot = val
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------