A quick and easy way to do this is to make indexed_rebroadcast_set a class that inherits from the boost::multi_index_container template class (defined in .cpp), forward declare it in .h and make m_attempt_tracker a unique pointer to indexed_rebroadcast_set. A few other things need to be shuffled around to make that work, but here's a rough implementation:
<details>
<summary>Diff</summary>
diff --git a/src/test/txrebroadcast_tests.cpp b/src/test/txrebroadcast_tests.cpp
index 6fdd5deb8f..d10a962ab3 100644
--- a/src/test/txrebroadcast_tests.cpp
+++ b/src/test/txrebroadcast_tests.cpp
@@ -27,34 +27,13 @@ public:
bool CheckRecordedAttempt(uint256 txhsh, int expected_count, std::chrono::microseconds expected_timestamp)
{
- const auto it = m_attempt_tracker.find(txhsh);
- if (it == m_attempt_tracker.end()) return false;
- if (it->m_count != expected_count) return false;
-
- // Check the recorded timestamp is within 2 seconds of the param passed in
- std::chrono::microseconds delta = expected_timestamp - it->m_last_attempt;
- if (delta.count() > 2) return false;
-
- return true;
- };
+ return TxRebroadcastHandler::CheckRecordedAttempt(txhsh, expected_count, expected_timestamp);
+ }
void UpdateAttempt(uint256 txhsh, int count)
{
- auto it = m_attempt_tracker.find(txhsh);
- for (int i = 0; i < count; ++i) {
- RecordAttempt(it);
- }
- };
-
- void RecordAttempt(indexed_rebroadcast_set::index<index_by_wtxid>::type::iterator& entry_it)
- {
- auto UpdateRebroadcastEntry = [](RebroadcastEntry& rebroadcast_entry) {
- rebroadcast_entry.m_last_attempt = GetTime<std::chrono::microseconds>() - 4h;
- ++rebroadcast_entry.m_count;
- };
-
- m_attempt_tracker.modify(entry_it, UpdateRebroadcastEntry);
- };
+ TxRebroadcastHandler::UpdateAttempt(txhsh, count);
+ }
void UpdateCachedFeeRate(CFeeRate new_fee_rate)
{
diff --git a/src/txrebroadcast.cpp b/src/txrebroadcast.cpp
index 3cb9abaf69..49050c0d39 100644
--- a/src/txrebroadcast.cpp
+++ b/src/txrebroadcast.cpp
@@ -10,6 +10,11 @@
#include <txrebroadcast.h>
#include <validation.h>
+#include <boost/multi_index/hashed_index.hpp>
+#include <boost/multi_index/member.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index_container.hpp>
+
/** We rebroadcast 3/4 of max block weight to reduce noise due to circumstances
* such as miners mining priority transactions. */
static constexpr unsigned int MAX_REBROADCAST_WEIGHT = 3 * MAX_BLOCK_WEIGHT / 4;
@@ -30,6 +35,46 @@ static constexpr int MAX_ENTRIES = 500;
/** The maximum age of an entry ~3 months */
static constexpr std::chrono::hours MAX_ENTRY_AGE = std::chrono::hours(3 * 30 * 24);
+/** Used for multi_index tag */
+struct index_by_last_attempt {};
+
+struct RebroadcastEntry {
+ RebroadcastEntry(std::chrono::microseconds now_time, uint256 wtxid)
+ : m_last_attempt(now_time),
+ m_wtxid(wtxid),
+ m_count(1) {}
+
+ std::chrono::microseconds m_last_attempt;
+ const uint256 m_wtxid;
+ int m_count;
+};
+
+class indexed_rebroadcast_set : public
+boost::multi_index_container<
+ RebroadcastEntry,
+ boost::multi_index::indexed_by<
+ // sorted by wtxid
+ boost::multi_index::hashed_unique<
+ boost::multi_index::tag<index_by_wtxid>,
+ boost::multi_index::member<RebroadcastEntry, const uint256, &RebroadcastEntry::m_wtxid>,
+ SaltedTxidHasher
+ >,
+ // sorted by last rebroadcast time
+ boost::multi_index::ordered_non_unique<
+ boost::multi_index::tag<index_by_last_attempt>,
+ boost::multi_index::member<RebroadcastEntry, std::chrono::microseconds, &RebroadcastEntry::m_last_attempt>
+ >
+ >
+> {};
+
+TxRebroadcastHandler::TxRebroadcastHandler(CTxMemPool& mempool, ChainstateManager& chainman)
+ : m_attempt_tracker{std::make_unique<indexed_rebroadcast_set>()}
+ , m_mempool{mempool}
+ , m_chainman{chainman}
+{}
+
+TxRebroadcastHandler::~TxRebroadcastHandler() = default;
+
std::vector<TxIds> TxRebroadcastHandler::GetRebroadcastTransactions()
{
std::vector<TxIds> rebroadcast_txs;
@@ -58,12 +103,12 @@ std::vector<TxIds> TxRebroadcastHandler::GetRebroadcastTransactions()
// Check if we have previously rebroadcasted, decide if we will this
// round, and if so, record the attempt.
- auto entry_it = m_attempt_tracker.find(wtxid);
+ auto entry_it = m_attempt_tracker->find(wtxid);
- if (entry_it == m_attempt_tracker.end()) {
+ if (entry_it == m_attempt_tracker->end()) {
// No existing entry, we will rebroadcast, so create a new one
RebroadcastEntry entry(start_time, wtxid);
- m_attempt_tracker.insert(entry);
+ m_attempt_tracker->insert(entry);
} else if (entry_it->m_count >= MAX_REBROADCAST_COUNT) {
// We have already rebroadcast this transaction the maximum number
// of times permitted, so skip rebroadcasting.
@@ -76,7 +121,12 @@ std::vector<TxIds> TxRebroadcastHandler::GetRebroadcastTransactions()
} else {
// We have rebroadcasted this transaction before, but will try
// again now.
- RecordAttempt(entry_it);
+ auto UpdateRebroadcastEntry = [](RebroadcastEntry& rebroadcast_entry) {
+ rebroadcast_entry.m_last_attempt = GetTime<std::chrono::microseconds>();
+ ++rebroadcast_entry.m_count;
+ };
+
+ m_attempt_tracker->modify(entry_it, UpdateRebroadcastEntry);
}
// Add to set of rebroadcast candidates
@@ -97,6 +147,32 @@ std::vector<TxIds> TxRebroadcastHandler::GetRebroadcastTransactions()
return rebroadcast_txs;
};
+void TxRebroadcastHandler::UpdateAttempt(uint256 txhsh, int count)
+{
+ auto it = m_attempt_tracker->find(txhsh);
+ for (int i = 0; i < count; ++i) {
+ auto UpdateRebroadcastEntry = [](RebroadcastEntry& rebroadcast_entry) {
+ rebroadcast_entry.m_last_attempt = GetTime<std::chrono::microseconds>() - 4h;
+ ++rebroadcast_entry.m_count;
+ };
+
+ m_attempt_tracker->modify(it, UpdateRebroadcastEntry);
+ }
+};
+
+bool TxRebroadcastHandler::CheckRecordedAttempt(uint256 txhsh, int expected_count, std::chrono::microseconds expected_timestamp)
+{
+ const auto it = m_attempt_tracker->find(txhsh);
+ if (it == m_attempt_tracker->end()) return false;
+ if (it->m_count != expected_count) return false;
+
+ // Check the recorded timestamp is within 2 seconds of the param passed in
+ std::chrono::microseconds delta = expected_timestamp - it->m_last_attempt;
+ if (delta.count() > 2) return false;
+
+ return true;
+};
+
void TxRebroadcastHandler::CacheMinRebroadcastFee()
{
// Update stamp of chain tip on cache run
@@ -109,33 +185,23 @@ void TxRebroadcastHandler::CacheMinRebroadcastFee()
LogPrint(BCLog::BENCH, "Caching minimum fee for rebroadcast to %s, took %d µs to calculate.\n", m_cached_fee_rate.ToString(FeeEstimateMode::SAT_VB), delta_time.count());
};
-void TxRebroadcastHandler::RecordAttempt(indexed_rebroadcast_set::index<index_by_wtxid>::type::iterator& entry_it)
-{
- auto UpdateRebroadcastEntry = [](RebroadcastEntry& rebroadcast_entry) {
- rebroadcast_entry.m_last_attempt = GetTime<std::chrono::microseconds>();
- ++rebroadcast_entry.m_count;
- };
-
- m_attempt_tracker.modify(entry_it, UpdateRebroadcastEntry);
-};
-
void TxRebroadcastHandler::TrimMaxRebroadcast()
{
// Delete any entries that are older than MAX_ENTRY_AGE
std::chrono::microseconds min_age = GetTime<std::chrono::microseconds>() - MAX_ENTRY_AGE;
- while (!m_attempt_tracker.empty()) {
- auto it = m_attempt_tracker.get<index_by_last_attempt>().begin();
+ while (!m_attempt_tracker->empty()) {
+ auto it = m_attempt_tracker->get<index_by_last_attempt>().begin();
if (it->m_last_attempt < min_age) {
- m_attempt_tracker.get<index_by_last_attempt>().erase(it);
+ m_attempt_tracker->get<index_by_last_attempt>().erase(it);
} else {
break;
}
}
// If there are still too many entries, delete the oldest ones
- while (m_attempt_tracker.size() > MAX_ENTRIES) {
- auto it = m_attempt_tracker.get<index_by_last_attempt>().begin();
- m_attempt_tracker.get<index_by_last_attempt>().erase(it);
+ while (m_attempt_tracker->size() > MAX_ENTRIES) {
+ auto it = m_attempt_tracker->get<index_by_last_attempt>().begin();
+ m_attempt_tracker->get<index_by_last_attempt>().erase(it);
}
};
diff --git a/src/txrebroadcast.h b/src/txrebroadcast.h
index 8a176a08bc..ec3ea34ed7 100644
--- a/src/txrebroadcast.h
+++ b/src/txrebroadcast.h
@@ -12,11 +12,6 @@
#include <util/time.h>
#include <validation.h>
-#include <boost/multi_index/hashed_index.hpp>
-#include <boost/multi_index/member.hpp>
-#include <boost/multi_index/ordered_index.hpp>
-#include <boost/multi_index_container.hpp>
-
struct TxIds {
TxIds(uint256 txid, uint256 wtxid) : m_txid(txid), m_wtxid(wtxid) {}
@@ -24,43 +19,15 @@ struct TxIds {
const uint256 m_wtxid;
};
-struct RebroadcastEntry {
- RebroadcastEntry(std::chrono::microseconds now_time, uint256 wtxid)
- : m_last_attempt(now_time),
- m_wtxid(wtxid),
- m_count(1) {}
-
- std::chrono::microseconds m_last_attempt;
- const uint256 m_wtxid;
- int m_count;
-};
-
-/** Used for multi_index tag */
-struct index_by_last_attempt {};
-
-using indexed_rebroadcast_set = boost::multi_index_container<
- RebroadcastEntry,
- boost::multi_index::indexed_by<
- // sorted by wtxid
- boost::multi_index::hashed_unique<
- boost::multi_index::tag<index_by_wtxid>,
- boost::multi_index::member<RebroadcastEntry, const uint256, &RebroadcastEntry::m_wtxid>,
- SaltedTxidHasher
- >,
- // sorted by last rebroadcast time
- boost::multi_index::ordered_non_unique<
- boost::multi_index::tag<index_by_last_attempt>,
- boost::multi_index::member<RebroadcastEntry, std::chrono::microseconds, &RebroadcastEntry::m_last_attempt>
- >
- >
->;
+class indexed_rebroadcast_set;
class TxRebroadcastHandler
{
public:
- TxRebroadcastHandler(CTxMemPool& mempool, ChainstateManager& chainman)
- : m_mempool(mempool),
- m_chainman(chainman){};
+ TxRebroadcastHandler(CTxMemPool& mempool, ChainstateManager& chainman);
+ ~TxRebroadcastHandler();
+
+ TxRebroadcastHandler(const TxRebroadcastHandler& other) = delete;
std::vector<TxIds> GetRebroadcastTransactions();
@@ -75,10 +42,13 @@ protected:
CFeeRate m_cached_fee_rate;
/** Keep track of previous rebroadcast attempts */
- indexed_rebroadcast_set m_attempt_tracker;
+ std::unique_ptr<indexed_rebroadcast_set> m_attempt_tracker;
+
+ /** Test only */
+ void UpdateAttempt(uint256 txhsh, int count);
- /** Update an existing RebroadcastEntry - increment count and update timestamp */
- void RecordAttempt(indexed_rebroadcast_set::index<index_by_wtxid>::type::iterator& entry_it);
+ /** Test only */
+ bool CheckRecordedAttempt(uint256 txhsh, int expected_count, std::chrono::microseconds expected_timestamp);
private:
const CTxMemPool& m_mempool;
</details>