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

lightningnetwork / lnd / 17101605539

20 Aug 2025 02:35PM UTC coverage: 57.321% (-9.4%) from 66.68%
17101605539

push

github

web-flow
Merge pull request #10102 from yyforyongyu/fix-UpdatesInHorizon

Catch bad gossip peer and fix `UpdatesInHorizon`

28 of 89 new or added lines in 4 files covered. (31.46%)

29163 existing lines in 459 files now uncovered.

99187 of 173038 relevant lines covered (57.32%)

1.78 hits per line

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

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

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

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

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

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

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

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

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

102
        return chanAnn, edge1Ann, edge2Ann, nil
3✔
103
}
104

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

109
// ValidateChannelAnn validates the channel announcement.
110
func ValidateChannelAnn(a lnwire.ChannelAnnouncement,
111
        fetchPkScript FetchPkScript) error {
3✔
112

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

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

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

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

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

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

193
        return nil
3✔
194
}
195

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

×
UNCOV
201
        dataHash, err := ChanAnn2DigestToSign(a)
×
UNCOV
202
        if err != nil {
×
203
                return err
×
204
        }
×
205

UNCOV
206
        sig, err := a.Signature.ToSignature()
×
UNCOV
207
        if err != nil {
×
208
                return err
×
209
        }
×
210

UNCOV
211
        nodeKey1, err := btcec.ParsePubKey(a.NodeID1.Val[:])
×
UNCOV
212
        if err != nil {
×
213
                return err
×
214
        }
×
215

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

UNCOV
221
        keys := []*btcec.PublicKey{
×
UNCOV
222
                nodeKey1, nodeKey2,
×
UNCOV
223
        }
×
UNCOV
224

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

×
UNCOV
234
                btcKey1 = a.BitcoinKey1.UnwrapOr(btcKey1)
×
UNCOV
235
                btcKey2 = a.BitcoinKey2.UnwrapOr(btcKey2)
×
UNCOV
236

×
UNCOV
237
                bitcoinKey1, err := btcec.ParsePubKey(btcKey1.Val[:])
×
UNCOV
238
                if err != nil {
×
239
                        return err
×
240
                }
×
241

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

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

UNCOV
257
                outputKey, err := schnorr.ParsePubKey(pkScript[2:])
×
UNCOV
258
                if err != nil {
×
259
                        return err
×
260
                }
×
261

UNCOV
262
                keys = append(keys, outputKey)
×
263
        }
264

UNCOV
265
        aggKey, _, _, err := musig2.AggregateKeys(keys, true)
×
UNCOV
266
        if err != nil {
×
267
                return err
×
268
        }
×
269

UNCOV
270
        if !sig.Verify(dataHash.CloneBytes(), aggKey.FinalKey) {
×
271
                return fmt.Errorf("invalid sig")
×
272
        }
×
273

UNCOV
274
        return nil
×
275
}
276

277
// ChanAnn2DigestToSign computes the digest of the message to be signed.
278
func ChanAnn2DigestToSign(a *lnwire.ChannelAnnouncement2) (*chainhash.Hash,
UNCOV
279
        error) {
×
UNCOV
280

×
UNCOV
281
        data, err := a.DataToSign()
×
UNCOV
282
        if err != nil {
×
283
                return nil, err
×
284
        }
×
285

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