• 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

0.0
/channeldb/migration26/channel.go
1
package migration26
2

3
import (
4
        "bytes"
5
        "fmt"
6

7
        lnwire "github.com/lightningnetwork/lnd/channeldb/migration/lnwire21"
8
        mig25 "github.com/lightningnetwork/lnd/channeldb/migration25"
9
        mig "github.com/lightningnetwork/lnd/channeldb/migration_01_to_11"
10
        "github.com/lightningnetwork/lnd/kvdb"
11
        "github.com/lightningnetwork/lnd/tlv"
12
)
13

14
const (
15
        // A tlv type definition used to serialize and deserialize a KeyLocator
16
        // from the database.
17
        keyLocType tlv.Type = 1
18

19
        // A tlv type used to serialize and deserialize the
20
        // `InitialLocalBalance` field.
21
        initialLocalBalanceType tlv.Type = 2
22

23
        // A tlv type used to serialize and deserialize the
24
        // `InitialRemoteBalance` field.
25
        initialRemoteBalanceType tlv.Type = 3
26
)
27

28
var (
29
        // chanInfoKey can be accessed within the bucket for a channel
30
        // (identified by its chanPoint). This key stores all the static
31
        // information for a channel which is decided at the end of  the
32
        // funding flow.
33
        chanInfoKey = []byte("chan-info-key")
34

35
        // localUpfrontShutdownKey can be accessed within the bucket for a
36
        // channel (identified by its chanPoint). This key stores an optional
37
        // upfront shutdown script for the local peer.
38
        localUpfrontShutdownKey = []byte("local-upfront-shutdown-key")
39

40
        // remoteUpfrontShutdownKey can be accessed within the bucket for a
41
        // channel (identified by its chanPoint). This key stores an optional
42
        // upfront shutdown script for the remote peer.
43
        remoteUpfrontShutdownKey = []byte("remote-upfront-shutdown-key")
44

45
        // lastWasRevokeKey is a key that stores true when the last update we
46
        // sent was a revocation and false when it was a commitment signature.
47
        // This is nil in the case of new channels with no updates exchanged.
48
        lastWasRevokeKey = []byte("last-was-revoke")
49

50
        // ErrNoChanInfoFound is returned when a particular channel does not
51
        // have any channels state.
52
        ErrNoChanInfoFound = fmt.Errorf("no chan info found")
53

54
        // ErrNoPastDeltas is returned when the channel delta bucket hasn't been
55
        // created.
56
        ErrNoPastDeltas = fmt.Errorf("channel has no recorded deltas")
57

58
        // ErrLogEntryNotFound is returned when we cannot find a log entry at
59
        // the height requested in the revocation log.
60
        ErrLogEntryNotFound = fmt.Errorf("log entry not found")
61

62
        // ErrNoCommitmentsFound is returned when a channel has not set
63
        // commitment states.
64
        ErrNoCommitmentsFound = fmt.Errorf("no commitments found")
65
)
66

67
// OpenChannel embeds a mig25.OpenChannel with the extra update-to-date
68
// serialization and deserialization methods.
69
//
70
// NOTE: doesn't have the Packager field as it's not used in current migration.
71
type OpenChannel struct {
72
        mig25.OpenChannel
73
}
74

75
// FetchChanInfo deserializes the channel info based on the legacy boolean.
76
// After migration25, the legacy format would have the fields
77
// `InitialLocalBalance` and `InitialRemoteBalance` directly encoded as bytes.
78
// For the new format, they will be put inside a tlv stream.
UNCOV
79
func FetchChanInfo(chanBucket kvdb.RBucket, c *OpenChannel, legacy bool) error {
×
UNCOV
80
        infoBytes := chanBucket.Get(chanInfoKey)
×
UNCOV
81
        if infoBytes == nil {
×
82
                return ErrNoChanInfoFound
×
83
        }
×
UNCOV
84
        r := bytes.NewReader(infoBytes)
×
UNCOV
85

×
UNCOV
86
        var (
×
UNCOV
87
                chanType   mig.ChannelType
×
UNCOV
88
                chanStatus mig.ChannelStatus
×
UNCOV
89
        )
×
UNCOV
90

×
UNCOV
91
        if err := mig.ReadElements(r,
×
UNCOV
92
                &chanType, &c.ChainHash, &c.FundingOutpoint,
×
UNCOV
93
                &c.ShortChannelID, &c.IsPending, &c.IsInitiator,
×
UNCOV
94
                &chanStatus, &c.FundingBroadcastHeight,
×
UNCOV
95
                &c.NumConfsRequired, &c.ChannelFlags,
×
UNCOV
96
                &c.IdentityPub, &c.Capacity, &c.TotalMSatSent,
×
UNCOV
97
                &c.TotalMSatReceived,
×
UNCOV
98
        ); err != nil {
×
99
                return err
×
100
        }
×
101

UNCOV
102
        c.ChanType = mig25.ChannelType(chanType)
×
UNCOV
103
        c.ChanStatus = mig25.ChannelStatus(chanStatus)
×
UNCOV
104

×
UNCOV
105
        // If this is the legacy format, we need to read the extra two new
×
UNCOV
106
        // fields.
×
UNCOV
107
        if legacy {
×
UNCOV
108
                if err := mig.ReadElements(r,
×
UNCOV
109
                        &c.InitialLocalBalance, &c.InitialRemoteBalance,
×
UNCOV
110
                ); err != nil {
×
111
                        return err
×
112
                }
×
113
        }
114

115
        // For single funder channels that we initiated and have the funding
116
        // transaction to, read the funding txn.
UNCOV
117
        if c.FundingTxPresent() {
×
UNCOV
118
                if err := mig.ReadElement(r, &c.FundingTxn); err != nil {
×
119
                        return err
×
120
                }
×
121
        }
122

UNCOV
123
        if err := mig.ReadChanConfig(r, &c.LocalChanCfg); err != nil {
×
124
                return err
×
125
        }
×
UNCOV
126
        if err := mig.ReadChanConfig(r, &c.RemoteChanCfg); err != nil {
×
127
                return err
×
128
        }
×
129

130
        // Retrieve the boolean stored under lastWasRevokeKey.
UNCOV
131
        lastWasRevokeBytes := chanBucket.Get(lastWasRevokeKey)
×
UNCOV
132
        if lastWasRevokeBytes == nil {
×
UNCOV
133
                // If nothing has been stored under this key, we store false in
×
UNCOV
134
                // the OpenChannel struct.
×
UNCOV
135
                c.LastWasRevoke = false
×
UNCOV
136
        } else {
×
137
                // Otherwise, read the value into the LastWasRevoke field.
×
138
                revokeReader := bytes.NewReader(lastWasRevokeBytes)
×
139
                err := mig.ReadElements(revokeReader, &c.LastWasRevoke)
×
140
                if err != nil {
×
141
                        return err
×
142
                }
×
143
        }
144

145
        // Make the tlv stream based on the legacy param.
UNCOV
146
        var (
×
UNCOV
147
                ts            *tlv.Stream
×
UNCOV
148
                err           error
×
UNCOV
149
                localBalance  uint64
×
UNCOV
150
                remoteBalance uint64
×
UNCOV
151
        )
×
UNCOV
152

×
UNCOV
153
        keyLocRecord := mig25.MakeKeyLocRecord(
×
UNCOV
154
                keyLocType, &c.RevocationKeyLocator,
×
UNCOV
155
        )
×
UNCOV
156

×
UNCOV
157
        // If it's legacy, create the stream with a single tlv record.
×
UNCOV
158
        if legacy {
×
UNCOV
159
                ts, err = tlv.NewStream(keyLocRecord)
×
UNCOV
160
        } else {
×
UNCOV
161
                // Otherwise, for the new format, we will encode the balance
×
UNCOV
162
                // fields in the tlv stream too.
×
UNCOV
163
                ts, err = tlv.NewStream(
×
UNCOV
164
                        keyLocRecord,
×
UNCOV
165
                        tlv.MakePrimitiveRecord(
×
UNCOV
166
                                initialLocalBalanceType, &localBalance,
×
UNCOV
167
                        ),
×
UNCOV
168
                        tlv.MakePrimitiveRecord(
×
UNCOV
169
                                initialRemoteBalanceType, &remoteBalance,
×
UNCOV
170
                        ),
×
UNCOV
171
                )
×
UNCOV
172
        }
×
UNCOV
173
        if err != nil {
×
174
                return err
×
175
        }
×
176

UNCOV
177
        if err := ts.Decode(r); err != nil {
×
178
                return err
×
179
        }
×
180

181
        // For the new format, attach the balance fields.
UNCOV
182
        if !legacy {
×
UNCOV
183
                c.InitialLocalBalance = lnwire.MilliSatoshi(localBalance)
×
UNCOV
184
                c.InitialRemoteBalance = lnwire.MilliSatoshi(remoteBalance)
×
UNCOV
185
        }
×
186

187
        // Finally, read the optional shutdown scripts.
UNCOV
188
        if err := mig25.GetOptionalUpfrontShutdownScript(
×
UNCOV
189
                chanBucket, localUpfrontShutdownKey, &c.LocalShutdownScript,
×
UNCOV
190
        ); err != nil {
×
191
                return err
×
192
        }
×
193

UNCOV
194
        return mig25.GetOptionalUpfrontShutdownScript(
×
UNCOV
195
                chanBucket, remoteUpfrontShutdownKey, &c.RemoteShutdownScript,
×
UNCOV
196
        )
×
197
}
198

199
// MakeTlvStream creates a tlv stream based on whether we are deadling with
200
// legacy format or not. For the legacy format, we have a single record in the
201
// stream. For the new format, we have the extra balance records.
UNCOV
202
func MakeTlvStream(c *OpenChannel, legacy bool) (*tlv.Stream, error) {
×
UNCOV
203
        keyLocRecord := mig25.MakeKeyLocRecord(
×
UNCOV
204
                keyLocType, &c.RevocationKeyLocator,
×
UNCOV
205
        )
×
UNCOV
206

×
UNCOV
207
        // If it's legacy, return the stream with a single tlv record.
×
UNCOV
208
        if legacy {
×
UNCOV
209
                return tlv.NewStream(keyLocRecord)
×
UNCOV
210
        }
×
211

212
        // Otherwise, for the new format, we will encode the balance fields in
213
        // the tlv stream too.
UNCOV
214
        localBalance := uint64(c.InitialLocalBalance)
×
UNCOV
215
        remoteBalance := uint64(c.InitialRemoteBalance)
×
UNCOV
216

×
UNCOV
217
        // Create the tlv stream.
×
UNCOV
218
        return tlv.NewStream(
×
UNCOV
219
                keyLocRecord,
×
UNCOV
220
                tlv.MakePrimitiveRecord(
×
UNCOV
221
                        initialLocalBalanceType, &localBalance,
×
UNCOV
222
                ),
×
UNCOV
223
                tlv.MakePrimitiveRecord(
×
UNCOV
224
                        initialRemoteBalanceType, &remoteBalance,
×
UNCOV
225
                ),
×
UNCOV
226
        )
×
227
}
228

229
// PutChanInfo serializes the channel info based on the legacy boolean. After
230
// migration25, the legacy format would have the fields `InitialLocalBalance`
231
// and `InitialRemoteBalance` directly encoded as bytes. For the new format,
232
// they will be put inside a tlv stream.
UNCOV
233
func PutChanInfo(chanBucket kvdb.RwBucket, c *OpenChannel, legacy bool) error {
×
UNCOV
234
        var w bytes.Buffer
×
UNCOV
235
        if err := mig.WriteElements(&w,
×
UNCOV
236
                mig.ChannelType(c.ChanType), c.ChainHash, c.FundingOutpoint,
×
UNCOV
237
                c.ShortChannelID, c.IsPending, c.IsInitiator,
×
UNCOV
238
                mig.ChannelStatus(c.ChanStatus), c.FundingBroadcastHeight,
×
UNCOV
239
                c.NumConfsRequired, c.ChannelFlags,
×
UNCOV
240
                c.IdentityPub, c.Capacity, c.TotalMSatSent,
×
UNCOV
241
                c.TotalMSatReceived,
×
UNCOV
242
        ); err != nil {
×
243
                return err
×
244
        }
×
245

246
        // If this is legacy format, we need to write the extra two fields.
UNCOV
247
        if legacy {
×
UNCOV
248
                if err := mig.WriteElements(&w,
×
UNCOV
249
                        c.InitialLocalBalance, c.InitialRemoteBalance,
×
UNCOV
250
                ); err != nil {
×
251
                        return err
×
252
                }
×
253
        }
254

255
        // For single funder channels that we initiated, and we have the
256
        // funding transaction, then write the funding txn.
UNCOV
257
        if c.FundingTxPresent() {
×
UNCOV
258
                if err := mig.WriteElement(&w, c.FundingTxn); err != nil {
×
259
                        return err
×
260
                }
×
261
        }
262

UNCOV
263
        if err := mig.WriteChanConfig(&w, &c.LocalChanCfg); err != nil {
×
264
                return err
×
265
        }
×
UNCOV
266
        if err := mig.WriteChanConfig(&w, &c.RemoteChanCfg); err != nil {
×
267
                return err
×
268
        }
×
269

270
        // Make the tlv stream based on the legacy param.
UNCOV
271
        tlvStream, err := MakeTlvStream(c, legacy)
×
UNCOV
272
        if err != nil {
×
273
                return err
×
274
        }
×
275

UNCOV
276
        if err := tlvStream.Encode(&w); err != nil {
×
277
                return err
×
278
        }
×
279

UNCOV
280
        if err := chanBucket.Put(chanInfoKey, w.Bytes()); err != nil {
×
281
                return err
×
282
        }
×
283

284
        // Finally, add optional shutdown scripts for the local and remote peer
285
        // if they are present.
UNCOV
286
        if err := mig25.PutOptionalUpfrontShutdownScript(
×
UNCOV
287
                chanBucket, localUpfrontShutdownKey, c.LocalShutdownScript,
×
UNCOV
288
        ); err != nil {
×
289
                return err
×
290
        }
×
291

UNCOV
292
        return mig25.PutOptionalUpfrontShutdownScript(
×
UNCOV
293
                chanBucket, remoteUpfrontShutdownKey, c.RemoteShutdownScript,
×
UNCOV
294
        )
×
295
}
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