nimbus-eth1/nimbus/utils/tx_pool/tx_tabs/tx_rank.nim

188 lines
5.4 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: `rank` ~ `sender`
## =========================================
##
import
std/[tables],
../tx_info,
eth/[common],
stew/[results, sorted_set]
{.push raises: [Defect].}
type
TxRank* = ##\
## Order relation, determins how the `EthAddresses` are ranked
distinct int64
TxRankAddrRef* = ##\
## Set of adresses having the same rank.
TableRef[EthAddress,TxRank]
TxRankTab* = object ##\
## Descriptor for `TxRank` <-> `EthAddress` mapping.
rankList: SortedSet[TxRank,TxRankAddrRef]
addrTab: Table[EthAddress,TxRank]
# ------------------------------------------------------------------------------
# Private helpers
# ------------------------------------------------------------------------------
proc cmp(a,b: TxRank): int {.borrow.}
## mixin for SortedSet
proc `==`(a,b: TxRank): bool {.borrow.}
# ------------------------------------------------------------------------------
# Public constructor
# ------------------------------------------------------------------------------
proc init*(rt: var TxRankTab) =
## Constructor
rt.rankList.init
proc clear*(rt: var TxRankTab) =
## Flush tables
rt.rankList.clear
rt.addrTab.clear
# ------------------------------------------------------------------------------
# Public functions, base management operations
# ------------------------------------------------------------------------------
proc insert*(rt: var TxRankTab; rank: TxRank; sender: EthAddress): bool
{.gcsafe,raises: [Defect,KeyError].} =
## Add or update a new ranked address. This function returns `true` it the
## address exists already with the current rank.
# Does this address exists already?
if rt.addrTab.hasKey(sender):
let oldRank = rt.addrTab[sender]
if oldRank == rank:
return false
# Delete address from oldRank address set
let oldRankSet = rt.rankList.eq(oldRank).value.data
if 1 < oldRankSet.len:
oldRankSet.del(sender)
else:
discard rt.rankList.delete(oldRank)
# Add new ranked address
var newRankSet: TxRankAddrRef
let rc = rt.rankList.insert(rank)
if rc.isOk:
newRankSet = newTable[EthAddress,TxRank](1)
rc.value.data = newRankSet
else:
newRankSet = rt.rankList.eq(rank).value.data
newRankSet[sender] = rank
rt.addrTab[sender] = rank
true
proc delete*(rt: var TxRankTab; sender: EthAddress): bool
{.gcsafe,raises: [Defect,KeyError].} =
## Delete argument address `sender` from rank table.
if rt.addrTab.hasKey(sender):
let
rankNum = rt.addrTab[sender]
rankSet = rt.rankList.eq(rankNum).value.data
# Delete address from oldRank address set
if 1 < rankSet.len:
rankSet.del(sender)
else:
discard rt.rankList.delete(rankNum)
rt.addrTab.del(sender)
return true
proc verify*(rt: var TxRankTab): Result[void,TxInfo]
{.gcsafe,raises: [Defect,CatchableError].} =
var
seen: Table[EthAddress,TxRank]
rc = rt.rankList.ge(TxRank.low)
while rc.isOk:
let (key, addrTab) = (rc.value.key, rc.value.data)
rc = rt.rankList.gt(key)
for (sender,rank) in addrTab.pairs:
if key != rank:
return err(txInfoVfyRankAddrMismatch)
if not rt.addrTab.hasKey(sender):
return err(txInfoVfyRankReverseLookup)
if rank != rt.addrTab[sender]:
return err(txInfoVfyRankReverseMismatch)
if seen.hasKey(sender):
return err(txInfoVfyRankDuplicateAddr)
seen[sender] = rank
if seen.len != rt.addrTab.len:
return err(txInfoVfyReverseZombies)
ok()
# ------------------------------------------------------------------------------
# Public functions: `TxRank` > `EthAddress`
# ------------------------------------------------------------------------------
proc len*(rt: var TxRankTab): int =
## Number of ranks available
rt.rankList.len
proc eq*(rt: var TxRankTab; rank: TxRank):
SortedSetResult[TxRank,TxRankAddrRef] =
rt.rankList.eq(rank)
proc ge*(rt: var TxRankTab; rank: TxRank):
SortedSetResult[TxRank,TxRankAddrRef] =
rt.rankList.ge(rank)
proc gt*(rt: var TxRankTab; rank: TxRank):
SortedSetResult[TxRank,TxRankAddrRef] =
rt.rankList.gt(rank)
proc le*(rt: var TxRankTab; rank: TxRank):
SortedSetResult[TxRank,TxRankAddrRef] =
rt.rankList.le(rank)
proc lt*(rt: var TxRankTab; rank: TxRank):
SortedSetResult[TxRank,TxRankAddrRef] =
rt.rankList.lt(rank)
# ------------------------------------------------------------------------------
# Public functions: `EthAddress` > `TxRank`
# ------------------------------------------------------------------------------
proc nItems*(rt: var TxRankTab): int =
## Total number of address items registered
rt.addrTab.len
proc eq*(rt: var TxRankTab; sender: EthAddress):
SortedSetResult[EthAddress,TxRank]
{.gcsafe,raises: [Defect,KeyError].} =
if rt.addrTab.hasKey(sender):
return toSortedSetResult(key = sender, data = rt.addrTab[sender])
err(rbNotFound)
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------