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

lightningnetwork / lnd / 16466354971

23 Jul 2025 09:05AM UTC coverage: 57.54% (-9.7%) from 67.201%
16466354971

Pull #9455

github

web-flow
Merge f914ae23c into 90e211684
Pull Request #9455: discovery+lnwire: add support for DNS host name in NodeAnnouncement msg

151 of 291 new or added lines in 7 files covered. (51.89%)

28441 existing lines in 456 files now uncovered.

98864 of 171817 relevant lines covered (57.54%)

1.79 hits per line

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

59.36
/netann/channel_update.go
1
package netann
2

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

8
        "github.com/btcsuite/btcd/btcec/v2"
9
        "github.com/btcsuite/btcd/btcutil"
10
        "github.com/btcsuite/btcd/chaincfg/chainhash"
11
        "github.com/davecgh/go-spew/spew"
12
        "github.com/lightningnetwork/lnd/graph/db/models"
13
        "github.com/lightningnetwork/lnd/keychain"
14
        "github.com/lightningnetwork/lnd/lnwallet"
15
        "github.com/lightningnetwork/lnd/lnwire"
16
        "github.com/lightningnetwork/lnd/tlv"
17
)
18

19
const (
20
        // chanUpdate2MsgName is a string representing the name of the
21
        // ChannelUpdate2 message. This string will be used during the
22
        // construction of the tagged hash message to be signed when producing
23
        // the signature for the ChannelUpdate2 message.
24
        chanUpdate2MsgName = "channel_update_2"
25

26
        // chanUpdate2SigField is the name of the signature field of the
27
        // ChannelUpdate2 message. This string will be used during the
28
        // construction of the tagged hash message to be signed when producing
29
        // the signature for the ChannelUpdate2 message.
30
        chanUpdate2SigField = "signature"
31
)
32

33
// ErrUnableToExtractChanUpdate is returned when a channel update cannot be
34
// found for one of our active channels.
35
var ErrUnableToExtractChanUpdate = fmt.Errorf("unable to extract ChannelUpdate")
36

37
// ChannelUpdateModifier is a closure that makes in-place modifications to an
38
// lnwire.ChannelUpdate.
39
type ChannelUpdateModifier func(*lnwire.ChannelUpdate1)
40

41
// ChanUpdSetDisable is a functional option that sets the disabled channel flag
42
// if disabled is true, and clears the bit otherwise.
43
func ChanUpdSetDisable(disabled bool) ChannelUpdateModifier {
3✔
44
        return func(update *lnwire.ChannelUpdate1) {
6✔
45
                if disabled {
6✔
46
                        // Set the bit responsible for marking a channel as
3✔
47
                        // disabled.
3✔
48
                        update.ChannelFlags |= lnwire.ChanUpdateDisabled
3✔
49
                } else {
6✔
50
                        // Clear the bit responsible for marking a channel as
3✔
51
                        // disabled.
3✔
52
                        update.ChannelFlags &= ^lnwire.ChanUpdateDisabled
3✔
53
                }
3✔
54
        }
55
}
56

57
// ChanUpdSetTimestamp is a functional option that sets the timestamp of the
58
// update to the current time, or increments it if the timestamp is already in
59
// the future.
60
func ChanUpdSetTimestamp(update *lnwire.ChannelUpdate1) {
3✔
61
        newTimestamp := uint32(time.Now().Unix())
3✔
62
        if newTimestamp <= update.Timestamp {
6✔
63
                // Increment the prior value to ensure the timestamp
3✔
64
                // monotonically increases, otherwise the update won't
3✔
65
                // propagate.
3✔
66
                newTimestamp = update.Timestamp + 1
3✔
67
        }
3✔
68
        update.Timestamp = newTimestamp
3✔
69
}
70

71
// SignChannelUpdate applies the given modifiers to the passed
72
// lnwire.ChannelUpdate, then signs the resulting update. The provided update
73
// should be the most recent, valid update, otherwise the timestamp may not
74
// monotonically increase from the prior.
75
//
76
// NOTE: This method modifies the given update.
77
func SignChannelUpdate(signer lnwallet.MessageSigner, keyLoc keychain.KeyLocator,
78
        update *lnwire.ChannelUpdate1, mods ...ChannelUpdateModifier) error {
3✔
79

3✔
80
        // Apply the requested changes to the channel update.
3✔
81
        for _, modifier := range mods {
6✔
82
                modifier(update)
3✔
83
        }
3✔
84

85
        // Create the DER-encoded ECDSA signature over the message digest.
86
        sig, err := SignAnnouncement(signer, keyLoc, update)
3✔
87
        if err != nil {
3✔
UNCOV
88
                return err
×
UNCOV
89
        }
×
90

91
        // Parse the DER-encoded signature into a fixed-size 64-byte array.
92
        update.Signature, err = lnwire.NewSigFromSignature(sig)
3✔
93
        if err != nil {
3✔
UNCOV
94
                return err
×
UNCOV
95
        }
×
96

97
        return nil
3✔
98
}
99

100
// ExtractChannelUpdate attempts to retrieve a lnwire.ChannelUpdate message from
101
// an edge's info and a set of routing policies.
102
//
103
// NOTE: The passed policies can be nil.
104
func ExtractChannelUpdate(ownerPubKey []byte,
105
        info *models.ChannelEdgeInfo,
106
        policies ...*models.ChannelEdgePolicy) (
107
        *lnwire.ChannelUpdate1, error) {
3✔
108

3✔
109
        // Helper function to extract the owner of the given policy.
3✔
110
        owner := func(edge *models.ChannelEdgePolicy) []byte {
6✔
111
                var pubKey *btcec.PublicKey
3✔
112
                if edge.ChannelFlags&lnwire.ChanUpdateDirection == 0 {
6✔
113
                        pubKey, _ = info.NodeKey1()
3✔
114
                } else {
6✔
115
                        pubKey, _ = info.NodeKey2()
3✔
116
                }
3✔
117

118
                // If pubKey was not found, just return nil.
119
                if pubKey == nil {
3✔
120
                        return nil
×
121
                }
×
122

123
                return pubKey.SerializeCompressed()
3✔
124
        }
125

126
        // Extract the channel update from the policy we own, if any.
127
        for _, edge := range policies {
6✔
128
                if edge != nil && bytes.Equal(ownerPubKey, owner(edge)) {
6✔
129
                        return ChannelUpdateFromEdge(info, edge)
3✔
130
                }
3✔
131
        }
132

133
        return nil, ErrUnableToExtractChanUpdate
×
134
}
135

136
// UnsignedChannelUpdateFromEdge reconstructs an unsigned ChannelUpdate from the
137
// given edge info and policy.
138
func UnsignedChannelUpdateFromEdge(info *models.ChannelEdgeInfo,
139
        policy *models.ChannelEdgePolicy) *lnwire.ChannelUpdate1 {
3✔
140

3✔
141
        update := &lnwire.ChannelUpdate1{
3✔
142
                ChainHash:       info.ChainHash,
3✔
143
                ShortChannelID:  lnwire.NewShortChanIDFromInt(policy.ChannelID),
3✔
144
                Timestamp:       uint32(policy.LastUpdate.Unix()),
3✔
145
                ChannelFlags:    policy.ChannelFlags,
3✔
146
                MessageFlags:    policy.MessageFlags,
3✔
147
                TimeLockDelta:   policy.TimeLockDelta,
3✔
148
                HtlcMinimumMsat: policy.MinHTLC,
3✔
149
                HtlcMaximumMsat: policy.MaxHTLC,
3✔
150
                BaseFee:         uint32(policy.FeeBaseMSat),
3✔
151
                FeeRate:         uint32(policy.FeeProportionalMillionths),
3✔
152
                ExtraOpaqueData: policy.ExtraOpaqueData,
3✔
153
        }
3✔
154
        policy.InboundFee.WhenSome(func(fee lnwire.Fee) {
6✔
155
                update.InboundFee = tlv.SomeRecordT(
3✔
156
                        tlv.NewRecordT[tlv.TlvType55555, lnwire.Fee](fee),
3✔
157
                )
3✔
158
        })
3✔
159

160
        return update
3✔
161
}
162

163
// ChannelUpdateFromEdge reconstructs a signed ChannelUpdate from the given edge
164
// info and policy.
165
func ChannelUpdateFromEdge(info *models.ChannelEdgeInfo,
166
        policy *models.ChannelEdgePolicy) (*lnwire.ChannelUpdate1, error) {
3✔
167

3✔
168
        update := UnsignedChannelUpdateFromEdge(info, policy)
3✔
169

3✔
170
        var err error
3✔
171
        update.Signature, err = lnwire.NewSigFromECDSARawSignature(
3✔
172
                policy.SigBytes,
3✔
173
        )
3✔
174
        if err != nil {
3✔
175
                return nil, err
×
176
        }
×
177

178
        return update, nil
3✔
179
}
180

181
// ValidateChannelUpdateAnn validates the channel update announcement by
182
// checking (1) that the included signature covers the announcement and has been
183
// signed by the node's private key, and (2) that the announcement's message
184
// flags and optional fields are sane.
185
func ValidateChannelUpdateAnn(pubKey *btcec.PublicKey, capacity btcutil.Amount,
186
        a lnwire.ChannelUpdate) error {
3✔
187

3✔
188
        if err := ValidateChannelUpdateFields(capacity, a); err != nil {
3✔
UNCOV
189
                return err
×
UNCOV
190
        }
×
191

192
        return VerifyChannelUpdateSignature(a, pubKey)
3✔
193
}
194

195
// VerifyChannelUpdateSignature verifies that the channel update message was
196
// signed by the party with the given node public key.
197
func VerifyChannelUpdateSignature(msg lnwire.ChannelUpdate,
198
        pubKey *btcec.PublicKey) error {
3✔
199

3✔
200
        switch u := msg.(type) {
3✔
201
        case *lnwire.ChannelUpdate1:
3✔
202
                return verifyChannelUpdate1Signature(u, pubKey)
3✔
203
        case *lnwire.ChannelUpdate2:
×
204
                return verifyChannelUpdate2Signature(u, pubKey)
×
205
        default:
×
206
                return fmt.Errorf("unhandled implementation of "+
×
207
                        "lnwire.ChannelUpdate: %T", msg)
×
208
        }
209
}
210

211
// verifyChannelUpdateSignature1 verifies that the channel update message was
212
// signed by the party with the given node public key.
213
func verifyChannelUpdate1Signature(msg *lnwire.ChannelUpdate1,
214
        pubKey *btcec.PublicKey) error {
3✔
215

3✔
216
        data, err := msg.DataToSign()
3✔
217
        if err != nil {
3✔
218
                return fmt.Errorf("unable to reconstruct message data: %w", err)
×
219
        }
×
220
        dataHash := chainhash.DoubleHashB(data)
3✔
221

3✔
222
        nodeSig, err := msg.Signature.ToSignature()
3✔
223
        if err != nil {
3✔
224
                return err
×
225
        }
×
226

227
        if !nodeSig.Verify(dataHash, pubKey) {
3✔
UNCOV
228
                return fmt.Errorf("invalid signature for channel update %v",
×
UNCOV
229
                        spew.Sdump(msg))
×
UNCOV
230
        }
×
231

232
        return nil
3✔
233
}
234

235
// verifyChannelUpdateSignature2 verifies that the channel update message was
236
// signed by the party with the given node public key.
237
func verifyChannelUpdate2Signature(c *lnwire.ChannelUpdate2,
238
        pubKey *btcec.PublicKey) error {
×
239

×
240
        digest, err := chanUpdate2DigestToSign(c)
×
241
        if err != nil {
×
242
                return fmt.Errorf("unable to reconstruct message data: %w", err)
×
243
        }
×
244

245
        nodeSig, err := c.Signature.ToSignature()
×
246
        if err != nil {
×
247
                return err
×
248
        }
×
249

250
        if !nodeSig.Verify(digest, pubKey) {
×
251
                return fmt.Errorf("invalid signature for channel update %v",
×
252
                        spew.Sdump(c))
×
253
        }
×
254

255
        return nil
×
256
}
257

258
// ValidateChannelUpdateFields validates a channel update's message flags and
259
// corresponding update fields.
260
func ValidateChannelUpdateFields(capacity btcutil.Amount,
261
        msg lnwire.ChannelUpdate) error {
3✔
262

3✔
263
        switch u := msg.(type) {
3✔
264
        case *lnwire.ChannelUpdate1:
3✔
265
                return validateChannelUpdate1Fields(capacity, u)
3✔
266
        case *lnwire.ChannelUpdate2:
×
267
                return validateChannelUpdate2Fields(capacity, u)
×
268
        default:
×
269
                return fmt.Errorf("unhandled implementation of "+
×
270
                        "lnwire.ChannelUpdate: %T", msg)
×
271
        }
272
}
273

274
// validateChannelUpdate1Fields validates a channel update's message flags and
275
// corresponding update fields.
276
func validateChannelUpdate1Fields(capacity btcutil.Amount,
277
        msg *lnwire.ChannelUpdate1) error {
3✔
278

3✔
279
        // The maxHTLC flag is mandatory.
3✔
280
        if !msg.MessageFlags.HasMaxHtlc() {
3✔
UNCOV
281
                return fmt.Errorf("max htlc flag not set for channel "+
×
UNCOV
282
                        "update %v", spew.Sdump(msg))
×
UNCOV
283
        }
×
284

285
        maxHtlc := msg.HtlcMaximumMsat
3✔
286
        if maxHtlc == 0 || maxHtlc < msg.HtlcMinimumMsat {
3✔
UNCOV
287
                return fmt.Errorf("invalid max htlc for channel "+
×
UNCOV
288
                        "update %v", spew.Sdump(msg))
×
UNCOV
289
        }
×
290

291
        // For light clients, the capacity will not be set so we'll skip
292
        // checking whether the MaxHTLC value respects the channel's
293
        // capacity.
294
        capacityMsat := lnwire.NewMSatFromSatoshis(capacity)
3✔
295
        if capacityMsat != 0 && maxHtlc > capacityMsat {
3✔
296
                return fmt.Errorf("max_htlc (%v) for channel update "+
×
297
                        "greater than capacity (%v)", maxHtlc, capacityMsat)
×
298
        }
×
299

300
        return nil
3✔
301
}
302

303
// validateChannelUpdate2Fields validates a channel update's message flags and
304
// corresponding update fields.
305
func validateChannelUpdate2Fields(capacity btcutil.Amount,
306
        c *lnwire.ChannelUpdate2) error {
×
307

×
308
        maxHtlc := c.HTLCMaximumMsat.Val
×
309
        if maxHtlc == 0 || maxHtlc < c.HTLCMinimumMsat.Val {
×
310
                return fmt.Errorf("invalid max htlc for channel update %v",
×
311
                        spew.Sdump(c))
×
312
        }
×
313

314
        // Checking whether the MaxHTLC value respects the channel's capacity.
315
        capacityMsat := lnwire.NewMSatFromSatoshis(capacity)
×
316
        if maxHtlc > capacityMsat {
×
317
                return fmt.Errorf("max_htlc (%v) for channel update greater "+
×
318
                        "than capacity (%v)", maxHtlc, capacityMsat)
×
319
        }
×
320

321
        return nil
×
322
}
323

324
// ChanUpdate2DigestTag returns the tag to be used when signing the digest of
325
// a channel_update_2 message.
326
func ChanUpdate2DigestTag() []byte {
×
327
        return MsgTag(chanUpdate2MsgName, chanUpdate2SigField)
×
328
}
×
329

330
// chanUpdate2DigestToSign computes the digest of the ChannelUpdate2 message to
331
// be signed.
332
func chanUpdate2DigestToSign(c *lnwire.ChannelUpdate2) ([]byte, error) {
×
333
        data, err := c.DataToSign()
×
334
        if err != nil {
×
335
                return nil, err
×
336
        }
×
337

338
        hash := MsgHash(chanUpdate2MsgName, chanUpdate2SigField, data)
×
339

×
340
        return hash[:], nil
×
341
}
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