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

lightningnetwork / lnd / 16075085547

04 Jul 2025 01:39PM UTC coverage: 58.707%. First build
16075085547

Pull #9986

github

web-flow
Merge 1f336a07f into 59a86b3b5
Pull Request #9986: release: create v0.19.2-rc1 branch

549 of 765 new or added lines in 39 files covered. (71.76%)

97873 of 166713 relevant lines covered (58.71%)

1.82 hits per line

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

58.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
)
17

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

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

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

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

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

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

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

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

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

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

96
        return nil
3✔
97
}
98

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

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

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

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

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

132
        return nil, ErrUnableToExtractChanUpdate
×
133
}
134

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

3✔
140
        return &lnwire.ChannelUpdate1{
3✔
141
                ChainHash:       info.ChainHash,
3✔
142
                ShortChannelID:  lnwire.NewShortChanIDFromInt(policy.ChannelID),
3✔
143
                Timestamp:       uint32(policy.LastUpdate.Unix()),
3✔
144
                ChannelFlags:    policy.ChannelFlags,
3✔
145
                MessageFlags:    policy.MessageFlags,
3✔
146
                TimeLockDelta:   policy.TimeLockDelta,
3✔
147
                HtlcMinimumMsat: policy.MinHTLC,
3✔
148
                HtlcMaximumMsat: policy.MaxHTLC,
3✔
149
                BaseFee:         uint32(policy.FeeBaseMSat),
3✔
150
                FeeRate:         uint32(policy.FeeProportionalMillionths),
3✔
151
                ExtraOpaqueData: policy.ExtraOpaqueData,
3✔
152
        }
3✔
153
}
3✔
154

155
// ChannelUpdateFromEdge reconstructs a signed ChannelUpdate from the given edge
156
// info and policy.
157
func ChannelUpdateFromEdge(info *models.ChannelEdgeInfo,
158
        policy *models.ChannelEdgePolicy) (*lnwire.ChannelUpdate1, error) {
3✔
159

3✔
160
        update := UnsignedChannelUpdateFromEdge(info, policy)
3✔
161

3✔
162
        var err error
3✔
163
        update.Signature, err = lnwire.NewSigFromECDSARawSignature(
3✔
164
                policy.SigBytes,
3✔
165
        )
3✔
166
        if err != nil {
3✔
167
                return nil, err
×
168
        }
×
169

170
        return update, nil
3✔
171
}
172

173
// ValidateChannelUpdateAnn validates the channel update announcement by
174
// checking (1) that the included signature covers the announcement and has been
175
// signed by the node's private key, and (2) that the announcement's message
176
// flags and optional fields are sane.
177
func ValidateChannelUpdateAnn(pubKey *btcec.PublicKey, capacity btcutil.Amount,
178
        a lnwire.ChannelUpdate) error {
3✔
179

3✔
180
        if err := ValidateChannelUpdateFields(capacity, a); err != nil {
3✔
181
                return err
×
182
        }
×
183

184
        return VerifyChannelUpdateSignature(a, pubKey)
3✔
185
}
186

187
// VerifyChannelUpdateSignature verifies that the channel update message was
188
// signed by the party with the given node public key.
189
func VerifyChannelUpdateSignature(msg lnwire.ChannelUpdate,
190
        pubKey *btcec.PublicKey) error {
3✔
191

3✔
192
        switch u := msg.(type) {
3✔
193
        case *lnwire.ChannelUpdate1:
3✔
194
                return verifyChannelUpdate1Signature(u, pubKey)
3✔
195
        case *lnwire.ChannelUpdate2:
×
196
                return verifyChannelUpdate2Signature(u, pubKey)
×
197
        default:
×
198
                return fmt.Errorf("unhandled implementation of "+
×
199
                        "lnwire.ChannelUpdate: %T", msg)
×
200
        }
201
}
202

203
// verifyChannelUpdateSignature1 verifies that the channel update message was
204
// signed by the party with the given node public key.
205
func verifyChannelUpdate1Signature(msg *lnwire.ChannelUpdate1,
206
        pubKey *btcec.PublicKey) error {
3✔
207

3✔
208
        data, err := msg.DataToSign()
3✔
209
        if err != nil {
3✔
210
                return fmt.Errorf("unable to reconstruct message data: %w", err)
×
211
        }
×
212
        dataHash := chainhash.DoubleHashB(data)
3✔
213

3✔
214
        nodeSig, err := msg.Signature.ToSignature()
3✔
215
        if err != nil {
3✔
216
                return err
×
217
        }
×
218

219
        if !nodeSig.Verify(dataHash, pubKey) {
3✔
220
                return fmt.Errorf("invalid signature for channel update %v",
×
221
                        spew.Sdump(msg))
×
222
        }
×
223

224
        return nil
3✔
225
}
226

227
// verifyChannelUpdateSignature2 verifies that the channel update message was
228
// signed by the party with the given node public key.
229
func verifyChannelUpdate2Signature(c *lnwire.ChannelUpdate2,
230
        pubKey *btcec.PublicKey) error {
×
231

×
232
        digest, err := chanUpdate2DigestToSign(c)
×
233
        if err != nil {
×
234
                return fmt.Errorf("unable to reconstruct message data: %w", err)
×
235
        }
×
236

237
        nodeSig, err := c.Signature.ToSignature()
×
238
        if err != nil {
×
239
                return err
×
240
        }
×
241

242
        if !nodeSig.Verify(digest, pubKey) {
×
243
                return fmt.Errorf("invalid signature for channel update %v",
×
244
                        spew.Sdump(c))
×
245
        }
×
246

247
        return nil
×
248
}
249

250
// ValidateChannelUpdateFields validates a channel update's message flags and
251
// corresponding update fields.
252
func ValidateChannelUpdateFields(capacity btcutil.Amount,
253
        msg lnwire.ChannelUpdate) error {
3✔
254

3✔
255
        switch u := msg.(type) {
3✔
256
        case *lnwire.ChannelUpdate1:
3✔
257
                return validateChannelUpdate1Fields(capacity, u)
3✔
258
        case *lnwire.ChannelUpdate2:
×
259
                return validateChannelUpdate2Fields(capacity, u)
×
260
        default:
×
261
                return fmt.Errorf("unhandled implementation of "+
×
262
                        "lnwire.ChannelUpdate: %T", msg)
×
263
        }
264
}
265

266
// validateChannelUpdate1Fields validates a channel update's message flags and
267
// corresponding update fields.
268
func validateChannelUpdate1Fields(capacity btcutil.Amount,
269
        msg *lnwire.ChannelUpdate1) error {
3✔
270

3✔
271
        // The maxHTLC flag is mandatory.
3✔
272
        if !msg.MessageFlags.HasMaxHtlc() {
3✔
NEW
273
                return fmt.Errorf("max htlc flag not set for channel "+
×
274
                        "update %v", spew.Sdump(msg))
×
275
        }
×
276

277
        maxHtlc := msg.HtlcMaximumMsat
3✔
278
        if maxHtlc == 0 || maxHtlc < msg.HtlcMinimumMsat {
3✔
NEW
279
                return fmt.Errorf("invalid max htlc for channel "+
×
280
                        "update %v", spew.Sdump(msg))
×
281
        }
×
282

283
        // For light clients, the capacity will not be set so we'll skip
284
        // checking whether the MaxHTLC value respects the channel's
285
        // capacity.
286
        capacityMsat := lnwire.NewMSatFromSatoshis(capacity)
3✔
287
        if capacityMsat != 0 && maxHtlc > capacityMsat {
3✔
NEW
288
                return fmt.Errorf("max_htlc (%v) for channel update "+
×
289
                        "greater than capacity (%v)", maxHtlc, capacityMsat)
×
290
        }
×
291

292
        return nil
3✔
293
}
294

295
// validateChannelUpdate2Fields validates a channel update's message flags and
296
// corresponding update fields.
297
func validateChannelUpdate2Fields(capacity btcutil.Amount,
298
        c *lnwire.ChannelUpdate2) error {
×
299

×
300
        maxHtlc := c.HTLCMaximumMsat.Val
×
301
        if maxHtlc == 0 || maxHtlc < c.HTLCMinimumMsat.Val {
×
302
                return fmt.Errorf("invalid max htlc for channel update %v",
×
303
                        spew.Sdump(c))
×
304
        }
×
305

306
        // Checking whether the MaxHTLC value respects the channel's capacity.
307
        capacityMsat := lnwire.NewMSatFromSatoshis(capacity)
×
308
        if maxHtlc > capacityMsat {
×
309
                return fmt.Errorf("max_htlc (%v) for channel update greater "+
×
310
                        "than capacity (%v)", maxHtlc, capacityMsat)
×
311
        }
×
312

313
        return nil
×
314
}
315

316
// ChanUpdate2DigestTag returns the tag to be used when signing the digest of
317
// a channel_update_2 message.
318
func ChanUpdate2DigestTag() []byte {
×
319
        return MsgTag(chanUpdate2MsgName, chanUpdate2SigField)
×
320
}
×
321

322
// chanUpdate2DigestToSign computes the digest of the ChannelUpdate2 message to
323
// be signed.
324
func chanUpdate2DigestToSign(c *lnwire.ChannelUpdate2) ([]byte, error) {
×
325
        data, err := c.DataToSign()
×
326
        if err != nil {
×
327
                return nil, err
×
328
        }
×
329

330
        hash := MsgHash(chanUpdate2MsgName, chanUpdate2SigField, data)
×
331

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