Skip to content

fix: LND payments made externally from the hub are not visible#2183

Open
frnandu wants to merge 1 commit intomasterfrom
fix/lnd-payments-external
Open

fix: LND payments made externally from the hub are not visible#2183
frnandu wants to merge 1 commit intomasterfrom
fix/lnd-payments-external

Conversation

@frnandu
Copy link
Copy Markdown
Contributor

@frnandu frnandu commented Mar 27, 2026

fixes #2044

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of unknown payment sent notifications. The system now correctly records outgoing transactions for payment events without prior records, instead of returning errors.
    • Enhanced idempotency in payment event processing to ensure duplicate payment notifications don't create multiple transaction records.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 27, 2026

📝 Walkthrough

Walkthrough

Tests and service logic updated to handle externally-made LND payments by creating transaction records instead of returning errors. Enables visibility of such payments in the hub's transaction list. Adds idempotency validation for repeated event processing.

Changes

Cohort / File(s) Summary
Test Expectations Updates
transactions/notifications_test.go
Modified TestNotifications_SentUnknownPayment to assert successful transaction creation with validated fields (amount, settled state, preimage, zero fee reserve, nil AppId). Added TestNotifications_SentUnknownPaymentIdempotent to verify idempotent handling of duplicate payment sent events.
Payment Event Consumer Logic
transactions/transactions_service.go
Enhanced ConsumeEvent handler for nwc_lnclient_payment_sent case to create a new outgoing transaction record when no existing transaction is found by payment hash, instead of returning a not-found error. Transaction is created in PENDING state with amount, invoice/payment details, and optional expiration, then marked as settled.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Poem

🐰 A hop, a skip, through payment threads,
External payments now get beds!
No more lost in the hub's dark cave,
These LND transactions we shall save.
Idempotent and wise, they dance twice through,
Yet only once in database true! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the primary change: enabling visibility of LND payments made externally from the hub in transaction lists.
Linked Issues check ✅ Passed The code changes implement the core requirement from issue #2044 by removing the restriction that skipped external LND payments and creating transactions for externally-made payments so they appear in the transaction list.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the objective to make externally-made LND payments visible; no out-of-scope modifications were introduced.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/lnd-payments-external

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@transactions/transactions_service.go`:
- Around line 857-889: The Find→Create race when inserting an outgoing
db.Transaction for lnClientTransaction.PaymentHash must be serialized: before
calling tx.Limit(1).Find(...) acquire a DB-level lock (e.g. Postgres advisory
lock via pg_advisory_xact_lock using a hash of lnClientTransaction.PaymentHash)
or otherwise ensure a unique constraint exists on outgoing payment_hash, then
perform the lookup and conditional tx.Create(&dbTransaction) while the lock is
held; reference the existing use of tx.Limit(1).Find(&dbTransaction,
&db.Transaction{Type: constants.TRANSACTION_TYPE_OUTGOING, PaymentHash:
lnClientTransaction.PaymentHash}) and tx.Create(&dbTransaction) to add the lock
acquisition and release (or add the DB migration to create a uniqueness
constraint on payment_hash for outgoing transactions) so concurrent workers
cannot both insert duplicates.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f5da7bf9-82b5-40ea-b511-16a7016d38a5

📥 Commits

Reviewing files that changed from the base of the PR and between 56a8df5 and ff9d7c7.

📒 Files selected for processing (2)
  • transactions/notifications_test.go
  • transactions/transactions_service.go

Comment on lines +857 to +889
result := tx.Limit(1).Find(&dbTransaction, &db.Transaction{
Type: constants.TRANSACTION_TYPE_OUTGOING,
PaymentHash: lnClientTransaction.PaymentHash,
})

if result.Error != nil {
return result.Error
}

if result.RowsAffected == 0 {
dbTransaction = db.Transaction{
Type: constants.TRANSACTION_TYPE_OUTGOING,
State: constants.TRANSACTION_STATE_PENDING,
AmountMsat: uint64(lnClientTransaction.Amount),
FeeReserveMsat: 0,
PaymentRequest: lnClientTransaction.Invoice,
PaymentHash: lnClientTransaction.PaymentHash,
Description: lnClientTransaction.Description,
DescriptionHash: lnClientTransaction.DescriptionHash,
}

if lnClientTransaction.ExpiresAt != nil {
expiresAtValue := time.Unix(*lnClientTransaction.ExpiresAt, 0)
dbTransaction.ExpiresAt = &expiresAtValue
}

err := tx.Create(&dbTransaction).Error
if err != nil {
logger.Logger.WithFields(logrus.Fields{
"payment_hash": lnClientTransaction.PaymentHash,
}).WithError(err).Error("Failed to create outgoing transaction")
return err
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Serialize this fallback before inserting a new outgoing record.

This FindCreate path is still non-idempotent under concurrent nwc_lnclient_payment_sent delivery. Two workers can both observe “no rows”, both insert, and then either settle duplicate rows or leave one duplicate pending row behind. The schema in db/migrations/202407012100_transactions.go does not enforce uniqueness on outgoing payment_hash, so this is a real data-integrity gap.

Please guard the lookup/create sequence with a DB-level lock/advisory lock on the hash, or add a constraint that guarantees only one settled outgoing record per payment hash.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@transactions/transactions_service.go` around lines 857 - 889, The Find→Create
race when inserting an outgoing db.Transaction for
lnClientTransaction.PaymentHash must be serialized: before calling
tx.Limit(1).Find(...) acquire a DB-level lock (e.g. Postgres advisory lock via
pg_advisory_xact_lock using a hash of lnClientTransaction.PaymentHash) or
otherwise ensure a unique constraint exists on outgoing payment_hash, then
perform the lookup and conditional tx.Create(&dbTransaction) while the lock is
held; reference the existing use of tx.Limit(1).Find(&dbTransaction,
&db.Transaction{Type: constants.TRANSACTION_TYPE_OUTGOING, PaymentHash:
lnClientTransaction.PaymentHash}) and tx.Create(&dbTransaction) to add the lock
acquisition and release (or add the DB migration to create a uniqueness
constraint on payment_hash for outgoing transactions) so concurrent workers
cannot both insert duplicates.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: LND payments made externally from the hub are not visible

1 participant