0diff --git a/src/test/txrebroadcast_tests.cpp b/src/test/txrebroadcast_tests.cpp
1index 6fdd5deb8f..d10a962ab3 100644
2--- a/src/test/txrebroadcast_tests.cpp
3+++ b/src/test/txrebroadcast_tests.cpp
4@@ -27,34 +27,13 @@ public:
5
6 bool CheckRecordedAttempt(uint256 txhsh, int expected_count, std::chrono::microseconds expected_timestamp)
7 {
8- const auto it = m_attempt_tracker.find(txhsh);
9- if (it == m_attempt_tracker.end()) return false;
10- if (it->m_count != expected_count) return false;
11-
12- // Check the recorded timestamp is within 2 seconds of the param passed in
13- std::chrono::microseconds delta = expected_timestamp - it->m_last_attempt;
14- if (delta.count() > 2) return false;
15-
16- return true;
17- };
18+ return TxRebroadcastHandler::CheckRecordedAttempt(txhsh, expected_count, expected_timestamp);
19+ }
20
21 void UpdateAttempt(uint256 txhsh, int count)
22 {
23- auto it = m_attempt_tracker.find(txhsh);
24- for (int i = 0; i < count; ++i) {
25- RecordAttempt(it);
26- }
27- };
28-
29- void RecordAttempt(indexed_rebroadcast_set::index<index_by_wtxid>::type::iterator& entry_it)
30- {
31- auto UpdateRebroadcastEntry = [](RebroadcastEntry& rebroadcast_entry) {
32- rebroadcast_entry.m_last_attempt = GetTime<std::chrono::microseconds>() - 4h;
33- ++rebroadcast_entry.m_count;
34- };
35-
36- m_attempt_tracker.modify(entry_it, UpdateRebroadcastEntry);
37- };
38+ TxRebroadcastHandler::UpdateAttempt(txhsh, count);
39+ }
40
41 void UpdateCachedFeeRate(CFeeRate new_fee_rate)
42 {
43diff --git a/src/txrebroadcast.cpp b/src/txrebroadcast.cpp
44index 3cb9abaf69..49050c0d39 100644
45--- a/src/txrebroadcast.cpp
46+++ b/src/txrebroadcast.cpp
47@@ -10,6 +10,11 @@
48 #include <txrebroadcast.h>
49 #include <validation.h>
50
51+#include <boost/multi_index/hashed_index.hpp>
52+#include <boost/multi_index/member.hpp>
53+#include <boost/multi_index/ordered_index.hpp>
54+#include <boost/multi_index_container.hpp>
55+
56 /** We rebroadcast 3/4 of max block weight to reduce noise due to circumstances
57 * such as miners mining priority transactions. */
58 static constexpr unsigned int MAX_REBROADCAST_WEIGHT = 3 * MAX_BLOCK_WEIGHT / 4;
59@@ -30,6 +35,46 @@ static constexpr int MAX_ENTRIES = 500;
60 /** The maximum age of an entry ~3 months */
61 static constexpr std::chrono::hours MAX_ENTRY_AGE = std::chrono::hours(3 * 30 * 24);
62
63+/** Used for multi_index tag */
64+struct index_by_last_attempt {};
65+
66+struct RebroadcastEntry {
67+ RebroadcastEntry(std::chrono::microseconds now_time, uint256 wtxid)
68+ : m_last_attempt(now_time),
69+ m_wtxid(wtxid),
70+ m_count(1) {}
71+
72+ std::chrono::microseconds m_last_attempt;
73+ const uint256 m_wtxid;
74+ int m_count;
75+};
76+
77+class indexed_rebroadcast_set : public
78+boost::multi_index_container<
79+ RebroadcastEntry,
80+ boost::multi_index::indexed_by<
81+ // sorted by wtxid
82+ boost::multi_index::hashed_unique<
83+ boost::multi_index::tag<index_by_wtxid>,
84+ boost::multi_index::member<RebroadcastEntry, const uint256, &RebroadcastEntry::m_wtxid>,
85+ SaltedTxidHasher
86+ >,
87+ // sorted by last rebroadcast time
88+ boost::multi_index::ordered_non_unique<
89+ boost::multi_index::tag<index_by_last_attempt>,
90+ boost::multi_index::member<RebroadcastEntry, std::chrono::microseconds, &RebroadcastEntry::m_last_attempt>
91+ >
92+ >
93+> {};
94+
95+TxRebroadcastHandler::TxRebroadcastHandler(CTxMemPool& mempool, ChainstateManager& chainman)
96+ : m_attempt_tracker{std::make_unique<indexed_rebroadcast_set>()}
97+ , m_mempool{mempool}
98+ , m_chainman{chainman}
99+{}
100+
101+TxRebroadcastHandler::~TxRebroadcastHandler() = default;
102+
103 std::vector<TxIds> TxRebroadcastHandler::GetRebroadcastTransactions()
104 {
105 std::vector<TxIds> rebroadcast_txs;
106@@ -58,12 +103,12 @@ std::vector<TxIds> TxRebroadcastHandler::GetRebroadcastTransactions()
107
108 // Check if we have previously rebroadcasted, decide if we will this
109 // round, and if so, record the attempt.
110- auto entry_it = m_attempt_tracker.find(wtxid);
111+ auto entry_it = m_attempt_tracker->find(wtxid);
112
113- if (entry_it == m_attempt_tracker.end()) {
114+ if (entry_it == m_attempt_tracker->end()) {
115 // No existing entry, we will rebroadcast, so create a new one
116 RebroadcastEntry entry(start_time, wtxid);
117- m_attempt_tracker.insert(entry);
118+ m_attempt_tracker->insert(entry);
119 } else if (entry_it->m_count >= MAX_REBROADCAST_COUNT) {
120 // We have already rebroadcast this transaction the maximum number
121 // of times permitted, so skip rebroadcasting.
122@@ -76,7 +121,12 @@ std::vector<TxIds> TxRebroadcastHandler::GetRebroadcastTransactions()
123 } else {
124 // We have rebroadcasted this transaction before, but will try
125 // again now.
126- RecordAttempt(entry_it);
127+ auto UpdateRebroadcastEntry = [](RebroadcastEntry& rebroadcast_entry) {
128+ rebroadcast_entry.m_last_attempt = GetTime<std::chrono::microseconds>();
129+ ++rebroadcast_entry.m_count;
130+ };
131+
132+ m_attempt_tracker->modify(entry_it, UpdateRebroadcastEntry);
133 }
134
135 // Add to set of rebroadcast candidates
136@@ -97,6 +147,32 @@ std::vector<TxIds> TxRebroadcastHandler::GetRebroadcastTransactions()
137 return rebroadcast_txs;
138 };
139
140+void TxRebroadcastHandler::UpdateAttempt(uint256 txhsh, int count)
141+{
142+ auto it = m_attempt_tracker->find(txhsh);
143+ for (int i = 0; i < count; ++i) {
144+ auto UpdateRebroadcastEntry = [](RebroadcastEntry& rebroadcast_entry) {
145+ rebroadcast_entry.m_last_attempt = GetTime<std::chrono::microseconds>() - 4h;
146+ ++rebroadcast_entry.m_count;
147+ };
148+
149+ m_attempt_tracker->modify(it, UpdateRebroadcastEntry);
150+ }
151+};
152+
153+bool TxRebroadcastHandler::CheckRecordedAttempt(uint256 txhsh, int expected_count, std::chrono::microseconds expected_timestamp)
154+{
155+ const auto it = m_attempt_tracker->find(txhsh);
156+ if (it == m_attempt_tracker->end()) return false;
157+ if (it->m_count != expected_count) return false;
158+
159+ // Check the recorded timestamp is within 2 seconds of the param passed in
160+ std::chrono::microseconds delta = expected_timestamp - it->m_last_attempt;
161+ if (delta.count() > 2) return false;
162+
163+ return true;
164+};
165+
166 void TxRebroadcastHandler::CacheMinRebroadcastFee()
167 {
168 // Update stamp of chain tip on cache run
169@@ -109,33 +185,23 @@ void TxRebroadcastHandler::CacheMinRebroadcastFee()
170 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());
171 };
172
173-void TxRebroadcastHandler::RecordAttempt(indexed_rebroadcast_set::index<index_by_wtxid>::type::iterator& entry_it)
174-{
175- auto UpdateRebroadcastEntry = [](RebroadcastEntry& rebroadcast_entry) {
176- rebroadcast_entry.m_last_attempt = GetTime<std::chrono::microseconds>();
177- ++rebroadcast_entry.m_count;
178- };
179-
180- m_attempt_tracker.modify(entry_it, UpdateRebroadcastEntry);
181-};
182-
183 void TxRebroadcastHandler::TrimMaxRebroadcast()
184 {
185 // Delete any entries that are older than MAX_ENTRY_AGE
186 std::chrono::microseconds min_age = GetTime<std::chrono::microseconds>() - MAX_ENTRY_AGE;
187
188- while (!m_attempt_tracker.empty()) {
189- auto it = m_attempt_tracker.get<index_by_last_attempt>().begin();
190+ while (!m_attempt_tracker->empty()) {
191+ auto it = m_attempt_tracker->get<index_by_last_attempt>().begin();
192 if (it->m_last_attempt < min_age) {
193- m_attempt_tracker.get<index_by_last_attempt>().erase(it);
194+ m_attempt_tracker->get<index_by_last_attempt>().erase(it);
195 } else {
196 break;
197 }
198 }
199
200 // If there are still too many entries, delete the oldest ones
201- while (m_attempt_tracker.size() > MAX_ENTRIES) {
202- auto it = m_attempt_tracker.get<index_by_last_attempt>().begin();
203- m_attempt_tracker.get<index_by_last_attempt>().erase(it);
204+ while (m_attempt_tracker->size() > MAX_ENTRIES) {
205+ auto it = m_attempt_tracker->get<index_by_last_attempt>().begin();
206+ m_attempt_tracker->get<index_by_last_attempt>().erase(it);
207 }
208 };
209diff --git a/src/txrebroadcast.h b/src/txrebroadcast.h
210index 8a176a08bc..ec3ea34ed7 100644
211--- a/src/txrebroadcast.h
212+++ b/src/txrebroadcast.h
213@@ -12,11 +12,6 @@
214 #include <util/time.h>
215 #include <validation.h>
216
217-#include <boost/multi_index/hashed_index.hpp>
218-#include <boost/multi_index/member.hpp>
219-#include <boost/multi_index/ordered_index.hpp>
220-#include <boost/multi_index_container.hpp>
221-
222 struct TxIds {
223 TxIds(uint256 txid, uint256 wtxid) : m_txid(txid), m_wtxid(wtxid) {}
224
225@@ -24,43 +19,15 @@ struct TxIds {
226 const uint256 m_wtxid;
227 };
228
229-struct RebroadcastEntry {
230- RebroadcastEntry(std::chrono::microseconds now_time, uint256 wtxid)
231- : m_last_attempt(now_time),
232- m_wtxid(wtxid),
233- m_count(1) {}
234-
235- std::chrono::microseconds m_last_attempt;
236- const uint256 m_wtxid;
237- int m_count;
238-};
239-
240-/** Used for multi_index tag */
241-struct index_by_last_attempt {};
242-
243-using indexed_rebroadcast_set = boost::multi_index_container<
244- RebroadcastEntry,
245- boost::multi_index::indexed_by<
246- // sorted by wtxid
247- boost::multi_index::hashed_unique<
248- boost::multi_index::tag<index_by_wtxid>,
249- boost::multi_index::member<RebroadcastEntry, const uint256, &RebroadcastEntry::m_wtxid>,
250- SaltedTxidHasher
251- >,
252- // sorted by last rebroadcast time
253- boost::multi_index::ordered_non_unique<
254- boost::multi_index::tag<index_by_last_attempt>,
255- boost::multi_index::member<RebroadcastEntry, std::chrono::microseconds, &RebroadcastEntry::m_last_attempt>
256- >
257- >
258->;
259+class indexed_rebroadcast_set;
260
261 class TxRebroadcastHandler
262 {
263 public:
264- TxRebroadcastHandler(CTxMemPool& mempool, ChainstateManager& chainman)
265- : m_mempool(mempool),
266- m_chainman(chainman){};
267+ TxRebroadcastHandler(CTxMemPool& mempool, ChainstateManager& chainman);
268+ ~TxRebroadcastHandler();
269+
270+ TxRebroadcastHandler(const TxRebroadcastHandler& other) = delete;
271
272 std::vector<TxIds> GetRebroadcastTransactions();
273
274@@ -75,10 +42,13 @@ protected:
275 CFeeRate m_cached_fee_rate;
276
277 /** Keep track of previous rebroadcast attempts */
278- indexed_rebroadcast_set m_attempt_tracker;
279+ std::unique_ptr<indexed_rebroadcast_set> m_attempt_tracker;
280+
281+ /** Test only */
282+ void UpdateAttempt(uint256 txhsh, int count);
283
284- /** Update an existing RebroadcastEntry - increment count and update timestamp */
285- void RecordAttempt(indexed_rebroadcast_set::index<index_by_wtxid>::type::iterator& entry_it);
286+ /** Test only */
287+ bool CheckRecordedAttempt(uint256 txhsh, int expected_count, std::chrono::microseconds expected_timestamp);
288
289 private:
290 const CTxMemPool& m_mempool;