# 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. ## Transaction Pool Descriptor ## =========================== ## import std/[times], eth/eip1559, ../../common/common, ../../evm/state, ../../evm/types, ../../db/ledger, ../../constants, ../pow/header, ../eip4844, ../casper, ./tx_item, ./tx_tabs, ./tx_tabs/tx_sender {.push raises: [].} type TxPoolFlags* = enum ##\ ## Processing strategy selector symbols autoUpdateBucketsDB ##\ ## Automatically update the state buckets after running batch jobs if ## the `dirtyBuckets` flag is also set. autoZombifyUnpacked ##\ ## Automatically dispose *pending* or *staged* txs that were queued ## at least `lifeTime` ago. TxPoolParam* = tuple ## Getter/setter accessible parameters dirtyBuckets: bool ## Buckets need to be updated doubleCheck: seq[TxItemRef] ## Check items after moving block chain head flags: set[TxPoolFlags] ## Processing strategy symbols TxPoolRef* = ref object of RootObj ##\ ## Transaction pool descriptor startDate: Time ## Start date (read-only) param: TxPoolParam ## Getter/Setter parameters vmState: BaseVMState txDB: TxTabsRef ## Transaction lists & tables lifeTime*: times.Duration ## Maximum life time of a tx in the system priceBump*: uint ## Min precentage price when superseding const txItemLifeTime = ##\ ## Maximum amount of time transactions can be held in the database\ ## unless they are packed already for a block. This default is chosen\ ## as found in core/tx_pool.go(184) of the geth implementation. initDuration(hours = 3) txPriceBump = ##\ ## Minimum price bump percentage to replace an already existing\ ## transaction (nonce). This default is chosen as found in\ ## core/tx_pool.go(177) of the geth implementation. 10u txPoolFlags = {autoUpdateBucketsDB, autoZombifyUnpacked} # ------------------------------------------------------------------------------ # Private functions # ------------------------------------------------------------------------------ proc baseFeeGet(com: CommonRef; parent: Header): 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: Header): 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) proc setupVMState(com: CommonRef; parent: Header): BaseVMState = # do hardfork transition before # BaseVMState querying any hardfork/consensus from CommonRef let pos = com.pos let blockCtx = BlockContext( timestamp : pos.timestamp, gasLimit : gasLimitsGet(com, parent), baseFeePerGas: baseFeeGet(com, parent), prevRandao : pos.prevRandao, difficulty : UInt256.zero(), coinbase : pos.feeRecipient, excessBlobGas: calcExcessBlobGas(parent), parentHash : parent.blockHash, ) BaseVMState.new( parent = parent, blockCtx = blockCtx, com = com) proc update(xp: TxPoolRef; parent: Header) = xp.vmState = setupVMState(xp.vmState.com, parent) # ------------------------------------------------------------------------------ # Public functions, constructor # ------------------------------------------------------------------------------ proc init*(xp: TxPoolRef; com: CommonRef) {.gcsafe,raises: [CatchableError].} = ## Constructor, returns new tx-pool descriptor. xp.startDate = getTime().utc.toTime xp.vmState = setupVMState(com, com.db.getCanonicalHead) xp.txDB = TxTabsRef.new xp.lifeTime = txItemLifeTime xp.priceBump = txPriceBump xp.param.reset xp.param.flags = txPoolFlags # ------------------------------------------------------------------------------ # Public functions # ------------------------------------------------------------------------------ proc clearAccounts*(xp: TxPoolRef) = ## Reset transaction environment, e.g. before packing a new block xp.update(xp.vmState.parent) # ------------------------------------------------------------------------------ # Public functions, getters # ------------------------------------------------------------------------------ func pFlags*(xp: TxPoolRef): set[TxPoolFlags] = ## Returns the set of algorithm strategy symbols for labelling items ## as`packed` xp.param.flags func pDirtyBuckets*(xp: TxPoolRef): bool = ## Getter, buckets need update xp.param.dirtyBuckets func pDoubleCheck*(xp: TxPoolRef): seq[TxItemRef] = ## Getter, cached block chain head was moved back xp.param.doubleCheck func startDate*(xp: TxPoolRef): Time = ## Getter xp.startDate func txDB*(xp: TxPoolRef): TxTabsRef = ## Getter, pool database xp.txDB func baseFee*(xp: TxPoolRef): GasInt = ## Getter, baseFee for the next bock header. This value is auto-generated ## when a new insertion point is set via `head=`. if xp.vmState.blockCtx.baseFeePerGas.isSome: xp.vmState.blockCtx.baseFeePerGas.get.truncate(GasInt) else: 0.GasInt func vmState*(xp: TxPoolRef): BaseVMState = xp.vmState func nextFork*(xp: TxPoolRef): EVMFork = xp.vmState.fork func gasLimit*(xp: TxPoolRef): GasInt = xp.vmState.blockCtx.gasLimit func excessBlobGas*(xp: TxPoolRef): GasInt = xp.vmState.blockCtx.excessBlobGas proc getBalance*(xp: TxPoolRef; account: Address): 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. xp.vmState.stateDB.getBalance(account) proc getNonce*(xp: TxPoolRef; account: Address): 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. xp.vmState.stateDB.getNonce(account) func head*(xp: TxPoolRef): Header = ## Getter, cached block chain insertion point. Typocally, this should be the ## the same header as retrieved by the `getCanonicalHead()` (unless in the ## middle of a mining update.) xp.vmState.parent # ------------------------------------------------------------------------------ # Public functions, setters # ------------------------------------------------------------------------------ func `pDirtyBuckets=`*(xp: TxPoolRef; val: bool) = ## Setter xp.param.dirtyBuckets = val func pDoubleCheckAdd*(xp: TxPoolRef; val: seq[TxItemRef]) = ## Pseudo setter xp.param.doubleCheck.add val func pDoubleCheckFlush*(xp: TxPoolRef) = ## Pseudo setter xp.param.doubleCheck.setLen(0) func `pFlags=`*(xp: TxPoolRef; val: set[TxPoolFlags]) = ## Install a set of algorithm strategy symbols for labelling items as`packed` xp.param.flags = val proc `head=`*(xp: TxPoolRef; val: Header) {.gcsafe,raises: [].} = ## Setter, updates descriptor. This setter re-positions the `vmState` and ## account caches to a new insertion point on the block chain database. xp.update(val) # ------------------------------------------------------------------------------ # End # ------------------------------------------------------------------------------