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

lightningnetwork / lnd / 15736109134

18 Jun 2025 02:46PM UTC coverage: 58.197% (-10.1%) from 68.248%
15736109134

Pull #9752

github

web-flow
Merge d2634a68c into 31c74f20f
Pull Request #9752: routerrpc: reject payment to invoice that don't have payment secret or blinded paths

6 of 13 new or added lines in 2 files covered. (46.15%)

28331 existing lines in 455 files now uncovered.

97860 of 168153 relevant lines covered (58.2%)

1.81 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
        "github.com/pkg/errors"
18
)
19

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

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

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

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

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

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

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

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

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

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

98
        return nil
3✔
99
}
100

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

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

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

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

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

134
        return nil, ErrUnableToExtractChanUpdate
×
135
}
136

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

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

161
        return update
3✔
162
}
163

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

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

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

179
        return update, nil
3✔
180
}
181

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

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

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

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

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

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

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

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

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

233
        return nil
3✔
234
}
235

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

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

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

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

256
        return nil
×
257
}
258

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

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

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

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

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

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

301
        return nil
3✔
302
}
303

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

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

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

322
        return nil
×
323
}
324

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

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

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

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