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

lightningnetwork / lnd / 16802564426

07 Aug 2025 10:58AM UTC coverage: 57.437% (-9.5%) from 66.938%
16802564426

Pull #9871

github

web-flow
Merge 0e84b7dbb into 8a2128ba4
Pull Request #9871: Add `NoopAdd` HTLCs

48 of 147 new or added lines in 3 files covered. (32.65%)

28319 existing lines in 455 files now uncovered.

99037 of 172428 relevant lines covered (57.44%)

1.78 hits per line

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

59.3
/lnwallet/payment_descriptor.go
1
package lnwallet
2

3
import (
4
        "crypto/sha256"
5

6
        "github.com/lightningnetwork/lnd/channeldb"
7
        "github.com/lightningnetwork/lnd/graph/db/models"
8
        "github.com/lightningnetwork/lnd/input"
9
        "github.com/lightningnetwork/lnd/lntypes"
10
        "github.com/lightningnetwork/lnd/lnwire"
11
)
12

13
// updateType is the exact type of an entry within the shared HTLC log.
14
type updateType uint8
15

16
const (
17
        // Add is an update type that adds a new HTLC entry into the log.
18
        // Either side can add a new pending HTLC by adding a new Add entry
19
        // into their update log.
20
        Add updateType = iota
21

22
        // Fail is an update type which removes a prior HTLC entry from the
23
        // log. Adding a Fail entry to one's log will modify the _remote_
24
        // party's update log once a new commitment view has been evaluated
25
        // which contains the Fail entry.
26
        Fail
27

28
        // MalformedFail is an update type which removes a prior HTLC entry
29
        // from the log. Adding a MalformedFail entry to one's log will modify
30
        // the _remote_ party's update log once a new commitment view has been
31
        // evaluated which contains the MalformedFail entry. The difference
32
        // from Fail type lie in the different data we have to store.
33
        MalformedFail
34

35
        // Settle is an update type which settles a prior HTLC crediting the
36
        // balance of the receiving node. Adding a Settle entry to a log will
37
        // result in the settle entry being removed on the log as well as the
38
        // original add entry from the remote party's log after the next state
39
        // transition.
40
        Settle
41

42
        // FeeUpdate is an update type sent by the channel initiator that
43
        // updates the fee rate used when signing the commitment transaction.
44
        FeeUpdate
45

46
        // NoOpAdd is an update type that adds a new HTLC entry into the log.
47
        // This differs from the normal Add type, in that when settled the
48
        // balance may go back to the sender, rather than be credited for the
49
        // receiver. The criteria about whether the balance will go back to the
50
        // sender is whether the receiver is sitting above the channel reserve.
51
        NoOpAdd
52
)
53

54
// String returns a human readable string that uniquely identifies the target
55
// update type.
UNCOV
56
func (u updateType) String() string {
×
UNCOV
57
        switch u {
×
UNCOV
58
        case Add:
×
UNCOV
59
                return "Add"
×
60
        case Fail:
×
61
                return "Fail"
×
62
        case MalformedFail:
×
63
                return "MalformedFail"
×
64
        case Settle:
×
65
                return "Settle"
×
66
        case FeeUpdate:
×
67
                return "FeeUpdate"
×
NEW
68
        case NoOpAdd:
×
NEW
69
                return "NoOpAdd"
×
70
        default:
×
71
                return "<unknown type>"
×
72
        }
73
}
74

75
// paymentDescriptor represents a commitment state update which either adds,
76
// settles, or removes an HTLC. paymentDescriptors encapsulate all necessary
77
// metadata w.r.t to an HTLC, and additional data pairing a settle message to
78
// the original added HTLC.
79
//
80
// TODO(roasbeef): LogEntry interface??
81
//   - need to separate attrs for cancel/add/settle/feeupdate
82
type paymentDescriptor struct {
83
        // ChanID is the ChannelID of the LightningChannel that this
84
        // paymentDescriptor belongs to. We track this here so we can
85
        // reconstruct the Messages that this paymentDescriptor is built from.
86
        ChanID lnwire.ChannelID
87

88
        // RHash is the payment hash for this HTLC. The HTLC can be settled iff
89
        // the preimage to this hash is presented.
90
        RHash PaymentHash
91

92
        // RPreimage is the preimage that settles the HTLC pointed to within the
93
        // log by the ParentIndex.
94
        RPreimage PaymentHash
95

96
        // Timeout is the absolute timeout in blocks, after which this HTLC
97
        // expires.
98
        Timeout uint32
99

100
        // Amount is the HTLC amount in milli-satoshis.
101
        Amount lnwire.MilliSatoshi
102

103
        // LogIndex is the log entry number that his HTLC update has within the
104
        // log. Depending on if IsIncoming is true, this is either an entry the
105
        // remote party added, or one that we added locally.
106
        LogIndex uint64
107

108
        // HtlcIndex is the index within the main update log for this HTLC.
109
        // Entries within the log of type Add will have this field populated,
110
        // as other entries will point to the entry via this counter.
111
        //
112
        // NOTE: This field will only be populate if EntryType is Add.
113
        HtlcIndex uint64
114

115
        // ParentIndex is the HTLC index of the entry that this update settles
116
        // or times out.
117
        //
118
        // NOTE: This field will only be populate if EntryType is Fail or
119
        // Settle.
120
        ParentIndex uint64
121

122
        // SourceRef points to an Add update in a forwarding package owned by
123
        // this channel.
124
        //
125
        // NOTE: This field will only be populated if EntryType is Fail or
126
        // Settle.
127
        SourceRef *channeldb.AddRef
128

129
        // DestRef points to a Fail/Settle update in another link's forwarding
130
        // package.
131
        //
132
        // NOTE: This field will only be populated if EntryType is Fail or
133
        // Settle, and the forwarded Add successfully included in an outgoing
134
        // link's commitment txn.
135
        DestRef *channeldb.SettleFailRef
136

137
        // OpenCircuitKey references the incoming Chan/HTLC ID of an Add HTLC
138
        // packet delivered by the switch.
139
        //
140
        // NOTE: This field is only populated for payment descriptors in the
141
        // *local* update log, and if the Add packet was delivered by the
142
        // switch.
143
        OpenCircuitKey *models.CircuitKey
144

145
        // ClosedCircuitKey references the incoming Chan/HTLC ID of the Add HTLC
146
        // that opened the circuit.
147
        //
148
        // NOTE: This field is only populated for payment descriptors in the
149
        // *local* update log, and if settle/fails have a committed circuit in
150
        // the circuit map.
151
        ClosedCircuitKey *models.CircuitKey
152

153
        // localOutputIndex is the output index of this HTLc output in the
154
        // commitment transaction of the local node.
155
        //
156
        // NOTE: If the output is dust from the PoV of the local commitment
157
        // chain, then this value will be -1.
158
        localOutputIndex int32
159

160
        // remoteOutputIndex is the output index of this HTLC output in the
161
        // commitment transaction of the remote node.
162
        //
163
        // NOTE: If the output is dust from the PoV of the remote commitment
164
        // chain, then this value will be -1.
165
        remoteOutputIndex int32
166

167
        // sig is the signature for the second-level HTLC transaction that
168
        // spends the version of this HTLC on the commitment transaction of the
169
        // local node. This signature is generated by the remote node and
170
        // stored by the local node in the case that local node needs to
171
        // broadcast their commitment transaction.
172
        sig input.Signature
173

174
        // addCommitHeight[Remote|Local] encodes the height of the commitment
175
        // which included this HTLC on either the remote or local commitment
176
        // chain. This value is used to determine when an HTLC is fully
177
        // "locked-in".
178
        addCommitHeights lntypes.Dual[uint64]
179

180
        // removeCommitHeight[Remote|Local] encodes the height of the
181
        // commitment which removed the parent pointer of this
182
        // paymentDescriptor either due to a timeout or a settle. Once both
183
        // these heights are below the tail of both chains, the log entries can
184
        // safely be removed.
185
        removeCommitHeights lntypes.Dual[uint64]
186

187
        // OnionBlob is an opaque blob which is used to complete multi-hop
188
        // routing.
189
        //
190
        // NOTE: Populated only on add payment descriptor entry types.
191
        OnionBlob [lnwire.OnionPacketSize]byte
192

193
        // ShaOnionBlob is a sha of the onion blob.
194
        //
195
        // NOTE: Populated only in payment descriptor with MalformedFail type.
196
        ShaOnionBlob [sha256.Size]byte
197

198
        // FailReason stores the reason why a particular payment was canceled.
199
        //
200
        // NOTE: Populate only in fail payment descriptor entry types.
201
        FailReason []byte
202

203
        // FailCode stores the code why a particular payment was canceled.
204
        //
205
        // NOTE: Populated only in payment descriptor with MalformedFail type.
206
        FailCode lnwire.FailCode
207

208
        // [our|their|]PkScript are the raw public key scripts that encodes the
209
        // redemption rules for this particular HTLC. These fields will only be
210
        // populated iff the EntryType of this paymentDescriptor is Add.
211
        // ourPkScript is the ourPkScript from the context of our local
212
        // commitment chain. theirPkScript is the latest pkScript from the
213
        // context of the remote commitment chain.
214
        //
215
        // NOTE: These values may change within the logs themselves, however,
216
        // they'll stay consistent within the commitment chain entries
217
        // themselves.
218
        ourPkScript        []byte
219
        ourWitnessScript   []byte
220
        theirPkScript      []byte
221
        theirWitnessScript []byte
222

223
        // EntryType denotes the exact type of the paymentDescriptor. In the
224
        // case of a Timeout, or Settle type, then the Parent field will point
225
        // into the log to the HTLC being modified.
226
        EntryType updateType
227

228
        // noOpSettle is a flag indicating whether a chain of entries resulted
229
        // in an effective no-op settle. That means that the amount was credited
230
        // back to the sender. This is useful as we need a way to mark whether
231
        // the noop add was effective, which can be useful at later stages,
232
        // where we might not be able to re-run the criteria for the
233
        // effectiveness of the noop-add.
234
        noOpSettle bool
235

236
        // isForwarded denotes if an incoming HTLC has been forwarded to any
237
        // possible upstream peers in the route.
238
        isForwarded bool
239

240
        // BlindingPoint is an optional ephemeral key used in route blinding.
241
        // This value is set for nodes that are relaying payments inside of a
242
        // blinded route (ie, not the introduction node) from update_add_htlc's
243
        // TLVs.
244
        BlindingPoint lnwire.BlindingPointRecord
245

246
        // CustomRecords also stores the set of optional custom records that
247
        // may have been attached to a sent HTLC.
248
        CustomRecords lnwire.CustomRecords
249
}
250

251
// toLogUpdate recovers the underlying LogUpdate from the paymentDescriptor.
252
// This operation is lossy and will forget some extra information tracked by the
253
// paymentDescriptor but the function is total in that all paymentDescriptors
254
// can be converted back to LogUpdates.
255
func (pd *paymentDescriptor) toLogUpdate() channeldb.LogUpdate {
3✔
256
        var msg lnwire.Message
3✔
257
        switch pd.EntryType {
3✔
258
        case Add, NoOpAdd:
3✔
259
                msg = &lnwire.UpdateAddHTLC{
3✔
260
                        ChanID:        pd.ChanID,
3✔
261
                        ID:            pd.HtlcIndex,
3✔
262
                        Amount:        pd.Amount,
3✔
263
                        PaymentHash:   pd.RHash,
3✔
264
                        Expiry:        pd.Timeout,
3✔
265
                        OnionBlob:     pd.OnionBlob,
3✔
266
                        BlindingPoint: pd.BlindingPoint,
3✔
267
                        CustomRecords: pd.CustomRecords.Copy(),
3✔
268
                }
3✔
269
        case Settle:
3✔
270
                msg = &lnwire.UpdateFulfillHTLC{
3✔
271
                        ChanID:          pd.ChanID,
3✔
272
                        ID:              pd.ParentIndex,
3✔
273
                        PaymentPreimage: pd.RPreimage,
3✔
274
                }
3✔
275
        case Fail:
3✔
276
                msg = &lnwire.UpdateFailHTLC{
3✔
277
                        ChanID: pd.ChanID,
3✔
278
                        ID:     pd.ParentIndex,
3✔
279
                        Reason: pd.FailReason,
3✔
280
                }
3✔
281
        case MalformedFail:
3✔
282
                msg = &lnwire.UpdateFailMalformedHTLC{
3✔
283
                        ChanID:       pd.ChanID,
3✔
284
                        ID:           pd.ParentIndex,
3✔
285
                        ShaOnionBlob: pd.ShaOnionBlob,
3✔
286
                        FailureCode:  pd.FailCode,
3✔
287
                }
3✔
UNCOV
288
        case FeeUpdate:
×
UNCOV
289
                // The Amount field holds the feerate denominated in
×
UNCOV
290
                // msat. Since feerates are only denominated in sat/kw,
×
UNCOV
291
                // we can convert it without loss of precision.
×
UNCOV
292
                msg = &lnwire.UpdateFee{
×
UNCOV
293
                        ChanID:   pd.ChanID,
×
UNCOV
294
                        FeePerKw: uint32(pd.Amount.ToSatoshis()),
×
UNCOV
295
                }
×
296
        }
297

298
        return channeldb.LogUpdate{
3✔
299
                LogIndex:  pd.LogIndex,
3✔
300
                UpdateMsg: msg,
3✔
301
        }
3✔
302
}
303

304
// setCommitHeight updates the appropriate addCommitHeight and/or
305
// removeCommitHeight for whoseCommitChain and locks it in at nextHeight.
306
func (pd *paymentDescriptor) setCommitHeight(
307
        whoseCommitChain lntypes.ChannelParty, nextHeight uint64) {
3✔
308

3✔
309
        switch pd.EntryType {
3✔
310
        case Add, NoOpAdd:
3✔
311
                pd.addCommitHeights.SetForParty(
3✔
312
                        whoseCommitChain, nextHeight,
3✔
313
                )
3✔
314
        case Settle, Fail, MalformedFail:
3✔
315
                pd.removeCommitHeights.SetForParty(
3✔
316
                        whoseCommitChain, nextHeight,
3✔
317
                )
3✔
UNCOV
318
        case FeeUpdate:
×
UNCOV
319
                // Fee updates are applied for all commitments
×
UNCOV
320
                // after they are sent/received, so we consider
×
UNCOV
321
                // them being added and removed at the same
×
UNCOV
322
                // height.
×
UNCOV
323
                pd.addCommitHeights.SetForParty(
×
UNCOV
324
                        whoseCommitChain, nextHeight,
×
UNCOV
325
                )
×
UNCOV
326
                pd.removeCommitHeights.SetForParty(
×
UNCOV
327
                        whoseCommitChain, nextHeight,
×
UNCOV
328
                )
×
329
        }
330
}
331

332
// isAdd returns true if the paymentDescriptor is of type Add.
333
func (pd *paymentDescriptor) isAdd() bool {
3✔
334
        return pd.EntryType == Add || pd.EntryType == NoOpAdd
3✔
335
}
3✔
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