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

lightningnetwork / lnd / 17132206455

21 Aug 2025 03:56PM UTC coverage: 54.685% (-2.6%) from 57.321%
17132206455

Pull #10167

github

web-flow
Merge 5dd2ed093 into 0c2f045f5
Pull Request #10167: multi: bump Go to 1.24.6

4 of 31 new or added lines in 10 files covered. (12.9%)

23854 existing lines in 284 files now uncovered.

108937 of 199210 relevant lines covered (54.68%)

22026.48 hits per line

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

65.24
/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 {
102✔
44
        return func(update *lnwire.ChannelUpdate1) {
204✔
45
                if disabled {
160✔
46
                        // Set the bit responsible for marking a channel as
58✔
47
                        // disabled.
58✔
48
                        update.ChannelFlags |= lnwire.ChanUpdateDisabled
58✔
49
                } else {
102✔
50
                        // Clear the bit responsible for marking a channel as
44✔
51
                        // disabled.
44✔
52
                        update.ChannelFlags &= ^lnwire.ChanUpdateDisabled
44✔
53
                }
44✔
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) {
106✔
61
        newTimestamp := uint32(time.Now().Unix())
106✔
62
        if newTimestamp <= update.Timestamp {
173✔
63
                // Increment the prior value to ensure the timestamp
67✔
64
                // monotonically increases, otherwise the update won't
67✔
65
                // propagate.
67✔
66
                newTimestamp = update.Timestamp + 1
67✔
67
        }
67✔
68
        update.Timestamp = newTimestamp
106✔
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 {
106✔
79

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

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

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

97
        return nil
104✔
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) {
313✔
108

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

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

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

126
        // Extract the channel update from the policy we own, if any.
127
        for _, edge := range policies {
762✔
128
                if edge != nil && bytes.Equal(ownerPubKey, owner(edge)) {
762✔
129
                        return ChannelUpdateFromEdge(info, edge)
313✔
130
                }
313✔
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 {
336✔
140

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

160
        return update
336✔
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) {
332✔
167

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

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

178
        return update, nil
332✔
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 {
58✔
187

58✔
188
        if err := ValidateChannelUpdateFields(capacity, a); err != nil {
62✔
189
                return err
4✔
190
        }
4✔
191

192
        return VerifyChannelUpdateSignature(a, pubKey)
54✔
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 {
64✔
199

64✔
200
        switch u := msg.(type) {
64✔
201
        case *lnwire.ChannelUpdate1:
64✔
202
                return verifyChannelUpdate1Signature(u, pubKey)
64✔
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 {
64✔
215

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

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

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

232
        return nil
63✔
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 {
58✔
262

58✔
263
        switch u := msg.(type) {
58✔
264
        case *lnwire.ChannelUpdate1:
58✔
265
                return validateChannelUpdate1Fields(capacity, u)
58✔
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 {
58✔
278

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

285
        maxHtlc := msg.HtlcMaximumMsat
56✔
286
        if maxHtlc == 0 || maxHtlc < msg.HtlcMinimumMsat {
58✔
287
                return fmt.Errorf("invalid max htlc for channel "+
2✔
288
                        "update %v", spew.Sdump(msg))
2✔
289
        }
2✔
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)
54✔
295
        if capacityMsat != 0 && maxHtlc > capacityMsat {
54✔
296
                return fmt.Errorf("max_htlc (%v) for channel update "+
×
297
                        "greater than capacity (%v)", maxHtlc, capacityMsat)
×
298
        }
×
299

300
        return nil
54✔
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