mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-01-27 12:35:00 +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.
239 lines
7.7 KiB
Nim
239 lines
7.7 KiB
Nim
# Nimbus
|
|
# Copyright (c) 2018-2019 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.
|
|
|
|
import
|
|
std/[algorithm, os, sequtils, strformat, tables, times],
|
|
../../nimbus/[config, chain_config, constants, genesis],
|
|
../../nimbus/db/db_chain,
|
|
../../nimbus/p2p/chain,
|
|
../../nimbus/utils/[ec_recover, tx_pool],
|
|
../../nimbus/utils/tx_pool/[tx_chain, tx_item],
|
|
./helpers,
|
|
./sign_helper,
|
|
eth/[common, keys, p2p, trie/db],
|
|
stew/[keyed_queue],
|
|
stint
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Private functions
|
|
# ------------------------------------------------------------------------------
|
|
|
|
proc setStatus(xp: TxPoolRef; item: TxItemRef; status: TxItemStatus)
|
|
{.gcsafe,raises: [Defect,CatchableError].} =
|
|
## Change/update the status of the transaction item.
|
|
if status != item.status:
|
|
discard xp.txDB.reassign(item, status)
|
|
|
|
proc importBlocks(c: Chain; h: seq[BlockHeader]; b: seq[BlockBody]): int =
|
|
if c.persistBlocks(h,b) != ValidationResult.OK:
|
|
raiseAssert "persistBlocks() failed at block #" & $h[0].blockNumber
|
|
for body in b:
|
|
result += body.transactions.len
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public functions
|
|
# ------------------------------------------------------------------------------
|
|
|
|
proc blockChainForTesting*(network: NetworkID): BaseChainDB =
|
|
|
|
result = newBaseChainDB(
|
|
newMemoryDb(),
|
|
id = network,
|
|
params = network.networkParams)
|
|
|
|
result.populateProgress
|
|
result.initializeEmptyDB
|
|
|
|
|
|
proc toTxPool*(
|
|
db: BaseChainDB; ## to be modified
|
|
file: string; ## input, file and transactions
|
|
getStatus: proc(): TxItemStatus; ## input, random function
|
|
loadBlocks: int; ## load at most this many blocks
|
|
minBlockTxs: int; ## load at least this many txs in blocks
|
|
loadTxs: int; ## load at most this many transactions
|
|
baseFee = 0.GasPrice; ## initalise with `baseFee` (unless 0)
|
|
noisy: bool): (TxPoolRef, int) =
|
|
|
|
var
|
|
txCount = 0
|
|
chainNo = 0
|
|
chainDB = db.newChain
|
|
nTxs = 0
|
|
|
|
doAssert not db.isNil
|
|
result[0] = TxPoolRef.new(db,testAddress)
|
|
result[0].baseFee = baseFee
|
|
|
|
for chain in file.undumpNextGroup:
|
|
let leadBlkNum = chain[0][0].blockNumber
|
|
chainNo.inc
|
|
|
|
if loadTxs <= txCount:
|
|
break
|
|
|
|
# Verify Genesis
|
|
if leadBlkNum == 0.u256:
|
|
doAssert chain[0][0] == db.getBlockHeader(0.u256)
|
|
continue
|
|
|
|
if leadBlkNum < loadBlocks.u256 or nTxs < minBlockTxs:
|
|
nTxs += chainDB.importBlocks(chain[0],chain[1])
|
|
continue
|
|
|
|
# Import transactions
|
|
for inx in 0 ..< chain[0].len:
|
|
let
|
|
num = chain[0][inx].blockNumber
|
|
txs = chain[1][inx].transactions
|
|
|
|
# Continue importing up until first non-trivial block
|
|
if txCount == 0 and txs.len == 0:
|
|
nTxs += chainDB.importBlocks(@[chain[0][inx]],@[chain[1][inx]])
|
|
continue
|
|
|
|
# Load transactions, one-by-one
|
|
for n in 0 ..< min(txs.len, loadTxs - txCount):
|
|
txCount.inc
|
|
let
|
|
status = statusInfo[getStatus()]
|
|
info = &"{txCount} #{num}({chainNo}) {n}/{txs.len} {status}"
|
|
noisy.showElapsed(&"insert: {info}"):
|
|
result[0].jobAddTx(txs[n], info)
|
|
|
|
if loadTxs <= txCount:
|
|
break
|
|
|
|
result[0].jobCommit
|
|
result[1] = nTxs
|
|
|
|
|
|
proc toTxPool*(
|
|
db: BaseChainDB; ## to be modified, initialisier for `TxPool`
|
|
itList: var seq[TxItemRef]; ## import items into new `TxPool` (read only)
|
|
baseFee = 0.GasPrice; ## initalise with `baseFee` (unless 0)
|
|
noisy = true): TxPoolRef =
|
|
|
|
doAssert not db.isNil
|
|
|
|
result = TxPoolRef.new(db,testAddress)
|
|
result.baseFee = baseFee
|
|
result.maxRejects = itList.len
|
|
|
|
noisy.showElapsed(&"Loading {itList.len} transactions"):
|
|
for item in itList:
|
|
result.jobAddTx(item.tx, item.info)
|
|
result.jobCommit
|
|
doAssert result.nItems.total == itList.len
|
|
|
|
|
|
proc toTxPool*(
|
|
db: BaseChainDB;
|
|
itList: seq[TxItemRef];
|
|
baseFee = 0.GasPrice;
|
|
noisy = true): TxPoolRef =
|
|
var newList = itList
|
|
db.toTxPool(newList, baseFee, noisy)
|
|
|
|
|
|
proc toTxPool*(
|
|
db: BaseChainDB; ## to be modified, initialisier for `TxPool`
|
|
timeGap: var Time; ## to be set, time in the middle of time gap
|
|
nGapItems: var int; ## to be set, # items before time gap
|
|
itList: var seq[TxItemRef]; ## import items into new `TxPool` (read only)
|
|
baseFee = 0.GasPrice; ## initalise with `baseFee` (unless 0)
|
|
itemsPC = 30; ## % number if items befor time gap
|
|
delayMSecs = 200; ## size of time vap
|
|
noisy = true): TxPoolRef =
|
|
## Variant of `toTxPoolFromSeq()` with a time gap between consecutive
|
|
## items on the `remote` queue
|
|
doAssert not db.isNil
|
|
doAssert 0 < itemsPC and itemsPC < 100
|
|
|
|
result = TxPoolRef.new(db,testAddress)
|
|
result.baseFee = baseFee
|
|
result.maxRejects = itList.len
|
|
|
|
let
|
|
delayAt = itList.len * itemsPC div 100
|
|
middleOfTimeGap = initDuration(milliSeconds = delayMSecs div 2)
|
|
|
|
noisy.showElapsed(&"Loading {itList.len} transactions"):
|
|
for n in 0 ..< itList.len:
|
|
let item = itList[n]
|
|
result.jobAddTx(item.tx, item.info)
|
|
if delayAt == n:
|
|
nGapItems = n # pass back value
|
|
noisy.say &"time gap after transactions"
|
|
let itemID = item.itemID
|
|
result.jobCommit
|
|
doAssert result.nItems.disposed == 0
|
|
timeGap = result.getItem(itemID).value.timeStamp + middleOfTimeGap
|
|
delayMSecs.sleep
|
|
|
|
result.jobCommit
|
|
doAssert result.nItems.total == itList.len
|
|
doAssert result.nItems.disposed == 0
|
|
|
|
|
|
proc toItems*(xp: TxPoolRef): seq[TxItemRef] =
|
|
toSeq(xp.txDB.byItemID.nextValues)
|
|
|
|
proc toItems*(xp: TxPoolRef; label: TxItemStatus): seq[TxItemRef] =
|
|
for (_,nonceList) in xp.txDB.decAccount(label):
|
|
result.add toSeq(nonceList.incNonce)
|
|
|
|
proc setItemStatusFromInfo*(xp: TxPoolRef) =
|
|
## Re-define status from last character of info field. Note that this might
|
|
## violate boundary conditions regarding nonces.
|
|
for item in xp.toItems:
|
|
let w = TxItemStatus.toSeq.filterIt(statusInfo[it][0] == item.info[^1])[0]
|
|
xp.setStatus(item, w)
|
|
|
|
|
|
proc getBackHeader*(xp: TxPoolRef; nTxs, nAccounts: int):
|
|
(BlockHeader, seq[Transaction], seq[EthAddress]) {.inline.} =
|
|
## back track the block chain for at least `nTxs` transactions and
|
|
## `nAccounts` sender accounts
|
|
var
|
|
accTab: Table[EthAddress,bool]
|
|
txsLst: seq[Transaction]
|
|
backHash = xp.head.blockHash
|
|
backHeader = xp.head
|
|
backBody = xp.chain.db.getBlockBody(backHash)
|
|
|
|
while true:
|
|
# count txs and step behind last block
|
|
txsLst.add backBody.transactions
|
|
backHash = backHeader.parentHash
|
|
if not xp.chain.db.getBlockHeader(backHash, backHeader) or
|
|
not xp.chain.db.getBlockBody(backHash, backBody):
|
|
break
|
|
|
|
# collect accounts unless max reached
|
|
if accTab.len < nAccounts:
|
|
for tx in backBody.transactions:
|
|
let rc = tx.ecRecover
|
|
if rc.isOK:
|
|
if xp.txDB.bySender.eq(rc.value).isOk:
|
|
accTab[rc.value] = true
|
|
if nAccounts <= accTab.len:
|
|
break
|
|
|
|
if nTxs <= txsLst.len and nAccounts <= accTab.len:
|
|
break
|
|
# otherwise get next block
|
|
|
|
(backHeader, txsLst.reversed, toSeq(accTab.keys))
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# End
|
|
# ------------------------------------------------------------------------------
|