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

lightningnetwork / lnd / 19155841408

07 Nov 2025 02:03AM UTC coverage: 66.675% (-0.04%) from 66.712%
19155841408

Pull #10352

github

web-flow
Merge e4313eba8 into 096ab65b1
Pull Request #10352: [WIP] chainrpc: return Unavailable while notifier starts

137328 of 205965 relevant lines covered (66.68%)

21333.36 hits per line

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

67.33
/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
        // ChainHash denotes the target chain that this channel was opened
26
        // within. This value should be the genesis hash of the target chain.
27
        // Along with the short channel ID, this uniquely identifies the
28
        // channel globally in a blockchain.
29
        ChainHash tlv.RecordT[tlv.TlvType0, chainhash.Hash]
30

31
        // ShortChannelID is the unique description of the funding transaction.
32
        ShortChannelID tlv.RecordT[tlv.TlvType2, ShortChannelID]
33

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

41
        // DisabledFlags is an optional bitfield that describes various reasons
42
        // that the node is communicating that the channel should be considered
43
        // disabled.
44
        DisabledFlags tlv.RecordT[tlv.TlvType6, ChanUpdateDisableFlags]
45

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

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

58
        // HTLCMinimumMsat is the minimum HTLC value which will be accepted.
59
        HTLCMinimumMsat tlv.RecordT[tlv.TlvType12, MilliSatoshi]
60

61
        // HtlcMaximumMsat is the maximum HTLC value which will be accepted.
62
        HTLCMaximumMsat tlv.RecordT[tlv.TlvType14, MilliSatoshi]
63

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

69
        // FeeProportionalMillionths is the fee rate that will be charged per
70
        // millionth of a satoshi.
71
        FeeProportionalMillionths tlv.RecordT[tlv.TlvType18, uint32]
72

73
        // InboundFee is an optional TLV record that contains the fee
74
        // information for incoming HTLCs.
75
        // TODO(elle): assign normal tlv type?
76
        InboundFee tlv.OptionalRecordT[tlv.TlvType55555, Fee]
77

78
        // Signature is used to validate the announced data and prove the
79
        // ownership of node id.
80
        Signature tlv.RecordT[tlv.TlvType160, Sig]
81

82
        // Any extra fields in the signed range that we do not yet know about,
83
        // but we need to keep them for signature validation and to produce a
84
        // valid message.
85
        ExtraSignedFields
86
}
87

88
// Encode serializes the target ChannelUpdate2 into the passed io.Writer
89
// observing the protocol version specified.
90
//
91
// This is part of the lnwire.Message interface.
92
func (c *ChannelUpdate2) Encode(w *bytes.Buffer, _ uint32) error {
120✔
93
        return EncodePureTLVMessage(c, w)
120✔
94
}
120✔
95

96
// Decode deserializes a serialized ChannelUpdate2 stored in the passed
97
// io.Reader observing the specified protocol version.
98
//
99
// This is part of the lnwire.Message interface.
100
func (c *ChannelUpdate2) Decode(r io.Reader, _ uint32) error {
239✔
101
        // First extract into extra opaque data.
239✔
102
        var tlvRecords ExtraOpaqueData
239✔
103
        if err := ReadElements(r, &tlvRecords); err != nil {
239✔
104
                return err
×
105
        }
×
106

107
        var (
239✔
108
                chainHash  = tlv.ZeroRecordT[tlv.TlvType0, [32]byte]()
239✔
109
                secondPeer = tlv.ZeroRecordT[tlv.TlvType8, TrueBoolean]()
239✔
110
                inboundFee = tlv.ZeroRecordT[tlv.TlvType55555, Fee]()
239✔
111
        )
239✔
112
        typeMap, err := tlvRecords.ExtractRecords(
239✔
113
                &chainHash, &c.ShortChannelID, &c.BlockHeight, &c.DisabledFlags,
239✔
114
                &secondPeer, &c.CLTVExpiryDelta, &c.HTLCMinimumMsat,
239✔
115
                &c.HTLCMaximumMsat, &c.FeeBaseMsat,
239✔
116
                &c.FeeProportionalMillionths, &inboundFee,
239✔
117
                &c.Signature,
239✔
118
        )
239✔
119
        if err != nil {
339✔
120
                return err
100✔
121
        }
100✔
122
        c.Signature.Val.ForceSchnorr()
139✔
123

139✔
124
        // By default, the chain-hash is the bitcoin mainnet genesis block hash.
139✔
125
        c.ChainHash.Val = *chaincfg.MainNetParams.GenesisHash
139✔
126
        if _, ok := typeMap[c.ChainHash.TlvType()]; ok {
242✔
127
                c.ChainHash.Val = chainHash.Val
103✔
128
        }
103✔
129

130
        // The presence of the second_peer tlv type indicates "true".
131
        if _, ok := typeMap[c.SecondPeer.TlvType()]; ok {
191✔
132
                c.SecondPeer = tlv.SomeRecordT(secondPeer)
52✔
133
        }
52✔
134

135
        // If the CLTV expiry delta was not encoded, then set it to the default
136
        // value.
137
        if _, ok := typeMap[c.CLTVExpiryDelta.TlvType()]; !ok {
171✔
138
                c.CLTVExpiryDelta.Val = defaultCltvExpiryDelta
32✔
139
        }
32✔
140

141
        // If the HTLC Minimum msat was not encoded, then set it to the default
142
        // value.
143
        if _, ok := typeMap[c.HTLCMinimumMsat.TlvType()]; !ok {
174✔
144
                c.HTLCMinimumMsat.Val = defaultHtlcMinMsat
35✔
145
        }
35✔
146

147
        // If the base fee was not encoded, then set it to the default value.
148
        if _, ok := typeMap[c.FeeBaseMsat.TlvType()]; !ok {
175✔
149
                c.FeeBaseMsat.Val = defaultFeeBaseMsat
36✔
150
        }
36✔
151

152
        // If the proportional fee was not encoded, then set it to the default
153
        // value.
154
        if _, ok := typeMap[c.FeeProportionalMillionths.TlvType()]; !ok {
182✔
155
                c.FeeProportionalMillionths.Val = defaultFeeProportionalMillionths //nolint:ll
43✔
156
        }
43✔
157

158
        // If the inbound fee was encoded, set it.
159
        if _, ok := typeMap[c.InboundFee.TlvType()]; ok {
194✔
160
                c.InboundFee = tlv.SomeRecordT(inboundFee)
55✔
161
        }
55✔
162

163
        c.ExtraSignedFields = ExtraSignedFieldsFromTypeMap(typeMap)
139✔
164

139✔
165
        return nil
139✔
166
}
167

168
// AllRecords returns all the TLV records for the message. This will include all
169
// the records we know about along with any that we don't know about but that
170
// fall in the signed TLV range.
171
//
172
// NOTE: this is part of the PureTLVMessage interface.
173
func (c *ChannelUpdate2) AllRecords() []tlv.Record {
120✔
174
        var recordProducers []tlv.RecordProducer
120✔
175

120✔
176
        // The chain-hash record is only included if it is _not_ equal to the
120✔
177
        // bitcoin mainnet genisis block hash.
120✔
178
        if !c.ChainHash.Val.IsEqual(chaincfg.MainNetParams.GenesisHash) {
222✔
179
                hash := tlv.ZeroRecordT[tlv.TlvType0, [32]byte]()
102✔
180
                hash.Val = c.ChainHash.Val
102✔
181

102✔
182
                recordProducers = append(recordProducers, &hash)
102✔
183
        }
102✔
184

185
        recordProducers = append(recordProducers,
120✔
186
                &c.ShortChannelID, &c.BlockHeight, &c.Signature,
120✔
187
        )
120✔
188

120✔
189
        // Only include the disable flags if any bit is set.
120✔
190
        if !c.DisabledFlags.Val.IsEnabled() {
199✔
191
                recordProducers = append(recordProducers, &c.DisabledFlags)
79✔
192
        }
79✔
193

194
        // We only need to encode the second peer boolean if it is true
195
        c.SecondPeer.WhenSome(func(r tlv.RecordT[tlv.TlvType8, TrueBoolean]) {
172✔
196
                recordProducers = append(recordProducers, &r)
52✔
197
        })
52✔
198

199
        // We only encode the cltv expiry delta if it is not equal to the
200
        // default.
201
        if c.CLTVExpiryDelta.Val != defaultCltvExpiryDelta {
224✔
202
                recordProducers = append(recordProducers, &c.CLTVExpiryDelta)
104✔
203
        }
104✔
204

205
        if c.HTLCMinimumMsat.Val != defaultHtlcMinMsat {
218✔
206
                recordProducers = append(recordProducers, &c.HTLCMinimumMsat)
98✔
207
        }
98✔
208

209
        recordProducers = append(recordProducers, &c.HTLCMaximumMsat)
120✔
210

120✔
211
        if c.FeeBaseMsat.Val != defaultFeeBaseMsat {
222✔
212
                recordProducers = append(recordProducers, &c.FeeBaseMsat)
102✔
213
        }
102✔
214

215
        if c.FeeProportionalMillionths.Val != defaultFeeProportionalMillionths {
215✔
216
                recordProducers = append(
95✔
217
                        recordProducers, &c.FeeProportionalMillionths,
95✔
218
                )
95✔
219
        }
95✔
220

221
        c.InboundFee.WhenSome(func(r tlv.RecordT[tlv.TlvType55555, Fee]) {
175✔
222
                recordProducers = append(recordProducers, &r)
55✔
223
        })
55✔
224

225
        recordProducers = append(recordProducers, RecordsAsProducers(
120✔
226
                tlv.MapToRecords(c.ExtraSignedFields),
120✔
227
        )...)
120✔
228

120✔
229
        return ProduceRecordsSorted(recordProducers...)
120✔
230
}
231

232
// MsgType returns the integer uniquely identifying this message type on the
233
// wire.
234
//
235
// This is part of the lnwire.Message interface.
236
func (c *ChannelUpdate2) MsgType() MessageType {
119✔
237
        return MsgChannelUpdate2
119✔
238
}
119✔
239

240
// SerializedSize returns the serialized size of the message in bytes.
241
//
242
// This is part of the lnwire.SizeableMessage interface.
243
func (c *ChannelUpdate2) SerializedSize() (uint32, error) {
×
244
        return MessageSerializedSize(c)
×
245
}
×
246

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

251
// A compile time check to ensure ChannelUpdate2 implements the
252
// lnwire.PureTLVMessage interface.
253
var _ PureTLVMessage = (*ChannelUpdate2)(nil)
254

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

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

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

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

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

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

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

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

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

341
// A compile time check to ensure ChannelUpdate2 implements the
342
// lnwire.ChannelUpdate interface.
343
var _ ChannelUpdate = (*ChannelUpdate2)(nil)
344

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

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

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

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

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

371
// IsEnabled returns true if none of the disable bits are set.
372
func (c ChanUpdateDisableFlags) IsEnabled() bool {
120✔
373
        return c == 0
120✔
374
}
120✔
375

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

381
// Record returns the tlv record for the disable flags.
382
func (c *ChanUpdateDisableFlags) Record() tlv.Record {
318✔
383
        return tlv.MakeStaticRecord(0, c, 1, encodeDisableFlags,
318✔
384
                decodeDisableFlags)
318✔
385
}
318✔
386

387
func encodeDisableFlags(w io.Writer, val interface{}, buf *[8]byte) error {
79✔
388
        if v, ok := val.(*ChanUpdateDisableFlags); ok {
158✔
389
                flagsInt := uint8(*v)
79✔
390

79✔
391
                return tlv.EUint8(w, &flagsInt, buf)
79✔
392
        }
79✔
393

394
        return tlv.NewTypeForEncodingErr(val, "lnwire.ChanUpdateDisableFlags")
×
395
}
396

397
func decodeDisableFlags(r io.Reader, val interface{}, buf *[8]byte,
398
        l uint64) error {
80✔
399

80✔
400
        if v, ok := val.(*ChanUpdateDisableFlags); ok {
160✔
401
                var flagsInt uint8
80✔
402
                err := tlv.DUint8(r, &flagsInt, buf, l)
80✔
403
                if err != nil {
80✔
404
                        return err
×
405
                }
×
406

407
                *v = ChanUpdateDisableFlags(flagsInt)
80✔
408

80✔
409
                return nil
80✔
410
        }
411

412
        return tlv.NewTypeForDecodingErr(val, "lnwire.ChanUpdateDisableFlags",
×
413
                l, l)
×
414
}
415

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

421
// Record returns the tlv record for the boolean entry.
422
func (b *TrueBoolean) Record() tlv.Record {
388✔
423
        return tlv.MakeStaticRecord(
388✔
424
                0, b, 0, booleanEncoder, booleanDecoder,
388✔
425
        )
388✔
426
}
388✔
427

428
func booleanEncoder(_ io.Writer, val interface{}, _ *[8]byte) error {
94✔
429
        if _, ok := val.(*TrueBoolean); ok {
188✔
430
                return nil
94✔
431
        }
94✔
432

433
        return tlv.NewTypeForEncodingErr(val, "TrueBoolean")
×
434
}
435

436
func booleanDecoder(_ io.Reader, val interface{}, _ *[8]byte,
437
        l uint64) error {
75✔
438

75✔
439
        if _, ok := val.(*TrueBoolean); ok && (l == 0 || l == 1) {
149✔
440
                return nil
74✔
441
        }
74✔
442

443
        return tlv.NewTypeForEncodingErr(val, "TrueBoolean")
1✔
444
}
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