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

lightningnetwork / lnd / 13211764208

08 Feb 2025 03:08AM UTC coverage: 49.288% (-9.5%) from 58.815%
13211764208

Pull #9489

github

calvinrzachman
itest: verify switchrpc server enforces send then track

We prevent the rpc server from allowing onion dispatches for
attempt IDs which have already been tracked by rpc clients.

This helps protect the client from leaking a duplicate onion
attempt. NOTE: This is not the only method for solving this
issue! The issue could be addressed via careful client side
programming which accounts for the uncertainty and async
nature of dispatching onions to a remote process via RPC.
This would require some lnd ChannelRouter changes for how
we intend to use these RPCs though.
Pull Request #9489: multi: add BuildOnion, SendOnion, and TrackOnion RPCs

474 of 990 new or added lines in 11 files covered. (47.88%)

27321 existing lines in 435 files now uncovered.

101192 of 205306 relevant lines covered (49.29%)

1.54 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