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

lightningnetwork / lnd / 12115442155

02 Dec 2024 08:28AM UTC coverage: 48.662% (-10.3%) from 58.948%
12115442155

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%)

27532 existing lines in 434 files now uncovered.

97890 of 201164 relevant lines covered (48.66%)

0.52 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/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) {
1✔
42

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

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

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

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

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

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

200
        return nil
1✔
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,
UNCOV
206
        fetchPkScript FetchPkScript) error {
×
UNCOV
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.
UNCOV
242
        dataHash, err := ChanAnn2DigestToSign(a)
×
UNCOV
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

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

UNCOV
274
        nodeKey2, err := btcec.ParsePubKey(a.NodeID2.Val[:])
×
UNCOV
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

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

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

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

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

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

UNCOV
354
                keys = append(keys, bitcoinKey1, bitcoinKey2)
×
UNCOV
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

UNCOV
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,
UNCOV
373
        error) {
×
UNCOV
374

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

UNCOV
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