# 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 Item Container & Wrapper ## ========================================= ## import std/[hashes, sequtils, strutils, times], ../../utils/ec_recover, ./tx_info, eth/[common, keys], stew/results {.push raises: [].} type GasPrice* = ##| ## Handy definition distinct from `GasInt` which is a commodity unit while ## the `GasPrice` is the commodity valuation per unit of gas, similar to a ## kind of currency. distinct uint64 GasPriceEx* = ##\ ## Similar to `GasPrice` but is allowed to be negative. distinct int64 TxItemStatus* = enum ##\ ## Current status of a transaction as seen by the pool. txItemPending = 0 txItemStaged txItemPacked TxItemRef* = ref object of RootObj ##\ ## Data container with transaction and meta data. Entries are *read-only*\ ## by default, for some there is a setter available. tx: Transaction ## Transaction data itemID: Hash256 ## Transaction hash timeStamp: Time ## Time when added sender: EthAddress ## Sender account address info: string ## Whatever status: TxItemStatus ## Transaction status (setter available) reject: TxInfo ## Reason for moving to waste basket # ------------------------------------------------------------------------------ # Private, helpers for debugging and pretty printing # ------------------------------------------------------------------------------ proc utcTime: Time = getTime().utc.toTime # ------------------------------------------------------------------------------ # Public helpers supporting distinct types # ------------------------------------------------------------------------------ proc `$`*(a: GasPrice): string {.borrow.} proc `<`*(a, b: GasPrice): bool {.borrow.} proc `<=`*(a, b: GasPrice): bool {.borrow.} proc `==`*(a, b: GasPrice): bool {.borrow.} proc `+`*(a, b: GasPrice): GasPrice {.borrow.} proc `-`*(a, b: GasPrice): GasPrice {.borrow.} proc `$`*(a: GasPriceEx): string {.borrow.} proc `<`*(a, b: GasPriceEx): bool {.borrow.} proc `<=`*(a, b: GasPriceEx): bool {.borrow.} proc `==`*(a, b: GasPriceEx): bool {.borrow.} proc `+`*(a, b: GasPriceEx): GasPriceEx {.borrow.} proc `-`*(a, b: GasPriceEx): GasPriceEx {.borrow.} proc `+=`*(a: var GasPriceEx; b: GasPriceEx) {.borrow.} proc `-=`*(a: var GasPriceEx; b: GasPriceEx) {.borrow.} # Multiplication/division of *price* and *commodity unit* proc `*`*(a: GasPrice; b: SomeUnsignedInt): GasPrice {.borrow.} proc `*`*(a: SomeUnsignedInt; b: GasPrice): GasPrice {.borrow.} proc `div`*(a: GasPrice; b: SomeUnsignedInt): GasPrice = (a.uint64 div b).GasPrice # beware of zero denominator proc `*`*(a: SomeInteger; b: GasPriceEx): GasPriceEx = (a * b.int64).GasPriceEx # beware of under/overflow # Mixed stuff, convenience ops proc `-`*(a: GasPrice; b: SomeUnsignedInt): GasPrice {.borrow.} proc `<`*(a: GasPriceEx; b: SomeSignedInt): bool = a.int64 < b proc `<`*(a: GasPriceEx|SomeSignedInt; b: GasPrice): bool = if a.int64 < 0: true else: a.GasPrice < b proc `<=`*(a: SomeSignedInt; b: GasPriceEx): bool = a < b.int64 # ------------------------------------------------------------------------------ # Public functions, Constructor # ------------------------------------------------------------------------------ proc init*(item: TxItemRef; status: TxItemStatus; info: string) = ## Update item descriptor. item.info = info item.status = status item.timeStamp = utcTime() item.reject = txInfoOk proc new*(T: type TxItemRef; tx: Transaction; itemID: Hash256; status: TxItemStatus; info: string): Result[T,void] {.gcsafe,raises: [].} = ## Create item descriptor. let rc = tx.ecRecover if rc.isErr: return err() ok(T(itemID: itemID, tx: tx, sender: rc.value, timeStamp: utcTime(), info: info, status: status)) proc new*(T: type TxItemRef; tx: Transaction; reject: TxInfo; status: TxItemStatus; info: string): T {.gcsafe,raises: [].} = ## Create incomplete item descriptor, so meta-data can be stored (e.g. ## for holding in the waste basket to be investigated later.) T(tx: tx, timeStamp: utcTime(), info: info, status: status) # ------------------------------------------------------------------------------ # Public functions, Table ID helper # ------------------------------------------------------------------------------ proc hash*(item: TxItemRef): Hash = ## Needed if `TxItemRef` is used as hash-`Table` index. cast[pointer](item).hash # ------------------------------------------------------------------------------ # Public functions, transaction getters # ------------------------------------------------------------------------------ proc itemID*(tx: Transaction): Hash256 = ## Getter, transaction ID tx.rlpHash # core/types/transaction.go(297): func (tx *Transaction) Cost() *big.Int { proc cost*(tx: Transaction): UInt256 = ## Getter (go/ref compat): gas * gasPrice + value. (tx.gasPrice * tx.gasLimit).u256 + tx.value # core/types/transaction.go(332): .. *Transaction) EffectiveGasTip(baseFee .. # core/types/transaction.go(346): .. EffectiveGasTipValue(baseFee .. proc effectiveGasTip*(tx: Transaction; baseFee: GasPrice): GasPriceEx = ## The effective miner gas tip for the globally argument `baseFee`. The ## result (which is a price per gas) might well be negative. if tx.txType < TxEip1559: (tx.gasPrice - baseFee.int64).GasPriceEx else: # London, EIP1559 min(tx.maxPriorityFee, tx.maxFee - baseFee.int64).GasPriceEx proc effectiveGasTip*(tx: Transaction; baseFee: UInt256): GasPriceEx = ## Variant of `effectiveGasTip()` tx.effectiveGasTip(baseFee.truncate(uint64).GasPrice) # ------------------------------------------------------------------------------ # Public functions, item getters # ------------------------------------------------------------------------------ proc dup*(item: TxItemRef): TxItemRef = ## Getter, provide contents copy TxItemRef( tx: item.tx, itemID: item.itemID, timeStamp: item.timeStamp, sender: item.sender, info: item.info, status: item.status, reject: item.reject ) proc info*(item: TxItemRef): string = ## Getter item.info proc itemID*(item: TxItemRef): Hash256 = ## Getter item.itemID proc reject*(item: TxItemRef): TxInfo = ## Getter item.reject proc sender*(item: TxItemRef): EthAddress = ## Getter item.sender proc status*(item: TxItemRef): TxItemStatus = ## Getter item.status proc timeStamp*(item: TxItemRef): Time = ## Getter item.timeStamp proc tx*(item: TxItemRef): Transaction = ## Getter item.tx func rejectInfo*(item: TxItemRef): string = ## Getter result = $item.reject if item.info.len > 0: result.add ": " result.add item.info # ------------------------------------------------------------------------------ # Public functions, setters # ------------------------------------------------------------------------------ proc `status=`*(item: TxItemRef; val: TxItemStatus) = ## Setter item.status = val proc `reject=`*(item: TxItemRef; val: TxInfo) = ## Setter item.reject = val proc `info=`*(item: TxItemRef; val: string) = ## Setter item.info = val # ------------------------------------------------------------------------------ # Public functions, pretty printing and debugging # ------------------------------------------------------------------------------ proc `$`*(w: TxItemRef): string = ## Visualise item ID (use for debugging) "<" & w.itemID.data.mapIt(it.toHex(2)).join[24 .. 31].toLowerAscii & ">" # ------------------------------------------------------------------------------ # End # ------------------------------------------------------------------------------