diff --git a/nimbus/utils/tx_pool.nim b/nimbus/utils/tx_pool.nim index cdda4e7ce..d4da0a195 100644 --- a/nimbus/utils/tx_pool.nim +++ b/nimbus/utils/tx_pool.nim @@ -676,7 +676,7 @@ proc ethBlock*(xp: TxPoolRef): EthBlock ## order to build the block. xp.packerVmExec # updates vmState result.header = xp.chain.getHeader # uses updated vmState - for (_,nonceList) in xp.txDB.decAccount(txItemPacked): + for (_,nonceList) in xp.txDB.packingOrderAccounts(txItemPacked): result.txs.add toSeq(nonceList.incNonce).mapIt(it.tx) proc gasCumulative*(xp: TxPoolRef): GasInt @@ -864,6 +864,10 @@ proc resLocal*(xp: TxPoolRef; account: EthAddress) = ## considered for packing after the locally tagged accounts. xp.txDB.resLocal(account) +proc flushLocals*(xp: TxPoolRef) = + ## Untag all *local* addresses on the system. + xp.txDB.flushLocals + proc accountRanks*(xp: TxPoolRef): TxTabsLocality = ## Returns two lists, one for local and the other for non-local accounts. ## Any of these lists is sorted by the highest rank first. This sorting diff --git a/nimbus/utils/tx_pool/tx_tabs.nim b/nimbus/utils/tx_pool/tx_tabs.nim index 8307c9c44..88fe4a633 100644 --- a/nimbus/utils/tx_pool/tx_tabs.nim +++ b/nimbus/utils/tx_pool/tx_tabs.nim @@ -347,6 +347,10 @@ proc resLocal*(xp: TxTabsRef; sender: EthAddress) = ## Untag *local* `sender` address argument. xp.byLocal.del(sender) +proc flushLocals*(xp: TxTabsRef) = + ## Untag all *local* addresses on the system. + xp.byLocal.clear + # ------------------------------------------------------------------------------ # Public iterators, `TxRank` > `(EthAddress,TxStatusNonceRef)` # ------------------------------------------------------------------------------ @@ -394,6 +398,21 @@ iterator decAccount*(xp: TxTabsRef; bucket: TxItemStatus; # Get next ranked address list (top down index walk) rcRank = xp.byRank.lt(rank) # potenially modified database +iterator packingOrderAccounts*(xp: TxTabsRef; bucket: TxItemStatus): + (EthAddress,TxStatusNonceRef) + {.gcsafe,raises: [Defect,KeyError].} = + ## Loop over accounts from a particular bucket ordered by + ## + local ranks, higest one first + ## + remote ranks, higest one first + ## For the `txItemStaged` bucket, this iterator defines the packing order + ## for transactions (important when calculationg the *txRoot*.) + for (account,nonceList) in xp.decAccount(bucket): + if xp.isLocal(account): + yield (account,nonceList) + for (account,nonceList) in xp.decAccount(bucket): + if not xp.isLocal(account): + yield (account,nonceList) + # ------------------------------------------------------------------------------ # Public iterators, `TxRank` > `(EthAddress,TxSenderNonceRef)` # ------------------------------------------------------------------------------ diff --git a/nimbus/utils/tx_pool/tx_tasks/tx_classify.nim b/nimbus/utils/tx_pool/tx_tasks/tx_classify.nim index 97500ac3b..17e8c9382 100644 --- a/nimbus/utils/tx_pool/tx_tasks/tx_classify.nim +++ b/nimbus/utils/tx_pool/tx_tasks/tx_classify.nim @@ -86,12 +86,6 @@ proc checkTxNonce(xp: TxPoolRef; item: TxItemRef): bool true -proc eip1559TxNormalization(tx: Transaction): Transaction = - result = tx - if tx.txType < TxEip1559: - result.maxPriorityFee = tx.gasPrice - result.maxFee = tx.gasPrice - # ------------------------------------------------------------------------------ # Private function: active tx classifier check helpers # ------------------------------------------------------------------------------ @@ -179,6 +173,22 @@ proc txPostLondonAcceptableTipAndFees(xp: TxPoolRef; item: TxItemRef): bool = return false true +# ------------------------------------------------------------------------------ +# Public helper function, needed here and in the packer +# ------------------------------------------------------------------------------ + +func eip1559TxNormalization*(tx: Transaction; + baseFee: GasPrice; fork: Fork): Transaction = + ## This function adjusts a legacy transaction to EIP-1559 standard. This + ## is needed particularly when using the `validateTransaction()` utility + ## with legacy transactions. + result = tx + if tx.txType < TxEip1559: + result.maxPriorityFee = tx.gasPrice + result.maxFee = tx.gasPrice + if FkLondon <= fork: + result.gasPrice = min(tx.maxPriorityFee + baseFee.int64, tx.maxFee) + # ------------------------------------------------------------------------------ # Public functionss # ------------------------------------------------------------------------------ @@ -238,10 +248,7 @@ proc classifyValidatePacked*(xp: TxPoolRef; xp.chain.limits.maxLimit else: xp.chain.limits.trgLimit - - var tx = item.tx.eip1559TxNormalization - if FkLondon <= fork: - tx.gasPrice = min(tx.maxPriorityFee + baseFee.truncate(int64), tx.maxFee) + tx = item.tx.eip1559TxNormalization(xp.chain.baseFee, fork) roDB.validateTransaction(tx, item.sender, gasLimit, baseFee, fork) diff --git a/nimbus/utils/tx_pool/tx_tasks/tx_packer.nim b/nimbus/utils/tx_pool/tx_tasks/tx_packer.nim index 9b39a9a75..9075712e0 100644 --- a/nimbus/utils/tx_pool/tx_tasks/tx_packer.nim +++ b/nimbus/utils/tx_pool/tx_tasks/tx_packer.nim @@ -66,12 +66,6 @@ template safeExecutor(info: string; code: untyped) = let e = getCurrentException() raise newException(TxPackerError, info & "(): " & $e.name & " -- " & e.msg) -proc eip1559TxNormalization(tx: Transaction): Transaction = - result = tx - if tx.txType < TxEip1559: - result.maxPriorityFee = tx.gasPrice - result.maxFee = tx.gasPrice - proc persist(pst: TxPackerStateRef) {.gcsafe,raises: [Defect,RlpError].} = ## Smart wrapper @@ -87,13 +81,10 @@ proc runTx(pst: TxPackerStateRef; item: TxItemRef): GasInt {.gcsafe,raises: [Defect,CatchableError].} = ## Execute item transaction and update `vmState` book keeping. Returns the ## `gasUsed` after executing the transaction. - var - tx = item.tx.eip1559TxNormalization let fork = pst.xp.chain.nextFork - baseFee = pst.xp.chain.head.baseFee - if FkLondon <= fork: - tx.gasPrice = min(tx.maxPriorityFee + baseFee.truncate(int64), tx.maxFee) + baseFee = pst.xp.chain.head.baseFee.truncate(uint64).GasPrice + tx = item.tx.eip1559TxNormalization(baseFee, fork) safeExecutor "tx_packer.runTx": # Execute transaction, may return a wildcard `Exception` @@ -252,18 +243,6 @@ proc vmExecCommit(pst: TxPackerStateRef) xp.chain.profit = balanceDelta() xp.chain.reward = balanceDelta() -iterator rankedAccounts(xp: TxPoolRef): TxStatusNonceRef - {.gcsafe,raises: [Defect,KeyError].} = - ## Loop over staged accounts ordered by - ## + local ranks, higest one first - ## + remote ranks, higest one first - for (account,nonceList) in xp.txDB.decAccount(txItemStaged): - if xp.txDB.isLocal(account): - yield nonceList - for (account,nonceList) in xp.txDB.decAccount(txItemStaged): - if not xp.txDB.isLocal(account): - yield nonceList - # ------------------------------------------------------------------------------ # Public functions # ------------------------------------------------------------------------------ @@ -277,7 +256,7 @@ proc packerVmExec*(xp: TxPoolRef) {.gcsafe,raises: [Defect,CatchableError].} = var pst = xp.vmExecInit block loop: - for nonceList in pst.xp.rankedAccounts: + for (_,nonceList) in pst.xp.txDB.packingOrderAccounts(txItemStaged): block account: for item in nonceList.incNonce: diff --git a/tests/test_txpool.nim b/tests/test_txpool.nim index 9942e9379..880a4eebe 100644 --- a/tests/test_txpool.nim +++ b/tests/test_txpool.nim @@ -60,6 +60,9 @@ const # parameter should be above 100%. decreasingBlockProfitRatioPC = 92 + # Make some percentage of the accounts local accouns. + accountExtractPC = 10 + # test block chain networkId = GoerliNet # MainNet @@ -69,15 +72,18 @@ var prng = prngSeed.initRand - # to be set up in runTxLoader() + # To be set up in runTxLoader() statCount: array[TxItemStatus,int] # per status bucket txList: seq[TxItemRef] effGasTips: seq[GasPriceEx] - # running block chain + # Running block chain bcDB: BaseChainDB + # Accounts to be considered local + localAccounts: seq[EthAddress] + # ------------------------------------------------------------------------------ # Helpers # ------------------------------------------------------------------------------ @@ -177,6 +183,13 @@ proc runTxLoader(noisy = true; capture = loadSpecs) = loadTxs = capture.numTxs, noisy = veryNoisy) + # Extract some of the least profitable accounts and hold them so + # they could be made local at a later stage + let + accr = xp.accountRanks + nExtract = (accr.remote.len * accountExtractPC + 50) div 100 + localAccounts = accr.remote[accr.remote.len - nExtract .. ^1] + # Make sure that sample extraction from file was ok check capture.minBlockTxs <= nTxs check capture.numTxs == xp.nItems.total @@ -742,7 +755,9 @@ proc runTxPackerTests(noisy = true) = block: var - xq = bcDB.toTxPool(txList, ntBaseFee, noisy = noisy) + xq = bcDB.toTxPool(txList, ntBaseFee, + local = localAccounts, + noisy = noisy) let (nMinTxs, nTrgTxs) = (15, 15) (nMinAccounts, nTrgAccounts) = (1, 8) @@ -814,22 +829,13 @@ proc runTxPackerTests(noisy = true) = " increase=", xq.gasCumulative - smallerBlockSize, " trg/max=", blockProfitRatio, "%" - let - accountExtractPC = 10 - acc = xq.accountRanks - nExtract = (acc.remote.len * accountExtractPC + 50) div 100 - accExtracted = acc.remote[acc.remote.len - nExtract .. ^1] - - noisy.say "***", "accounts", - " local=", acc.local.pp, - " remote=", acc.remote.pp, - " remote.len=", acc.remote.len, - " nExtract=", nExtract, - " accExtracted=", accExtracted.pp - # if true: return test "Store generated block in block chain database": + noisy.say "***", "locality", + " locals=", xq.accountRanks.local.len, + " remotes=", xq.accountRanks.remote.len + # Force maximal block size. Accidentally, the latest tx should have # a `gasLimit` exceeding the available space on the block `gasLimit` # which will be checked below. @@ -875,11 +881,15 @@ proc runTxPackerTests(noisy = true) = # much less than permitted so this block will be accepted. check 0 < overlap + #setTraceLevel() + # Test low-level function for adding the new block to the database xq.chain.maxMode = (packItemsMaxGasLimit in xq.flags) xq.chain.clearAccounts check xq.chain.vmState.processBlock(poa, hdr, bdy).isOK + setErrorLevel() + # Re-allocate using VM environment from `persistBlocks()` check BaseVMState.new(hdr, bcDB).processBlock(poa, hdr, bdy).isOK