nimbus-eth1/nimbus/utils/tx_pool/tx_tasks/tx_bucket.nim

174 lines
5.8 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 Tasklets: Update by Bucket
## ===========================================
##
import
std/[tables],
../../../constants,
../tx_chain,
../tx_desc,
../tx_info,
../tx_item,
../tx_tabs,
../tx_tabs/tx_status,
./tx_classify,
./tx_dispose,
chronicles,
eth/[common, keys],
stew/[sorted_set]
{.push raises: [Defect].}
const
minNonce = AccountNonce.low
logScope:
topics = "tx-pool buckets"
# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
proc bucketItemsReassignPending*(xp: TxPoolRef; labelFrom: TxItemStatus;
account: EthAddress; nonceFrom = minNonce)
{.gcsafe,raises: [Defect,CatchableError].} =
## Move all items in bucket `lblFrom` with nonces not less than `nonceFrom`
## to the `pending` bucket
let rc = xp.txDB.byStatus.eq(labelFrom).eq(account)
if rc.isOk:
for item in rc.value.data.incNonce(nonceFrom):
discard xp.txDB.reassign(item, txItemPending)
proc bucketItemsReassignPending*(xp: TxPoolRef; item: TxItemRef)
{.gcsafe,raises: [Defect,CatchableError].} =
## Variant of `bucketItemsReassignPending()`
xp.bucketItemsReassignPending(item.status, item.sender, item.tx.nonce)
proc bucketUpdateAll*(xp: TxPoolRef): bool
{.discardable,gcsafe,raises: [Defect,CatchableError].} =
## Update all buckets. The function returns `true` if some items were added
## to the `staged` bucket.
# Sort order: `EthAddress` > `AccountNonce` > item.
var
stagedItemsAdded = false
stashed: Table[EthAddress,seq[TxItemRef]]
# Prepare
if 0 < xp.pDoubleCheck.len:
for item in xp.pDoubleCheck:
if item.reject == txInfoOk:
# Check whether there was a gap when the head was moved backwards.
let rc = xp.txDB.bySender.eq(item.sender).any.gt(item.tx.nonce)
if rc.isOk:
let nextItem = rc.value.data
if item.tx.nonce + 1 < nextItem.tx.nonce:
discard xp.disposeItemAndHigherNonces(
item, txInfoErrNonceGap, txInfoErrImpliedNonceGap)
else:
# For failed txs, make sure that the account state has not
# changed. Assuming that this list is complete, then there are
# no other account affected.
let rc = xp.txDB.bySender.eq(item.sender).any.ge(minNonce)
if rc.isOk:
let firstItem = rc.value.data
if not xp.classifyValid(firstItem):
discard xp.disposeItemAndHigherNonces(
firstItem, txInfoErrNonceGap, txInfoErrImpliedNonceGap)
# Clean up that queue
xp.pDoubleCheckFlush
# PENDING
#
# Stash the items from the `pending` bucket The nonces in this list are
# greater than the ones from other lists. When processing the `staged`
# list, all that can happen is that loer nonces (than the stashed ones)
# are added.
for (sender,nonceList) in xp.txDB.incAccount(txItemPending):
# New per-sender-account sub-sequence
stashed[sender] = newSeq[TxItemRef]()
for item in nonceList.incNonce:
# Add to sub-sequence
stashed[sender].add item
# STAGED
#
# Update/edit `staged` bucket.
for (_,nonceList) in xp.txDB.incAccount(txItemStaged):
for item in nonceList.incNonce:
if not xp.classifyActive(item):
# Larger nonces cannot be held in the `staged` bucket anymore for this
# sender account. So they are moved back to the `pending` bucket.
xp.bucketItemsReassignPending(item)
# The nonces in the `staged` bucket are always smaller than the one in
# the `pending` bucket. So, if the lower nonce items must go to the
# `pending` bucket, then the stashed `pending` bucket items can only
# stay there.
stashed.del(item.sender)
break # inner `incItemList()` loop
# PACKED
#
# Update `packed` bucket. The items are a subset of all possibly staged
# (aka active) items. So they follow a similar logic as for the `staged`
# items above.
for (_,nonceList) in xp.txDB.incAccount(txItemPacked):
for item in nonceList.incNonce:
if not xp.classifyActive(item):
xp.bucketItemsReassignPending(item)
# For the `sender` all staged items have smaller nonces, so they have
# to go to the `pending` bucket, as well.
xp.bucketItemsReassignPending(txItemStaged, item.sender)
stagedItemsAdded = true
stashed.del(item.sender)
break # inner `incItemList()` loop
# PENDING re-visted
#
# Post-process `pending` and `staged` buckets. Re-insert the
# list of stashed `pending` items.
for itemList in stashed.values:
for item in itemList:
if not xp.classifyActive(item):
# Ignore higher nonces
break # inner loop for `itemList` sequence
# Move to staged bucket
discard xp.txDB.reassign(item, txItemStaged)
stagedItemsAdded
# ---------------------------
proc bucketFlushPacked*(xp: TxPoolRef)
{.gcsafe,raises: [Defect,CatchableError].} =
## Move all items from the `packed` bucket to the `pending` bucket
for (_,nonceList) in xp.txDB.decAccount(txItemPacked):
for item in nonceList.incNonce:
discard xp.txDB.reassign(item,txItemStaged)
# Reset bucket status info
xp.chain.clearAccounts
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------