@@ -16,8 +16,11 @@ bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
16
16
{
17
17
const Txid& hash = tx->GetHash ();
18
18
const Wtxid& wtxid = tx->GetWitnessHash ();
19
- if (m_orphans.count (wtxid))
19
+ if (auto it{m_orphans.find (wtxid)}; it != m_orphans.end ()) {
20
+ AddAnnouncer (wtxid, peer);
21
+ // No new orphan entry was created. An announcer may have been added.
20
22
return false ;
23
+ }
21
24
22
25
// Ignore big transactions, to avoid a
23
26
// send-big-orphans memory exhaustion attack. If a peer has a legitimate
@@ -33,7 +36,7 @@ bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
33
36
return false ;
34
37
}
35
38
36
- auto ret = m_orphans.emplace (wtxid, OrphanTx{{tx, peer, Now<NodeSeconds>() + ORPHAN_TX_EXPIRE_TIME}, m_orphan_list.size ()});
39
+ auto ret = m_orphans.emplace (wtxid, OrphanTx{{tx, { peer} , Now<NodeSeconds>() + ORPHAN_TX_EXPIRE_TIME}, m_orphan_list.size ()});
37
40
assert (ret.second );
38
41
m_orphan_list.push_back (ret.first );
39
42
for (const CTxIn& txin : tx->vin ) {
@@ -45,6 +48,20 @@ bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
45
48
return true ;
46
49
}
47
50
51
+ bool TxOrphanage::AddAnnouncer (const Wtxid& wtxid, NodeId peer)
52
+ {
53
+ const auto it = m_orphans.find (wtxid);
54
+ if (it != m_orphans.end ()) {
55
+ Assume (!it->second .announcers .empty ());
56
+ const auto ret = it->second .announcers .insert (peer);
57
+ if (ret.second ) {
58
+ LogDebug (BCLog::TXPACKAGES, " added peer=%d as announcer of orphan tx %s\n " , peer, wtxid.ToString ());
59
+ return true ;
60
+ }
61
+ }
62
+ return false ;
63
+ }
64
+
48
65
int TxOrphanage::EraseTx (const Wtxid& wtxid)
49
66
{
50
67
std::map<Wtxid, OrphanTx>::iterator it = m_orphans.find (wtxid);
@@ -89,9 +106,15 @@ void TxOrphanage::EraseForPeer(NodeId peer)
89
106
while (iter != m_orphans.end ())
90
107
{
91
108
// increment to avoid iterator becoming invalid after erasure
92
- const auto & [wtxid, orphan] = *iter++;
93
- if (orphan.fromPeer == peer) {
94
- nErased += EraseTx (wtxid);
109
+ auto & [wtxid, orphan] = *iter++;
110
+ auto orphan_it = orphan.announcers .find (peer);
111
+ if (orphan_it != orphan.announcers .end ()) {
112
+ orphan.announcers .erase (peer);
113
+
114
+ // No remaining annnouncers: clean up entry
115
+ if (orphan.announcers .empty ()) {
116
+ nErased += EraseTx (orphan.tx ->GetWitnessHash ());
117
+ }
95
118
}
96
119
}
97
120
if (nErased > 0 ) LogDebug (BCLog::TXPACKAGES, " Erased %d orphan transaction(s) from peer=%d\n " , nErased, peer);
@@ -110,7 +133,7 @@ void TxOrphanage::LimitOrphans(unsigned int max_orphans, FastRandomContext& rng)
110
133
{
111
134
std::map<Wtxid, OrphanTx>::iterator maybeErase = iter++;
112
135
if (maybeErase->second .nTimeExpire <= nNow) {
113
- nErased += EraseTx (maybeErase->second . tx -> GetWitnessHash () );
136
+ nErased += EraseTx (maybeErase->first );
114
137
} else {
115
138
nMinExpTime = std::min (maybeErase->second .nTimeExpire , nMinExpTime);
116
139
}
@@ -123,7 +146,7 @@ void TxOrphanage::LimitOrphans(unsigned int max_orphans, FastRandomContext& rng)
123
146
{
124
147
// Evict a random orphan:
125
148
size_t randompos = rng.randrange (m_orphan_list.size ());
126
- EraseTx (m_orphan_list[randompos]->second . tx -> GetWitnessHash () );
149
+ EraseTx (m_orphan_list[randompos]->first );
127
150
++nEvicted;
128
151
}
129
152
if (nEvicted > 0 ) LogDebug (BCLog::TXPACKAGES, " orphanage overflow, removed %u tx\n " , nEvicted);
@@ -135,13 +158,17 @@ void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx)
135
158
const auto it_by_prev = m_outpoint_to_orphan_it.find (COutPoint (tx.GetHash (), i));
136
159
if (it_by_prev != m_outpoint_to_orphan_it.end ()) {
137
160
for (const auto & elem : it_by_prev->second ) {
138
- // Get this source peer's work set, emplacing an empty set if it didn't exist
139
- // (note: if this peer wasn't still connected, we would have removed the orphan tx already)
140
- std::set<Wtxid>& orphan_work_set = m_peer_work_set.try_emplace (elem->second .fromPeer ).first ->second ;
141
- // Add this tx to the work set
142
- orphan_work_set.insert (elem->first );
143
- LogDebug (BCLog::TXPACKAGES, " added %s (wtxid=%s) to peer %d workset\n " ,
144
- tx.GetHash ().ToString (), tx.GetWitnessHash ().ToString (), elem->second .fromPeer );
161
+ // Belt and suspenders, each orphan should always have at least 1 announcer.
162
+ if (!Assume (!elem->second .announcers .empty ())) continue ;
163
+ for (const auto announcer: elem->second .announcers ) {
164
+ // Get this source peer's work set, emplacing an empty set if it didn't exist
165
+ // (note: if this peer wasn't still connected, we would have removed the orphan tx already)
166
+ std::set<Wtxid>& orphan_work_set = m_peer_work_set.try_emplace (announcer).first ->second ;
167
+ // Add this tx to the work set
168
+ orphan_work_set.insert (elem->first );
169
+ LogDebug (BCLog::TXPACKAGES, " added %s (wtxid=%s) to peer %d workset\n " ,
170
+ tx.GetHash ().ToString (), tx.GetWitnessHash ().ToString (), announcer);
171
+ }
145
172
}
146
173
}
147
174
}
@@ -152,6 +179,12 @@ bool TxOrphanage::HaveTx(const Wtxid& wtxid) const
152
179
return m_orphans.count (wtxid);
153
180
}
154
181
182
+ bool TxOrphanage::HaveTxFromPeer (const Wtxid& wtxid, NodeId peer) const
183
+ {
184
+ auto it = m_orphans.find (wtxid);
185
+ return (it != m_orphans.end () && it->second .announcers .contains (peer));
186
+ }
187
+
155
188
CTransactionRef TxOrphanage::GetTxToReconsider (NodeId peer)
156
189
{
157
190
auto work_set_it = m_peer_work_set.find (peer);
@@ -219,7 +252,7 @@ std::vector<CTransactionRef> TxOrphanage::GetChildrenFromSamePeer(const CTransac
219
252
const auto it_by_prev = m_outpoint_to_orphan_it.find (COutPoint (parent->GetHash (), i));
220
253
if (it_by_prev != m_outpoint_to_orphan_it.end ()) {
221
254
for (const auto & elem : it_by_prev->second ) {
222
- if (elem->second .fromPeer == nodeid) {
255
+ if (elem->second .announcers . contains ( nodeid) ) {
223
256
iters.emplace_back (elem);
224
257
}
225
258
}
@@ -258,7 +291,7 @@ std::vector<std::pair<CTransactionRef, NodeId>> TxOrphanage::GetChildrenFromDiff
258
291
const auto it_by_prev = m_outpoint_to_orphan_it.find (COutPoint (parent->GetHash (), i));
259
292
if (it_by_prev != m_outpoint_to_orphan_it.end ()) {
260
293
for (const auto & elem : it_by_prev->second ) {
261
- if (elem->second .fromPeer != nodeid) {
294
+ if (! elem->second .announcers . contains ( nodeid) ) {
262
295
iters.emplace_back (elem);
263
296
}
264
297
}
@@ -273,7 +306,9 @@ std::vector<std::pair<CTransactionRef, NodeId>> TxOrphanage::GetChildrenFromDiff
273
306
std::vector<std::pair<CTransactionRef, NodeId>> children_found;
274
307
children_found.reserve (iters.size ());
275
308
for (const auto & child_iter : iters) {
276
- children_found.emplace_back (child_iter->second .tx , child_iter->second .fromPeer );
309
+ // Use first peer in announcers list
310
+ auto peer = *(child_iter->second .announcers .begin ());
311
+ children_found.emplace_back (child_iter->second .tx , peer);
277
312
}
278
313
return children_found;
279
314
}
@@ -283,7 +318,7 @@ std::vector<TxOrphanage::OrphanTxBase> TxOrphanage::GetOrphanTransactions() cons
283
318
std::vector<OrphanTxBase> ret;
284
319
ret.reserve (m_orphans.size ());
285
320
for (auto const & o : m_orphans) {
286
- ret.push_back ({o.second .tx , o.second .fromPeer , o.second .nTimeExpire });
321
+ ret.push_back ({o.second .tx , o.second .announcers , o.second .nTimeExpire });
287
322
}
288
323
return ret;
289
324
}
0 commit comments