11package mempool
22
33import (
4+ "context"
45 "errors"
56 "fmt"
67 "sync"
@@ -160,25 +161,47 @@ func (om *OrphanManager) AddOrphan(tx *btcutil.Tx, tag Tag) error {
160161 om .mu .Lock ()
161162 defer om .mu .Unlock ()
162163
164+ ctx := context .Background ()
163165 hash := * tx .Hash ()
164166
165167 // Check if already exists.
166168 if _ , exists := om .metadata [hash ]; exists {
169+ log .DebugS (ctx , "Orphan already exists" ,
170+ "tx_hash" , hash ,
171+ "tag" , tag )
167172 return fmt .Errorf ("%w: %v" , ErrOrphanAlreadyExists , hash )
168173 }
169174
170175 // Check size limit.
171176 size := tx .MsgTx ().SerializeSize ()
172177 if size > om .config .MaxOrphanSize {
178+ log .WarnS (ctx , "Orphan too large" , nil ,
179+ "tx_hash" , hash ,
180+ "size" , size ,
181+ "max_size" , om .config .MaxOrphanSize )
173182 return fmt .Errorf ("%w: %d bytes (max %d)" ,
174183 ErrOrphanTooLarge , size , om .config .MaxOrphanSize )
175184 }
176185
186+ currentCount := len (om .metadata )
177187 // Check count limit.
178- if len (om .metadata ) >= om .config .MaxOrphans {
188+ if currentCount >= om .config .MaxOrphans {
189+ log .WarnS (ctx , "Orphan pool limit reached" , nil ,
190+ "tx_hash" , hash ,
191+ "current_count" , currentCount ,
192+ "max_orphans" , om .config .MaxOrphans ,
193+ "tag" , tag )
179194 return fmt .Errorf ("%w: %d" , ErrOrphanLimitReached , om .config .MaxOrphans )
180195 }
181196
197+ // Warn when approaching capacity (potential memory DoS).
198+ if currentCount > om .config .MaxOrphans * 8 / 10 {
199+ log .InfoS (ctx , "Orphan pool nearing capacity" ,
200+ "current_count" , currentCount ,
201+ "max_orphans" , om .config .MaxOrphans ,
202+ "utilization_pct" , currentCount * 100 / om .config .MaxOrphans )
203+ }
204+
182205 // Add to graph. Use a minimal TxDesc since we don't need fee
183206 // information for orphans (they're not mined until promoted).
184207 desc := & txgraph.TxDesc {
@@ -206,6 +229,13 @@ func (om *OrphanManager) AddOrphan(tx *btcutil.Tx, tag Tag) error {
206229 }
207230 om.byTag [tag ][hash ] = struct {}{}
208231
232+ log .DebugS (ctx , "Orphan added" ,
233+ "tx_hash" , hash ,
234+ "size" , size ,
235+ "tag" , tag ,
236+ "pool_size" , len (om .metadata ),
237+ "max_orphans" , om .config .MaxOrphans )
238+
209239 return nil
210240}
211241
@@ -222,8 +252,11 @@ func (om *OrphanManager) RemoveOrphan(hash chainhash.Hash, cascade bool) error {
222252// removeOrphanUnsafe removes an orphan without locking. Must be called with
223253// lock held.
224254func (om * OrphanManager ) removeOrphanUnsafe (hash chainhash.Hash , cascade bool ) error {
255+ ctx := context .Background ()
225256 _ , exists := om .metadata [hash ]
226257 if ! exists {
258+ log .DebugS (ctx , "Orphan not found for removal" ,
259+ "tx_hash" , hash )
227260 return fmt .Errorf ("%w: %v" , ErrOrphanNotFound , hash )
228261 }
229262
@@ -238,6 +271,12 @@ func (om *OrphanManager) removeOrphanUnsafe(hash chainhash.Hash, cascade bool) e
238271 for descHash := range descendants {
239272 toRemove = append (toRemove , descHash )
240273 }
274+
275+ if len (descendants ) > 0 {
276+ log .DebugS (ctx , "Cascading orphan removal" ,
277+ "tx_hash" , hash ,
278+ "descendant_count" , len (descendants ))
279+ }
241280 }
242281
243282 // Remove metadata and tag index entries for all transactions.
@@ -256,6 +295,12 @@ func (om *OrphanManager) removeOrphanUnsafe(hash chainhash.Hash, cascade bool) e
256295 }
257296
258297 // Remove from graph. Use cascade based on caller's preference.
298+ log .DebugS (ctx , "Orphan removed" ,
299+ "tx_hash" , hash ,
300+ "cascade" , cascade ,
301+ "removed_count" , len (toRemove ),
302+ "pool_size" , len (om .metadata ))
303+
259304 if cascade {
260305 return om .graph .RemoveTransaction (hash )
261306 }
@@ -270,6 +315,7 @@ func (om *OrphanManager) RemoveOrphansByTag(tag Tag) int {
270315 om .mu .Lock ()
271316 defer om .mu .Unlock ()
272317
318+ ctx := context .Background ()
273319 tagSet , exists := om .byTag [tag ]
274320 if ! exists {
275321 return 0
@@ -281,6 +327,10 @@ func (om *OrphanManager) RemoveOrphansByTag(tag Tag) int {
281327 toRemove = append (toRemove , hash )
282328 }
283329
330+ log .DebugS (ctx , "Removing orphans by tag" ,
331+ "tag" , tag ,
332+ "count" , len (toRemove ))
333+
284334 // Remove each orphan.
285335 removed := 0
286336 for _ , hash := range toRemove {
@@ -289,6 +339,13 @@ func (om *OrphanManager) RemoveOrphansByTag(tag Tag) int {
289339 }
290340 }
291341
342+ if removed > 0 {
343+ log .DebugS (ctx , "Orphans removed by tag" ,
344+ "tag" , tag ,
345+ "removed_count" , removed ,
346+ "pool_size" , len (om .metadata ))
347+ }
348+
292349 return removed
293350}
294351
@@ -300,6 +357,7 @@ func (om *OrphanManager) ExpireOrphans() int {
300357 om .mu .Lock ()
301358 defer om .mu .Unlock ()
302359
360+ ctx := context .Background ()
303361 // Only scan if it's time for the next expiration scan.
304362 now := time .Now ()
305363 if now .Before (om .nextExpireScan ) {
@@ -309,6 +367,11 @@ func (om *OrphanManager) ExpireOrphans() int {
309367 // Update next scan time.
310368 om .nextExpireScan = now .Add (om .config .ExpireScanInterval )
311369
370+ poolSize := len (om .metadata )
371+ log .TraceS (ctx , "Scanning for expired orphans" ,
372+ "pool_size" , poolSize ,
373+ "ttl" , om .config .OrphanTTL )
374+
312375 // Find expired orphans.
313376 var expired []chainhash.Hash
314377 for hash , meta := range om .metadata {
@@ -325,6 +388,13 @@ func (om *OrphanManager) ExpireOrphans() int {
325388 }
326389 }
327390
391+ if removed > 0 {
392+ log .DebugS (ctx , "Expired orphans removed" ,
393+ "removed_count" , removed ,
394+ "pool_size" , len (om .metadata ),
395+ "ttl" , om .config .OrphanTTL )
396+ }
397+
328398 return removed
329399}
330400
@@ -376,6 +446,12 @@ func (om *OrphanManager) ProcessOrphans(
376446 om .mu .Lock ()
377447 defer om .mu .Unlock ()
378448
449+ ctx := context .Background ()
450+ parentHash := parentTx .Hash ()
451+ log .TraceS (ctx , "Processing orphans for parent" ,
452+ "parent_tx" , parentHash ,
453+ "orphan_pool_size" , len (om .metadata ))
454+
379455 // Find orphans that spend from this parent's outputs using the spentBy
380456 // index. The parent itself is not in the orphan graph (it's in the main
381457 // mempool), but orphans that spend its outputs are indexed by outpoint.
@@ -385,7 +461,6 @@ func (om *OrphanManager) ProcessOrphans(
385461
386462 // Check each output of the parent to see if it's spent by an orphan.
387463 // Iterate through actual outputs of the transaction.
388- parentHash := parentTx .Hash ()
389464 for txOutIdx := range parentTx .MsgTx ().TxOut {
390465 outpoint := wire.OutPoint {
391466 Hash : * parentHash ,
@@ -414,15 +489,25 @@ func (om *OrphanManager) ProcessOrphans(
414489 // If promotion fails, skip this orphan and its descendants.
415490 // They'll remain in the orphan pool for potential future
416491 // promotion or eventual expiration.
492+ log .DebugS (ctx , "Orphan promotion failed" ,
493+ "orphan_tx" , node .Tx .Hash (),
494+ "parent_tx" , parentHash ,
495+ "reason" , err .Error ())
417496 continue
418497 }
419498
420499 // Promotion succeeded! Remove from orphan manager.
421500 if err := om .removeOrphanUnsafe (* node .Tx .Hash (), false ); err != nil {
422501 // Log error but continue processing other orphans.
502+ log .WarnS (ctx , "Failed to remove promoted orphan" , err ,
503+ "orphan_tx" , node .Tx .Hash ())
423504 continue
424505 }
425506
507+ log .DebugS (ctx , "Orphan promoted" ,
508+ "orphan_tx" , node .Tx .Hash (),
509+ "parent_tx" , parentHash )
510+
426511 promoted = append (promoted , node .Tx )
427512
428513 // Add this orphan's children to the processing queue.
@@ -434,6 +519,13 @@ func (om *OrphanManager) ProcessOrphans(
434519 }
435520 }
436521
522+ if len (promoted ) > 0 {
523+ log .DebugS (ctx , "Orphan processing complete" ,
524+ "parent_tx" , parentHash ,
525+ "promoted_count" , len (promoted ),
526+ "orphan_pool_size" , len (om .metadata ))
527+ }
528+
437529 return promoted , nil
438530}
439531
0 commit comments