• 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/migration25/channel.go
1
package migration25
2

3
import (
4
        "bytes"
5
        "fmt"
6
        "io"
7
        "strconv"
8
        "strings"
9

10
        lnwire "github.com/lightningnetwork/lnd/channeldb/migration/lnwire21"
11
        mig24 "github.com/lightningnetwork/lnd/channeldb/migration24"
12
        mig "github.com/lightningnetwork/lnd/channeldb/migration_01_to_11"
13
        "github.com/lightningnetwork/lnd/keychain"
14
        "github.com/lightningnetwork/lnd/kvdb"
15
        "github.com/lightningnetwork/lnd/tlv"
16
)
17

18
const (
19
        // A tlv type definition used to serialize and deserialize a KeyLocator
20
        // from the database.
21
        keyLocType tlv.Type = 1
22
)
23

24
var (
25
        // chanCommitmentKey can be accessed within the sub-bucket for a
26
        // particular channel. This key stores the up to date commitment state
27
        // for a particular channel party. Appending a 0 to the end of this key
28
        // indicates it's the commitment for the local party, and appending a 1
29
        // to the end of this key indicates it's the commitment for the remote
30
        // party.
31
        chanCommitmentKey = []byte("chan-commitment-key")
32

33
        // revocationLogBucketLegacy is the legacy bucket where we store the
34
        // revocation log in old format.
35
        revocationLogBucketLegacy = []byte("revocation-log-key")
36

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

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

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

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

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

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

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

69
// ChannelType is an enum-like type that describes one of several possible
70
// channel types. Each open channel is associated with a particular type as the
71
// channel type may determine how higher level operations are conducted such as
72
// fee negotiation, channel closing, the format of HTLCs, etc. Structure-wise,
73
// a ChannelType is a bit field, with each bit denoting a modification from the
74
// base channel type of single funder.
75
type ChannelType uint8
76

77
const (
78
        // NOTE: iota isn't used here for this enum needs to be stable
79
        // long-term as it will be persisted to the database.
80

81
        // SingleFunderBit represents a channel wherein one party solely funds
82
        // the entire capacity of the channel.
83
        SingleFunderBit ChannelType = 0
84

85
        // DualFunderBit represents a channel wherein both parties contribute
86
        // funds towards the total capacity of the channel. The channel may be
87
        // funded symmetrically or asymmetrically.
88
        DualFunderBit ChannelType = 1 << 0
89

90
        // SingleFunderTweaklessBit is similar to the basic SingleFunder channel
91
        // type, but it omits the tweak for one's key in the commitment
92
        // transaction of the remote party.
93
        SingleFunderTweaklessBit ChannelType = 1 << 1
94

95
        // NoFundingTxBit denotes if we have the funding transaction locally on
96
        // disk. This bit may be on if the funding transaction was crafted by a
97
        // wallet external to the primary daemon.
98
        NoFundingTxBit ChannelType = 1 << 2
99

100
        // AnchorOutputsBit indicates that the channel makes use of anchor
101
        // outputs to bump the commitment transaction's effective feerate. This
102
        // channel type also uses a delayed to_remote output script.
103
        AnchorOutputsBit ChannelType = 1 << 3
104

105
        // FrozenBit indicates that the channel is a frozen channel, meaning
106
        // that only the responder can decide to cooperatively close the
107
        // channel.
108
        FrozenBit ChannelType = 1 << 4
109

110
        // ZeroHtlcTxFeeBit indicates that the channel should use zero-fee
111
        // second-level HTLC transactions.
112
        ZeroHtlcTxFeeBit ChannelType = 1 << 5
113

114
        // LeaseExpirationBit indicates that the channel has been leased for a
115
        // period of time, constraining every output that pays to the channel
116
        // initiator with an additional CLTV of the lease maturity.
117
        LeaseExpirationBit ChannelType = 1 << 6
118
)
119

120
// IsSingleFunder returns true if the channel type if one of the known single
121
// funder variants.
UNCOV
122
func (c ChannelType) IsSingleFunder() bool {
×
UNCOV
123
        return c&DualFunderBit == 0
×
UNCOV
124
}
×
125

126
// IsDualFunder returns true if the ChannelType has the DualFunderBit set.
127
func (c ChannelType) IsDualFunder() bool {
×
128
        return c&DualFunderBit == DualFunderBit
×
129
}
×
130

131
// IsTweakless returns true if the target channel uses a commitment that
132
// doesn't tweak the key for the remote party.
UNCOV
133
func (c ChannelType) IsTweakless() bool {
×
UNCOV
134
        return c&SingleFunderTweaklessBit == SingleFunderTweaklessBit
×
UNCOV
135
}
×
136

137
// HasFundingTx returns true if this channel type is one that has a funding
138
// transaction stored locally.
UNCOV
139
func (c ChannelType) HasFundingTx() bool {
×
UNCOV
140
        return c&NoFundingTxBit == 0
×
UNCOV
141
}
×
142

143
// HasAnchors returns true if this channel type has anchor outputs on its
144
// commitment.
UNCOV
145
func (c ChannelType) HasAnchors() bool {
×
UNCOV
146
        return c&AnchorOutputsBit == AnchorOutputsBit
×
UNCOV
147
}
×
148

149
// ZeroHtlcTxFee returns true if this channel type uses second-level HTLC
150
// transactions signed with zero-fee.
151
func (c ChannelType) ZeroHtlcTxFee() bool {
×
152
        return c&ZeroHtlcTxFeeBit == ZeroHtlcTxFeeBit
×
153
}
×
154

155
// IsFrozen returns true if the channel is considered to be "frozen". A frozen
156
// channel means that only the responder can initiate a cooperative channel
157
// closure.
158
func (c ChannelType) IsFrozen() bool {
×
159
        return c&FrozenBit == FrozenBit
×
160
}
×
161

162
// HasLeaseExpiration returns true if the channel originated from a lease.
UNCOV
163
func (c ChannelType) HasLeaseExpiration() bool {
×
UNCOV
164
        return c&LeaseExpirationBit == LeaseExpirationBit
×
UNCOV
165
}
×
166

167
// ChannelStatus is a bit vector used to indicate whether an OpenChannel is in
168
// the default usable state, or a state where it shouldn't be used.
169
type ChannelStatus uint8
170

171
var (
172
        // ChanStatusDefault is the normal state of an open channel.
173
        ChanStatusDefault ChannelStatus
174

175
        // ChanStatusBorked indicates that the channel has entered an
176
        // irreconcilable state, triggered by a state desynchronization or
177
        // channel breach.  Channels in this state should never be added to the
178
        // htlc switch.
179
        ChanStatusBorked ChannelStatus = 1
180

181
        // ChanStatusCommitBroadcasted indicates that a commitment for this
182
        // channel has been broadcasted.
183
        ChanStatusCommitBroadcasted ChannelStatus = 1 << 1
184

185
        // ChanStatusLocalDataLoss indicates that we have lost channel state
186
        // for this channel, and broadcasting our latest commitment might be
187
        // considered a breach.
188
        //
189
        // TODO(halseh): actually enforce that we are not force closing such a
190
        // channel.
191
        ChanStatusLocalDataLoss ChannelStatus = 1 << 2
192

193
        // ChanStatusRestored is a status flag that signals that the channel
194
        // has been restored, and doesn't have all the fields a typical channel
195
        // will have.
196
        ChanStatusRestored ChannelStatus = 1 << 3
197

198
        // ChanStatusCoopBroadcasted indicates that a cooperative close for
199
        // this channel has been broadcasted. Older cooperatively closed
200
        // channels will only have this status set. Newer ones will also have
201
        // close initiator information stored using the local/remote initiator
202
        // status. This status is set in conjunction with the initiator status
203
        // so that we do not need to check multiple channel statues for
204
        // cooperative closes.
205
        ChanStatusCoopBroadcasted ChannelStatus = 1 << 4
206

207
        // ChanStatusLocalCloseInitiator indicates that we initiated closing
208
        // the channel.
209
        ChanStatusLocalCloseInitiator ChannelStatus = 1 << 5
210

211
        // ChanStatusRemoteCloseInitiator indicates that the remote node
212
        // initiated closing the channel.
213
        ChanStatusRemoteCloseInitiator ChannelStatus = 1 << 6
214
)
215

216
// chanStatusStrings maps a ChannelStatus to a human friendly string that
217
// describes that status.
218
var chanStatusStrings = map[ChannelStatus]string{
219
        ChanStatusDefault:              "ChanStatusDefault",
220
        ChanStatusBorked:               "ChanStatusBorked",
221
        ChanStatusCommitBroadcasted:    "ChanStatusCommitBroadcasted",
222
        ChanStatusLocalDataLoss:        "ChanStatusLocalDataLoss",
223
        ChanStatusRestored:             "ChanStatusRestored",
224
        ChanStatusCoopBroadcasted:      "ChanStatusCoopBroadcasted",
225
        ChanStatusLocalCloseInitiator:  "ChanStatusLocalCloseInitiator",
226
        ChanStatusRemoteCloseInitiator: "ChanStatusRemoteCloseInitiator",
227
}
228

229
// orderedChanStatusFlags is an in-order list of all that channel status flags.
230
var orderedChanStatusFlags = []ChannelStatus{
231
        ChanStatusBorked,
232
        ChanStatusCommitBroadcasted,
233
        ChanStatusLocalDataLoss,
234
        ChanStatusRestored,
235
        ChanStatusCoopBroadcasted,
236
        ChanStatusLocalCloseInitiator,
237
        ChanStatusRemoteCloseInitiator,
238
}
239

240
// String returns a human-readable representation of the ChannelStatus.
241
func (c ChannelStatus) String() string {
×
242
        // If no flags are set, then this is the default case.
×
243
        if c == ChanStatusDefault {
×
244
                return chanStatusStrings[ChanStatusDefault]
×
245
        }
×
246

247
        // Add individual bit flags.
248
        statusStr := ""
×
249
        for _, flag := range orderedChanStatusFlags {
×
250
                if c&flag == flag {
×
251
                        statusStr += chanStatusStrings[flag] + "|"
×
252
                        c -= flag
×
253
                }
×
254
        }
255

256
        // Remove anything to the right of the final bar, including it as well.
257
        statusStr = strings.TrimRight(statusStr, "|")
×
258

×
259
        // Add any remaining flags which aren't accounted for as hex.
×
260
        if c != 0 {
×
261
                statusStr += "|0x" + strconv.FormatUint(uint64(c), 16)
×
262
        }
×
263

264
        // If this was purely an unknown flag, then remove the extra bar at the
265
        // start of the string.
266
        statusStr = strings.TrimLeft(statusStr, "|")
×
267

×
268
        return statusStr
×
269
}
270

271
// OpenChannel embeds a mig.OpenChannel with the extra update-to-date fields.
272
//
273
// NOTE: doesn't have the Packager field as it's not used in current migration.
274
type OpenChannel struct {
275
        mig.OpenChannel
276

277
        // ChanType denotes which type of channel this is.
278
        ChanType ChannelType
279

280
        // ChanStatus is the current status of this channel. If it is not in
281
        // the state Default, it should not be used for forwarding payments.
282
        //
283
        // NOTE: In `channeldb.OpenChannel`, this field is private. We choose
284
        // to export this private field such that following migrations can
285
        // access this field directly.
286
        ChanStatus ChannelStatus
287

288
        // InitialLocalBalance is the balance we have during the channel
289
        // opening. When we are not the initiator, this value represents the
290
        // push amount.
291
        InitialLocalBalance lnwire.MilliSatoshi
292

293
        // InitialRemoteBalance is the balance they have during the channel
294
        // opening.
295
        InitialRemoteBalance lnwire.MilliSatoshi
296

297
        // LocalShutdownScript is set to a pre-set script if the channel was
298
        // opened by the local node with option_upfront_shutdown_script set. If
299
        // the option was not set, the field is empty.
300
        LocalShutdownScript lnwire.DeliveryAddress
301

302
        // RemoteShutdownScript is set to a pre-set script if the channel was
303
        // opened by the remote node with option_upfront_shutdown_script set.
304
        // If the option was not set, the field is empty.
305
        RemoteShutdownScript lnwire.DeliveryAddress
306

307
        // ThawHeight is the height when a frozen channel once again becomes a
308
        // normal channel. If this is zero, then there're no restrictions on
309
        // this channel. If the value is lower than 500,000, then it's
310
        // interpreted as a relative height, or an absolute height otherwise.
311
        ThawHeight uint32
312

313
        // LastWasRevoke is a boolean that determines if the last update we
314
        // sent was a revocation (true) or a commitment signature (false).
315
        LastWasRevoke bool
316

317
        // RevocationKeyLocator stores the KeyLocator information that we will
318
        // need to derive the shachain root for this channel. This allows us to
319
        // have private key isolation from lnd.
320
        RevocationKeyLocator keychain.KeyLocator
321
}
322

UNCOV
323
func (c *OpenChannel) hasChanStatus(status ChannelStatus) bool {
×
UNCOV
324
        // Special case ChanStatusDefualt since it isn't actually flag, but a
×
UNCOV
325
        // particular combination (or lack-there-of) of flags.
×
UNCOV
326
        if status == ChanStatusDefault {
×
327
                return c.ChanStatus == ChanStatusDefault
×
328
        }
×
329

UNCOV
330
        return c.ChanStatus&status == status
×
331
}
332

333
// FundingTxPresent returns true if expect the funding transcation to be found
334
// on disk or already populated within the passed open channel struct.
UNCOV
335
func (c *OpenChannel) FundingTxPresent() bool {
×
UNCOV
336
        chanType := c.ChanType
×
UNCOV
337

×
UNCOV
338
        return chanType.IsSingleFunder() && chanType.HasFundingTx() &&
×
UNCOV
339
                c.IsInitiator &&
×
UNCOV
340
                !c.hasChanStatus(ChanStatusRestored)
×
UNCOV
341
}
×
342

343
// fetchChanInfo deserializes the channel info based on the legacy boolean.
UNCOV
344
func fetchChanInfo(chanBucket kvdb.RBucket, c *OpenChannel, legacy bool) error {
×
UNCOV
345
        infoBytes := chanBucket.Get(chanInfoKey)
×
UNCOV
346
        if infoBytes == nil {
×
347
                return ErrNoChanInfoFound
×
348
        }
×
UNCOV
349
        r := bytes.NewReader(infoBytes)
×
UNCOV
350

×
UNCOV
351
        var (
×
UNCOV
352
                chanType   mig.ChannelType
×
UNCOV
353
                chanStatus mig.ChannelStatus
×
UNCOV
354
        )
×
UNCOV
355

×
UNCOV
356
        if err := mig.ReadElements(r,
×
UNCOV
357
                &chanType, &c.ChainHash, &c.FundingOutpoint,
×
UNCOV
358
                &c.ShortChannelID, &c.IsPending, &c.IsInitiator,
×
UNCOV
359
                &chanStatus, &c.FundingBroadcastHeight,
×
UNCOV
360
                &c.NumConfsRequired, &c.ChannelFlags,
×
UNCOV
361
                &c.IdentityPub, &c.Capacity, &c.TotalMSatSent,
×
UNCOV
362
                &c.TotalMSatReceived,
×
UNCOV
363
        ); err != nil {
×
364
                return err
×
365
        }
×
366

UNCOV
367
        c.ChanType = ChannelType(chanType)
×
UNCOV
368
        c.ChanStatus = ChannelStatus(chanStatus)
×
UNCOV
369

×
UNCOV
370
        // If this is not the legacy format, we need to read the extra two new
×
UNCOV
371
        // fields.
×
UNCOV
372
        if !legacy {
×
UNCOV
373
                if err := mig.ReadElements(r,
×
UNCOV
374
                        &c.InitialLocalBalance, &c.InitialRemoteBalance,
×
UNCOV
375
                ); err != nil {
×
376
                        return err
×
377
                }
×
378
        }
379

380
        // For single funder channels that we initiated and have the funding
381
        // transaction to, read the funding txn.
UNCOV
382
        if c.FundingTxPresent() {
×
383
                if err := mig.ReadElement(r, &c.FundingTxn); err != nil {
×
384
                        return err
×
385
                }
×
386
        }
387

UNCOV
388
        if err := mig.ReadChanConfig(r, &c.LocalChanCfg); err != nil {
×
389
                return err
×
390
        }
×
UNCOV
391
        if err := mig.ReadChanConfig(r, &c.RemoteChanCfg); err != nil {
×
392
                return err
×
393
        }
×
394

395
        // Retrieve the boolean stored under lastWasRevokeKey.
UNCOV
396
        lastWasRevokeBytes := chanBucket.Get(lastWasRevokeKey)
×
UNCOV
397
        if lastWasRevokeBytes == nil {
×
UNCOV
398
                // If nothing has been stored under this key, we store false in
×
UNCOV
399
                // the OpenChannel struct.
×
UNCOV
400
                c.LastWasRevoke = false
×
UNCOV
401
        } else {
×
402
                // Otherwise, read the value into the LastWasRevoke field.
×
403
                revokeReader := bytes.NewReader(lastWasRevokeBytes)
×
404
                err := mig.ReadElements(revokeReader, &c.LastWasRevoke)
×
405
                if err != nil {
×
406
                        return err
×
407
                }
×
408
        }
409

UNCOV
410
        keyLocRecord := MakeKeyLocRecord(keyLocType, &c.RevocationKeyLocator)
×
UNCOV
411
        tlvStream, err := tlv.NewStream(keyLocRecord)
×
UNCOV
412
        if err != nil {
×
413
                return err
×
414
        }
×
415

UNCOV
416
        if err := tlvStream.Decode(r); err != nil {
×
417
                return err
×
418
        }
×
419

420
        // Finally, read the optional shutdown scripts.
UNCOV
421
        if err := GetOptionalUpfrontShutdownScript(
×
UNCOV
422
                chanBucket, localUpfrontShutdownKey, &c.LocalShutdownScript,
×
UNCOV
423
        ); err != nil {
×
424
                return err
×
425
        }
×
426

UNCOV
427
        return GetOptionalUpfrontShutdownScript(
×
UNCOV
428
                chanBucket, remoteUpfrontShutdownKey, &c.RemoteShutdownScript,
×
UNCOV
429
        )
×
430
}
431

432
// fetchChanInfo serializes the channel info based on the legacy boolean and
433
// saves it to disk.
UNCOV
434
func putChanInfo(chanBucket kvdb.RwBucket, c *OpenChannel, legacy bool) error {
×
UNCOV
435
        var w bytes.Buffer
×
UNCOV
436
        if err := mig.WriteElements(&w,
×
UNCOV
437
                mig.ChannelType(c.ChanType), c.ChainHash, c.FundingOutpoint,
×
UNCOV
438
                c.ShortChannelID, c.IsPending, c.IsInitiator,
×
UNCOV
439
                mig.ChannelStatus(c.ChanStatus), c.FundingBroadcastHeight,
×
UNCOV
440
                c.NumConfsRequired, c.ChannelFlags,
×
UNCOV
441
                c.IdentityPub, c.Capacity, c.TotalMSatSent,
×
UNCOV
442
                c.TotalMSatReceived,
×
UNCOV
443
        ); err != nil {
×
444
                return err
×
445
        }
×
446

447
        // If this is not legacy format, we need to write the extra two fields.
UNCOV
448
        if !legacy {
×
UNCOV
449
                if err := mig.WriteElements(&w,
×
UNCOV
450
                        c.InitialLocalBalance, c.InitialRemoteBalance,
×
UNCOV
451
                ); err != nil {
×
452
                        return err
×
453
                }
×
454
        }
455

456
        // For single funder channels that we initiated, and we have the
457
        // funding transaction, then write the funding txn.
UNCOV
458
        if c.FundingTxPresent() {
×
459
                if err := mig.WriteElement(&w, c.FundingTxn); err != nil {
×
460
                        return err
×
461
                }
×
462
        }
463

UNCOV
464
        if err := mig.WriteChanConfig(&w, &c.LocalChanCfg); err != nil {
×
465
                return err
×
466
        }
×
UNCOV
467
        if err := mig.WriteChanConfig(&w, &c.RemoteChanCfg); err != nil {
×
468
                return err
×
469
        }
×
470

471
        // Write the RevocationKeyLocator as the first entry in a tlv stream.
UNCOV
472
        keyLocRecord := MakeKeyLocRecord(
×
UNCOV
473
                keyLocType, &c.RevocationKeyLocator,
×
UNCOV
474
        )
×
UNCOV
475

×
UNCOV
476
        tlvStream, err := tlv.NewStream(keyLocRecord)
×
UNCOV
477
        if err != nil {
×
478
                return err
×
479
        }
×
480

UNCOV
481
        if err := tlvStream.Encode(&w); err != nil {
×
482
                return err
×
483
        }
×
484

UNCOV
485
        if err := chanBucket.Put(chanInfoKey, w.Bytes()); err != nil {
×
486
                return err
×
487
        }
×
488

489
        // Finally, add optional shutdown scripts for the local and remote peer
490
        // if they are present.
UNCOV
491
        if err := PutOptionalUpfrontShutdownScript(
×
UNCOV
492
                chanBucket, localUpfrontShutdownKey, c.LocalShutdownScript,
×
UNCOV
493
        ); err != nil {
×
494
                return err
×
495
        }
×
496

UNCOV
497
        return PutOptionalUpfrontShutdownScript(
×
UNCOV
498
                chanBucket, remoteUpfrontShutdownKey, c.RemoteShutdownScript,
×
UNCOV
499
        )
×
500
}
501

502
// EKeyLocator is an encoder for keychain.KeyLocator.
UNCOV
503
func EKeyLocator(w io.Writer, val interface{}, buf *[8]byte) error {
×
UNCOV
504
        if v, ok := val.(*keychain.KeyLocator); ok {
×
UNCOV
505
                err := tlv.EUint32T(w, uint32(v.Family), buf)
×
UNCOV
506
                if err != nil {
×
507
                        return err
×
508
                }
×
509

UNCOV
510
                return tlv.EUint32T(w, v.Index, buf)
×
511
        }
512
        return tlv.NewTypeForEncodingErr(val, "keychain.KeyLocator")
×
513
}
514

515
// DKeyLocator is a decoder for keychain.KeyLocator.
UNCOV
516
func DKeyLocator(r io.Reader, val interface{}, buf *[8]byte, l uint64) error {
×
UNCOV
517
        if v, ok := val.(*keychain.KeyLocator); ok {
×
UNCOV
518
                var family uint32
×
UNCOV
519
                err := tlv.DUint32(r, &family, buf, 4)
×
UNCOV
520
                if err != nil {
×
521
                        return err
×
522
                }
×
UNCOV
523
                v.Family = keychain.KeyFamily(family)
×
UNCOV
524

×
UNCOV
525
                return tlv.DUint32(r, &v.Index, buf, 4)
×
526
        }
527
        return tlv.NewTypeForDecodingErr(val, "keychain.KeyLocator", l, 8)
×
528
}
529

530
// MakeKeyLocRecord creates a Record out of a KeyLocator using the passed
531
// Type and the EKeyLocator and DKeyLocator functions. The size will always be
532
// 8 as KeyFamily is uint32 and the Index is uint32.
UNCOV
533
func MakeKeyLocRecord(typ tlv.Type, keyLoc *keychain.KeyLocator) tlv.Record {
×
UNCOV
534
        return tlv.MakeStaticRecord(typ, keyLoc, 8, EKeyLocator, DKeyLocator)
×
UNCOV
535
}
×
536

537
// PutOptionalUpfrontShutdownScript adds a shutdown script under the key
538
// provided if it has a non-zero length.
539
func PutOptionalUpfrontShutdownScript(chanBucket kvdb.RwBucket, key []byte,
UNCOV
540
        script []byte) error {
×
UNCOV
541
        // If the script is empty, we do not need to add anything.
×
UNCOV
542
        if len(script) == 0 {
×
UNCOV
543
                return nil
×
UNCOV
544
        }
×
545

546
        var w bytes.Buffer
×
547
        if err := mig.WriteElement(&w, script); err != nil {
×
548
                return err
×
549
        }
×
550

551
        return chanBucket.Put(key, w.Bytes())
×
552
}
553

554
// GetOptionalUpfrontShutdownScript reads the shutdown script stored under the
555
// key provided if it is present. Upfront shutdown scripts are optional, so the
556
// function returns with no error if the key is not present.
557
func GetOptionalUpfrontShutdownScript(chanBucket kvdb.RBucket, key []byte,
UNCOV
558
        script *lnwire.DeliveryAddress) error {
×
UNCOV
559

×
UNCOV
560
        // Return early if the bucket does not exit, a shutdown script was not
×
UNCOV
561
        // set.
×
UNCOV
562
        bs := chanBucket.Get(key)
×
UNCOV
563
        if bs == nil {
×
UNCOV
564
                return nil
×
UNCOV
565
        }
×
566

567
        var tempScript []byte
×
568
        r := bytes.NewReader(bs)
×
569
        if err := mig.ReadElement(r, &tempScript); err != nil {
×
570
                return err
×
571
        }
×
572
        *script = tempScript
×
573

×
574
        return nil
×
575
}
576

577
// FetchChanCommitments fetches both the local and remote commitments. This
578
// function is exported so it can be used by later migrations.
UNCOV
579
func FetchChanCommitments(chanBucket kvdb.RBucket, channel *OpenChannel) error {
×
UNCOV
580
        var err error
×
UNCOV
581

×
UNCOV
582
        // If this is a restored channel, then we don't have any commitments to
×
UNCOV
583
        // read.
×
UNCOV
584
        if channel.hasChanStatus(ChanStatusRestored) {
×
585
                return nil
×
586
        }
×
587

UNCOV
588
        channel.LocalCommitment, err = FetchChanCommitment(chanBucket, true)
×
UNCOV
589
        if err != nil {
×
590
                return err
×
591
        }
×
UNCOV
592
        channel.RemoteCommitment, err = FetchChanCommitment(chanBucket, false)
×
UNCOV
593
        if err != nil {
×
594
                return err
×
595
        }
×
596

UNCOV
597
        return nil
×
598
}
599

600
// FetchChanCommitment fetches a channel commitment. This function is exported
601
// so it can be used by later migrations.
602
func FetchChanCommitment(chanBucket kvdb.RBucket,
UNCOV
603
        local bool) (mig.ChannelCommitment, error) {
×
UNCOV
604

×
UNCOV
605
        commitKey := chanCommitmentKey
×
UNCOV
606
        if local {
×
UNCOV
607
                commitKey = append(commitKey, byte(0x00))
×
UNCOV
608
        } else {
×
UNCOV
609
                commitKey = append(commitKey, byte(0x01))
×
UNCOV
610
        }
×
611

UNCOV
612
        commitBytes := chanBucket.Get(commitKey)
×
UNCOV
613
        if commitBytes == nil {
×
614
                return mig.ChannelCommitment{}, ErrNoCommitmentsFound
×
615
        }
×
616

UNCOV
617
        r := bytes.NewReader(commitBytes)
×
UNCOV
618
        return mig.DeserializeChanCommit(r)
×
619
}
620

621
func PutChanCommitment(chanBucket kvdb.RwBucket, c *mig.ChannelCommitment,
UNCOV
622
        local bool) error {
×
UNCOV
623

×
UNCOV
624
        commitKey := chanCommitmentKey
×
UNCOV
625
        if local {
×
UNCOV
626
                commitKey = append(commitKey, byte(0x00))
×
UNCOV
627
        } else {
×
UNCOV
628
                commitKey = append(commitKey, byte(0x01))
×
UNCOV
629
        }
×
630

UNCOV
631
        var b bytes.Buffer
×
UNCOV
632
        if err := mig.SerializeChanCommit(&b, c); err != nil {
×
633
                return err
×
634
        }
×
635

UNCOV
636
        return chanBucket.Put(commitKey, b.Bytes())
×
637
}
638

UNCOV
639
func PutChanCommitments(chanBucket kvdb.RwBucket, channel *OpenChannel) error {
×
UNCOV
640
        // If this is a restored channel, then we don't have any commitments to
×
UNCOV
641
        // write.
×
UNCOV
642
        if channel.hasChanStatus(ChanStatusRestored) {
×
643
                return nil
×
644
        }
×
645

UNCOV
646
        err := PutChanCommitment(
×
UNCOV
647
                chanBucket, &channel.LocalCommitment, true,
×
UNCOV
648
        )
×
UNCOV
649
        if err != nil {
×
650
                return err
×
651
        }
×
652

UNCOV
653
        return PutChanCommitment(
×
UNCOV
654
                chanBucket, &channel.RemoteCommitment, false,
×
UNCOV
655
        )
×
656
}
657

658
// balancesAtHeight returns the local and remote balances on our commitment
659
// transactions as of a given height. This function is not exported as it's
660
// deprecated.
661
//
662
// NOTE: these are our balances *after* subtracting the commitment fee and
663
// anchor outputs.
664
func (c *OpenChannel) balancesAtHeight(chanBucket kvdb.RBucket,
UNCOV
665
        height uint64) (lnwire.MilliSatoshi, lnwire.MilliSatoshi, error) {
×
UNCOV
666

×
UNCOV
667
        // If our current commit is as the desired height, we can return our
×
UNCOV
668
        // current balances.
×
UNCOV
669
        if c.LocalCommitment.CommitHeight == height {
×
UNCOV
670
                return c.LocalCommitment.LocalBalance,
×
UNCOV
671
                        c.LocalCommitment.RemoteBalance, nil
×
UNCOV
672
        }
×
673

674
        // If our current remote commit is at the desired height, we can return
675
        // the current balances.
UNCOV
676
        if c.RemoteCommitment.CommitHeight == height {
×
677
                return c.RemoteCommitment.LocalBalance,
×
678
                        c.RemoteCommitment.RemoteBalance, nil
×
679
        }
×
680

681
        // If we are not currently on the height requested, we need to look up
682
        // the previous height to obtain our balances at the given height.
UNCOV
683
        commit, err := c.FindPreviousStateLegacy(chanBucket, height)
×
UNCOV
684
        if err != nil {
×
UNCOV
685
                return 0, 0, err
×
UNCOV
686
        }
×
687

UNCOV
688
        return commit.LocalBalance, commit.RemoteBalance, nil
×
689
}
690

691
// FindPreviousStateLegacy scans through the append-only log in an attempt to
692
// recover the previous channel state indicated by the update number. This
693
// method is intended to be used for obtaining the relevant data needed to
694
// claim all funds rightfully spendable in the case of an on-chain broadcast of
695
// the commitment transaction.
696
func (c *OpenChannel) FindPreviousStateLegacy(chanBucket kvdb.RBucket,
UNCOV
697
        updateNum uint64) (*mig.ChannelCommitment, error) {
×
UNCOV
698

×
UNCOV
699
        c.RLock()
×
UNCOV
700
        defer c.RUnlock()
×
UNCOV
701

×
UNCOV
702
        logBucket := chanBucket.NestedReadBucket(revocationLogBucketLegacy)
×
UNCOV
703
        if logBucket == nil {
×
UNCOV
704
                return nil, ErrNoPastDeltas
×
UNCOV
705
        }
×
706

UNCOV
707
        commit, err := fetchChannelLogEntry(logBucket, updateNum)
×
UNCOV
708
        if err != nil {
×
UNCOV
709
                return nil, err
×
UNCOV
710
        }
×
711

UNCOV
712
        return &commit, nil
×
713
}
714

715
func fetchChannelLogEntry(log kvdb.RBucket,
UNCOV
716
        updateNum uint64) (mig.ChannelCommitment, error) {
×
UNCOV
717

×
UNCOV
718
        logEntrykey := mig24.MakeLogKey(updateNum)
×
UNCOV
719
        commitBytes := log.Get(logEntrykey[:])
×
UNCOV
720
        if commitBytes == nil {
×
UNCOV
721
                return mig.ChannelCommitment{}, ErrLogEntryNotFound
×
UNCOV
722
        }
×
723

UNCOV
724
        commitReader := bytes.NewReader(commitBytes)
×
UNCOV
725
        return mig.DeserializeChanCommit(commitReader)
×
726
}
727

UNCOV
728
func CreateChanBucket(tx kvdb.RwTx, c *OpenChannel) (kvdb.RwBucket, error) {
×
UNCOV
729
        // First fetch the top level bucket which stores all data related to
×
UNCOV
730
        // current, active channels.
×
UNCOV
731
        openChanBucket, err := tx.CreateTopLevelBucket(openChannelBucket)
×
UNCOV
732
        if err != nil {
×
733
                return nil, err
×
734
        }
×
735

736
        // Within this top level bucket, fetch the bucket dedicated to storing
737
        // open channel data specific to the remote node.
UNCOV
738
        nodePub := c.IdentityPub.SerializeCompressed()
×
UNCOV
739
        nodeChanBucket, err := openChanBucket.CreateBucketIfNotExists(nodePub)
×
UNCOV
740
        if err != nil {
×
741
                return nil, err
×
742
        }
×
743

744
        // We'll then recurse down an additional layer in order to fetch the
745
        // bucket for this particular chain.
UNCOV
746
        chainBucket, err := nodeChanBucket.CreateBucketIfNotExists(
×
UNCOV
747
                c.ChainHash[:],
×
UNCOV
748
        )
×
UNCOV
749
        if err != nil {
×
750
                return nil, err
×
751
        }
×
752

UNCOV
753
        var chanPointBuf bytes.Buffer
×
UNCOV
754
        err = mig.WriteOutpoint(&chanPointBuf, &c.FundingOutpoint)
×
UNCOV
755
        if err != nil {
×
756
                return nil, err
×
757
        }
×
758

759
        // With the bucket for the node fetched, we can now go down another
760
        // level, creating the bucket for this channel itself.
UNCOV
761
        return chainBucket.CreateBucketIfNotExists(chanPointBuf.Bytes())
×
762
}
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