mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-20 23:29:12 +01:00
miner: fix empty mempool case for waitNext()
Block template fees are calculated by looping over new_tmpl->vTxFees and return (early) once the fee_threshold is exceeded. This left an edge case when the mempool is empty, which this commit fixes and adds a test for. It does so by using std::accumulate instead of manual loops. Also update interface_ipc.py to account for the new behavior. Co-authored-by: Raimo33 <claudio.raimondi@protonmail.com>
This commit is contained in:
@@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
|
|
||||||
@@ -522,18 +523,13 @@ std::unique_ptr<CBlockTemplate> WaitAndCreateNewBlock(ChainstateManager& chainma
|
|||||||
|
|
||||||
// Calculate the original template total fees if we haven't already
|
// Calculate the original template total fees if we haven't already
|
||||||
if (current_fees == -1) {
|
if (current_fees == -1) {
|
||||||
current_fees = 0;
|
current_fees = std::accumulate(block_template->vTxFees.begin(), block_template->vTxFees.end(), CAmount{0});
|
||||||
for (CAmount fee : block_template->vTxFees) {
|
|
||||||
current_fees += fee;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CAmount new_fees = 0;
|
// Check if fees increased enough to return the new template
|
||||||
for (CAmount fee : new_tmpl->vTxFees) {
|
const CAmount new_fees = std::accumulate(new_tmpl->vTxFees.begin(), new_tmpl->vTxFees.end(), CAmount{0});
|
||||||
new_fees += fee;
|
Assume(options.fee_threshold != MAX_MONEY);
|
||||||
Assume(options.fee_threshold != MAX_MONEY);
|
if (new_fees >= current_fees + options.fee_threshold) return new_tmpl;
|
||||||
if (new_fees >= current_fees + options.fee_threshold) return new_tmpl;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
now = NodeClock::now();
|
now = NodeClock::now();
|
||||||
|
|||||||
@@ -113,6 +113,22 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
|
|||||||
options.coinbase_output_script = scriptPubKey;
|
options.coinbase_output_script = scriptPubKey;
|
||||||
|
|
||||||
LOCK(tx_mempool.cs);
|
LOCK(tx_mempool.cs);
|
||||||
|
BOOST_CHECK(tx_mempool.size() == 0);
|
||||||
|
|
||||||
|
// Block template should only have a coinbase when there's nothing in the mempool
|
||||||
|
std::unique_ptr<BlockTemplate> block_template = mining->createNewBlock(options);
|
||||||
|
BOOST_REQUIRE(block_template);
|
||||||
|
CBlock block{block_template->getBlock()};
|
||||||
|
BOOST_REQUIRE_EQUAL(block.vtx.size(), 1U);
|
||||||
|
|
||||||
|
// waitNext() on an empty mempool should return nullptr because there is no better template
|
||||||
|
auto should_be_nullptr = block_template->waitNext({.timeout = MillisecondsDouble{0}, .fee_threshold = 1});
|
||||||
|
BOOST_REQUIRE(should_be_nullptr == nullptr);
|
||||||
|
|
||||||
|
// Unless fee_threshold is 0
|
||||||
|
block_template = block_template->waitNext({.timeout = MillisecondsDouble{0}, .fee_threshold = 0});
|
||||||
|
BOOST_REQUIRE(block_template);
|
||||||
|
|
||||||
// Test the ancestor feerate transaction selection.
|
// Test the ancestor feerate transaction selection.
|
||||||
TestMemPoolEntryHelper entry;
|
TestMemPoolEntryHelper entry;
|
||||||
|
|
||||||
@@ -144,9 +160,9 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
|
|||||||
const auto high_fee_tx{entry.Fee(50000).Time(Now<NodeSeconds>()).SpendsCoinbase(false).FromTx(tx)};
|
const auto high_fee_tx{entry.Fee(50000).Time(Now<NodeSeconds>()).SpendsCoinbase(false).FromTx(tx)};
|
||||||
AddToMempool(tx_mempool, high_fee_tx);
|
AddToMempool(tx_mempool, high_fee_tx);
|
||||||
|
|
||||||
std::unique_ptr<BlockTemplate> block_template = mining->createNewBlock(options);
|
block_template = mining->createNewBlock(options);
|
||||||
BOOST_REQUIRE(block_template);
|
BOOST_REQUIRE(block_template);
|
||||||
CBlock block{block_template->getBlock()};
|
block = block_template->getBlock();
|
||||||
BOOST_REQUIRE_EQUAL(block.vtx.size(), 4U);
|
BOOST_REQUIRE_EQUAL(block.vtx.size(), 4U);
|
||||||
BOOST_CHECK(block.vtx[1]->GetHash() == hashParentTx);
|
BOOST_CHECK(block.vtx[1]->GetHash() == hashParentTx);
|
||||||
BOOST_CHECK(block.vtx[2]->GetHash() == hashHighFeeTx);
|
BOOST_CHECK(block.vtx[2]->GetHash() == hashHighFeeTx);
|
||||||
@@ -184,7 +200,7 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
|
|||||||
AddToMempool(tx_mempool, entry.Fee(feeToUse).FromTx(tx));
|
AddToMempool(tx_mempool, entry.Fee(feeToUse).FromTx(tx));
|
||||||
|
|
||||||
// waitNext() should return nullptr because there is no better template
|
// waitNext() should return nullptr because there is no better template
|
||||||
auto should_be_nullptr = block_template->waitNext({.timeout = MillisecondsDouble{0}, .fee_threshold = 1});
|
should_be_nullptr = block_template->waitNext({.timeout = MillisecondsDouble{0}, .fee_threshold = 1});
|
||||||
BOOST_REQUIRE(should_be_nullptr == nullptr);
|
BOOST_REQUIRE(should_be_nullptr == nullptr);
|
||||||
|
|
||||||
block = block_template->getBlock();
|
block = block_template->getBlock();
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ class IPCInterfaceTest(BitcoinTestFramework):
|
|||||||
self.log.debug("Wait for a new template")
|
self.log.debug("Wait for a new template")
|
||||||
waitoptions = self.capnp_modules['mining'].BlockWaitOptions()
|
waitoptions = self.capnp_modules['mining'].BlockWaitOptions()
|
||||||
waitoptions.timeout = timeout
|
waitoptions.timeout = timeout
|
||||||
|
waitoptions.feeThreshold = 1
|
||||||
waitnext = template.result.waitNext(ctx, waitoptions)
|
waitnext = template.result.waitNext(ctx, waitoptions)
|
||||||
self.generate(self.nodes[0], 1)
|
self.generate(self.nodes[0], 1)
|
||||||
template2 = await waitnext
|
template2 = await waitnext
|
||||||
@@ -168,6 +169,7 @@ class IPCInterfaceTest(BitcoinTestFramework):
|
|||||||
block3 = await self.parse_and_deserialize_block(template4, ctx)
|
block3 = await self.parse_and_deserialize_block(template4, ctx)
|
||||||
assert_equal(len(block3.vtx), 2)
|
assert_equal(len(block3.vtx), 2)
|
||||||
self.log.debug("Wait again, this should return the same template, since the fee threshold is zero")
|
self.log.debug("Wait again, this should return the same template, since the fee threshold is zero")
|
||||||
|
waitoptions.feeThreshold = 0
|
||||||
template5 = await template4.result.waitNext(ctx, waitoptions)
|
template5 = await template4.result.waitNext(ctx, waitoptions)
|
||||||
block4 = await self.parse_and_deserialize_block(template5, ctx)
|
block4 = await self.parse_and_deserialize_block(template5, ctx)
|
||||||
assert_equal(len(block4.vtx), 2)
|
assert_equal(len(block4.vtx), 2)
|
||||||
|
|||||||
Reference in New Issue
Block a user