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

lightningnetwork / lnd / 12118348650

02 Dec 2024 11:25AM UTC coverage: 58.552% (-0.4%) from 58.977%
12118348650

Pull #9175

github

ellemouton
lnwire: add NodeAnnouncement2
Pull Request #9175: lnwire+netann: update structure of g175 messages to be pure TLV

405 of 571 new or added lines in 11 files covered. (70.93%)

1754 existing lines in 33 files now uncovered.

133774 of 228469 relevant lines covered (58.55%)

19422.52 hits per line

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

67.06
/netann/channel_announcement.go
1
package netann
2

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

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

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

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

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

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

16✔
58
        err := chanAnn.Features.Decode(bytes.NewReader(chanInfo.Features))
16✔
59
        if err != nil {
16✔
60
                return nil, nil, nil, err
×
61
        }
×
62
        chanAnn.BitcoinSig1, err = lnwire.NewSigFromECDSARawSignature(
16✔
63
                chanProof.BitcoinSig1Bytes,
16✔
64
        )
16✔
65
        if err != nil {
16✔
66
                return nil, nil, nil, err
×
67
        }
×
68
        chanAnn.BitcoinSig2, err = lnwire.NewSigFromECDSARawSignature(
16✔
69
                chanProof.BitcoinSig2Bytes,
16✔
70
        )
16✔
71
        if err != nil {
16✔
72
                return nil, nil, nil, err
×
73
        }
×
74
        chanAnn.NodeSig1, err = lnwire.NewSigFromECDSARawSignature(
16✔
75
                chanProof.NodeSig1Bytes,
16✔
76
        )
16✔
77
        if err != nil {
16✔
78
                return nil, nil, nil, err
×
79
        }
×
80
        chanAnn.NodeSig2, err = lnwire.NewSigFromECDSARawSignature(
16✔
81
                chanProof.NodeSig2Bytes,
16✔
82
        )
16✔
83
        if err != nil {
16✔
84
                return nil, nil, nil, err
×
85
        }
×
86

87
        // We'll unconditionally queue the channel's existence chanProof as it
88
        // will need to be processed before either of the channel update
89
        // networkMsgs.
90

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

108
        return chanAnn, edge1Ann, edge2Ann, nil
16✔
109
}
110

111
// FetchPkScript defines a function that can be used to fetch the output script
112
// for the transaction with the given SCID.
113
type FetchPkScript func(*lnwire.ShortChannelID) (txscript.ScriptClass,
114
        btcutil.Address, error)
115

116
// ValidateChannelAnn validates the channel announcement.
117
func ValidateChannelAnn(a lnwire.ChannelAnnouncement,
118
        fetchPkScript FetchPkScript) error {
230✔
119

230✔
120
        switch ann := a.(type) {
230✔
121
        case *lnwire.ChannelAnnouncement1:
227✔
122
                return validateChannelAnn1(ann)
227✔
123
        case *lnwire.ChannelAnnouncement2:
3✔
124
                return validateChannelAnn2(ann, fetchPkScript)
3✔
125
        default:
×
126
                return fmt.Errorf("unhandled implementation of "+
×
127
                        "lnwire.ChannelAnnouncement: %T", a)
×
128
        }
129
}
130

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

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

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

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

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

200
        return nil
227✔
201
}
202

203
// validateChannelAnn2 validates the channel announcement message and checks
204
// that message signature covers the announcement message.
205
func validateChannelAnn2(a *lnwire.ChannelAnnouncement2,
206
        fetchPkScript FetchPkScript) error {
3✔
207

3✔
208
        // Next, we fetch the funding transaction's PK script. We need this so
3✔
209
        // that we know what type of channel we will be validating: P2WSH or
3✔
210
        // P2TR.
3✔
211
        scriptClass, scriptAddr, err := fetchPkScript(&a.ShortChannelID.Val)
3✔
212
        if err != nil {
3✔
NEW
213
                return err
×
NEW
214
        }
×
215

216
        var keys []*btcec.PublicKey
3✔
217

3✔
218
        switch scriptClass {
3✔
219
        case txscript.WitnessV0ScriptHashTy:
1✔
220
                keys, err = chanAnn2P2WSHMuSig2Keys(a)
1✔
221
                if err != nil {
1✔
NEW
222
                        return err
×
NEW
223
                }
×
224
        case txscript.WitnessV1TaprootTy:
2✔
225
                keys, err = chanAnn2P2TRMuSig2Keys(a, scriptAddr)
2✔
226
                if err != nil {
2✔
NEW
227
                        return err
×
NEW
228
                }
×
NEW
229
        default:
×
NEW
230
                return fmt.Errorf("invalid on-chain pk script type for "+
×
NEW
231
                        "channel_announcement_2: %s", scriptClass)
×
232
        }
233

234
        // Do a MuSig2 aggregation of the keys to obtain the aggregate key that
235
        // the signature will be validated against.
236
        aggKey, _, _, err := musig2.AggregateKeys(keys, true)
3✔
237
        if err != nil {
3✔
NEW
238
                return err
×
NEW
239
        }
×
240

241
        // Get the message that the signature should have signed.
242
        dataHash, err := ChanAnn2DigestToSign(a)
3✔
243
        if err != nil {
3✔
244
                return err
×
245
        }
×
246

247
        // Obtain the signature.
248
        sig, err := a.Signature.Val.ToSignature()
3✔
249
        if err != nil {
3✔
250
                return err
×
251
        }
×
252

253
        // Check that the signature is valid for the aggregate key given the
254
        // message digest.
255
        if !sig.Verify(dataHash.CloneBytes(), aggKey.FinalKey) {
3✔
NEW
256
                return fmt.Errorf("invalid sig")
×
NEW
257
        }
×
258

259
        return nil
3✔
260
}
261

262
// chanAnn2P2WSHMuSig2Keys returns the set of keys that should be used to
263
// construct the aggregate key that the signature in an
264
// lnwire.ChannelAnnouncement2 message should be verified against in the case
265
// where the channel being announced is a P2WSH channel.
266
func chanAnn2P2WSHMuSig2Keys(a *lnwire.ChannelAnnouncement2) (
267
        []*btcec.PublicKey, error) {
1✔
268

1✔
269
        nodeKey1, err := btcec.ParsePubKey(a.NodeID1.Val[:])
1✔
270
        if err != nil {
1✔
NEW
271
                return nil, err
×
272
        }
×
273

274
        nodeKey2, err := btcec.ParsePubKey(a.NodeID2.Val[:])
1✔
275
        if err != nil {
1✔
NEW
276
                return nil, err
×
NEW
277
        }
×
278

279
        btcKeyMissingErrString := "bitcoin key %d missing for announcement " +
1✔
280
                "of a P2WSH channel"
1✔
281

1✔
282
        btcKey1Bytes, err := a.BitcoinKey1.UnwrapOrErr(
1✔
283
                fmt.Errorf(btcKeyMissingErrString, 1),
1✔
284
        )
1✔
285
        if err != nil {
1✔
NEW
286
                return nil, err
×
NEW
287
        }
×
288

289
        btcKey1, err := btcec.ParsePubKey(btcKey1Bytes.Val[:])
1✔
290
        if err != nil {
1✔
NEW
291
                return nil, err
×
NEW
292
        }
×
293

294
        btcKey2Bytes, err := a.BitcoinKey2.UnwrapOrErr(
1✔
295
                fmt.Errorf(btcKeyMissingErrString, 2),
1✔
296
        )
1✔
297
        if err != nil {
1✔
NEW
298
                return nil, err
×
NEW
299
        }
×
300

301
        btcKey2, err := btcec.ParsePubKey(btcKey2Bytes.Val[:])
1✔
302
        if err != nil {
1✔
NEW
303
                return nil, err
×
NEW
304
        }
×
305

306
        return []*btcec.PublicKey{
1✔
307
                nodeKey1, nodeKey2, btcKey1, btcKey2,
1✔
308
        }, nil
1✔
309
}
310

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

2✔
318
        nodeKey1, err := btcec.ParsePubKey(a.NodeID1.Val[:])
2✔
319
        if err != nil {
2✔
NEW
320
                return nil, err
×
NEW
321
        }
×
322

323
        nodeKey2, err := btcec.ParsePubKey(a.NodeID2.Val[:])
2✔
324
        if err != nil {
2✔
NEW
325
                return nil, err
×
UNCOV
326
        }
×
327

328
        keys := []*btcec.PublicKey{
2✔
329
                nodeKey1, nodeKey2,
2✔
330
        }
2✔
331

2✔
332
        // If the bitcoin keys are provided in the announcement, then it is
2✔
333
        // assumed that the signature of the announcement is a 4-of-4 MuSig2
2✔
334
        // over the bitcoin keys and node ID keys.
2✔
335
        if a.BitcoinKey1.IsSome() && a.BitcoinKey2.IsSome() {
3✔
336
                var (
1✔
337
                        btcKey1 tlv.RecordT[tlv.TlvType12, [33]byte]
1✔
338
                        btcKey2 tlv.RecordT[tlv.TlvType14, [33]byte]
1✔
339
                )
1✔
340

1✔
341
                btcKey1 = a.BitcoinKey1.UnwrapOr(btcKey1)
1✔
342
                btcKey2 = a.BitcoinKey2.UnwrapOr(btcKey2)
1✔
343

1✔
344
                bitcoinKey1, err := btcec.ParsePubKey(btcKey1.Val[:])
1✔
345
                if err != nil {
1✔
NEW
346
                        return nil, err
×
347
                }
×
348

349
                bitcoinKey2, err := btcec.ParsePubKey(btcKey2.Val[:])
1✔
350
                if err != nil {
1✔
NEW
351
                        return nil, err
×
352
                }
×
353

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

365
                keys = append(keys, outputKey)
1✔
366
        }
367

368
        return keys, nil
2✔
369
}
370

371
// ChanAnn2DigestToSign computes the digest of the message to be signed.
372
func ChanAnn2DigestToSign(a *lnwire.ChannelAnnouncement2) (*chainhash.Hash,
373
        error) {
6✔
374

6✔
375
        data, err := lnwire.SerialiseFieldsToSign(a)
6✔
376
        if err != nil {
6✔
377
                return nil, err
×
378
        }
×
379

380
        return MsgHash(chanAnn2MsgName, chanAnn2SigFieldName, data), nil
6✔
381
}
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