• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 13211764208

08 Feb 2025 03:08AM UTC coverage: 49.288% (-9.5%) from 58.815%
13211764208

Pull #9489

github

calvinrzachman
itest: verify switchrpc server enforces send then track

We prevent the rpc server from allowing onion dispatches for
attempt IDs which have already been tracked by rpc clients.

This helps protect the client from leaking a duplicate onion
attempt. NOTE: This is not the only method for solving this
issue! The issue could be addressed via careful client side
programming which accounts for the uncertainty and async
nature of dispatching onions to a remote process via RPC.
This would require some lnd ChannelRouter changes for how
we intend to use these RPCs though.
Pull Request #9489: multi: add BuildOnion, SendOnion, and TrackOnion RPCs

474 of 990 new or added lines in 11 files covered. (47.88%)

27321 existing lines in 435 files now uncovered.

101192 of 205306 relevant lines covered (49.29%)

1.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

69.71
/htlcswitch/htlcnotifier.go
1
package htlcswitch
2

3
import (
4
        "fmt"
5
        "strings"
6
        "sync"
7
        "time"
8

9
        "github.com/lightningnetwork/lnd/channeldb"
10
        "github.com/lightningnetwork/lnd/graph/db/models"
11
        "github.com/lightningnetwork/lnd/htlcswitch/hop"
12
        "github.com/lightningnetwork/lnd/lntypes"
13
        "github.com/lightningnetwork/lnd/lnwire"
14
        "github.com/lightningnetwork/lnd/subscribe"
15
)
16

17
// HtlcNotifier notifies clients of htlc forwards, failures and settles for
18
// htlcs that the switch handles. It takes subscriptions for its events and
19
// notifies them when htlc events occur. These are served on a best-effort
20
// basis; events are not persisted, delivery is not guaranteed (in the event
21
// of a crash in the switch, forward events may be lost) and some events may
22
// be replayed upon restart. Events consumed from this package should be
23
// de-duplicated by the htlc's unique combination of incoming+outgoing circuit
24
// and not relied upon for critical operations.
25
//
26
// The htlc notifier sends the following kinds of events:
27
// Forwarding Event:
28
// - Represents a htlc which is forwarded onward from our node.
29
// - Present for htlc forwards through our node and local sends.
30
//
31
// Link Failure Event:
32
//   - Indicates that a htlc has failed on our incoming or outgoing link,
33
//     with an incoming boolean which indicates where the failure occurred.
34
//   - Incoming link failures are present for failed attempts to pay one of
35
//     our invoices (insufficient amount or mpp timeout, for example) and for
36
//     forwards that we cannot decode to forward onwards.
37
//   - Outgoing link failures are present for forwards or local payments that
38
//     do not meet our outgoing link's policy (insufficient fees, for example)
39
//     and when we fail to forward the payment on (insufficient outgoing
40
//     capacity, or an unknown outgoing link).
41
//
42
// Forwarding Failure Event:
43
//   - Forwarding failures indicate that a htlc we forwarded has failed at
44
//     another node down the route.
45
//   - Present for local sends and htlc forwards which fail after they left
46
//     our node.
47
//
48
// Settle event:
49
//   - Settle events are present when a htlc which we added is settled through
50
//     the release of a preimage.
51
//   - Present for local receives, and successful local sends or forwards.
52
//
53
// Each htlc is identified by its incoming and outgoing circuit key. Htlcs,
54
// and their subsequent settles or fails, can be identified by the combination
55
// of incoming and outgoing circuits. Note that receives to our node will
56
// have a zero outgoing circuit key because the htlc terminates at our
57
// node, and sends from our node will have a zero incoming circuit key because
58
// the send originates at our node.
59
type HtlcNotifier struct {
60
        started sync.Once
61
        stopped sync.Once
62

63
        // now returns the current time, it is set in the htlcnotifier to allow
64
        // for timestamp mocking in tests.
65
        now func() time.Time
66

67
        ntfnServer *subscribe.Server
68
}
69

70
// NewHtlcNotifier creates a new HtlcNotifier which gets htlc forwarded,
71
// failed and settled events from links our node has established with peers
72
// and sends notifications to subscribing clients.
73
func NewHtlcNotifier(now func() time.Time) *HtlcNotifier {
3✔
74
        return &HtlcNotifier{
3✔
75
                now:        now,
3✔
76
                ntfnServer: subscribe.NewServer(),
3✔
77
        }
3✔
78
}
3✔
79

80
// Start starts the HtlcNotifier and all goroutines it needs to consume
81
// events and provide subscriptions to clients.
82
func (h *HtlcNotifier) Start() error {
3✔
83
        var err error
3✔
84
        h.started.Do(func() {
6✔
85
                log.Info("HtlcNotifier starting")
3✔
86
                err = h.ntfnServer.Start()
3✔
87
        })
3✔
88
        return err
3✔
89
}
90

91
// Stop signals the notifier for a graceful shutdown.
92
func (h *HtlcNotifier) Stop() error {
3✔
93
        var err error
3✔
94
        h.stopped.Do(func() {
6✔
95
                log.Info("HtlcNotifier shutting down...")
3✔
96
                defer log.Debug("HtlcNotifier shutdown complete")
3✔
97

3✔
98
                if err = h.ntfnServer.Stop(); err != nil {
3✔
99
                        log.Warnf("error stopping htlc notifier: %v", err)
×
100
                }
×
101
        })
102
        return err
3✔
103
}
104

105
// SubscribeHtlcEvents returns a subscribe.Client that will receive updates
106
// any time the server is made aware of a new event.
107
func (h *HtlcNotifier) SubscribeHtlcEvents() (*subscribe.Client, error) {
3✔
108
        return h.ntfnServer.Subscribe()
3✔
109
}
3✔
110

111
// HtlcKey uniquely identifies the htlc.
112
type HtlcKey struct {
113
        // IncomingCircuit is the channel an htlc id of the incoming htlc.
114
        IncomingCircuit models.CircuitKey
115

116
        // OutgoingCircuit is the channel and htlc id of the outgoing htlc.
117
        OutgoingCircuit models.CircuitKey
118
}
119

120
// String returns a string representation of a htlc key.
121
func (k HtlcKey) String() string {
×
122
        switch {
×
123
        case k.IncomingCircuit.ChanID == hop.Source:
×
124
                return k.OutgoingCircuit.String()
×
125

126
        case k.OutgoingCircuit.ChanID == hop.Exit:
×
127
                return k.IncomingCircuit.String()
×
128

129
        default:
×
130
                return fmt.Sprintf("%v -> %v", k.IncomingCircuit,
×
131
                        k.OutgoingCircuit)
×
132
        }
133
}
134

135
// HtlcInfo provides the details of a htlc that our node has processed. For
136
// forwards, incoming and outgoing values are set, whereas sends and receives
137
// will only have outgoing or incoming details set.
138
type HtlcInfo struct {
139
        // IncomingTimelock is the time lock of the htlc on our incoming
140
        // channel.
141
        IncomingTimeLock uint32
142

143
        // OutgoingTimelock is the time lock the htlc on our outgoing channel.
144
        OutgoingTimeLock uint32
145

146
        // IncomingAmt is the amount of the htlc on our incoming channel.
147
        IncomingAmt lnwire.MilliSatoshi
148

149
        // OutgoingAmt is the amount of the htlc on our outgoing channel.
150
        OutgoingAmt lnwire.MilliSatoshi
151
}
152

153
// String returns a string representation of a htlc.
154
func (h HtlcInfo) String() string {
×
155
        var details []string
×
156

×
157
        // If the incoming information is not zero, as is the case for a send,
×
158
        // we include the incoming amount and timelock.
×
159
        if h.IncomingAmt != 0 || h.IncomingTimeLock != 0 {
×
160
                str := fmt.Sprintf("incoming amount: %v, "+
×
161
                        "incoming timelock: %v", h.IncomingAmt,
×
162
                        h.IncomingTimeLock)
×
163

×
164
                details = append(details, str)
×
165
        }
×
166

167
        // If the outgoing information is not zero, as is the case for a
168
        // receive, we include the outgoing amount and timelock.
169
        if h.OutgoingAmt != 0 || h.OutgoingTimeLock != 0 {
×
170
                str := fmt.Sprintf("outgoing amount: %v, "+
×
171
                        "outgoing timelock: %v", h.OutgoingAmt,
×
172
                        h.OutgoingTimeLock)
×
173

×
174
                details = append(details, str)
×
175
        }
×
176

177
        return strings.Join(details, ", ")
×
178
}
179

180
// HtlcEventType represents the type of event that a htlc was part of.
181
type HtlcEventType int
182

183
const (
184
        // HtlcEventTypeSend represents a htlc that was part of a send from
185
        // our node.
186
        HtlcEventTypeSend HtlcEventType = iota
187

188
        // HtlcEventTypeReceive represents a htlc that was part of a receive
189
        // to our node.
190
        HtlcEventTypeReceive
191

192
        // HtlcEventTypeForward represents a htlc that was forwarded through
193
        // our node.
194
        HtlcEventTypeForward
195
)
196

197
// String returns a string representation of a htlc event type.
198
func (h HtlcEventType) String() string {
×
199
        switch h {
×
200
        case HtlcEventTypeSend:
×
201
                return "send"
×
202

203
        case HtlcEventTypeReceive:
×
204
                return "receive"
×
205

206
        case HtlcEventTypeForward:
×
207
                return "forward"
×
208

209
        default:
×
210
                return "unknown"
×
211
        }
212
}
213

214
// ForwardingEvent represents a htlc that was forwarded onwards from our node.
215
// Sends which originate from our node will report forward events with zero
216
// incoming circuits in their htlc key.
217
type ForwardingEvent struct {
218
        // HtlcKey uniquely identifies the htlc, and can be used to match the
219
        // forwarding event with subsequent settle/fail events.
220
        HtlcKey
221

222
        // HtlcInfo contains details about the htlc.
223
        HtlcInfo
224

225
        // HtlcEventType classifies the event as part of a local send or
226
        // receive, or as part of a forward.
227
        HtlcEventType
228

229
        // Timestamp is the time when this htlc was forwarded.
230
        Timestamp time.Time
231
}
232

233
// LinkFailEvent describes a htlc that failed on our incoming or outgoing
234
// link. The incoming bool is true for failures on incoming links, and false
235
// for failures on outgoing links. The failure reason is provided by a lnwire
236
// failure message which is enriched with a failure detail in the cases where
237
// the wire failure message does not contain full information about the
238
// failure.
239
type LinkFailEvent struct {
240
        // HtlcKey uniquely identifies the htlc.
241
        HtlcKey
242

243
        // HtlcInfo contains details about the htlc.
244
        HtlcInfo
245

246
        // HtlcEventType classifies the event as part of a local send or
247
        // receive, or as part of a forward.
248
        HtlcEventType
249

250
        // LinkError is the reason that we failed the htlc.
251
        LinkError *LinkError
252

253
        // Incoming is true if the htlc was failed on an incoming link.
254
        // If it failed on the outgoing link, it is false.
255
        Incoming bool
256

257
        // Timestamp is the time when the link failure occurred.
258
        Timestamp time.Time
259
}
260

261
// ForwardingFailEvent represents a htlc failure which occurred down the line
262
// after we forwarded a htlc onwards. An error is not included in this event
263
// because errors returned down the route are encrypted. HtlcInfo is not
264
// reliably available for forwarding failures, so it is omitted. These events
265
// should be matched with their corresponding forward event to obtain this
266
// information.
267
type ForwardingFailEvent struct {
268
        // HtlcKey uniquely identifies the htlc, and can be used to match the
269
        // htlc with its corresponding forwarding event.
270
        HtlcKey
271

272
        // HtlcEventType classifies the event as part of a local send or
273
        // receive, or as part of a forward.
274
        HtlcEventType
275

276
        // Timestamp is the time when the forwarding failure was received.
277
        Timestamp time.Time
278
}
279

280
// SettleEvent represents a htlc that was settled. HtlcInfo is not reliably
281
// available for forwarding failures, so it is omitted. These events should
282
// be matched with corresponding forward events or invoices (for receives)
283
// to obtain additional information about the htlc.
284
type SettleEvent struct {
285
        // HtlcKey uniquely identifies the htlc, and can be used to match
286
        // forwards with their corresponding forwarding event.
287
        HtlcKey
288

289
        // Preimage that was released for settling the htlc.
290
        Preimage lntypes.Preimage
291

292
        // HtlcEventType classifies the event as part of a local send or
293
        // receive, or as part of a forward.
294
        HtlcEventType
295

296
        // Timestamp is the time when this htlc was settled.
297
        Timestamp time.Time
298
}
299

300
type FinalHtlcEvent struct {
301
        CircuitKey
302

303
        Settled bool
304

305
        // Offchain is indicating whether the htlc was resolved off-chain.
306
        Offchain bool
307

308
        // Timestamp is the time when this htlc was settled.
309
        Timestamp time.Time
310
}
311

312
// NotifyForwardingEvent notifies the HtlcNotifier than a htlc has been
313
// forwarded.
314
//
315
// Note this is part of the htlcNotifier interface.
316
func (h *HtlcNotifier) NotifyForwardingEvent(key HtlcKey, info HtlcInfo,
317
        eventType HtlcEventType) {
3✔
318

3✔
319
        event := &ForwardingEvent{
3✔
320
                HtlcKey:       key,
3✔
321
                HtlcInfo:      info,
3✔
322
                HtlcEventType: eventType,
3✔
323
                Timestamp:     h.now(),
3✔
324
        }
3✔
325

3✔
326
        log.Tracef("Notifying forward event: %v over %v, %v", eventType, key,
3✔
327
                info)
3✔
328

3✔
329
        if err := h.ntfnServer.SendUpdate(event); err != nil {
3✔
330
                log.Warnf("Unable to send forwarding event: %v", err)
×
331
        }
×
332
}
333

334
// NotifyLinkFailEvent notifies that a htlc has failed on our incoming
335
// or outgoing link.
336
//
337
// Note this is part of the htlcNotifier interface.
338
func (h *HtlcNotifier) NotifyLinkFailEvent(key HtlcKey, info HtlcInfo,
339
        eventType HtlcEventType, linkErr *LinkError, incoming bool) {
3✔
340

3✔
341
        event := &LinkFailEvent{
3✔
342
                HtlcKey:       key,
3✔
343
                HtlcInfo:      info,
3✔
344
                HtlcEventType: eventType,
3✔
345
                LinkError:     linkErr,
3✔
346
                Incoming:      incoming,
3✔
347
                Timestamp:     h.now(),
3✔
348
        }
3✔
349

3✔
350
        log.Tracef("Notifying link failure event: %v over %v, %v", eventType,
3✔
351
                key, info)
3✔
352

3✔
353
        if err := h.ntfnServer.SendUpdate(event); err != nil {
3✔
354
                log.Warnf("Unable to send link fail event: %v", err)
×
355
        }
×
356
}
357

358
// NotifyForwardingFailEvent notifies the HtlcNotifier that a htlc we
359
// forwarded has failed down the line.
360
//
361
// Note this is part of the htlcNotifier interface.
362
func (h *HtlcNotifier) NotifyForwardingFailEvent(key HtlcKey,
363
        eventType HtlcEventType) {
3✔
364

3✔
365
        event := &ForwardingFailEvent{
3✔
366
                HtlcKey:       key,
3✔
367
                HtlcEventType: eventType,
3✔
368
                Timestamp:     h.now(),
3✔
369
        }
3✔
370

3✔
371
        log.Tracef("Notifying forwarding failure event: %v over %v", eventType,
3✔
372
                key)
3✔
373

3✔
374
        if err := h.ntfnServer.SendUpdate(event); err != nil {
3✔
375
                log.Warnf("Unable to send forwarding fail event: %v", err)
×
376
        }
×
377
}
378

379
// NotifySettleEvent notifies the HtlcNotifier that a htlc that we committed
380
// to as part of a forward or a receive to our node has been settled.
381
//
382
// Note this is part of the htlcNotifier interface.
383
func (h *HtlcNotifier) NotifySettleEvent(key HtlcKey,
384
        preimage lntypes.Preimage, eventType HtlcEventType) {
3✔
385

3✔
386
        event := &SettleEvent{
3✔
387
                HtlcKey:       key,
3✔
388
                Preimage:      preimage,
3✔
389
                HtlcEventType: eventType,
3✔
390
                Timestamp:     h.now(),
3✔
391
        }
3✔
392

3✔
393
        log.Tracef("Notifying settle event: %v over %v", eventType, key)
3✔
394

3✔
395
        if err := h.ntfnServer.SendUpdate(event); err != nil {
3✔
396
                log.Warnf("Unable to send settle event: %v", err)
×
397
        }
×
398
}
399

400
// NotifyFinalHtlcEvent notifies the HtlcNotifier that the final outcome for an
401
// htlc has been determined.
402
//
403
// Note this is part of the htlcNotifier interface.
404
func (h *HtlcNotifier) NotifyFinalHtlcEvent(key models.CircuitKey,
405
        info channeldb.FinalHtlcInfo) {
3✔
406

3✔
407
        event := &FinalHtlcEvent{
3✔
408
                CircuitKey: key,
3✔
409
                Settled:    info.Settled,
3✔
410
                Offchain:   info.Offchain,
3✔
411
                Timestamp:  h.now(),
3✔
412
        }
3✔
413

3✔
414
        log.Tracef("Notifying final settle event: %v", key)
3✔
415

3✔
416
        if err := h.ntfnServer.SendUpdate(event); err != nil {
3✔
417
                log.Warnf("Unable to send settle event: %v", err)
×
418
        }
×
419
}
420

421
// newHtlc key returns a htlc key for the packet provided. If the packet
422
// has a zero incoming channel ID, the packet is for one of our own sends,
423
// which has the payment id stashed in the incoming htlc id. If this is the
424
// case, we replace the incoming htlc id with zero so that the notifier
425
// consistently reports zero circuit keys for events that terminate or
426
// originate at our node.
427
func newHtlcKey(pkt *htlcPacket) HtlcKey {
3✔
428
        htlcKey := HtlcKey{
3✔
429
                IncomingCircuit: models.CircuitKey{
3✔
430
                        ChanID: pkt.incomingChanID,
3✔
431
                        HtlcID: pkt.incomingHTLCID,
3✔
432
                },
3✔
433
                OutgoingCircuit: CircuitKey{
3✔
434
                        ChanID: pkt.outgoingChanID,
3✔
435
                        HtlcID: pkt.outgoingHTLCID,
3✔
436
                },
3✔
437
        }
3✔
438

3✔
439
        // If the packet has a zero incoming channel ID, it is a send that was
3✔
440
        // initiated at our node. If this is the case, our internal pid is in
3✔
441
        // the incoming htlc ID, so we overwrite it with 0 for notification
3✔
442
        // purposes.
3✔
443
        if pkt.incomingChanID == hop.Source {
6✔
444
                htlcKey.IncomingCircuit.HtlcID = 0
3✔
445
        }
3✔
446

447
        return htlcKey
3✔
448
}
449

450
// newHtlcInfo returns HtlcInfo for the packet provided.
451
func newHtlcInfo(pkt *htlcPacket) HtlcInfo {
3✔
452
        return HtlcInfo{
3✔
453
                IncomingTimeLock: pkt.incomingTimeout,
3✔
454
                OutgoingTimeLock: pkt.outgoingTimeout,
3✔
455
                IncomingAmt:      pkt.incomingAmount,
3✔
456
                OutgoingAmt:      pkt.amount,
3✔
457
        }
3✔
458
}
3✔
459

460
// getEventType returns the htlc type based on the fields set in the htlc
461
// packet. Sends that originate at our node have the source (zero) incoming
462
// channel ID. Receives to our node have the exit (zero) outgoing channel ID
463
// and forwards have both fields set.
464
func getEventType(pkt *htlcPacket) HtlcEventType {
3✔
465
        switch {
3✔
466
        case pkt.incomingChanID == hop.Source:
3✔
467
                return HtlcEventTypeSend
3✔
468

UNCOV
469
        case pkt.outgoingChanID == hop.Exit:
×
UNCOV
470
                return HtlcEventTypeReceive
×
471

472
        default:
3✔
473
                return HtlcEventTypeForward
3✔
474
        }
475
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc