From 9dedc5a229ba50d9fdf8701ac6a0c7ba17636563 Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Sat, 10 Oct 2026 17:00:11 -0400 Subject: [PATCH] Skip repeated pool rows of a tx in get_pooled_txs. --- .../impl/query/consensus/consensus_states.ipp | 8 +++++-- test/query/properties_tx.cpp | 24 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/include/bitcoin/database/impl/query/consensus/consensus_states.ipp b/include/bitcoin/database/impl/query/consensus/consensus_states.ipp index 0b2a8a2f3..de2efd1ee 100644 --- a/include/bitcoin/database/impl/query/consensus/consensus_states.ipp +++ b/include/bitcoin/database/impl/query/consensus/consensus_states.ipp @@ -171,9 +171,13 @@ code CLASS::get_pooled_txs(pool_link& cursor, tx_links& out, const auto stop = std::min(end.value, store_.pool.count().value); for (; row < stop && out.size() < limit; ++row) { + // The first row of a tx is its last committed, so this skips both its + // earlier rows and any uncommitted row (its key does not resolve to it). const tx_link link{ store_.pool.get_key(row) }; - if (!is_confirmed_tx(link)) - out.push_back(link); + if (store_.pool.first(link) != row || is_confirmed_tx(link)) + continue; + + out.push_back(link); } cursor = row == stop ? end : pool_link{ row }; diff --git a/test/query/properties_tx.cpp b/test/query/properties_tx.cpp index 9809e289d..32eda0edc 100644 --- a/test/query/properties_tx.cpp +++ b/test/query/properties_tx.cpp @@ -525,6 +525,30 @@ BOOST_AUTO_TEST_CASE(query_properties_tx__get_pooled_txs__confirmed__skipped) BOOST_REQUIRE_EQUAL(cursor, 2u); } +BOOST_AUTO_TEST_CASE(query_properties_tx__get_pooled_txs__duplicate_row__first_only) +{ + settings settings{}; + settings.path = TEST_DIRECTORY; + test::chunk_store store{ settings }; + test::query_accessor query{ store }; + BOOST_REQUIRE(!store.create(test::events_handler)); + BOOST_REQUIRE(query.initialize(test::genesis)); + BOOST_REQUIRE(query.set(test::block1, test::context, {}, false, false)); + + const auto& in = *test::tx_spend_one_hash.inputs_ptr()->front(); + in.prevout = system::to_shared(0x30, script{}); + in.metadata.parent_tx = 42; + BOOST_REQUIRE(query.set_pooled(1, test::tx_spend_one_hash, context{ bip113, 8, 9 })); + BOOST_REQUIRE(query.set_pooled(1, test::tx_spend_one_hash, context{ bip113, 8, 9 })); + + tx_links out{}; + pool_link cursor{}; + BOOST_REQUIRE_EQUAL(query.get_pooled_txs(cursor, out, pool_link{ 2 }, max_size_t), error::success); + BOOST_REQUIRE_EQUAL(out.size(), one); + BOOST_REQUIRE_EQUAL(out.front(), 1u); + BOOST_REQUIRE_EQUAL(cursor, 2u); +} + BOOST_AUTO_TEST_CASE(query_properties_tx__get_pooled_txs__limit__resumed) { settings settings{};