Skip to content

Commit a449a5d

Browse files
authored
Merge pull request #8174 from yyforyongyu/fix-inflight-payments
routing: fix stuck inflight payments
2 parents d776174 + bc31a8b commit a449a5d

File tree

16 files changed

+1310
-483
lines changed

16 files changed

+1310
-483
lines changed

channeldb/forwarding_package.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,11 @@ func (f *PkgFilter) Decode(r io.Reader) error {
211211
return err
212212
}
213213

214+
// String returns a human-readable string.
215+
func (f *PkgFilter) String() string {
216+
return fmt.Sprintf("count=%v, filter=%v", f.count, f.filter)
217+
}
218+
214219
// FwdPkg records all adds, settles, and fails that were locked in as a result
215220
// of the remote peer sending us a revocation. Each package is identified by
216221
// the short chanid and remote commitment height corresponding to the revocation

channeldb/payment_control.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,9 @@ func (p *PaymentControl) RegisterAttempt(paymentHash lntypes.Hash,
424424
// Ensure we aren't sending more than the total payment amount.
425425
sentAmt, _ := payment.SentAmt()
426426
if sentAmt+amt > payment.Info.Value {
427-
return ErrValueExceedsAmt
427+
return fmt.Errorf("%w: attempted=%v, payment amount="+
428+
"%v", ErrValueExceedsAmt, sentAmt+amt,
429+
payment.Info.Value)
428430
}
429431

430432
htlcsBucket, err := bucket.CreateBucketIfNotExists(

channeldb/payment_control_test.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -734,10 +734,7 @@ func TestPaymentControlMultiShard(t *testing.T) {
734734
b := *attempt
735735
b.AttemptID = 3
736736
_, err = pControl.RegisterAttempt(info.PaymentIdentifier, &b)
737-
if err != ErrValueExceedsAmt {
738-
t.Fatalf("expected ErrValueExceedsAmt, got: %v",
739-
err)
740-
}
737+
require.ErrorIs(t, err, ErrValueExceedsAmt)
741738

742739
// Fail the second attempt.
743740
a := attempts[1]

docs/release-notes/release-notes-0.18.3.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,15 @@
4848
bumping an anchor channel closing was not possible when no HTLCs were on the
4949
commitment when the channel was force closed.
5050

51+
* [Fixed](https://github.com/lightningnetwork/lnd/pull/8174) old payments that
52+
are stuck inflight. Though the root cause is unknown, it's possible the
53+
network result for a given HTLC attempt was not saved, which is now fixed.
54+
Check
55+
[here](https://github.com/lightningnetwork/lnd/pull/8174#issue-1992055103)
56+
for the details about the analysis, and
57+
[here](https://github.com/lightningnetwork/lnd/issues/8146) for a summary of
58+
the issue.
59+
5160
# New Features
5261
## Functional Enhancements
5362
## RPC Additions

htlcswitch/link.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3297,7 +3297,7 @@ func (l *channelLink) processRemoteSettleFails(fwdPkg *channeldb.FwdPkg,
32973297
return
32983298
}
32993299

3300-
l.log.Debugf("settle-fail-filter %v", fwdPkg.SettleFailFilter)
3300+
l.log.Debugf("settle-fail-filter: %v", fwdPkg.SettleFailFilter)
33013301

33023302
var switchPackets []*htlcPacket
33033303
for i, pd := range settleFails {

htlcswitch/payment_result.go

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -90,41 +90,41 @@ type networkResultStore struct {
9090
results map[uint64][]chan *networkResult
9191
resultsMtx sync.Mutex
9292

93-
// paymentIDMtx is a multimutex used to make sure the database and
94-
// result subscribers map is consistent for each payment ID in case of
93+
// attemptIDMtx is a multimutex used to make sure the database and
94+
// result subscribers map is consistent for each attempt ID in case of
9595
// concurrent callers.
96-
paymentIDMtx *multimutex.Mutex[uint64]
96+
attemptIDMtx *multimutex.Mutex[uint64]
9797
}
9898

9999
func newNetworkResultStore(db kvdb.Backend) *networkResultStore {
100100
return &networkResultStore{
101101
backend: db,
102102
results: make(map[uint64][]chan *networkResult),
103-
paymentIDMtx: multimutex.NewMutex[uint64](),
103+
attemptIDMtx: multimutex.NewMutex[uint64](),
104104
}
105105
}
106106

107-
// storeResult stores the networkResult for the given paymentID, and
108-
// notifies any subscribers.
109-
func (store *networkResultStore) storeResult(paymentID uint64,
107+
// storeResult stores the networkResult for the given attemptID, and notifies
108+
// any subscribers.
109+
func (store *networkResultStore) storeResult(attemptID uint64,
110110
result *networkResult) error {
111111

112-
// We get a mutex for this payment ID. This is needed to ensure
112+
// We get a mutex for this attempt ID. This is needed to ensure
113113
// consistency between the database state and the subscribers in case
114114
// of concurrent calls.
115-
store.paymentIDMtx.Lock(paymentID)
116-
defer store.paymentIDMtx.Unlock(paymentID)
115+
store.attemptIDMtx.Lock(attemptID)
116+
defer store.attemptIDMtx.Unlock(attemptID)
117117

118-
log.Debugf("Storing result for paymentID=%v", paymentID)
118+
log.Debugf("Storing result for attemptID=%v", attemptID)
119119

120120
// Serialize the payment result.
121121
var b bytes.Buffer
122122
if err := serializeNetworkResult(&b, result); err != nil {
123123
return err
124124
}
125125

126-
var paymentIDBytes [8]byte
127-
binary.BigEndian.PutUint64(paymentIDBytes[:], paymentID)
126+
var attemptIDBytes [8]byte
127+
binary.BigEndian.PutUint64(attemptIDBytes[:], attemptID)
128128

129129
err := kvdb.Batch(store.backend, func(tx kvdb.RwTx) error {
130130
networkResults, err := tx.CreateTopLevelBucket(
@@ -134,7 +134,7 @@ func (store *networkResultStore) storeResult(paymentID uint64,
134134
return err
135135
}
136136

137-
return networkResults.Put(paymentIDBytes[:], b.Bytes())
137+
return networkResults.Put(attemptIDBytes[:], b.Bytes())
138138
})
139139
if err != nil {
140140
return err
@@ -143,28 +143,27 @@ func (store *networkResultStore) storeResult(paymentID uint64,
143143
// Now that the result is stored in the database, we can notify any
144144
// active subscribers.
145145
store.resultsMtx.Lock()
146-
for _, res := range store.results[paymentID] {
146+
for _, res := range store.results[attemptID] {
147147
res <- result
148148
}
149-
delete(store.results, paymentID)
149+
delete(store.results, attemptID)
150150
store.resultsMtx.Unlock()
151151

152152
return nil
153153
}
154154

155-
// subscribeResult is used to get the payment result for the given
156-
// payment ID. It returns a channel on which the result will be delivered when
157-
// ready.
158-
func (store *networkResultStore) subscribeResult(paymentID uint64) (
155+
// subscribeResult is used to get the HTLC attempt result for the given attempt
156+
// ID. It returns a channel on which the result will be delivered when ready.
157+
func (store *networkResultStore) subscribeResult(attemptID uint64) (
159158
<-chan *networkResult, error) {
160159

161160
// We get a mutex for this payment ID. This is needed to ensure
162161
// consistency between the database state and the subscribers in case
163162
// of concurrent calls.
164-
store.paymentIDMtx.Lock(paymentID)
165-
defer store.paymentIDMtx.Unlock(paymentID)
163+
store.attemptIDMtx.Lock(attemptID)
164+
defer store.attemptIDMtx.Unlock(attemptID)
166165

167-
log.Debugf("Subscribing to result for paymentID=%v", paymentID)
166+
log.Debugf("Subscribing to result for attemptID=%v", attemptID)
168167

169168
var (
170169
result *networkResult
@@ -173,7 +172,7 @@ func (store *networkResultStore) subscribeResult(paymentID uint64) (
173172

174173
err := kvdb.View(store.backend, func(tx kvdb.RTx) error {
175174
var err error
176-
result, err = fetchResult(tx, paymentID)
175+
result, err = fetchResult(tx, attemptID)
177176
switch {
178177

179178
// Result not yet available, we will notify once a result is
@@ -205,8 +204,8 @@ func (store *networkResultStore) subscribeResult(paymentID uint64) (
205204
// Otherwise we store the result channel for when the result is
206205
// available.
207206
store.resultsMtx.Lock()
208-
store.results[paymentID] = append(
209-
store.results[paymentID], resultChan,
207+
store.results[attemptID] = append(
208+
store.results[attemptID], resultChan,
210209
)
211210
store.resultsMtx.Unlock()
212211

@@ -234,16 +233,16 @@ func (store *networkResultStore) getResult(pid uint64) (
234233
}
235234

236235
func fetchResult(tx kvdb.RTx, pid uint64) (*networkResult, error) {
237-
var paymentIDBytes [8]byte
238-
binary.BigEndian.PutUint64(paymentIDBytes[:], pid)
236+
var attemptIDBytes [8]byte
237+
binary.BigEndian.PutUint64(attemptIDBytes[:], pid)
239238

240239
networkResults := tx.ReadBucket(networkResultStoreBucketKey)
241240
if networkResults == nil {
242241
return nil, ErrPaymentIDNotFound
243242
}
244243

245244
// Check whether a result is already available.
246-
resultBytes := networkResults.Get(paymentIDBytes[:])
245+
resultBytes := networkResults.Get(attemptIDBytes[:])
247246
if resultBytes == nil {
248247
return nil, ErrPaymentIDNotFound
249248
}

0 commit comments

Comments
 (0)