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

lightningnetwork / lnd / 15561477203

10 Jun 2025 01:54PM UTC coverage: 58.351% (-10.1%) from 68.487%
15561477203

Pull #9356

github

web-flow
Merge 6440b25db into c6d6d4c0b
Pull Request #9356: lnrpc: add incoming/outgoing channel ids filter to forwarding history request

33 of 36 new or added lines in 2 files covered. (91.67%)

28366 existing lines in 455 files now uncovered.

97715 of 167461 relevant lines covered (58.35%)

1.81 hits per line

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

40.82
/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/chaincfg/chainhash"
12
        "github.com/lightningnetwork/lnd/graph/db/models"
13
        "github.com/lightningnetwork/lnd/lnwire"
14
        "github.com/lightningnetwork/lnd/tlv"
15
)
16

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

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

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

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

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

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

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

106
        return chanAnn, edge1Ann, edge2Ann, nil
3✔
107
}
108

109
// FetchPkScript defines a function that can be used to fetch the output script
110
// for the transaction with the given SCID.
111
type FetchPkScript func(*lnwire.ShortChannelID) ([]byte, error)
112

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

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

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

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

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

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

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

197
        return nil
3✔
198
}
199

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

×
UNCOV
205
        dataHash, err := ChanAnn2DigestToSign(a)
×
UNCOV
206
        if err != nil {
×
207
                return err
×
208
        }
×
209

UNCOV
210
        sig, err := a.Signature.ToSignature()
×
UNCOV
211
        if err != nil {
×
212
                return err
×
213
        }
×
214

UNCOV
215
        nodeKey1, err := btcec.ParsePubKey(a.NodeID1.Val[:])
×
UNCOV
216
        if err != nil {
×
217
                return err
×
218
        }
×
219

UNCOV
220
        nodeKey2, err := btcec.ParsePubKey(a.NodeID2.Val[:])
×
UNCOV
221
        if err != nil {
×
222
                return err
×
223
        }
×
224

UNCOV
225
        keys := []*btcec.PublicKey{
×
UNCOV
226
                nodeKey1, nodeKey2,
×
UNCOV
227
        }
×
UNCOV
228

×
UNCOV
229
        // If the bitcoin keys are provided in the announcement, then it is
×
UNCOV
230
        // assumed that the signature of the announcement is a 4-of-4 MuSig2
×
UNCOV
231
        // over the bitcoin keys and node ID keys.
×
UNCOV
232
        if a.BitcoinKey1.IsSome() && a.BitcoinKey2.IsSome() {
×
UNCOV
233
                var (
×
UNCOV
234
                        btcKey1 tlv.RecordT[tlv.TlvType12, [33]byte]
×
UNCOV
235
                        btcKey2 tlv.RecordT[tlv.TlvType14, [33]byte]
×
UNCOV
236
                )
×
UNCOV
237

×
UNCOV
238
                btcKey1 = a.BitcoinKey1.UnwrapOr(btcKey1)
×
UNCOV
239
                btcKey2 = a.BitcoinKey2.UnwrapOr(btcKey2)
×
UNCOV
240

×
UNCOV
241
                bitcoinKey1, err := btcec.ParsePubKey(btcKey1.Val[:])
×
UNCOV
242
                if err != nil {
×
243
                        return err
×
244
                }
×
245

UNCOV
246
                bitcoinKey2, err := btcec.ParsePubKey(btcKey2.Val[:])
×
UNCOV
247
                if err != nil {
×
248
                        return err
×
249
                }
×
250

UNCOV
251
                keys = append(keys, bitcoinKey1, bitcoinKey2)
×
UNCOV
252
        } else {
×
UNCOV
253
                // If bitcoin keys are not provided, then we need to get the
×
UNCOV
254
                // on-chain output key since this will be the 3rd key in the
×
UNCOV
255
                // 3-of-3 MuSig2 signature.
×
UNCOV
256
                pkScript, err := fetchPkScript(&a.ShortChannelID.Val)
×
UNCOV
257
                if err != nil {
×
258
                        return err
×
259
                }
×
260

UNCOV
261
                outputKey, err := schnorr.ParsePubKey(pkScript[2:])
×
UNCOV
262
                if err != nil {
×
263
                        return err
×
264
                }
×
265

UNCOV
266
                keys = append(keys, outputKey)
×
267
        }
268

UNCOV
269
        aggKey, _, _, err := musig2.AggregateKeys(keys, true)
×
UNCOV
270
        if err != nil {
×
271
                return err
×
272
        }
×
273

UNCOV
274
        if !sig.Verify(dataHash.CloneBytes(), aggKey.FinalKey) {
×
275
                return fmt.Errorf("invalid sig")
×
276
        }
×
277

UNCOV
278
        return nil
×
279
}
280

281
// ChanAnn2DigestToSign computes the digest of the message to be signed.
282
func ChanAnn2DigestToSign(a *lnwire.ChannelAnnouncement2) (*chainhash.Hash,
UNCOV
283
        error) {
×
UNCOV
284

×
UNCOV
285
        data, err := a.DataToSign()
×
UNCOV
286
        if err != nil {
×
287
                return nil, err
×
288
        }
×
289

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