• 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

6.8
/lnwire/channel_update_2.go
1
package lnwire
2

3
import (
4
        "bytes"
5
        "fmt"
6
        "io"
7

8
        "github.com/btcsuite/btcd/chaincfg"
9
        "github.com/btcsuite/btcd/chaincfg/chainhash"
10
        "github.com/lightningnetwork/lnd/tlv"
11
)
12

13
const (
14
        defaultCltvExpiryDelta           = uint16(80)
15
        defaultHtlcMinMsat               = MilliSatoshi(1)
16
        defaultFeeBaseMsat               = uint32(1000)
17
        defaultFeeProportionalMillionths = uint32(1)
18
)
19

20
// ChannelUpdate2 message is used after taproot channel has been initially
21
// announced. Each side independently announces its fees and minimum expiry for
22
// HTLCs and other parameters. This message is also used to redeclare initially
23
// set channel parameters.
24
type ChannelUpdate2 struct {
25
        // Signature is used to validate the announced data and prove the
26
        // ownership of node id.
27
        Signature Sig
28

29
        // ChainHash denotes the target chain that this channel was opened
30
        // within. This value should be the genesis hash of the target chain.
31
        // Along with the short channel ID, this uniquely identifies the
32
        // channel globally in a blockchain.
33
        ChainHash tlv.RecordT[tlv.TlvType0, chainhash.Hash]
34

35
        // ShortChannelID is the unique description of the funding transaction.
36
        ShortChannelID tlv.RecordT[tlv.TlvType2, ShortChannelID]
37

38
        // BlockHeight allows ordering in the case of multiple announcements. We
39
        // should ignore the message if block height is not greater than the
40
        // last-received. The block height must always be greater or equal to
41
        // the block height that the channel funding transaction was confirmed
42
        // in.
43
        BlockHeight tlv.RecordT[tlv.TlvType4, uint32]
44

45
        // DisabledFlags is an optional bitfield that describes various reasons
46
        // that the node is communicating that the channel should be considered
47
        // disabled.
48
        DisabledFlags tlv.RecordT[tlv.TlvType6, ChanUpdateDisableFlags]
49

50
        // SecondPeer is used to indicate which node the channel node has
51
        // created and signed this message. If this field is present, it was
52
        // node 2 otherwise it was node 1.
53
        SecondPeer tlv.OptionalRecordT[tlv.TlvType8, TrueBoolean]
54

55
        // CLTVExpiryDelta is the minimum number of blocks this node requires to
56
        // be added to the expiry of HTLCs. This is a security parameter
57
        // determined by the node operator. This value represents the required
58
        // gap between the time locks of the incoming and outgoing HTLC's set
59
        // to this node.
60
        CLTVExpiryDelta tlv.RecordT[tlv.TlvType10, uint16]
61

62
        // HTLCMinimumMsat is the minimum HTLC value which will be accepted.
63
        HTLCMinimumMsat tlv.RecordT[tlv.TlvType12, MilliSatoshi]
64

65
        // HtlcMaximumMsat is the maximum HTLC value which will be accepted.
66
        HTLCMaximumMsat tlv.RecordT[tlv.TlvType14, MilliSatoshi]
67

68
        // FeeBaseMsat is the base fee that must be used for incoming HTLC's to
69
        // this particular channel. This value will be tacked onto the required
70
        // for a payment independent of the size of the payment.
71
        FeeBaseMsat tlv.RecordT[tlv.TlvType16, uint32]
72

73
        // FeeProportionalMillionths is the fee rate that will be charged per
74
        // millionth of a satoshi.
75
        FeeProportionalMillionths tlv.RecordT[tlv.TlvType18, uint32]
76

77
        // ExtraOpaqueData is the set of data that was appended to this message
78
        // to fill out the full maximum transport message size. These fields can
79
        // be used to specify optional data such as custom TLV fields.
80
        ExtraOpaqueData ExtraOpaqueData
81
}
82

83
// Decode deserializes a serialized ChannelUpdate2 stored in the passed
84
// io.Reader observing the specified protocol version.
85
//
86
// This is part of the lnwire.Message interface.
UNCOV
87
func (c *ChannelUpdate2) Decode(r io.Reader, _ uint32) error {
×
UNCOV
88
        err := ReadElement(r, &c.Signature)
×
UNCOV
89
        if err != nil {
×
UNCOV
90
                return err
×
UNCOV
91
        }
×
UNCOV
92
        c.Signature.ForceSchnorr()
×
UNCOV
93

×
UNCOV
94
        return c.DecodeTLVRecords(r)
×
95
}
96

97
// DecodeTLVRecords decodes only the TLV section of the message.
UNCOV
98
func (c *ChannelUpdate2) DecodeTLVRecords(r io.Reader) error {
×
UNCOV
99
        // First extract into extra opaque data.
×
UNCOV
100
        var tlvRecords ExtraOpaqueData
×
UNCOV
101
        if err := ReadElements(r, &tlvRecords); err != nil {
×
102
                return err
×
103
        }
×
104

UNCOV
105
        var (
×
UNCOV
106
                chainHash  = tlv.ZeroRecordT[tlv.TlvType0, [32]byte]()
×
UNCOV
107
                secondPeer = tlv.ZeroRecordT[tlv.TlvType8, TrueBoolean]()
×
UNCOV
108
        )
×
UNCOV
109
        typeMap, err := tlvRecords.ExtractRecords(
×
UNCOV
110
                &chainHash, &c.ShortChannelID, &c.BlockHeight, &c.DisabledFlags,
×
UNCOV
111
                &secondPeer, &c.CLTVExpiryDelta, &c.HTLCMinimumMsat,
×
UNCOV
112
                &c.HTLCMaximumMsat, &c.FeeBaseMsat,
×
UNCOV
113
                &c.FeeProportionalMillionths,
×
UNCOV
114
        )
×
UNCOV
115
        if err != nil {
×
UNCOV
116
                return err
×
UNCOV
117
        }
×
118

119
        // By default, the chain-hash is the bitcoin mainnet genesis block hash.
UNCOV
120
        c.ChainHash.Val = *chaincfg.MainNetParams.GenesisHash
×
UNCOV
121
        if _, ok := typeMap[c.ChainHash.TlvType()]; ok {
×
UNCOV
122
                c.ChainHash.Val = chainHash.Val
×
UNCOV
123
        }
×
124

125
        // The presence of the second_peer tlv type indicates "true".
UNCOV
126
        if _, ok := typeMap[c.SecondPeer.TlvType()]; ok {
×
UNCOV
127
                c.SecondPeer = tlv.SomeRecordT(secondPeer)
×
UNCOV
128
        }
×
129

130
        // If the CLTV expiry delta was not encoded, then set it to the default
131
        // value.
UNCOV
132
        if _, ok := typeMap[c.CLTVExpiryDelta.TlvType()]; !ok {
×
UNCOV
133
                c.CLTVExpiryDelta.Val = defaultCltvExpiryDelta
×
UNCOV
134
        }
×
135

136
        // If the HTLC Minimum msat was not encoded, then set it to the default
137
        // value.
UNCOV
138
        if _, ok := typeMap[c.HTLCMinimumMsat.TlvType()]; !ok {
×
UNCOV
139
                c.HTLCMinimumMsat.Val = defaultHtlcMinMsat
×
UNCOV
140
        }
×
141

142
        // If the base fee was not encoded, then set it to the default value.
UNCOV
143
        if _, ok := typeMap[c.FeeBaseMsat.TlvType()]; !ok {
×
UNCOV
144
                c.FeeBaseMsat.Val = defaultFeeBaseMsat
×
UNCOV
145
        }
×
146

147
        // If the proportional fee was not encoded, then set it to the default
148
        // value.
UNCOV
149
        if _, ok := typeMap[c.FeeProportionalMillionths.TlvType()]; !ok {
×
UNCOV
150
                c.FeeProportionalMillionths.Val = defaultFeeProportionalMillionths //nolint:ll
×
UNCOV
151
        }
×
152

UNCOV
153
        if len(tlvRecords) != 0 {
×
UNCOV
154
                c.ExtraOpaqueData = tlvRecords
×
UNCOV
155
        }
×
156

UNCOV
157
        return nil
×
158
}
159

160
// Encode serializes the target ChannelUpdate2 into the passed io.Writer
161
// observing the protocol version specified.
162
//
163
// This is part of the lnwire.Message interface.
UNCOV
164
func (c *ChannelUpdate2) Encode(w *bytes.Buffer, _ uint32) error {
×
UNCOV
165
        _, err := w.Write(c.Signature.RawBytes())
×
UNCOV
166
        if err != nil {
×
167
                return err
×
168
        }
×
169

UNCOV
170
        _, err = c.DataToSign()
×
UNCOV
171
        if err != nil {
×
172
                return err
×
173
        }
×
174

UNCOV
175
        return WriteBytes(w, c.ExtraOpaqueData)
×
176
}
177

178
// DataToSign is used to retrieve part of the announcement message which should
179
// be signed. For the ChannelUpdate2 message, this includes the serialised TLV
180
// records.
UNCOV
181
func (c *ChannelUpdate2) DataToSign() ([]byte, error) {
×
UNCOV
182
        // The chain-hash record is only included if it is _not_ equal to the
×
UNCOV
183
        // bitcoin mainnet genisis block hash.
×
UNCOV
184
        var recordProducers []tlv.RecordProducer
×
UNCOV
185
        if !c.ChainHash.Val.IsEqual(chaincfg.MainNetParams.GenesisHash) {
×
UNCOV
186
                hash := tlv.ZeroRecordT[tlv.TlvType0, [32]byte]()
×
UNCOV
187
                hash.Val = c.ChainHash.Val
×
UNCOV
188

×
UNCOV
189
                recordProducers = append(recordProducers, &hash)
×
UNCOV
190
        }
×
191

UNCOV
192
        recordProducers = append(recordProducers,
×
UNCOV
193
                &c.ShortChannelID, &c.BlockHeight,
×
UNCOV
194
        )
×
UNCOV
195

×
UNCOV
196
        // Only include the disable flags if any bit is set.
×
UNCOV
197
        if !c.DisabledFlags.Val.IsEnabled() {
×
UNCOV
198
                recordProducers = append(recordProducers, &c.DisabledFlags)
×
UNCOV
199
        }
×
200

201
        // We only need to encode the second peer boolean if it is true
UNCOV
202
        c.SecondPeer.WhenSome(func(r tlv.RecordT[tlv.TlvType8, TrueBoolean]) {
×
UNCOV
203
                recordProducers = append(recordProducers, &r)
×
UNCOV
204
        })
×
205

206
        // We only encode the cltv expiry delta if it is not equal to the
207
        // default.
UNCOV
208
        if c.CLTVExpiryDelta.Val != defaultCltvExpiryDelta {
×
UNCOV
209
                recordProducers = append(recordProducers, &c.CLTVExpiryDelta)
×
UNCOV
210
        }
×
211

UNCOV
212
        if c.HTLCMinimumMsat.Val != defaultHtlcMinMsat {
×
UNCOV
213
                recordProducers = append(recordProducers, &c.HTLCMinimumMsat)
×
UNCOV
214
        }
×
215

UNCOV
216
        recordProducers = append(recordProducers, &c.HTLCMaximumMsat)
×
UNCOV
217

×
UNCOV
218
        if c.FeeBaseMsat.Val != defaultFeeBaseMsat {
×
UNCOV
219
                recordProducers = append(recordProducers, &c.FeeBaseMsat)
×
UNCOV
220
        }
×
221

UNCOV
222
        if c.FeeProportionalMillionths.Val != defaultFeeProportionalMillionths {
×
UNCOV
223
                recordProducers = append(
×
UNCOV
224
                        recordProducers, &c.FeeProportionalMillionths,
×
UNCOV
225
                )
×
UNCOV
226
        }
×
227

UNCOV
228
        err := EncodeMessageExtraData(&c.ExtraOpaqueData, recordProducers...)
×
UNCOV
229
        if err != nil {
×
230
                return nil, err
×
231
        }
×
232

UNCOV
233
        return c.ExtraOpaqueData, nil
×
234
}
235

236
// MsgType returns the integer uniquely identifying this message type on the
237
// wire.
238
//
239
// This is part of the lnwire.Message interface.
UNCOV
240
func (c *ChannelUpdate2) MsgType() MessageType {
×
UNCOV
241
        return MsgChannelUpdate2
×
UNCOV
242
}
×
243

244
func (c *ChannelUpdate2) ExtraData() ExtraOpaqueData {
×
245
        return c.ExtraOpaqueData
×
246
}
×
247

248
// A compile time check to ensure ChannelUpdate2 implements the
249
// lnwire.Message interface.
250
var _ Message = (*ChannelUpdate2)(nil)
251

252
// SCID returns the ShortChannelID of the channel that the update applies to.
253
//
254
// NOTE: this is part of the ChannelUpdate interface.
255
func (c *ChannelUpdate2) SCID() ShortChannelID {
×
256
        return c.ShortChannelID.Val
×
257
}
×
258

259
// IsNode1 is true if the update was produced by node 1 of the channel peers.
260
// Node 1 is the node with the lexicographically smaller public key.
261
//
262
// NOTE: this is part of the ChannelUpdate interface.
263
func (c *ChannelUpdate2) IsNode1() bool {
×
264
        return c.SecondPeer.IsNone()
×
265
}
×
266

267
// IsDisabled is true if the update is announcing that the channel should be
268
// considered disabled.
269
//
270
// NOTE: this is part of the ChannelUpdate interface.
271
func (c *ChannelUpdate2) IsDisabled() bool {
×
272
        return !c.DisabledFlags.Val.IsEnabled()
×
273
}
×
274

275
// GetChainHash returns the hash of the chain that the message is referring to.
276
//
277
// NOTE: this is part of the ChannelUpdate interface.
278
func (c *ChannelUpdate2) GetChainHash() chainhash.Hash {
×
279
        return c.ChainHash.Val
×
280
}
×
281

282
// ForwardingPolicy returns the set of forwarding constraints of the update.
283
//
284
// NOTE: this is part of the ChannelUpdate interface.
285
func (c *ChannelUpdate2) ForwardingPolicy() *ForwardingPolicy {
×
286
        return &ForwardingPolicy{
×
287
                TimeLockDelta: c.CLTVExpiryDelta.Val,
×
288
                BaseFee:       MilliSatoshi(c.FeeBaseMsat.Val),
×
289
                FeeRate:       MilliSatoshi(c.FeeProportionalMillionths.Val),
×
290
                MinHTLC:       c.HTLCMinimumMsat.Val,
×
291
                HasMaxHTLC:    true,
×
292
                MaxHTLC:       c.HTLCMaximumMsat.Val,
×
293
        }
×
294
}
×
295

296
// CmpAge can be used to determine if the update is older or newer than the
297
// passed update. It returns 1 if this update is newer, -1 if it is older, and
298
// 0 if they are the same age.
299
//
300
// NOTE: this is part of the ChannelUpdate interface.
301
func (c *ChannelUpdate2) CmpAge(update ChannelUpdate) (CompareResult, error) {
×
302
        other, ok := update.(*ChannelUpdate2)
×
303
        if !ok {
×
304
                return 0, fmt.Errorf("expected *ChannelUpdate2, got: %T",
×
305
                        update)
×
306
        }
×
307

308
        switch {
×
309
        case c.BlockHeight.Val > other.BlockHeight.Val:
×
310
                return GreaterThan, nil
×
311
        case c.BlockHeight.Val < other.BlockHeight.Val:
×
312
                return LessThan, nil
×
313
        default:
×
314
                return EqualTo, nil
×
315
        }
316
}
317

318
// SetDisabledFlag can be used to adjust the disabled flag of an update.
319
//
320
// NOTE: this is part of the ChannelUpdate interface.
321
func (c *ChannelUpdate2) SetDisabledFlag(disabled bool) {
×
322
        if disabled {
×
323
                c.DisabledFlags.Val |= ChanUpdateDisableIncoming
×
324
                c.DisabledFlags.Val |= ChanUpdateDisableOutgoing
×
325
        } else {
×
326
                c.DisabledFlags.Val &^= ChanUpdateDisableIncoming
×
327
                c.DisabledFlags.Val &^= ChanUpdateDisableOutgoing
×
328
        }
×
329
}
330

331
// SetSCID can be used to overwrite the SCID of the update.
332
//
333
// NOTE: this is part of the ChannelUpdate interface.
334
func (c *ChannelUpdate2) SetSCID(scid ShortChannelID) {
×
335
        c.ShortChannelID.Val = scid
×
336
}
×
337

338
// A compile time check to ensure ChannelUpdate2 implements the
339
// lnwire.ChannelUpdate interface.
340
var _ ChannelUpdate = (*ChannelUpdate2)(nil)
341

342
// ChanUpdateDisableFlags is a bit vector that can be used to indicate various
343
// reasons for the channel being marked as disabled.
344
type ChanUpdateDisableFlags uint8
345

346
const (
347
        // ChanUpdateDisableIncoming is a bit indicates that a channel is
348
        // disabled in the inbound direction meaning that the node broadcasting
349
        // the update is communicating that they cannot receive funds.
350
        ChanUpdateDisableIncoming ChanUpdateDisableFlags = 1 << iota
351

352
        // ChanUpdateDisableOutgoing is a bit indicates that a channel is
353
        // disabled in the outbound direction meaning that the node broadcasting
354
        // the update is communicating that they cannot send or route funds.
355
        ChanUpdateDisableOutgoing = 2
356
)
357

358
// IncomingDisabled returns true if the ChanUpdateDisableIncoming bit is set.
359
func (c ChanUpdateDisableFlags) IncomingDisabled() bool {
×
360
        return c&ChanUpdateDisableIncoming == ChanUpdateDisableIncoming
×
361
}
×
362

363
// OutgoingDisabled returns true if the ChanUpdateDisableOutgoing bit is set.
364
func (c ChanUpdateDisableFlags) OutgoingDisabled() bool {
×
365
        return c&ChanUpdateDisableOutgoing == ChanUpdateDisableOutgoing
×
366
}
×
367

368
// IsEnabled returns true if none of the disable bits are set.
UNCOV
369
func (c ChanUpdateDisableFlags) IsEnabled() bool {
×
UNCOV
370
        return c == 0
×
UNCOV
371
}
×
372

373
// String returns the bitfield flags as a string.
374
func (c ChanUpdateDisableFlags) String() string {
×
375
        return fmt.Sprintf("%08b", c)
×
376
}
×
377

378
// Record returns the tlv record for the disable flags.
UNCOV
379
func (c *ChanUpdateDisableFlags) Record() tlv.Record {
×
UNCOV
380
        return tlv.MakeStaticRecord(0, c, 1, encodeDisableFlags,
×
UNCOV
381
                decodeDisableFlags)
×
UNCOV
382
}
×
383

UNCOV
384
func encodeDisableFlags(w io.Writer, val interface{}, buf *[8]byte) error {
×
UNCOV
385
        if v, ok := val.(*ChanUpdateDisableFlags); ok {
×
UNCOV
386
                flagsInt := uint8(*v)
×
UNCOV
387

×
UNCOV
388
                return tlv.EUint8(w, &flagsInt, buf)
×
UNCOV
389
        }
×
390

391
        return tlv.NewTypeForEncodingErr(val, "lnwire.ChanUpdateDisableFlags")
×
392
}
393

394
func decodeDisableFlags(r io.Reader, val interface{}, buf *[8]byte,
UNCOV
395
        l uint64) error {
×
UNCOV
396

×
UNCOV
397
        if v, ok := val.(*ChanUpdateDisableFlags); ok {
×
UNCOV
398
                var flagsInt uint8
×
UNCOV
399
                err := tlv.DUint8(r, &flagsInt, buf, l)
×
UNCOV
400
                if err != nil {
×
UNCOV
401
                        return err
×
UNCOV
402
                }
×
403

UNCOV
404
                *v = ChanUpdateDisableFlags(flagsInt)
×
UNCOV
405

×
UNCOV
406
                return nil
×
407
        }
408

409
        return tlv.NewTypeForDecodingErr(val, "lnwire.ChanUpdateDisableFlags",
×
410
                l, l)
×
411
}
412

413
// TrueBoolean is a record that indicates true or false using the presence of
414
// the record. If the record is absent, it indicates false. If it is present,
415
// it indicates true.
416
type TrueBoolean struct{}
417

418
// Record returns the tlv record for the boolean entry.
419
func (b *TrueBoolean) Record() tlv.Record {
3✔
420
        return tlv.MakeStaticRecord(
3✔
421
                0, b, 0, booleanEncoder, booleanDecoder,
3✔
422
        )
3✔
423
}
3✔
424

425
func booleanEncoder(_ io.Writer, val interface{}, _ *[8]byte) error {
3✔
426
        if _, ok := val.(*TrueBoolean); ok {
6✔
427
                return nil
3✔
428
        }
3✔
429

430
        return tlv.NewTypeForEncodingErr(val, "TrueBoolean")
×
431
}
432

433
func booleanDecoder(_ io.Reader, val interface{}, _ *[8]byte,
434
        l uint64) error {
3✔
435

3✔
436
        if _, ok := val.(*TrueBoolean); ok && (l == 0 || l == 1) {
6✔
437
                return nil
3✔
438
        }
3✔
439

UNCOV
440
        return tlv.NewTypeForEncodingErr(val, "TrueBoolean")
×
441
}
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