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

lightningnetwork / lnd / 11364879019

16 Oct 2024 11:39AM UTC coverage: 49.778% (+0.5%) from 49.297%
11364879019

Pull #9175

github

ellemouton
netann: update ChanAnn2 validation to work for P2WSH channels

This commit expands the ChannelAnnouncement2 validation for the case
where it is announcing a P2WSH channel.
Pull Request #9175: lnwire+netann: update structure of g175 messages to be pure TLV

6 of 314 new or added lines in 9 files covered. (1.91%)

99 existing lines in 18 files now uncovered.

98794 of 198470 relevant lines covered (49.78%)

2.07 hits per line

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

31.37
/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/channeldb/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) {
4✔
42

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

4✔
58
        err := chanAnn.Features.Decode(bytes.NewReader(chanInfo.Features))
4✔
59
        if err != nil {
4✔
60
                return nil, nil, nil, err
×
61
        }
×
62
        chanAnn.BitcoinSig1, err = lnwire.NewSigFromECDSARawSignature(
4✔
63
                chanProof.BitcoinSig1Bytes,
4✔
64
        )
4✔
65
        if err != nil {
4✔
66
                return nil, nil, nil, err
×
67
        }
×
68
        chanAnn.BitcoinSig2, err = lnwire.NewSigFromECDSARawSignature(
4✔
69
                chanProof.BitcoinSig2Bytes,
4✔
70
        )
4✔
71
        if err != nil {
4✔
72
                return nil, nil, nil, err
×
73
        }
×
74
        chanAnn.NodeSig1, err = lnwire.NewSigFromECDSARawSignature(
4✔
75
                chanProof.NodeSig1Bytes,
4✔
76
        )
4✔
77
        if err != nil {
4✔
78
                return nil, nil, nil, err
×
79
        }
×
80
        chanAnn.NodeSig2, err = lnwire.NewSigFromECDSARawSignature(
4✔
81
                chanProof.NodeSig2Bytes,
4✔
82
        )
4✔
83
        if err != nil {
4✔
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
4✔
95
        if e1 != nil {
8✔
96
                edge1Ann, err = ChannelUpdateFromEdge(chanInfo, e1)
4✔
97
                if err != nil {
4✔
98
                        return nil, nil, nil, err
×
99
                }
×
100
        }
101
        if e2 != nil {
8✔
102
                edge2Ann, err = ChannelUpdateFromEdge(chanInfo, e2)
4✔
103
                if err != nil {
4✔
104
                        return nil, nil, nil, err
×
105
                }
×
106
        }
107

108
        return chanAnn, edge1Ann, edge2Ann, nil
4✔
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 {
4✔
119

4✔
120
        switch ann := a.(type) {
4✔
121
        case *lnwire.ChannelAnnouncement1:
4✔
122
                return validateChannelAnn1(ann)
4✔
123
        case *lnwire.ChannelAnnouncement2:
×
124
                return validateChannelAnn2(ann, fetchPkScript)
×
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 {
4✔
135
        // First, we'll compute the digest (h) which is to be signed by each of
4✔
136
        // the keys included within the node announcement message. This hash
4✔
137
        // digest includes all the keys, so the (up to 4 signatures) will
4✔
138
        // attest to the validity of each of the keys.
4✔
139
        data, err := a.DataToSign()
4✔
140
        if err != nil {
4✔
141
                return err
×
142
        }
×
143
        dataHash := chainhash.DoubleHashB(data)
4✔
144

4✔
145
        // First we'll verify that the passed bitcoin key signature is indeed a
4✔
146
        // signature over the computed hash digest.
4✔
147
        bitcoinSig1, err := a.BitcoinSig1.ToSignature()
4✔
148
        if err != nil {
4✔
149
                return err
×
150
        }
×
151
        bitcoinKey1, err := btcec.ParsePubKey(a.BitcoinKey1[:])
4✔
152
        if err != nil {
4✔
153
                return err
×
154
        }
×
155
        if !bitcoinSig1.Verify(dataHash, bitcoinKey1) {
4✔
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()
4✔
163
        if err != nil {
4✔
164
                return err
×
165
        }
×
166
        bitcoinKey2, err := btcec.ParsePubKey(a.BitcoinKey2[:])
4✔
167
        if err != nil {
4✔
168
                return err
×
169
        }
×
170
        if !bitcoinSig2.Verify(dataHash, bitcoinKey2) {
4✔
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()
4✔
177
        if err != nil {
4✔
178
                return err
×
179
        }
×
180
        nodeKey1, err := btcec.ParsePubKey(a.NodeID1[:])
4✔
181
        if err != nil {
4✔
182
                return err
×
183
        }
×
184
        if !nodeSig1.Verify(dataHash, nodeKey1) {
4✔
185
                return errors.New("can't verify data in first node signature")
×
186
        }
×
187

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

200
        return nil
4✔
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 {
×
207

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

NEW
216
        var keys []*btcec.PublicKey
×
NEW
217

×
NEW
218
        switch scriptClass {
×
NEW
219
        case txscript.WitnessV0ScriptHashTy:
×
NEW
220
                keys, err = chanAnn2P2WSHMuSig2Keys(a)
×
NEW
221
                if err != nil {
×
NEW
222
                        return err
×
NEW
223
                }
×
NEW
224
        case txscript.WitnessV1TaprootTy:
×
NEW
225
                keys, err = chanAnn2P2TRMuSig2Keys(a, scriptAddr)
×
NEW
226
                if err != nil {
×
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.
NEW
236
        aggKey, _, _, err := musig2.AggregateKeys(keys, true)
×
NEW
237
        if err != nil {
×
NEW
238
                return err
×
NEW
239
        }
×
240

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

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

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

NEW
259
        return nil
×
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) (
NEW
267
        []*btcec.PublicKey, error) {
×
NEW
268

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

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

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

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

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

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

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

NEW
306
        return []*btcec.PublicKey{
×
NEW
307
                nodeKey1, nodeKey2, btcKey1, btcKey2,
×
NEW
308
        }, nil
×
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,
NEW
316
        scriptAddr btcutil.Address) ([]*btcec.PublicKey, error) {
×
NEW
317

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

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

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

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

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

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

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

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

365
                keys = append(keys, outputKey)
×
366
        }
367

NEW
368
        return keys, nil
×
369
}
370

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

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

380
        return MsgHash(chanAnn2MsgName, chanAnn2SigFieldName, data), nil
×
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