Skip to content

Commit 82306fe

Browse files
refactor(requester): unify the per-batch drop/submit log fields
Audit finding M3: the three WARN-level per-batch logs (submission failure in logSubmission, stale prune, TTL submit-anyway) had drifted to different, overlapping field sets, so the no-silent-drops fields were inconsistent and the invariant did not really live in one place. Add a batchFields helper that attaches the common fields (eoa, tx-hashes, low/high nonce, batch-size, local-indexed-nonce) to a caller-supplied zerolog event, and route all three WARN sites through it. Each site then only adds its specifics (reason+err / expected-nonce / none). The nonce range is computed as the true min/max so it is correct even for the stale-prune batch, which is collected in unordered map iteration. The lighter success DEBUG line is intentionally left as-is. No behavior change beyond the prune/TTL logs gaining the shared fields. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent b04c8ed commit 82306fe

1 file changed

Lines changed: 40 additions & 19 deletions

File tree

services/requester/tx_mempool.go

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -667,15 +667,46 @@ func (t *TxMemPool) submitWork(ctx context.Context, w flushWork) error {
667667
return err
668668
}
669669

670+
// batchFields attaches to e the structured fields common to every per-batch
671+
// lifecycle log — submission failure, stale prune, and TTL submit-anyway — so
672+
// the no-silent-drops fields stay consistent and greppable across all three
673+
// sites (eoa, tx hashes, nonce range, batch size, the indexed frontier). The
674+
// caller creates e at the desired level, adds any site-specific fields (reason,
675+
// error, expected-nonce, ...), and calls Msg. The nonce range is computed as the
676+
// true min/max so it is correct even when txs is not sorted (e.g. a stale-prune
677+
// batch collected in map order).
678+
func batchFields(
679+
e *zerolog.Event,
680+
from gethCommon.Address,
681+
txs []heldTx,
682+
indexNonce uint64,
683+
) *zerolog.Event {
684+
var lowNonce, highNonce uint64
685+
for i, htx := range txs {
686+
if i == 0 || htx.nonce < lowNonce {
687+
lowNonce = htx.nonce
688+
}
689+
if i == 0 || htx.nonce > highNonce {
690+
highNonce = htx.nonce
691+
}
692+
}
693+
return e.
694+
Str("eoa", from.Hex()).
695+
Strs("tx-hashes", txHashHexes(txs)).
696+
Uint64("low-nonce", lowNonce).
697+
Uint64("high-nonce", highNonce).
698+
Int("batch-size", len(txs)).
699+
Uint64("local-indexed-nonce", indexNonce)
700+
}
701+
670702
// logSubmission records the fate of a submitted batch so a transaction is never
671703
// silently lost. This is the observability half of the no-silent-drops
672704
// invariant: for any tx id you can either find it on-chain (sent) OR find a WARN
673705
// log here (dropped) — never nothing.
674706
//
675707
// - On a Flow submit FAILURE the batch's EVM transactions are dropped (we do
676-
// not retry — clients resubmit), so we WARN with everything needed to debug
677-
// a "lost transaction" report: eoa, tx hashes, nonce range, the indexed
678-
// frontier, batch size, the flush reason, and the error.
708+
// not retry — clients resubmit), so we WARN with the full batch context
709+
// (batchFields) plus the flush reason and the error.
679710
// - On SUCCESS we emit a lighter DEBUG line (eoa + nonce range) so a sent
680711
// batch is traceable without the noise of a warning.
681712
//
@@ -691,27 +722,19 @@ func (t *TxMemPool) logSubmission(
691722
if len(txs) == 0 {
692723
return
693724
}
694-
lowNonce := txs[0].nonce
695-
highNonce := txs[len(txs)-1].nonce
696725

697726
if submitErr != nil {
698-
t.logger.Warn().
699-
Err(submitErr).
700-
Str("eoa", from.Hex()).
701-
Strs("tx-hashes", txHashHexes(txs)).
702-
Uint64("low-nonce", lowNonce).
703-
Uint64("high-nonce", highNonce).
704-
Uint64("local-indexed-nonce", localIndexedNonce).
705-
Int("batch-size", len(txs)).
727+
batchFields(t.logger.Warn(), from, txs, localIndexedNonce).
706728
Str("reason", reason).
729+
Err(submitErr).
707730
Msg("Flow submission failed, EVM transactions dropped")
708731
return
709732
}
710733

711734
t.logger.Debug().
712735
Str("eoa", from.Hex()).
713-
Uint64("low-nonce", lowNonce).
714-
Uint64("high-nonce", highNonce).
736+
Uint64("low-nonce", txs[0].nonce).
737+
Uint64("high-nonce", txs[len(txs)-1].nonce).
715738
Int("batch-size", len(txs)).
716739
Str("reason", reason).
717740
Msg("submitted EVM transactions to Flow")
@@ -911,8 +934,7 @@ func (t *TxMemPool) collectExpired(
911934
deleteByNonce(q.txs, expired)
912935
q.lastSubmittedAt = now
913936
q.lastActivity = now
914-
t.logger.Warn().Strs("tx-hashes", txHashHexes(expired)).Str("eoa", from.Hex()).
915-
Uint64("local-indexed-nonce", indexNonce).
937+
batchFields(t.logger.Warn(), from, expired, indexNonce).
916938
Uint64("expected-nonce", q.nonces.expectedNonce()).
917939
Msg("nonce gap never filled within TTL, submitting held transactions anyway")
918940
return flushWork{
@@ -950,8 +972,7 @@ func (t *TxMemPool) pruneStaleTxs(
950972
}
951973
if len(stale) > 0 {
952974
deleteByNonce(q.txs, stale)
953-
t.logger.Warn().Strs("tx-hashes", txHashHexes(stale)).Str("eoa", from.Hex()).
954-
Uint64("local-indexed-nonce", indexNonce).
975+
batchFields(t.logger.Warn(), from, stale, indexNonce).
955976
Msg("dropping stale transactions with nonce below indexed state")
956977
}
957978
}

0 commit comments

Comments
 (0)