Mining: Skip recent transactions if fee difference is small #10200

pull sdaftuar wants to merge 13 commits into bitcoin:master from sdaftuar:2017-04-dont-mine-recent-tx changing 10 files +636 −111
  1. sdaftuar commented at 7:59 pm on April 12, 2017: member

    After compact blocks (BIP 152), including recently received transactions in a block may lead to propagation delays relative to blocks without such transactions, because recently received transactions may not yet be in peers’ mempools.

    Account for this in CreateNewBlock() by adding two new parameters:

    • -recenttxwindow defines a “recent transaction” as having been received less than this many milliseconds ago
    • -recenttxstalerate models the additional likelihood of a block being stale because it includes a recent transaction.

    Briefly, if I_0 is the block income you’d receive using only non-recent transactions, and I_1 is the income including recently received transactions, and s is the value being used for -recenttxstalerate, then CreateNewBlock is now going to choose the block that doesn’t include recent transactions if I_0 >= (1-s) * I_1.

    Notes for reviewers:

    • I struggled to name and explain these two parameters, so suggestions on alternate/less confusing parameter names and explanation would be appreciated! @ryanofsky suggested -recenttxblockdiscount or -recenttxminingpenalty to try to describe what I called the stale rate; I think those are reasonable suggestions as well.
    • Suggestions are also welcome for what the defaults should be (in particular, should this be enabled at all by default?)
    • In my most recent performance measurements of a slightly earlier version of this patch, I measured an approximately 3ms slowdown (~ 7ms -> ~ 10ms) in the transaction selection portion of CreateNewBlock(). I suspect we can optimize CreateNewBlock further, but I’d like to save that for a future PR as this is already a lengthy patch.
    • The idea in this PR is to produce two block candidates, one with recent transactions and one without, and then compare block income and pick a winner. The candidate which does not include recent transactions is not going to be as good in general as the regular ancestor-feerate-mining algorithm being called on a mempool that doesn’t contain any recent transactions, because for performance reasons we might continue to include low-fee ancestors of recent transactions (ie, we kick out recent transactions from the block, but not their ancestors). Despite this, I think for most reasonable parameters we will be overwhelmingly choosing the block that doesn’t include recent transactions, so I don’t think this is a big deal at the moment. We could try to improve this in the future, however (possibly by adding a smart caching layer on top).
    • This PR includes a (slow) functional test for the mining code, which I’ve added to the extended test suite.

    Thanks @JeremyRubin and @ryanofsky for feedback on an earlier version of this patch.

  2. fanquake added the label Mining on Apr 12, 2017
  3. gmaxwell commented at 8:27 pm on April 14, 2017: contributor
    Concept ACK.
  4. luke-jr commented at 6:46 pm on April 19, 2017: member
    Concept OK, not so sure about implementation. This seems to be all-or-nothing with regard to recent transactions - wouldn’t you want to evaluate it as a gradient (eg, maybe it makes sense to include 29-second-old txs, but not newer)?
  5. in src/miner.h:35 in a986a97ed4 outdated
    30+        block(tmpl.block), vTxFees(tmpl.vTxFees),
    31+        vTxSigOpsCost(tmpl.vTxSigOpsCost),
    32+        vchCoinbaseCommitment(tmpl.vchCoinbaseCommitment)
    33+    {
    34+        block.vtx = tmpl.block.vtx;
    35+    }
    


    luke-jr commented at 6:46 pm on April 19, 2017:
    How does this differ from the default copy constructor?

    sdaftuar commented at 4:41 pm on April 20, 2017:

    Apparently it doesn’t (I was confused by whether a CBlock could be copy-constructed from another CBlock). Will remove.

    Removed in dfd4e73c97818eb57bdf9e34a9085904a5250a92

  6. gmaxwell commented at 0:42 am on April 20, 2017: contributor

    @luke-jr You could go hog wild with multiple choices, >X, >Y, >Z second old.. but if its tuned right it should pretty much always be choosing to exclude recent transactions, and the other alternative is just so that you don’t regret your decisions when some bozo sends a very high fee transaction. The marginal gain from more options is going to be pretty small compared to the block creation delay from the CPU costs in considering more options– and what you’ll be mining on instead. E.g. spending another 10ms mining on an empty block is probably not worth whatever gain you could have in considering another recentness level.

    It’s also the case that block propagation performance between miners is probably pretty consistent and narrowly spread… meaning that either you made a block that could get everywhere with 0-rtt or you didn’t– and there probably isn’t a lot of space in-between. So I think thats what this is modeling: the no-recent alternative will make it ~everywhere with no round trips, and the other one is going to take a round trip– so you only want it if it pays enough more to justify the increased orphan risk.

  7. luke-jr commented at 0:45 am on April 20, 2017: member
    I didn’t mean more options, just that the algorithm could evaluate the age-cost/value initially when building the template rather than building the entire template twice. :)
  8. sdaftuar commented at 3:29 pm on April 21, 2017: member

    I didn’t mean more options, just that the algorithm could evaluate the age-cost/value initially when building the template rather than building the entire template twice. :) @luke-jr Well note that this implementation doesn’t build the entire template twice; it copies the template, and throws out the recent transactions and rebuilds from there.

    If I understand it right, I think it’s hard to make your suggestion work: the fee adjustment based on time-received is not independent of the other choices you make when selecting transactions (in modeling the binary “0 vs >=1” round trips to relay a block), and trying to model it with an implementation that makes a single pass, where some transactions are evaluated with only local information, is hard to get right. Either you’ll include recent transactions too often because your criteria is too loose, or if your criteria is tighter, you risk giving up too much fee income if network conditions change.

  9. sdaftuar force-pushed on May 6, 2017
  10. sdaftuar commented at 9:59 am on May 6, 2017: member
    Rebased
  11. sdaftuar force-pushed on May 6, 2017
  12. in src/miner.cpp:356 in 9ae85a6965 outdated
    348@@ -349,10 +349,6 @@ void BlockAssembler::addPackageTxs(int &nPackagesSelected, int &nDescendantsUpda
    349     // Keep track of entries that failed inclusion, to avoid duplicate work
    350     CTxMemPool::setEntries failedTx;
    351 
    352-    // Start by adding all descendants of previously added txs to mapModifiedTx
    353-    // and modifying them for their already included ancestors
    354-    UpdatePackagesForAdded(inBlock, mapModifiedTx);
    


    ryanofsky commented at 6:11 pm on June 9, 2017:

    In commit “Eliminate unnecessary call to UpdatePackagesForAdded”

    Note: reason this call is not needed is that resetBlock is called right before addPackageTxs, so inBlock will be empty.

  13. in src/miner.cpp:193 in 1d1191ddbd outdated
    165@@ -166,9 +166,13 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
    166     // transaction (which in most cases can be a no-op).
    167     fIncludeWitness = IsWitnessEnabled(pindexPrev, chainparams.GetConsensus()) && fMineWitnessTx;
    168 
    169+    // mapModifiedTx will store sorted packages after they are modified
    170+    // because some of their txs are already in the block
    171+    indexed_modified_transaction_set mapModifiedTx;
    


    ryanofsky commented at 6:13 pm on June 9, 2017:

    In commit “Move mapModifiedTx outside of addPackageTxs”

    Note: Reason for the move is to be able to reuse mapModifiedTx when a second addPackageTxs call is added later (to refill the block after removing recent transactions).

  14. in src/miner.h:150 in 1cfa4a9740 outdated
    133@@ -134,25 +134,30 @@ struct update_for_parent_inclusion
    134 class BlockAssembler
    135 {
    136 private:
    137-    // The constructed block template
    138-    std::unique_ptr<CBlockTemplate> pblocktemplate;
    139-    // A convenience pointer that always refers to the CBlock in pblocktemplate
    140-    CBlock* pblock;
    141+    struct WorkingState {
    


    ryanofsky commented at 6:15 pm on June 9, 2017:

    In commit “Encapsulate working state for a single call to CNB”

    Note: Reason for moving state to this struct is to be able to make a second addPackageTxs() call in a later commit without losing the state of the first call.

  15. in src/miner.cpp:352 in 47c933cd05 outdated
    348@@ -348,7 +349,7 @@ void BlockAssembler::addPackageTxs(WorkingState &workState, int &nPackagesSelect
    349     {
    350         // First try to find a new transaction in mapTx to evaluate.
    351         if (mi != mempool.mapTx.get<ancestor_score>().end() &&
    352-                SkipMapTxEntry(workState, mempool.mapTx.project<0>(mi), mapModifiedTx, failedTx)) {
    353+                SkipMapTxEntry(workState, mempool.mapTx.project<0>(mi), mapModifiedTx, failedTx, nTimeCutoff)) {
    


    ryanofsky commented at 6:36 pm on June 9, 2017:

    In commit “Mining: Add not-yet-used option to avoid recent transactions”

    I think it might be slightly better to do || mi->GetTime() > nTimeCutoff here instead of modifying SkipMapTxEntry, both for consistency with the mapModifiedTx check below, and because SkipMapTxEntry is already a pretty overloaded function with a paragraph of text describing it, so it’s not like it needs another job.

    (Update: done in new version of PR)

  16. in src/miner.cpp:292 in 65af6c9860 outdated
    287+            vTxSigOpsCostCopy.push_back(workState.pblocktemplate->vTxSigOpsCost[0]);
    288+            continue;
    289+        }
    290+        CTxMemPool::txiter it = mempool.mapTx.find(ptx->GetHash());
    291+        assert(it != mempool.mapTx.end());
    292+        if (it->GetTime() < timeCutoff && descendantTransactions.count(it) == 0) {
    


    ryanofsky commented at 6:46 pm on June 9, 2017:

    In commit “Add helper to remove transactions from block”

    Strictly speaking < should probably be <= to be consistent with the addPackageTxs and SkipMapTxEntry code also applying the cutoff.

    (Update: fixed in new version of PR)

  17. in src/miner.cpp:279 in 65af6c9860 outdated
    274+    std::vector<CAmount> vTxFeesCopy;
    275+    vTxFeesCopy.reserve(vtxCopy.size());
    276+    std::vector<int64_t> vTxSigOpsCostCopy;
    277+    vTxSigOpsCostCopy.reserve(vtxCopy.size());
    278+
    279+    CTxMemPool::setEntries skippedTransactions;
    


    ryanofsky commented at 6:56 pm on June 9, 2017:

    In commit “Add helper to remove transactions from block

    Maybe should be called “removed” rather than “skipped” for consistency with the function name (especially since this gets exposed in the next commit).

    (Update: variable is removed in new version of PR)

  18. in src/miner.cpp:376 in c392582000 outdated
    371+{
    372+    for (const CTxMemPool::txiter it : removed) {
    373+        CTxMemPool::setEntries descendants;
    374+        mempool.CalculateDescendants(it, descendants);
    375+        for(CTxMemPool::txiter desc : descendants) {
    376+            if (removed.count(desc)) {
    


    ryanofsky commented at 7:13 pm on June 9, 2017:

    In commit “Skip recent transactions in CNB if fee difference is small”

    Could you add a comment explaining why this skips updating mapModifiedTx entries of transactions that were removed? I’m probably misunderstanding something, but It seems like if you skip those, you won’t be updating any mapModifiedTx entries at all, because RemoveRecentTransactionsFromBlock also removes descendants.


    sdaftuar commented at 8:57 pm on August 15, 2017:

    FYI I’ve reworked this code in the latest version, combining the removal of transactions and updating mapModifiedTx, as this was needlessly inefficient (with an extra call to CalculateDescendants).

    But to answer your question: mapModifiedTx contains transactions that are not in the constructed block which have ancestors that are in the block. When we remove a transaction from the block, we need to remove any in-block descendants (which won’t be in mapModifiedTx), but we need to update the ancestor feerate score for the entries in mapModifiedTx, so that the packages are properly sorted.

    Since we generally should never select those transactions on the next call to addPackageTxs (outside of reorg situations, the arrival time of a descendant should be later than the arrival time of the parent), it might be better to just remove those descendants from mapModifiedTx rather than correct the package information, but I think its simpler to reason about the code if mapModifiedTx is always sorted correctly.

  19. in src/miner.cpp:253 in c392582000 outdated
    229+    if (!TestBlockValidity(state, chainparams, *winner->pblock, pindexPrev, false, false)) {
    230         throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state)));
    231     }
    232     int64_t nTime2 = GetTimeMicros();
    233 
    234     LogPrint(BCLog::BENCH, "CreateNewBlock() packages: %.2fms (%d packages, %d updated descendants), validity: %.2fms (total %.2fms)\n", 0.001 * (nTime1 - nTimeStart), nPackagesSelected, nDescendantsUpdated, 0.001 * (nTime2 - nTime1), 0.001 * (nTime2 - nTimeStart));
    


    ryanofsky commented at 7:20 pm on June 9, 2017:

    In commit “Skip recent transactions in CNB if fee difference is small”

    It seems like this will always log information about the noRecent block even if it wasn’t the winner. Maybe move nPackagesSelected and nDescendantsUpdated variables into the state struct to keep separate copies of them and print them here. (This would also cut down on the number of arguments you need to pass to addPackageTxs.)


    sdaftuar commented at 7:11 pm on August 16, 2017:

    My thought was that nPackagesSelected and nDescendantsUpdated are mostly here to help parameterize the runtime, as we expect this to be slower the more packages are looked at and the more descendants that have to be walked to update scores.

    I guess it can be a little confusing if someone might try to interpret this as the actual number of packages in the block, because that will be incorrect after this PR…

  20. in src/init.cpp:500 in 73eaf01453 outdated
    488@@ -489,6 +489,8 @@ std::string HelpMessage(HelpMessageMode mode)
    489     strUsage += HelpMessageOpt("-blockmaxweight=<n>", strprintf(_("Set maximum BIP141 block weight (default: %d)"), DEFAULT_BLOCK_MAX_WEIGHT));
    490     strUsage += HelpMessageOpt("-blockmaxsize=<n>", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE));
    491     strUsage += HelpMessageOpt("-blockmintxfee=<amt>", strprintf(_("Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE)));
    492+    strUsage += HelpMessageOpt("-recenttxwindow=<n>", strprintf(_("Set window size in milliseconds for defining transactions as 'recently received' (default: %d)"), DEFAULT_RECENT_TX_WINDOW));
    493+    strUsage += HelpMessageOpt("-recenttxstalerate=<n>", strprintf(_("Set stale (orphan) rate for blocks that contain 'recently received' transactions. This is the probability that block income will be lost given that a recently received transaction was included; transaction selection skips over recently received transactions (defined by -recenttxwindow) unless doing so would result in a decrease of block income exceeding this rate (default: %f)"), DEFAULT_RECENT_TX_STALE_RATE));
    


    ryanofsky commented at 7:35 pm on June 9, 2017:

    In commit “Mining: configure ‘recency’ and ‘stale rate’ from command line”

    I would maybe say “if a recently received transaction were included” instead of “given that a recently received transaction was included.” It doesn’t seem like “given” is right here in the probabilistic sense.


    sdaftuar commented at 8:59 pm on August 15, 2017:
    I think “given” is right in the probabilistic sense here; I think I do mean P(stale block | block has recent tx).

    ryanofsky commented at 3:56 pm on August 30, 2017:
    Makes sense. I don’t actually remember what I meant in that comment.
  21. in test/functional/mining.py:40 in 5d07339a06 outdated
    35+                          { 'window': 50000, 'stalerate': 0.001}
    36+                        ]
    37+
    38+        extra_args = [["-debug", "-blockmaxweight=400000", "-minrelaytxfee=0"]
    39+                      for i in range(self.num_nodes)]
    40+        for i in range(3):
    


    ryanofsky commented at 7:49 pm on June 9, 2017:

    In commit “Add functional test for CreateNewBlock”

    Could go with for extra_arg, cnb_arg in zip(extra_args, self.cnb_args): to shorten code below and avoid hardcoded 3.

  22. in test/functional/mining.py:92 in 5d07339a06 outdated
    87+        weight = 0
    88+        ancestor_size = 0
    89+        for txid,entry in mempool.items():
    90+            # Scale by the witness multiplier, since we get vsize back
    91+            weight += entry['size'] * 4
    92+            ancestor_size += entry['ancestorsize']
    


    ryanofsky commented at 7:54 pm on June 9, 2017:

    In commit “Add functional test for CreateNewBlock”

    ancestor_size unused

  23. in test/functional/mining.py:31 in 5d07339a06 outdated
    26+    def __init__(self):
    27+        self.num_nodes = 6
    28+        self.setup_clean_chain = True
    29+
    30+    def setup_network(self, split=False):
    31+        # Set blockmaxweight to be low, to require fewer transactions
    


    ryanofsky commented at 8:02 pm on June 9, 2017:

    In commit “Add functional test for CreateNewBlock”

    Could move comment down closer to -blockmaxweight setting.

  24. in test/functional/mining.py:84 in 5d07339a06 outdated
    79+                    raise AssertionError("Unexpected JSON-RPC error: "+e.error['message'])
    80+            except Exception as e:
    81+                raise AssertionError("Unexpected exception raised: "+type(e).__name__)
    82+        self.sync_all()
    83+
    84+    # Approximate weight of the transactions in the mempool
    


    ryanofsky commented at 8:03 pm on June 9, 2017:

    In commit “Add functional test for CreateNewBlock”

    Some of the comments on top these python functions could be made into docstrings.

  25. in test/functional/mining.py:331 in 5d07339a06 outdated
    326+        for i in range(3):
    327+            self.test_recent_transactions_excluded(self.nodes[i], recent_tx_window=self.cnb_args[i]['window'], recent_tx_stale_rate=self.cnb_args[i]['stalerate'])
    328+
    329+        self.sync_all()
    330+        self.log.info("Running test_ancestor_feerate_sort")
    331+        self.test_ancestor_feerate_sort(self.nodes[0], recent_tx_window=self.cnb_args[i]['window'])
    


    ryanofsky commented at 8:05 pm on June 9, 2017:

    In commit “Add functional test for CreateNewBlock”

    i should be 0 here I think

  26. in test/functional/mining.py:99 in 5d07339a06 outdated
    94+
    95+    # Requires mempool to be populated ahead of time
    96+    def test_max_block_weight(self, node):
    97+        block_max_weight = 400000 - 4000 # 4000 reserved for coinbase
    98+        total_weight = 0
    99+        assert(self.get_mempool_weight(node) > block_max_weight)
    


    ryanofsky commented at 8:10 pm on June 9, 2017:

    In commit “Add functional test for CreateNewBlock”

    assert isn’t really a function call in python, so the parentheses aren’t needed. Also asserts are skipped if python is running with optimizations (-O), so maybe prefer to use one of the test framework assert functions.

    Also lots of if statements in this test with extra parentheses.

  27. in test/functional/mining.py:196 in 5d07339a06 outdated
    191+
    192+    def test_recent_transactions_excluded(self, node, recent_tx_window, recent_tx_stale_rate):
    193+        # Repeatedly try to call getblocktemplate, using mocktime to choose
    194+        # random points at which to call gbt
    195+        # If the block template contains recently-received transactions:
    196+        # - Prioritise all recent transactions to 0 fee, and then call gbt again
    


    ryanofsky commented at 8:18 pm on June 9, 2017:

    In commit “Add functional test for CreateNewBlock”

    I guess it’s too late for the prioritize spelling to catch on in bitcoin.

  28. in test/functional/mining.py:203 in 5d07339a06 outdated
    198+        # Otherwise:
    199+        # - Verify that the block fee is within our tolerance of the highest fee
    200+        #   block we've seen so far.
    201+
    202+        # First, convert recent_tx_window from milliseconds to seconds.
    203+        recent_tx_window = int((recent_tx_window+999)/1000)
    


    ryanofsky commented at 8:20 pm on June 9, 2017:

    In commit “Add functional test for CreateNewBlock”

    Can use // operator for integer division in python

  29. in test/functional/mining.py:258 in 5d07339a06 outdated
    253+                        node.prioritisetransaction(txid, int(-(entry["fee"]*COIN)))
    254+                        if (txid in template_txids):
    255+                            # The mining algorithm will include non-recent
    256+                            # ancestors of recently included transactions.
    257+                            # If this is improved in the future, this portion
    258+                            # of the test would need to change.
    


    ryanofsky commented at 8:41 pm on June 9, 2017:

    In commit “Add functional test for CreateNewBlock”

    Maybe spell this out a little more. It seems like the point of setting fees to 0 is to force gbt to generate and return the noRecent block which lost the competition against the recent block. But if the noRecent block currently includes these ancestor transactions, then why set their fees to 0 here?

  30. ryanofsky commented at 8:53 pm on June 9, 2017: member
    utACK 4ae84e3f5ae433b88aaa418a1ce615454603d4ca. All comments minor, so feel free to ignore.
  31. gmaxwell commented at 5:46 pm on July 18, 2017: contributor
    @sdaftuar Needs rebase soonish. Lets get this in after branching.
  32. sdaftuar force-pushed on Aug 15, 2017
  33. sdaftuar force-pushed on Aug 15, 2017
  34. Eliminate unnecessary call to UpdatePackagesForAdded
    This is unnecessary now that priority transaction selection is gone.
    9cea7e3715
  35. Move mapModifiedTx outside of addPackageTxs
    This will allow the updated package scores to be reused in a second call to
    addPackageTxs that will be added later.
    b9d26428ce
  36. Encapsulate working state for a single call to CreateNewBlock()
    Create block working state in CreateNewBlock(), and pass where needed.
    86cf1386db
  37. Mining: Add not-yet-used option to avoid recent transactions in addPackageTxs 92aa822339
  38. Skip recent transactions in CNB if fee difference is small e8dcf5018c
  39. Mining: configure 'recency' and 'stale rate' from command line
    This adds two command line arguments, -recenttxwindow and
    -recenttxstalerate, which control the new transaction selection model.
    
    Disables new mining behavior by default in functional tests by setting
    -recenttxstalerate=0.
    72e2ab0378
  40. [qa] random_transaction can use unconfirmed inputs 23e212410f
  41. [qa] mininode.ToHex() always serializes with witness 5b4b0854f6
  42. sdaftuar force-pushed on Aug 16, 2017
  43. sdaftuar commented at 7:11 pm on August 16, 2017: member
    Rebased.
  44. [qa] Add functional test for CreateNewBlock 9662d42142
  45. sdaftuar force-pushed on Aug 29, 2017
  46. in src/miner.h:141 in 86cf1386db outdated
    140+    struct WorkingState {
    141+        WorkingState() :
    142+            pblocktemplate(new CBlockTemplate()),
    143+            pblock(&pblocktemplate->block),
    144+            // Reserve space for coinbase tx
    145+            nBlockWeight(4000),
    


    ryanofsky commented at 3:00 pm on August 30, 2017:

    In commit “Encapsulate working state for a single call to CreateNewBlock()”

    Might be good to make these 4000/1000 values constants since they are used in other places.


    sdaftuar commented at 7:43 pm on September 5, 2017:
    Holding off on this one until after #11100, which I think will cause a rebase for this PR anyway.
  47. in src/miner.h:203 in 86cf1386db outdated
    198@@ -172,32 +199,30 @@ class BlockAssembler
    199 
    200 private:
    201     // utility functions
    202-    /** Clear the block's state and prepare for assembling a new block */
    203-    void resetBlock();
    204     /** Add a tx to the block */
    205-    void AddToBlock(CTxMemPool::txiter iter);
    206+    void AddToBlock(WorkingState &workState, CTxMemPool::txiter iter);
    


    ryanofsky commented at 3:09 pm on August 30, 2017:

    In commit “Encapsulate working state for a single call to CreateNewBlock()”

    Looks like AddToBlock, addPackageTxs, onlyUnconfirmed, TestPackageTransactions, SkipMapTxEntry, SortForBlock, and UpdatePackagesForAdded could all be const methods now.


    sdaftuar commented at 7:42 pm on September 5, 2017:
    Done in 97d439af0feb8b4ccc34e7226cd11a791393c93a
  48. in src/miner.cpp:206 in e8dcf5018c outdated
    204-    nLastBlockWeight = workState.nBlockWeight;
    205+    // Now compare and decide which block to use
    206+    WorkingState *winner = &workState;
    207+    // TODO: replace this with a configurable threshold
    208+    CAmount blockSubsidy = GetBlockSubsidy(nHeight, chainparams.GetConsensus());
    209+    if (blockSubsidy + noRecentWorkState.nModifiedFees >= 1.0 * (workState.nModifiedFees + blockSubsidy)) {
    


    ryanofsky commented at 4:18 pm on August 30, 2017:

    In commit “Skip recent transactions in CNB if fee difference is small”

    Since this isn’t penalizing recent transactions yet, does this commit change behavior at all? Might be worth mentioning if it does in the commit message.


    sdaftuar commented at 6:05 pm on August 31, 2017:
    This does change behavior, both performance and there’s some chance that you choose different transactions (eg if you get the same fees or higher by looking at only an older set of transactions, which is possible, eg if all the fees in the mempool were the same, or just by chance due to what ends up fitting in the block etc).
  49. in src/miner.cpp:341 in e8dcf5018c outdated
    336+        // TODO: Store the mempool entry iterators in another vector to save this
    337+        // lookup.
    338+        CTxMemPool::txiter it = mempool.mapTx.find(ptx->GetHash());
    339+        assert(it != mempool.mapTx.end());
    340+        // Keep any transactions that are sufficiently old, but skip transactions
    341+        // that depend on removed transactions.  (Note that it's technically
    


    ryanofsky commented at 5:50 pm on August 30, 2017:

    In commit “Skip recent transactions in CNB if fee difference is small”

    Should this say older? Child having a newer arrival time sounds like the normal case.


    sdaftuar commented at 7:42 pm on September 5, 2017:
    Thanks, fixed.
  50. in test/functional/createnewblock.py:247 in 9662d42142 outdated
    242+
    243+            # Check that calling gbt on the given node produces a block
    244+            # within the expected tolerance of max_income.
    245+            # Return true if the returned block includes recent transactions;
    246+            # false otherwise
    247+            def check_gbt_results(node, max_income, cnb_args, cur_time):
    


    ryanofsky commented at 6:23 pm on August 30, 2017:

    In commit “[qa] Add functional test for CreateNewBlock”

    Don’t need to pass max_income and cur_time variables as arguments since they are already in scope.


    sdaftuar commented at 7:43 pm on September 5, 2017:
    Fixed in 497ae4844922caf4e2244b847a5a63fd2d1a999a
  51. in src/miner.cpp:69 in 72e2ab0378 outdated
    65@@ -65,7 +66,8 @@ BlockAssembler::Options::Options() {
    66     blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE);
    67     nBlockMaxWeight = DEFAULT_BLOCK_MAX_WEIGHT;
    68     nBlockMaxSize = DEFAULT_BLOCK_MAX_SIZE;
    69-    nRecentTxWindow = 10; // TODO: make this configurable
    70+    nRecentTxWindow = DEFAULT_RECENT_TX_WINDOW;
    


    ryanofsky commented at 6:26 pm on August 30, 2017:

    In commit “Mining: configure ‘recency’ and ‘stale rate’ from command line”

    Should convert milliseconds -> seconds?


    sdaftuar commented at 7:42 pm on September 5, 2017:
    done
  52. in test/functional/createnewblock.py:239 in 9662d42142 outdated
    234+            # will be consistent.
    235+            cur_time = int(time.time())
    236+            [node.setmocktime(cur_time) for node in self.nodes]
    237+
    238+            # Check that total block reward is within stale_rate of node3
    239+            max_income = self.nodes[3].getblocktemplate({
    


    ryanofsky commented at 6:44 pm on August 30, 2017:

    In commit “[qa] Add functional test for CreateNewBlock”

    Could you add a comment saying why it is right to treat node 3 income as max income. This seems like it would be correct if node 3 were started with -recenttxwindow=0 -recenttxstalerate=0, but nodes 0 & 2 have shorter recent window and stale rate values (respectively) than node 3.


    sdaftuar commented at 5:17 pm on September 5, 2017:
    I made the functional tests default to having -recenttxstalerate=0, to avoid breakage of existing tests due to this new behavior (see the change to test_node.py in https://github.com/bitcoin/bitcoin/pull/10200/commits/72e2ab0378c2baa13b6f1e80b3adcc302787c367#diff-86294e5ae5283eebdd9f98d79007a0e1). I can add a comment as well…
  53. in test/functional/createnewblock.py:160 in 9662d42142 outdated
    155+        template = node.getblocktemplate({"rules": ["segwit"]})
    156+        block_txids = [x["txid"] for x in template["transactions"]]
    157+        mempool_txids = node.getrawmempool()
    158+
    159+        # Find a transaction that has exactly one ancestor that is also not in
    160+        # the block.
    


    ryanofsky commented at 6:53 pm on August 30, 2017:

    In commit “[qa] Add functional test for CreateNewBlock”

    Can you expand comment to say why the transaction should exactly one ancestor not in the block. I notice in the previous version of the test it was looking for a transaction with at least one ancestor not in the block.


    sdaftuar commented at 5:40 pm on August 31, 2017:

    Yeah that’s worth explaining. The issue was that if you got unlucky you might find a transaction with, eg, 2 ancestors, one in the block and one not in the block. Then the ancestor feerate stats for the child tx would not be correct for determining how much to fee bump (eg you might be including some higher feerate parent in the calculation!) which could cause the test to fail.

    By requiring that we look for a transaction with exactly one not-in-block parent, we can exclude edge cases where other transactions might affect the package feerate used in CNB.

  54. in test/functional/createnewblock.py:285 in 9662d42142 outdated
    280+                self.add_empty_block(self.nodes[0])
    281+            self.sync_all()
    282+
    283+        if sum(recent_tx_block_count) == 3 * max_iterations:
    284+            self.log.warn(
    285+                "Warning: every block contained recent transactions!"
    


    ryanofsky commented at 7:00 pm on August 30, 2017:

    In commit “[qa] Add functional test for CreateNewBlock”

    Does it make sense to warn if no block contains recent transactions?


    sdaftuar commented at 7:43 pm on September 5, 2017:
    Done in 497ae4844922caf4e2244b847a5a63fd2d1a999a
  55. ryanofsky commented at 7:11 pm on August 30, 2017: member

    utACK 9662d42142e7efa8e5add8e6953566c95e7735cc. Looks good, left minor comments. Changes since last review: rebase, combining commits, getting rid of UpdatePackagesForRemoved / skippedTransactions, reverting SkipMapTxEntry, rewriting recent_transactions test, and minor changes to other tests.

    New recent transactions test seems less fragile, though doesn’t do as much to verify recent transactions actually get excluded. Maybe it would be possible to check this in a simple way with recenttxstalerate=1.

  56. Make some BlockAssembler methods const 97d439af0f
  57. fixup! Mining: configure 'recency' and 'stale rate' from command line c94248c910
  58. fixup! Skip recent transactions in CNB if fee difference is small 35882579ba
  59. fixup! [qa] Add functional test for CreateNewBlock 497ae48449
  60. sdaftuar commented at 7:44 pm on September 5, 2017: member
    Updated to address @ryanofsky’s comments. I think I managed to avoid a rebase due to test framework changes with a hacky change to the new test in this PR, but I can squash/rebase whenever reviewers are ready.
  61. ryanofsky commented at 5:57 pm on September 7, 2017: member
    utACK 497ae4844922caf4e2244b847a5a63fd2d1a999a. The three new commits all look good, and the new test comment is especially helpful.
  62. sdaftuar commented at 5:02 pm on October 5, 2017: member
    Not sure if/when I’ll pick this up, so closing for now.
  63. sdaftuar closed this on Oct 5, 2017

  64. ryanofsky commented at 5:32 pm on October 5, 2017: member
    @sdaftuar, I’d volunteer to periodically rebase this & respond to review comments if that’s the only thing holding this back.
  65. TheBlueMatt commented at 9:55 pm on October 5, 2017: member
    I would also like to see this one happen, sorry I’ve been lax on review, too many PRs :(.
  66. sdaftuar commented at 7:20 pm on October 13, 2017: member

    @ryanofsky gmaxwell made a good suggestion on IRC recently for simplifying the feature/functionality provided here. If I remember right, I think he suggested that we could just keep a cache of the last block generated in the BlockAssembler, and when a new call to CNB is made, after we addPackages, but before we call TBV, check to see if this criteria for recent transactions has been met – if not, return the old template and skip the TBV call.

    This should speed up calls to CreateNewBlock substantially (most of the time spent is in TestBlockValidity), and provide the benefit of only including recent transactions when its economically beneficial, with a relatively small code change.

    I think we’d also reduce the window on the cache that already exists in getblocktemplate.

  67. naumenkogs commented at 6:07 pm on July 11, 2019: member

    According to my simulation(see source code), current strategy (assuming that only coinbase tx is missing) works almost perfect (0.2 RTT or 10% overhead comparing to the best case (relaying empty block), in terms of end-to-end compact block relay between two random private nodes in the network).

    Erlay makes it even closer to optimal (0.04 RT or 2% overhead ), so it seems like this feature won’t provide much benefit at this point.

  68. MarcoFalke locked this on Dec 16, 2021

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-11-21 09:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me