mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-01-28 13:05:18 +00:00
103656dbb5
details: For documentation, see comments in the file tx_pool.nim. For prettified manual pages run 'make docs' in the nimbus directory and point your web browser to the newly created 'docs' directory.
323 lines
9.7 KiB
Nim
323 lines
9.7 KiB
Nim
# 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.
|
|
|
|
## Transaction Pool Table: `status` > `nonce`
|
|
## ==========================================
|
|
##
|
|
|
|
import
|
|
../tx_info,
|
|
../tx_item,
|
|
eth/[common],
|
|
stew/[results, keyed_queue, keyed_queue/kq_debug, sorted_set]
|
|
|
|
{.push raises: [Defect].}
|
|
|
|
type
|
|
TxStatusNonceRef* = ref object ##\
|
|
## Sub-list ordered by `AccountNonce` or `TxItemRef` insertion order.
|
|
nonceList: SortedSet[AccountNonce,TxItemRef]
|
|
|
|
TxStatusSenderRef* = ref object ##\
|
|
## Per address table. This table is provided as a keyed queue so deletion\
|
|
## while traversing is supported and predictable.
|
|
size: int ## Total number of items
|
|
gasLimits: GasInt ## Accumulated gas limits
|
|
addrList: KeyedQueue[EthAddress,TxStatusNonceRef]
|
|
|
|
TxStatusTab* = object ##\
|
|
## Per status table
|
|
size: int ## Total number of items
|
|
statusList: array[TxItemStatus,TxStatusSenderRef]
|
|
|
|
TxStatusInx = object ##\
|
|
## Internal access data
|
|
addrData: TxStatusSenderRef
|
|
nonceData: TxStatusNonceRef
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Private helpers
|
|
# ------------------------------------------------------------------------------
|
|
|
|
proc `$`(rq: TxStatusNonceRef): string =
|
|
## Needed by `rq.verify()` for printing error messages
|
|
$rq.nonceList.len
|
|
|
|
proc nActive(sq: TxStatusTab): int =
|
|
## Number of non-nil items
|
|
for status in TxItemStatus:
|
|
if not sq.statusList[status].isNil:
|
|
result.inc
|
|
|
|
proc mkInxImpl(sq: var TxStatusTab; item: TxItemRef): Result[TxStatusInx,void]
|
|
{.gcsafe,raises: [Defect,KeyError].} =
|
|
## Fails if item exists, already
|
|
var inx: TxStatusInx
|
|
|
|
# array of buckets (aka status) => senders
|
|
inx.addrData = sq.statusList[item.status]
|
|
if inx.addrData.isNil:
|
|
new inx.addrData
|
|
inx.addrData.addrList.init
|
|
sq.statusList[item.status] = inx.addrData
|
|
|
|
# sender address sub-list => nonces
|
|
if inx.addrData.addrList.hasKey(item.sender):
|
|
inx.nonceData = inx.addrData.addrList[item.sender]
|
|
else:
|
|
new inx.nonceData
|
|
inx.nonceData.nonceList.init
|
|
inx.addrData.addrList[item.sender] = inx.nonceData
|
|
|
|
# nonce sublist
|
|
let rc = inx.nonceData.nonceList.insert(item.tx.nonce)
|
|
if rc.isErr:
|
|
return err()
|
|
rc.value.data = item
|
|
|
|
return ok(inx)
|
|
|
|
|
|
proc getInxImpl(sq: var TxStatusTab; item: TxItemRef): Result[TxStatusInx,void]
|
|
{.gcsafe,raises: [Defect,KeyError].} =
|
|
var inx: TxStatusInx
|
|
|
|
# array of buckets (aka status) => senders
|
|
inx.addrData = sq.statusList[item.status]
|
|
if inx.addrData.isNil:
|
|
return err()
|
|
|
|
# sender address sub-list => nonces
|
|
if not inx.addrData.addrList.hasKey(item.sender):
|
|
return err()
|
|
inx.nonceData = inx.addrData.addrList[item.sender]
|
|
|
|
ok(inx)
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public all-queue helpers
|
|
# ------------------------------------------------------------------------------
|
|
|
|
proc init*(sq: var TxStatusTab; size = 10) =
|
|
## Optional constructor
|
|
sq.size = 0
|
|
sq.statusList.reset
|
|
|
|
|
|
proc insert*(sq: var TxStatusTab; item: TxItemRef): bool
|
|
{.gcsafe,raises: [Defect,KeyError].} =
|
|
## Add transaction `item` to the list. The function has no effect if the
|
|
## transaction exists, already (apart from returning `false`.)
|
|
let rc = sq.mkInxImpl(item)
|
|
if rc.isOK:
|
|
let inx = rc.value
|
|
sq.size.inc
|
|
inx.addrData.size.inc
|
|
inx.addrData.gasLimits += item.tx.gasLimit
|
|
return true
|
|
|
|
|
|
proc delete*(sq: var TxStatusTab; item: TxItemRef): bool
|
|
{.gcsafe,raises: [Defect,KeyError].} =
|
|
let rc = sq.getInxImpl(item)
|
|
if rc.isOK:
|
|
let inx = rc.value
|
|
|
|
sq.size.dec
|
|
inx.addrData.size.dec
|
|
inx.addrData.gasLimits -= item.tx.gasLimit
|
|
|
|
discard inx.nonceData.nonceList.delete(item.tx.nonce)
|
|
if inx.nonceData.nonceList.len == 0:
|
|
discard inx.addrData.addrList.delete(item.sender)
|
|
|
|
if inx.addrData.addrList.len == 0:
|
|
sq.statusList[item.status] = nil
|
|
|
|
return true
|
|
|
|
|
|
proc verify*(sq: var TxStatusTab): Result[void,TxInfo]
|
|
{.gcsafe,raises: [Defect,CatchableError].} =
|
|
## walk `TxItemStatus` > `EthAddress` > `AccountNonce`
|
|
|
|
var totalCount = 0
|
|
for status in TxItemStatus:
|
|
let addrData = sq.statusList[status]
|
|
if not addrData.isNil:
|
|
|
|
block:
|
|
let rc = addrData.addrList.verify
|
|
if rc.isErr:
|
|
return err(txInfoVfyStatusSenderList)
|
|
var
|
|
addrCount = 0
|
|
gasLimits = 0.GasInt
|
|
for p in addrData.addrList.nextPairs:
|
|
let (addrKey, nonceData) = (p.key, p.data)
|
|
|
|
block:
|
|
let rc = nonceData.nonceList.verify
|
|
if rc.isErr:
|
|
return err(txInfoVfyStatusNonceList)
|
|
|
|
var rcNonce = nonceData.nonceList.ge(AccountNonce.low)
|
|
while rcNonce.isOK:
|
|
let (nonceKey, item) = (rcNonce.value.key, rcNonce.value.data)
|
|
rcNonce = nonceData.nonceList.gt(nonceKey)
|
|
|
|
gasLimits += item.tx.gasLimit
|
|
addrCount.inc
|
|
|
|
if addrCount != addrData.size:
|
|
return err(txInfoVfyStatusTotal)
|
|
if gasLimits != addrData.gasLimits:
|
|
return err(txInfoVfyStatusGasLimits)
|
|
|
|
totalCount += addrCount
|
|
|
|
# end while
|
|
if totalCount != sq.size:
|
|
return err(txInfoVfyStatusTotal)
|
|
|
|
ok()
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public array ops -- `TxItemStatus` (level 0)
|
|
# ------------------------------------------------------------------------------
|
|
|
|
proc len*(sq: var TxStatusTab): int =
|
|
sq.nActive
|
|
|
|
proc nItems*(sq: var TxStatusTab): int =
|
|
## Getter, total number of items in the list
|
|
sq.size
|
|
|
|
proc eq*(sq: var TxStatusTab; status: TxItemStatus):
|
|
SortedSetResult[TxItemStatus,TxStatusSenderRef] =
|
|
let addrData = sq.statusList[status]
|
|
if addrData.isNil:
|
|
return err(rbNotFound)
|
|
toSortedSetResult(key = status, data = addrData)
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public array ops -- `EthAddress` (level 1)
|
|
# ------------------------------------------------------------------------------
|
|
|
|
proc nItems*(addrData: TxStatusSenderRef): int =
|
|
## Getter, total number of items in the sub-list
|
|
addrData.size
|
|
|
|
proc nItems*(rc: SortedSetResult[TxItemStatus,TxStatusSenderRef]): int =
|
|
if rc.isOK:
|
|
return rc.value.data.nItems
|
|
0
|
|
|
|
|
|
proc gasLimits*(addrData: TxStatusSenderRef): GasInt =
|
|
## Getter, accumulated `gasLimit` values
|
|
addrData.gasLimits
|
|
|
|
proc gasLimits*(rc: SortedSetResult[TxItemStatus,TxStatusSenderRef]): GasInt =
|
|
if rc.isOK:
|
|
return rc.value.data.gasLimits
|
|
0
|
|
|
|
|
|
proc eq*(addrData: TxStatusSenderRef; sender: EthAddress):
|
|
SortedSetResult[EthAddress,TxStatusNonceRef]
|
|
{.gcsafe,raises: [Defect,KeyError].} =
|
|
if addrData.addrList.haskey(sender):
|
|
return toSortedSetResult(key = sender, data = addrData.addrList[sender])
|
|
err(rbNotFound)
|
|
|
|
proc eq*(rc: SortedSetResult[TxItemStatus,TxStatusSenderRef];
|
|
sender: EthAddress): SortedSetResult[EthAddress,TxStatusNonceRef]
|
|
{.gcsafe,raises: [Defect,KeyError].} =
|
|
if rc.isOK:
|
|
return rc.value.data.eq(sender)
|
|
err(rc.error)
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public array ops -- `AccountNonce` (level 2)
|
|
# ------------------------------------------------------------------------------
|
|
|
|
proc len*(nonceData: TxStatusNonceRef): int =
|
|
## Getter, same as `nItems` (for last level list)
|
|
nonceData.nonceList.len
|
|
|
|
proc nItems*(nonceData: TxStatusNonceRef): int =
|
|
## Getter, total number of items in the sub-list
|
|
nonceData.nonceList.len
|
|
|
|
proc nItems*(rc: SortedSetResult[EthAddress,TxStatusNonceRef]): int =
|
|
if rc.isOK:
|
|
return rc.value.data.nItems
|
|
0
|
|
|
|
|
|
proc eq*(nonceData: TxStatusNonceRef; nonce: AccountNonce):
|
|
SortedSetResult[AccountNonce,TxItemRef] =
|
|
nonceData.nonceList.eq(nonce)
|
|
|
|
proc eq*(rc: SortedSetResult[EthAddress,TxStatusNonceRef]; nonce: AccountNonce):
|
|
SortedSetResult[AccountNonce,TxItemRef] =
|
|
if rc.isOK:
|
|
return rc.value.data.eq(nonce)
|
|
err(rc.error)
|
|
|
|
|
|
proc ge*(nonceData: TxStatusNonceRef; nonce: AccountNonce):
|
|
SortedSetResult[AccountNonce,TxItemRef] =
|
|
nonceData.nonceList.ge(nonce)
|
|
|
|
proc ge*(rc: SortedSetResult[EthAddress,TxStatusNonceRef]; nonce: AccountNonce):
|
|
SortedSetResult[AccountNonce,TxItemRef] =
|
|
if rc.isOK:
|
|
return rc.value.data.ge(nonce)
|
|
err(rc.error)
|
|
|
|
|
|
proc gt*(nonceData: TxStatusNonceRef; nonce: AccountNonce):
|
|
SortedSetResult[AccountNonce,TxItemRef] =
|
|
nonceData.nonceList.gt(nonce)
|
|
|
|
proc gt*(rc: SortedSetResult[EthAddress,TxStatusNonceRef]; nonce: AccountNonce):
|
|
SortedSetResult[AccountNonce,TxItemRef] =
|
|
if rc.isOK:
|
|
return rc.value.data.gt(nonce)
|
|
err(rc.error)
|
|
|
|
|
|
proc le*(nonceData: TxStatusNonceRef; nonce: AccountNonce):
|
|
SortedSetResult[AccountNonce,TxItemRef] =
|
|
nonceData.nonceList.le(nonce)
|
|
|
|
proc le*(rc: SortedSetResult[EthAddress,TxStatusNonceRef]; nonce: AccountNonce):
|
|
SortedSetResult[AccountNonce,TxItemRef] =
|
|
if rc.isOK:
|
|
return rc.value.data.le(nonce)
|
|
err(rc.error)
|
|
|
|
|
|
proc lt*(nonceData: TxStatusNonceRef; nonce: AccountNonce):
|
|
SortedSetResult[AccountNonce,TxItemRef] =
|
|
nonceData.nonceList.lt(nonce)
|
|
|
|
proc lt*(rc: SortedSetResult[EthAddress,TxStatusNonceRef]; nonce: AccountNonce):
|
|
SortedSetResult[AccountNonce,TxItemRef] =
|
|
if rc.isOK:
|
|
return rc.value.data.lt(nonce)
|
|
err(rc.error)
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# End
|
|
# ------------------------------------------------------------------------------
|