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

lightningnetwork / lnd / 17917482292

22 Sep 2025 01:50PM UTC coverage: 56.562% (-10.1%) from 66.668%
17917482292

Pull #10182

github

web-flow
Merge 9efe3bd8c into 055fb436e
Pull Request #10182: Aux feature bits

32 of 68 new or added lines in 5 files covered. (47.06%)

29734 existing lines in 467 files now uncovered.

98449 of 174056 relevant lines covered (56.56%)

1.18 hits per line

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

31.35
/netann/channel_announcement.go
1
package netann
2

3
import (
4
        "errors"
5
        "fmt"
6

7
        "github.com/btcsuite/btcd/btcec/v2"
8
        "github.com/btcsuite/btcd/btcec/v2/schnorr"
9
        "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
10
        "github.com/btcsuite/btcd/btcutil"
11
        "github.com/btcsuite/btcd/chaincfg/chainhash"
12
        "github.com/btcsuite/btcd/txscript"
13
        "github.com/lightningnetwork/lnd/graph/db/models"
14
        "github.com/lightningnetwork/lnd/lnwire"
15
        "github.com/lightningnetwork/lnd/tlv"
16
)
17

18
const (
19
        // chanAnn2MsgName is a string representing the name of the
20
        // ChannelAnnouncement2 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 ChannelAnnouncement2 message.
23
        chanAnn2MsgName = "channel_announcement_2"
24

25
        // chanAnn2SigFieldName is the name of the signature field of the
26
        // ChannelAnnouncement2 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 ChannelAnnouncement2 message.
29
        chanAnn2SigFieldName = "signature"
30
)
31

32
// CreateChanAnnouncement is a helper function which creates all channel
33
// announcements given the necessary channel related database items. This
34
// function is used to transform out database structs into the corresponding wire
35
// structs for announcing new channels to other peers, or simply syncing up a
36
// peer's initial routing table upon connect.
37
func CreateChanAnnouncement(chanProof *models.ChannelAuthProof,
38
        chanInfo *models.ChannelEdgeInfo,
39
        e1, e2 *models.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement1,
40
        *lnwire.ChannelUpdate1, *lnwire.ChannelUpdate1, error) {
2✔
41

2✔
42
        // First, using the parameters of the channel, along with the channel
2✔
43
        // authentication chanProof, we'll create re-create the original
2✔
44
        // authenticated channel announcement.
2✔
45
        chanID := lnwire.NewShortChanIDFromInt(chanInfo.ChannelID)
2✔
46
        chanAnn := &lnwire.ChannelAnnouncement1{
2✔
47
                ShortChannelID:  chanID,
2✔
48
                NodeID1:         chanInfo.NodeKey1Bytes,
2✔
49
                NodeID2:         chanInfo.NodeKey2Bytes,
2✔
50
                ChainHash:       chanInfo.ChainHash,
2✔
51
                BitcoinKey1:     chanInfo.BitcoinKey1Bytes,
2✔
52
                BitcoinKey2:     chanInfo.BitcoinKey2Bytes,
2✔
53
                Features:        chanInfo.Features.RawFeatureVector,
2✔
54
                ExtraOpaqueData: chanInfo.ExtraOpaqueData,
2✔
55
        }
2✔
56

2✔
57
        var err error
2✔
58
        chanAnn.BitcoinSig1, err = lnwire.NewSigFromECDSARawSignature(
2✔
59
                chanProof.BitcoinSig1Bytes,
2✔
60
        )
2✔
61
        if err != nil {
2✔
62
                return nil, nil, nil, err
×
63
        }
×
64
        chanAnn.BitcoinSig2, err = lnwire.NewSigFromECDSARawSignature(
2✔
65
                chanProof.BitcoinSig2Bytes,
2✔
66
        )
2✔
67
        if err != nil {
2✔
68
                return nil, nil, nil, err
×
69
        }
×
70
        chanAnn.NodeSig1, err = lnwire.NewSigFromECDSARawSignature(
2✔
71
                chanProof.NodeSig1Bytes,
2✔
72
        )
2✔
73
        if err != nil {
2✔
74
                return nil, nil, nil, err
×
75
        }
×
76
        chanAnn.NodeSig2, err = lnwire.NewSigFromECDSARawSignature(
2✔
77
                chanProof.NodeSig2Bytes,
2✔
78
        )
2✔
79
        if err != nil {
2✔
80
                return nil, nil, nil, err
×
81
        }
×
82

83
        // We'll unconditionally queue the channel's existence chanProof as it
84
        // will need to be processed before either of the channel update
85
        // networkMsgs.
86

87
        // Since it's up to a node's policy as to whether they advertise the
88
        // edge in a direction, we don't create an advertisement if the edge is
89
        // nil.
90
        var edge1Ann, edge2Ann *lnwire.ChannelUpdate1
2✔
91
        if e1 != nil {
4✔
92
                edge1Ann, err = ChannelUpdateFromEdge(chanInfo, e1)
2✔
93
                if err != nil {
2✔
94
                        return nil, nil, nil, err
×
95
                }
×
96
        }
97
        if e2 != nil {
4✔
98
                edge2Ann, err = ChannelUpdateFromEdge(chanInfo, e2)
2✔
99
                if err != nil {
2✔
100
                        return nil, nil, nil, err
×
101
                }
×
102
        }
103

104
        return chanAnn, edge1Ann, edge2Ann, nil
2✔
105
}
106

107
// FetchPkScript defines a function that can be used to fetch the output script
108
// for the transaction with the given SCID.
109
type FetchPkScript func(lnwire.ShortChannelID) (txscript.ScriptClass,
110
        btcutil.Address, error)
111

112
// ValidateChannelAnn validates the channel announcement.
113
func ValidateChannelAnn(a lnwire.ChannelAnnouncement,
114
        fetchPkScript FetchPkScript) error {
2✔
115

2✔
116
        switch ann := a.(type) {
2✔
117
        case *lnwire.ChannelAnnouncement1:
2✔
118
                return validateChannelAnn1(ann)
2✔
UNCOV
119
        case *lnwire.ChannelAnnouncement2:
×
UNCOV
120
                return validateChannelAnn2(ann, fetchPkScript)
×
121
        default:
×
122
                return fmt.Errorf("unhandled implementation of "+
×
123
                        "lnwire.ChannelAnnouncement: %T", a)
×
124
        }
125
}
126

127
// validateChannelAnn1 validates the channel announcement message and checks
128
// that node signatures covers the announcement message, and that the bitcoin
129
// signatures covers the node keys.
130
func validateChannelAnn1(a *lnwire.ChannelAnnouncement1) error {
2✔
131
        // First, we'll compute the digest (h) which is to be signed by each of
2✔
132
        // the keys included within the node announcement message. This hash
2✔
133
        // digest includes all the keys, so the (up to 4 signatures) will
2✔
134
        // attest to the validity of each of the keys.
2✔
135
        data, err := a.DataToSign()
2✔
136
        if err != nil {
2✔
137
                return err
×
138
        }
×
139
        dataHash := chainhash.DoubleHashB(data)
2✔
140

2✔
141
        // First we'll verify that the passed bitcoin key signature is indeed a
2✔
142
        // signature over the computed hash digest.
2✔
143
        bitcoinSig1, err := a.BitcoinSig1.ToSignature()
2✔
144
        if err != nil {
2✔
145
                return err
×
146
        }
×
147
        bitcoinKey1, err := btcec.ParsePubKey(a.BitcoinKey1[:])
2✔
148
        if err != nil {
2✔
149
                return err
×
150
        }
×
151
        if !bitcoinSig1.Verify(dataHash, bitcoinKey1) {
2✔
152
                return errors.New("can't verify first bitcoin signature")
×
153
        }
×
154

155
        // If that checks out, then we'll verify that the second bitcoin
156
        // signature is a valid signature of the bitcoin public key over hash
157
        // digest as well.
158
        bitcoinSig2, err := a.BitcoinSig2.ToSignature()
2✔
159
        if err != nil {
2✔
160
                return err
×
161
        }
×
162
        bitcoinKey2, err := btcec.ParsePubKey(a.BitcoinKey2[:])
2✔
163
        if err != nil {
2✔
164
                return err
×
165
        }
×
166
        if !bitcoinSig2.Verify(dataHash, bitcoinKey2) {
2✔
167
                return errors.New("can't verify second bitcoin signature")
×
168
        }
×
169

170
        // Both node signatures attached should indeed be a valid signature
171
        // over the selected digest of the channel announcement signature.
172
        nodeSig1, err := a.NodeSig1.ToSignature()
2✔
173
        if err != nil {
2✔
174
                return err
×
175
        }
×
176
        nodeKey1, err := btcec.ParsePubKey(a.NodeID1[:])
2✔
177
        if err != nil {
2✔
178
                return err
×
179
        }
×
180
        if !nodeSig1.Verify(dataHash, nodeKey1) {
2✔
181
                return errors.New("can't verify data in first node signature")
×
182
        }
×
183

184
        nodeSig2, err := a.NodeSig2.ToSignature()
2✔
185
        if err != nil {
2✔
186
                return err
×
187
        }
×
188
        nodeKey2, err := btcec.ParsePubKey(a.NodeID2[:])
2✔
189
        if err != nil {
2✔
190
                return err
×
191
        }
×
192
        if !nodeSig2.Verify(dataHash, nodeKey2) {
2✔
193
                return errors.New("can't verify data in second node signature")
×
194
        }
×
195

196
        return nil
2✔
197
}
198

199
// validateChannelAnn2 validates the channel announcement message and checks
200
// that message signature covers the announcement message.
201
func validateChannelAnn2(a *lnwire.ChannelAnnouncement2,
UNCOV
202
        fetchPkScript FetchPkScript) error {
×
UNCOV
203

×
UNCOV
204
        // Next, we fetch the funding transaction's PK script. We need this so
×
UNCOV
205
        // that we know what type of channel we will be validating: P2WSH or
×
UNCOV
206
        // P2TR.
×
UNCOV
207
        scriptClass, scriptAddr, err := fetchPkScript(a.ShortChannelID.Val)
×
UNCOV
208
        if err != nil {
×
209
                return err
×
210
        }
×
211

UNCOV
212
        var keys []*btcec.PublicKey
×
UNCOV
213

×
UNCOV
214
        switch scriptClass {
×
UNCOV
215
        case txscript.WitnessV0ScriptHashTy:
×
UNCOV
216
                keys, err = chanAnn2P2WSHMuSig2Keys(a)
×
UNCOV
217
                if err != nil {
×
218
                        return err
×
219
                }
×
UNCOV
220
        case txscript.WitnessV1TaprootTy:
×
UNCOV
221
                keys, err = chanAnn2P2TRMuSig2Keys(a, scriptAddr)
×
UNCOV
222
                if err != nil {
×
223
                        return err
×
224
                }
×
225
        default:
×
226
                return fmt.Errorf("invalid on-chain pk script type for "+
×
227
                        "channel_announcement_2: %s", scriptClass)
×
228
        }
229

230
        // Do a MuSig2 aggregation of the keys to obtain the aggregate key that
231
        // the signature will be validated against.
UNCOV
232
        aggKey, _, _, err := musig2.AggregateKeys(keys, true)
×
UNCOV
233
        if err != nil {
×
234
                return err
×
235
        }
×
236

237
        // Get the message that the signature should have signed.
UNCOV
238
        dataHash, err := ChanAnn2DigestToSign(a)
×
UNCOV
239
        if err != nil {
×
240
                return err
×
241
        }
×
242

243
        // Obtain the signature.
UNCOV
244
        sig, err := a.Signature.Val.ToSignature()
×
UNCOV
245
        if err != nil {
×
246
                return err
×
247
        }
×
248

249
        // Check that the signature is valid for the aggregate key given the
250
        // message digest.
UNCOV
251
        if !sig.Verify(dataHash.CloneBytes(), aggKey.FinalKey) {
×
252
                return fmt.Errorf("invalid sig")
×
253
        }
×
254

UNCOV
255
        return nil
×
256
}
257

258
// chanAnn2P2WSHMuSig2Keys returns the set of keys that should be used to
259
// construct the aggregate key that the signature in an
260
// lnwire.ChannelAnnouncement2 message should be verified against in the case
261
// where the channel being announced is a P2WSH channel.
262
func chanAnn2P2WSHMuSig2Keys(a *lnwire.ChannelAnnouncement2) (
UNCOV
263
        []*btcec.PublicKey, error) {
×
UNCOV
264

×
UNCOV
265
        nodeKey1, err := btcec.ParsePubKey(a.NodeID1.Val[:])
×
UNCOV
266
        if err != nil {
×
267
                return nil, err
×
268
        }
×
269

UNCOV
270
        nodeKey2, err := btcec.ParsePubKey(a.NodeID2.Val[:])
×
UNCOV
271
        if err != nil {
×
272
                return nil, err
×
273
        }
×
274

UNCOV
275
        btcKeyMissingErrString := "bitcoin key %d missing for announcement " +
×
UNCOV
276
                "of a P2WSH channel"
×
UNCOV
277

×
UNCOV
278
        btcKey1Bytes, err := a.BitcoinKey1.UnwrapOrErr(
×
UNCOV
279
                fmt.Errorf(btcKeyMissingErrString, 1),
×
UNCOV
280
        )
×
UNCOV
281
        if err != nil {
×
282
                return nil, err
×
283
        }
×
284

UNCOV
285
        btcKey1, err := btcec.ParsePubKey(btcKey1Bytes.Val[:])
×
UNCOV
286
        if err != nil {
×
287
                return nil, err
×
288
        }
×
289

UNCOV
290
        btcKey2Bytes, err := a.BitcoinKey2.UnwrapOrErr(
×
UNCOV
291
                fmt.Errorf(btcKeyMissingErrString, 2),
×
UNCOV
292
        )
×
UNCOV
293
        if err != nil {
×
294
                return nil, err
×
295
        }
×
296

UNCOV
297
        btcKey2, err := btcec.ParsePubKey(btcKey2Bytes.Val[:])
×
UNCOV
298
        if err != nil {
×
299
                return nil, err
×
300
        }
×
301

UNCOV
302
        return []*btcec.PublicKey{
×
UNCOV
303
                nodeKey1, nodeKey2, btcKey1, btcKey2,
×
UNCOV
304
        }, nil
×
305
}
306

307
// chanAnn2P2TRMuSig2Keys returns the set of keys that should be used to
308
// construct the aggregate key that the signature in an
309
// lnwire.ChannelAnnouncement2 message should be verified against in the case
310
// where the channel being announced is a P2TR channel.
311
func chanAnn2P2TRMuSig2Keys(a *lnwire.ChannelAnnouncement2,
UNCOV
312
        scriptAddr btcutil.Address) ([]*btcec.PublicKey, error) {
×
UNCOV
313

×
UNCOV
314
        nodeKey1, err := btcec.ParsePubKey(a.NodeID1.Val[:])
×
UNCOV
315
        if err != nil {
×
316
                return nil, err
×
317
        }
×
318

UNCOV
319
        nodeKey2, err := btcec.ParsePubKey(a.NodeID2.Val[:])
×
UNCOV
320
        if err != nil {
×
321
                return nil, err
×
322
        }
×
323

UNCOV
324
        keys := []*btcec.PublicKey{
×
UNCOV
325
                nodeKey1, nodeKey2,
×
UNCOV
326
        }
×
UNCOV
327

×
UNCOV
328
        // If the bitcoin keys are provided in the announcement, then it is
×
UNCOV
329
        // assumed that the signature of the announcement is a 4-of-4 MuSig2
×
UNCOV
330
        // over the bitcoin keys and node ID keys.
×
UNCOV
331
        if a.BitcoinKey1.IsSome() && a.BitcoinKey2.IsSome() {
×
UNCOV
332
                var (
×
UNCOV
333
                        btcKey1 tlv.RecordT[tlv.TlvType12, [33]byte]
×
UNCOV
334
                        btcKey2 tlv.RecordT[tlv.TlvType14, [33]byte]
×
UNCOV
335
                )
×
UNCOV
336

×
UNCOV
337
                btcKey1 = a.BitcoinKey1.UnwrapOr(btcKey1)
×
UNCOV
338
                btcKey2 = a.BitcoinKey2.UnwrapOr(btcKey2)
×
UNCOV
339

×
UNCOV
340
                bitcoinKey1, err := btcec.ParsePubKey(btcKey1.Val[:])
×
UNCOV
341
                if err != nil {
×
342
                        return nil, err
×
343
                }
×
344

UNCOV
345
                bitcoinKey2, err := btcec.ParsePubKey(btcKey2.Val[:])
×
UNCOV
346
                if err != nil {
×
347
                        return nil, err
×
348
                }
×
349

UNCOV
350
                keys = append(keys, bitcoinKey1, bitcoinKey2)
×
UNCOV
351
        } else {
×
UNCOV
352
                // If bitcoin keys are not provided, then the on-chain output
×
UNCOV
353
                // key is considered the 3rd key in the 3-of-3 MuSig2 signature.
×
UNCOV
354
                outputKey, err := schnorr.ParsePubKey(
×
UNCOV
355
                        scriptAddr.ScriptAddress(),
×
UNCOV
356
                )
×
UNCOV
357
                if err != nil {
×
358
                        return nil, err
×
359
                }
×
360

UNCOV
361
                keys = append(keys, outputKey)
×
362
        }
363

UNCOV
364
        return keys, nil
×
365
}
366

367
// ChanAnn2DigestToSign computes the digest of the message to be signed.
368
func ChanAnn2DigestToSign(a *lnwire.ChannelAnnouncement2) (*chainhash.Hash,
UNCOV
369
        error) {
×
UNCOV
370

×
UNCOV
371
        data, err := lnwire.SerialiseFieldsToSign(a)
×
UNCOV
372
        if err != nil {
×
373
                return nil, err
×
374
        }
×
375

UNCOV
376
        return MsgHash(chanAnn2MsgName, chanAnn2SigFieldName, data), nil
×
377
}
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