Skip to content

Commit 68ecaa4

Browse files
committed
mempool: add structured logging to orphan management and validation
This commit completes the structured logging coverage for TxMempoolV2 by instrumenting the orphan management, validation pipeline, and transaction validator components.
1 parent 1b5e9ef commit 68ecaa4

File tree

3 files changed

+107
-4
lines changed

3 files changed

+107
-4
lines changed

mempool/orphan.go

Lines changed: 94 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package mempool
22

33
import (
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.
224254
func (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

mempool/tx_validator.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package mempool
66

77
import (
8+
"context"
89
"time"
910

1011
"github.com/btcsuite/btcd/blockchain"
@@ -101,13 +102,20 @@ func (v *StandardTxValidator) ValidateScripts(
101102
tx *btcutil.Tx,
102103
utxoView *blockchain.UtxoViewpoint,
103104
) error {
105+
ctx := context.Background()
106+
log.TraceS(ctx, "Validating transaction scripts",
107+
"tx_hash", tx.Hash(),
108+
"input_count", len(tx.MsgTx().TxIn))
104109

105110
// Use blockchain's script validation with standard verification flags.
106111
err := blockchain.ValidateTransactionScripts(
107112
tx, utxoView, txscript.StandardVerifyFlags, v.cfg.SigCache,
108113
v.cfg.HashCache,
109114
)
110115
if err != nil {
116+
log.DebugS(ctx, "Script validation failed",
117+
"tx_hash", tx.Hash(),
118+
"reason", err.Error())
111119
if cerr, ok := err.(blockchain.RuleError); ok {
112120
return chainRuleError(cerr)
113121
}

mempool/validation_v2.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package mempool
66

77
import (
8+
"context"
89
"fmt"
910

1011
"github.com/btcsuite/btcd/blockchain"
@@ -102,8 +103,10 @@ func (mp *TxMempoolV2) checkMempoolAcceptance(
102103

103104
// Exit early if this transaction is missing parents (it's an orphan).
104105
if len(missingParents) > 0 {
105-
log.Debugf("Tx %v is an orphan with missing parents: %v",
106-
txHash, missingParents)
106+
ctx := context.Background()
107+
log.DebugS(ctx, "Transaction has missing parents",
108+
"tx_hash", txHash,
109+
"missing_count", len(missingParents))
107110

108111
return &MempoolAcceptResult{
109112
MissingParents: missingParents,

0 commit comments

Comments
 (0)