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

lightningnetwork / lnd / 13035292482

29 Jan 2025 03:59PM UTC coverage: 49.3% (-9.5%) from 58.777%
13035292482

Pull #9456

github

mohamedawnallah
docs: update release-notes-0.19.0.md

In this commit, we warn users about the removal
of RPCs `SendToRoute`, `SendToRouteSync`, `SendPayment`,
and `SendPaymentSync` in the next release 0.20.
Pull Request #9456: lnrpc+docs: deprecate warning `SendToRoute`, `SendToRouteSync`, `SendPayment`, and `SendPaymentSync` in Release 0.19

100634 of 204126 relevant lines covered (49.3%)

1.54 hits per line

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

69.63
/channeldb/revocation_log.go
1
package channeldb
2

3
import (
4
        "bytes"
5
        "errors"
6
        "io"
7
        "math"
8

9
        "github.com/btcsuite/btcd/btcutil"
10
        "github.com/lightningnetwork/lnd/fn/v2"
11
        "github.com/lightningnetwork/lnd/kvdb"
12
        "github.com/lightningnetwork/lnd/lntypes"
13
        "github.com/lightningnetwork/lnd/lnwire"
14
        "github.com/lightningnetwork/lnd/tlv"
15
)
16

17
const (
18
        // OutputIndexEmpty is used when the output index doesn't exist.
19
        OutputIndexEmpty = math.MaxUint16
20
)
21

22
type (
23
        // BigSizeAmount is a type alias for a TLV record of a btcutil.Amount.
24
        BigSizeAmount = tlv.BigSizeT[btcutil.Amount]
25

26
        // BigSizeMilliSatoshi is a type alias for a TLV record of a
27
        // lnwire.MilliSatoshi.
28
        BigSizeMilliSatoshi = tlv.BigSizeT[lnwire.MilliSatoshi]
29
)
30

31
var (
32
        // revocationLogBucketDeprecated is dedicated for storing the necessary
33
        // delta state between channel updates required to re-construct a past
34
        // state in order to punish a counterparty attempting a non-cooperative
35
        // channel closure. This key should be accessed from within the
36
        // sub-bucket of a target channel, identified by its channel point.
37
        //
38
        // Deprecated: This bucket is kept for read-only in case the user
39
        // choose not to migrate the old data.
40
        revocationLogBucketDeprecated = []byte("revocation-log-key")
41

42
        // revocationLogBucket is a sub-bucket under openChannelBucket. This
43
        // sub-bucket is dedicated for storing the minimal info required to
44
        // re-construct a past state in order to punish a counterparty
45
        // attempting a non-cooperative channel closure.
46
        revocationLogBucket = []byte("revocation-log")
47

48
        // ErrLogEntryNotFound is returned when we cannot find a log entry at
49
        // the height requested in the revocation log.
50
        ErrLogEntryNotFound = errors.New("log entry not found")
51

52
        // ErrOutputIndexTooBig is returned when the output index is greater
53
        // than uint16.
54
        ErrOutputIndexTooBig = errors.New("output index is over uint16")
55
)
56

57
// SparsePayHash is a type alias for a 32 byte array, which when serialized is
58
// able to save some space by not including an empty payment hash on disk.
59
type SparsePayHash [32]byte
60

61
// NewSparsePayHash creates a new SparsePayHash from a 32 byte array.
62
func NewSparsePayHash(rHash [32]byte) SparsePayHash {
3✔
63
        return SparsePayHash(rHash)
3✔
64
}
3✔
65

66
// Record returns a tlv record for the SparsePayHash.
67
func (s *SparsePayHash) Record() tlv.Record {
3✔
68
        // We use a zero for the type here, as this'll be used along with the
3✔
69
        // RecordT type.
3✔
70
        return tlv.MakeDynamicRecord(
3✔
71
                0, s, s.hashLen,
3✔
72
                sparseHashEncoder, sparseHashDecoder,
3✔
73
        )
3✔
74
}
3✔
75

76
// hashLen is used by MakeDynamicRecord to return the size of the RHash.
77
//
78
// NOTE: for zero hash, we return a length 0.
79
func (s *SparsePayHash) hashLen() uint64 {
3✔
80
        if bytes.Equal(s[:], lntypes.ZeroHash[:]) {
3✔
81
                return 0
×
82
        }
×
83

84
        return 32
3✔
85
}
86

87
// sparseHashEncoder is the customized encoder which skips encoding the empty
88
// hash.
89
func sparseHashEncoder(w io.Writer, val interface{}, buf *[8]byte) error {
3✔
90
        v, ok := val.(*SparsePayHash)
3✔
91
        if !ok {
3✔
92
                return tlv.NewTypeForEncodingErr(val, "SparsePayHash")
×
93
        }
×
94

95
        // If the value is an empty hash, we will skip encoding it.
96
        if bytes.Equal(v[:], lntypes.ZeroHash[:]) {
3✔
97
                return nil
×
98
        }
×
99

100
        vArray := (*[32]byte)(v)
3✔
101

3✔
102
        return tlv.EBytes32(w, vArray, buf)
3✔
103
}
104

105
// sparseHashDecoder is the customized decoder which skips decoding the empty
106
// hash.
107
func sparseHashDecoder(r io.Reader, val interface{}, buf *[8]byte,
108
        l uint64) error {
3✔
109

3✔
110
        v, ok := val.(*SparsePayHash)
3✔
111
        if !ok {
3✔
112
                return tlv.NewTypeForEncodingErr(val, "SparsePayHash")
×
113
        }
×
114

115
        // If the length is zero, we will skip encoding the empty hash.
116
        if l == 0 {
3✔
117
                return nil
×
118
        }
×
119

120
        vArray := (*[32]byte)(v)
3✔
121

3✔
122
        return tlv.DBytes32(r, vArray, buf, 32)
3✔
123
}
124

125
// HTLCEntry specifies the minimal info needed to be stored on disk for ALL the
126
// historical HTLCs, which is useful for constructing RevocationLog when a
127
// breach is detected.
128
// The actual size of each HTLCEntry varies based on its RHash and Amt(sat),
129
// summarized as follows,
130
//
131
//        | RHash empty | Amt<=252 | Amt<=65,535 | Amt<=4,294,967,295 | otherwise |
132
//        |:-----------:|:--------:|:-----------:|:------------------:|:---------:|
133
//        |     true    |    19    |      21     |         23         |     26    |
134
//        |     false   |    51    |      53     |         55         |     58    |
135
//
136
// So the size varies from 19 bytes to 58 bytes, where most likely to be 23 or
137
// 55 bytes.
138
//
139
// NOTE: all the fields saved to disk use the primitive go types so they can be
140
// made into tlv records without further conversion.
141
type HTLCEntry struct {
142
        // RHash is the payment hash of the HTLC.
143
        RHash tlv.RecordT[tlv.TlvType0, SparsePayHash]
144

145
        // RefundTimeout is the absolute timeout on the HTLC that the sender
146
        // must wait before reclaiming the funds in limbo.
147
        RefundTimeout tlv.RecordT[tlv.TlvType1, uint32]
148

149
        // OutputIndex is the output index for this particular HTLC output
150
        // within the commitment transaction.
151
        //
152
        // NOTE: we use uint16 instead of int32 here to save us 2 bytes, which
153
        // gives us a max number of HTLCs of 65K.
154
        OutputIndex tlv.RecordT[tlv.TlvType2, uint16]
155

156
        // Incoming denotes whether we're the receiver or the sender of this
157
        // HTLC.
158
        Incoming tlv.RecordT[tlv.TlvType3, bool]
159

160
        // Amt is the amount of satoshis this HTLC escrows.
161
        Amt tlv.RecordT[tlv.TlvType4, tlv.BigSizeT[btcutil.Amount]]
162

163
        // CustomBlob is an optional blob that can be used to store information
164
        // specific to revocation handling for a custom channel type.
165
        CustomBlob tlv.OptionalRecordT[tlv.TlvType5, tlv.Blob]
166

167
        // HtlcIndex is the index of the HTLC in the channel.
168
        HtlcIndex tlv.OptionalRecordT[tlv.TlvType6, uint16]
169
}
170

171
// toTlvStream converts an HTLCEntry record into a tlv representation.
172
func (h *HTLCEntry) toTlvStream() (*tlv.Stream, error) {
3✔
173
        records := []tlv.Record{
3✔
174
                h.RHash.Record(),
3✔
175
                h.RefundTimeout.Record(),
3✔
176
                h.OutputIndex.Record(),
3✔
177
                h.Incoming.Record(),
3✔
178
                h.Amt.Record(),
3✔
179
        }
3✔
180

3✔
181
        h.CustomBlob.WhenSome(func(r tlv.RecordT[tlv.TlvType5, tlv.Blob]) {
6✔
182
                records = append(records, r.Record())
3✔
183
        })
3✔
184

185
        h.HtlcIndex.WhenSome(func(r tlv.RecordT[tlv.TlvType6, uint16]) {
6✔
186
                records = append(records, r.Record())
3✔
187
        })
3✔
188

189
        tlv.SortRecords(records)
3✔
190

3✔
191
        return tlv.NewStream(records...)
3✔
192
}
193

194
// NewHTLCEntryFromHTLC creates a new HTLCEntry from an HTLC.
195
func NewHTLCEntryFromHTLC(htlc HTLC) (*HTLCEntry, error) {
3✔
196
        h := &HTLCEntry{
3✔
197
                RHash: tlv.NewRecordT[tlv.TlvType0](
3✔
198
                        NewSparsePayHash(htlc.RHash),
3✔
199
                ),
3✔
200
                RefundTimeout: tlv.NewPrimitiveRecord[tlv.TlvType1](
3✔
201
                        htlc.RefundTimeout,
3✔
202
                ),
3✔
203
                OutputIndex: tlv.NewPrimitiveRecord[tlv.TlvType2](
3✔
204
                        uint16(htlc.OutputIndex),
3✔
205
                ),
3✔
206
                Incoming: tlv.NewPrimitiveRecord[tlv.TlvType3](htlc.Incoming),
3✔
207
                Amt: tlv.NewRecordT[tlv.TlvType4](
3✔
208
                        tlv.NewBigSizeT(htlc.Amt.ToSatoshis()),
3✔
209
                ),
3✔
210
                HtlcIndex: tlv.SomeRecordT(tlv.NewPrimitiveRecord[tlv.TlvType6](
3✔
211
                        uint16(htlc.HtlcIndex),
3✔
212
                )),
3✔
213
        }
3✔
214

3✔
215
        if len(htlc.CustomRecords) != 0 {
6✔
216
                blob, err := htlc.CustomRecords.Serialize()
3✔
217
                if err != nil {
3✔
218
                        return nil, err
×
219
                }
×
220

221
                h.CustomBlob = tlv.SomeRecordT(
3✔
222
                        tlv.NewPrimitiveRecord[tlv.TlvType5, tlv.Blob](blob),
3✔
223
                )
3✔
224
        }
225

226
        return h, nil
3✔
227
}
228

229
// RevocationLog stores the info needed to construct a breach retribution. Its
230
// fields can be viewed as a subset of a ChannelCommitment's. In the database,
231
// all historical versions of the RevocationLog are saved using the
232
// CommitHeight as the key.
233
type RevocationLog struct {
234
        // OurOutputIndex specifies our output index in this commitment. In a
235
        // remote commitment transaction, this is the to remote output index.
236
        OurOutputIndex tlv.RecordT[tlv.TlvType0, uint16]
237

238
        // TheirOutputIndex specifies their output index in this commitment. In
239
        // a remote commitment transaction, this is the to local output index.
240
        TheirOutputIndex tlv.RecordT[tlv.TlvType1, uint16]
241

242
        // CommitTxHash is the hash of the latest version of the commitment
243
        // state, broadcast able by us.
244
        CommitTxHash tlv.RecordT[tlv.TlvType2, [32]byte]
245

246
        // HTLCEntries is the set of HTLCEntry's that are pending at this
247
        // particular commitment height.
248
        HTLCEntries []*HTLCEntry
249

250
        // OurBalance is the current available balance within the channel
251
        // directly spendable by us. In other words, it is the value of the
252
        // to_remote output on the remote parties' commitment transaction.
253
        //
254
        // NOTE: this is an option so that it is clear if the value is zero or
255
        // nil. Since migration 30 of the channeldb initially did not include
256
        // this field, it could be the case that the field is not present for
257
        // all revocation logs.
258
        OurBalance tlv.OptionalRecordT[tlv.TlvType3, BigSizeMilliSatoshi]
259

260
        // TheirBalance is the current available balance within the channel
261
        // directly spendable by the remote node. In other words, it is the
262
        // value of the to_local output on the remote parties' commitment.
263
        //
264
        // NOTE: this is an option so that it is clear if the value is zero or
265
        // nil. Since migration 30 of the channeldb initially did not include
266
        // this field, it could be the case that the field is not present for
267
        // all revocation logs.
268
        TheirBalance tlv.OptionalRecordT[tlv.TlvType4, BigSizeMilliSatoshi]
269

270
        // CustomBlob is an optional blob that can be used to store information
271
        // specific to a custom channel type. This information is only created
272
        // at channel funding time, and after wards is to be considered
273
        // immutable.
274
        CustomBlob tlv.OptionalRecordT[tlv.TlvType5, tlv.Blob]
275
}
276

277
// NewRevocationLog creates a new RevocationLog from the given parameters.
278
func NewRevocationLog(ourOutputIndex uint16, theirOutputIndex uint16,
279
        commitHash [32]byte, ourBalance,
280
        theirBalance fn.Option[lnwire.MilliSatoshi], htlcs []*HTLCEntry,
281
        customBlob fn.Option[tlv.Blob]) RevocationLog {
×
282

×
283
        rl := RevocationLog{
×
284
                OurOutputIndex: tlv.NewPrimitiveRecord[tlv.TlvType0](
×
285
                        ourOutputIndex,
×
286
                ),
×
287
                TheirOutputIndex: tlv.NewPrimitiveRecord[tlv.TlvType1](
×
288
                        theirOutputIndex,
×
289
                ),
×
290
                CommitTxHash: tlv.NewPrimitiveRecord[tlv.TlvType2](commitHash),
×
291
                HTLCEntries:  htlcs,
×
292
        }
×
293

×
294
        ourBalance.WhenSome(func(balance lnwire.MilliSatoshi) {
×
295
                rl.OurBalance = tlv.SomeRecordT(tlv.NewRecordT[tlv.TlvType3](
×
296
                        tlv.NewBigSizeT(balance),
×
297
                ))
×
298
        })
×
299

300
        theirBalance.WhenSome(func(balance lnwire.MilliSatoshi) {
×
301
                rl.TheirBalance = tlv.SomeRecordT(tlv.NewRecordT[tlv.TlvType4](
×
302
                        tlv.NewBigSizeT(balance),
×
303
                ))
×
304
        })
×
305

306
        customBlob.WhenSome(func(blob tlv.Blob) {
×
307
                rl.CustomBlob = tlv.SomeRecordT(
×
308
                        tlv.NewPrimitiveRecord[tlv.TlvType5, tlv.Blob](blob),
×
309
                )
×
310
        })
×
311

312
        return rl
×
313
}
314

315
// putRevocationLog uses the fields `CommitTx` and `Htlcs` from a
316
// ChannelCommitment to construct a revocation log entry and saves them to
317
// disk. It also saves our output index and their output index, which are
318
// useful when creating breach retribution.
319
func putRevocationLog(bucket kvdb.RwBucket, commit *ChannelCommitment,
320
        ourOutputIndex, theirOutputIndex uint32, noAmtData bool) error {
3✔
321

3✔
322
        // Sanity check that the output indexes can be safely converted.
3✔
323
        if ourOutputIndex > math.MaxUint16 {
3✔
324
                return ErrOutputIndexTooBig
×
325
        }
×
326
        if theirOutputIndex > math.MaxUint16 {
3✔
327
                return ErrOutputIndexTooBig
×
328
        }
×
329

330
        rl := &RevocationLog{
3✔
331
                OurOutputIndex: tlv.NewPrimitiveRecord[tlv.TlvType0](
3✔
332
                        uint16(ourOutputIndex),
3✔
333
                ),
3✔
334
                TheirOutputIndex: tlv.NewPrimitiveRecord[tlv.TlvType1](
3✔
335
                        uint16(theirOutputIndex),
3✔
336
                ),
3✔
337
                CommitTxHash: tlv.NewPrimitiveRecord[tlv.TlvType2, [32]byte](
3✔
338
                        commit.CommitTx.TxHash(),
3✔
339
                ),
3✔
340
                HTLCEntries: make([]*HTLCEntry, 0, len(commit.Htlcs)),
3✔
341
        }
3✔
342

3✔
343
        commit.CustomBlob.WhenSome(func(blob tlv.Blob) {
3✔
344
                rl.CustomBlob = tlv.SomeRecordT(
×
345
                        tlv.NewPrimitiveRecord[tlv.TlvType5, tlv.Blob](blob),
×
346
                )
×
347
        })
×
348

349
        if !noAmtData {
6✔
350
                rl.OurBalance = tlv.SomeRecordT(tlv.NewRecordT[tlv.TlvType3](
3✔
351
                        tlv.NewBigSizeT(commit.LocalBalance),
3✔
352
                ))
3✔
353

3✔
354
                rl.TheirBalance = tlv.SomeRecordT(tlv.NewRecordT[tlv.TlvType4](
3✔
355
                        tlv.NewBigSizeT(commit.RemoteBalance),
3✔
356
                ))
3✔
357
        }
3✔
358

359
        for _, htlc := range commit.Htlcs {
6✔
360
                // Skip dust HTLCs.
3✔
361
                if htlc.OutputIndex < 0 {
6✔
362
                        continue
3✔
363
                }
364

365
                // Sanity check that the output indexes can be safely
366
                // converted.
367
                if htlc.OutputIndex > math.MaxUint16 {
3✔
368
                        return ErrOutputIndexTooBig
×
369
                }
×
370

371
                entry, err := NewHTLCEntryFromHTLC(htlc)
3✔
372
                if err != nil {
3✔
373
                        return err
×
374
                }
×
375
                rl.HTLCEntries = append(rl.HTLCEntries, entry)
3✔
376
        }
377

378
        var b bytes.Buffer
3✔
379
        err := serializeRevocationLog(&b, rl)
3✔
380
        if err != nil {
3✔
381
                return err
×
382
        }
×
383

384
        logEntrykey := makeLogKey(commit.CommitHeight)
3✔
385
        return bucket.Put(logEntrykey[:], b.Bytes())
3✔
386
}
387

388
// fetchRevocationLog queries the revocation log bucket to find an log entry.
389
// Return an error if not found.
390
func fetchRevocationLog(log kvdb.RBucket,
391
        updateNum uint64) (RevocationLog, error) {
3✔
392

3✔
393
        logEntrykey := makeLogKey(updateNum)
3✔
394
        commitBytes := log.Get(logEntrykey[:])
3✔
395
        if commitBytes == nil {
6✔
396
                return RevocationLog{}, ErrLogEntryNotFound
3✔
397
        }
3✔
398

399
        commitReader := bytes.NewReader(commitBytes)
3✔
400

3✔
401
        return deserializeRevocationLog(commitReader)
3✔
402
}
403

404
// serializeRevocationLog serializes a RevocationLog record based on tlv
405
// format.
406
func serializeRevocationLog(w io.Writer, rl *RevocationLog) error {
3✔
407
        // Add the tlv records for all non-optional fields.
3✔
408
        records := []tlv.Record{
3✔
409
                rl.OurOutputIndex.Record(),
3✔
410
                rl.TheirOutputIndex.Record(),
3✔
411
                rl.CommitTxHash.Record(),
3✔
412
        }
3✔
413

3✔
414
        // Now we add any optional fields that are non-nil.
3✔
415
        rl.OurBalance.WhenSome(
3✔
416
                func(r tlv.RecordT[tlv.TlvType3, BigSizeMilliSatoshi]) {
6✔
417
                        records = append(records, r.Record())
3✔
418
                },
3✔
419
        )
420

421
        rl.TheirBalance.WhenSome(
3✔
422
                func(r tlv.RecordT[tlv.TlvType4, BigSizeMilliSatoshi]) {
6✔
423
                        records = append(records, r.Record())
3✔
424
                },
3✔
425
        )
426

427
        rl.CustomBlob.WhenSome(func(r tlv.RecordT[tlv.TlvType5, tlv.Blob]) {
3✔
428
                records = append(records, r.Record())
×
429
        })
×
430

431
        // Create the tlv stream.
432
        tlvStream, err := tlv.NewStream(records...)
3✔
433
        if err != nil {
3✔
434
                return err
×
435
        }
×
436

437
        // Write the tlv stream.
438
        if err := writeTlvStream(w, tlvStream); err != nil {
3✔
439
                return err
×
440
        }
×
441

442
        // Write the HTLCs.
443
        return serializeHTLCEntries(w, rl.HTLCEntries)
3✔
444
}
445

446
// serializeHTLCEntries serializes a list of HTLCEntry records based on tlv
447
// format.
448
func serializeHTLCEntries(w io.Writer, htlcs []*HTLCEntry) error {
3✔
449
        for _, htlc := range htlcs {
6✔
450
                // Create the tlv stream.
3✔
451
                tlvStream, err := htlc.toTlvStream()
3✔
452
                if err != nil {
3✔
453
                        return err
×
454
                }
×
455

456
                // Write the tlv stream.
457
                if err := writeTlvStream(w, tlvStream); err != nil {
3✔
458
                        return err
×
459
                }
×
460
        }
461

462
        return nil
3✔
463
}
464

465
// deserializeRevocationLog deserializes a RevocationLog based on tlv format.
466
func deserializeRevocationLog(r io.Reader) (RevocationLog, error) {
3✔
467
        var rl RevocationLog
3✔
468

3✔
469
        ourBalance := rl.OurBalance.Zero()
3✔
470
        theirBalance := rl.TheirBalance.Zero()
3✔
471
        customBlob := rl.CustomBlob.Zero()
3✔
472

3✔
473
        // Create the tlv stream.
3✔
474
        tlvStream, err := tlv.NewStream(
3✔
475
                rl.OurOutputIndex.Record(),
3✔
476
                rl.TheirOutputIndex.Record(),
3✔
477
                rl.CommitTxHash.Record(),
3✔
478
                ourBalance.Record(),
3✔
479
                theirBalance.Record(),
3✔
480
                customBlob.Record(),
3✔
481
        )
3✔
482
        if err != nil {
3✔
483
                return rl, err
×
484
        }
×
485

486
        // Read the tlv stream.
487
        parsedTypes, err := readTlvStream(r, tlvStream)
3✔
488
        if err != nil {
3✔
489
                return rl, err
×
490
        }
×
491

492
        if t, ok := parsedTypes[ourBalance.TlvType()]; ok && t == nil {
6✔
493
                rl.OurBalance = tlv.SomeRecordT(ourBalance)
3✔
494
        }
3✔
495

496
        if t, ok := parsedTypes[theirBalance.TlvType()]; ok && t == nil {
6✔
497
                rl.TheirBalance = tlv.SomeRecordT(theirBalance)
3✔
498
        }
3✔
499

500
        if t, ok := parsedTypes[customBlob.TlvType()]; ok && t == nil {
3✔
501
                rl.CustomBlob = tlv.SomeRecordT(customBlob)
×
502
        }
×
503

504
        // Read the HTLC entries.
505
        rl.HTLCEntries, err = deserializeHTLCEntries(r)
3✔
506

3✔
507
        return rl, err
3✔
508
}
509

510
// deserializeHTLCEntries deserializes a list of HTLC entries based on tlv
511
// format.
512
func deserializeHTLCEntries(r io.Reader) ([]*HTLCEntry, error) {
3✔
513
        var htlcs []*HTLCEntry
3✔
514

3✔
515
        for {
6✔
516
                var htlc HTLCEntry
3✔
517

3✔
518
                customBlob := htlc.CustomBlob.Zero()
3✔
519
                htlcIndex := htlc.HtlcIndex.Zero()
3✔
520

3✔
521
                // Create the tlv stream.
3✔
522
                records := []tlv.Record{
3✔
523
                        htlc.RHash.Record(),
3✔
524
                        htlc.RefundTimeout.Record(),
3✔
525
                        htlc.OutputIndex.Record(),
3✔
526
                        htlc.Incoming.Record(),
3✔
527
                        htlc.Amt.Record(),
3✔
528
                        customBlob.Record(),
3✔
529
                        htlcIndex.Record(),
3✔
530
                }
3✔
531

3✔
532
                tlvStream, err := tlv.NewStream(records...)
3✔
533
                if err != nil {
3✔
534
                        return nil, err
×
535
                }
×
536

537
                // Read the HTLC entry.
538
                parsedTypes, err := readTlvStream(r, tlvStream)
3✔
539
                if err != nil {
6✔
540
                        // We've reached the end when hitting an EOF.
3✔
541
                        if err == io.ErrUnexpectedEOF {
6✔
542
                                break
3✔
543
                        }
544
                        return nil, err
×
545
                }
546

547
                if t, ok := parsedTypes[customBlob.TlvType()]; ok && t == nil {
6✔
548
                        htlc.CustomBlob = tlv.SomeRecordT(customBlob)
3✔
549
                }
3✔
550

551
                if t, ok := parsedTypes[htlcIndex.TlvType()]; ok && t == nil {
6✔
552
                        htlc.HtlcIndex = tlv.SomeRecordT(htlcIndex)
3✔
553
                }
3✔
554

555
                // Append the entry.
556
                htlcs = append(htlcs, &htlc)
3✔
557
        }
558

559
        return htlcs, nil
3✔
560
}
561

562
// writeTlvStream is a helper function that encodes the tlv stream into the
563
// writer.
564
func writeTlvStream(w io.Writer, s *tlv.Stream) error {
3✔
565
        var b bytes.Buffer
3✔
566
        if err := s.Encode(&b); err != nil {
3✔
567
                return err
×
568
        }
×
569

570
        // Write the stream's length as a varint.
571
        err := tlv.WriteVarInt(w, uint64(b.Len()), &[8]byte{})
3✔
572
        if err != nil {
3✔
573
                return err
×
574
        }
×
575

576
        if _, err = w.Write(b.Bytes()); err != nil {
3✔
577
                return err
×
578
        }
×
579

580
        return nil
3✔
581
}
582

583
// readTlvStream is a helper function that decodes the tlv stream from the
584
// reader.
585
func readTlvStream(r io.Reader, s *tlv.Stream) (tlv.TypeMap, error) {
3✔
586
        var bodyLen uint64
3✔
587

3✔
588
        // Read the stream's length.
3✔
589
        bodyLen, err := tlv.ReadVarInt(r, &[8]byte{})
3✔
590
        switch {
3✔
591
        // We'll convert any EOFs to ErrUnexpectedEOF, since this results in an
592
        // invalid record.
593
        case err == io.EOF:
3✔
594
                return nil, io.ErrUnexpectedEOF
3✔
595

596
        // Other unexpected errors.
597
        case err != nil:
×
598
                return nil, err
×
599
        }
600

601
        // TODO(yy): add overflow check.
602
        lr := io.LimitReader(r, int64(bodyLen))
3✔
603

3✔
604
        return s.DecodeWithParsedTypes(lr)
3✔
605
}
606

607
// fetchOldRevocationLog finds the revocation log from the deprecated
608
// sub-bucket.
609
func fetchOldRevocationLog(log kvdb.RBucket,
610
        updateNum uint64) (ChannelCommitment, error) {
×
611

×
612
        logEntrykey := makeLogKey(updateNum)
×
613
        commitBytes := log.Get(logEntrykey[:])
×
614
        if commitBytes == nil {
×
615
                return ChannelCommitment{}, ErrLogEntryNotFound
×
616
        }
×
617

618
        commitReader := bytes.NewReader(commitBytes)
×
619
        return deserializeChanCommit(commitReader)
×
620
}
621

622
// fetchRevocationLogCompatible finds the revocation log from both the
623
// revocationLogBucket and revocationLogBucketDeprecated for compatibility
624
// concern. It returns three values,
625
//   - RevocationLog, if this is non-nil, it means we've found the log in the
626
//     new bucket.
627
//   - ChannelCommitment, if this is non-nil, it means we've found the log in the
628
//     old bucket.
629
//   - error, this can happen if the log cannot be found in neither buckets.
630
func fetchRevocationLogCompatible(chanBucket kvdb.RBucket,
631
        updateNum uint64) (*RevocationLog, *ChannelCommitment, error) {
3✔
632

3✔
633
        // Look into the new bucket first.
3✔
634
        logBucket := chanBucket.NestedReadBucket(revocationLogBucket)
3✔
635
        if logBucket != nil {
6✔
636
                rl, err := fetchRevocationLog(logBucket, updateNum)
3✔
637
                // We've found the record, no need to visit the old bucket.
3✔
638
                if err == nil {
6✔
639
                        return &rl, nil, nil
3✔
640
                }
3✔
641

642
                // Return the error if it doesn't say the log cannot be found.
643
                if err != ErrLogEntryNotFound {
3✔
644
                        return nil, nil, err
×
645
                }
×
646
        }
647

648
        // Otherwise, look into the old bucket and try to find the log there.
649
        oldBucket := chanBucket.NestedReadBucket(revocationLogBucketDeprecated)
3✔
650
        if oldBucket != nil {
3✔
651
                c, err := fetchOldRevocationLog(oldBucket, updateNum)
×
652
                if err != nil {
×
653
                        return nil, nil, err
×
654
                }
×
655

656
                // Found an old record and return it.
657
                return nil, &c, nil
×
658
        }
659

660
        // If both the buckets are nil, then the sub-buckets haven't been
661
        // created yet.
662
        if logBucket == nil && oldBucket == nil {
6✔
663
                return nil, nil, ErrNoPastDeltas
3✔
664
        }
3✔
665

666
        // Otherwise, we've tried to query the new bucket but the log cannot be
667
        // found.
668
        return nil, nil, ErrLogEntryNotFound
3✔
669
}
670

671
// fetchLogBucket returns a read bucket by visiting both the new and the old
672
// bucket.
673
func fetchLogBucket(chanBucket kvdb.RBucket) (kvdb.RBucket, error) {
×
674
        logBucket := chanBucket.NestedReadBucket(revocationLogBucket)
×
675
        if logBucket == nil {
×
676
                logBucket = chanBucket.NestedReadBucket(
×
677
                        revocationLogBucketDeprecated,
×
678
                )
×
679
                if logBucket == nil {
×
680
                        return nil, ErrNoPastDeltas
×
681
                }
×
682
        }
683

684
        return logBucket, nil
×
685
}
686

687
// deleteLogBucket deletes the both the new and old revocation log buckets.
688
func deleteLogBucket(chanBucket kvdb.RwBucket) error {
3✔
689
        // Check if the bucket exists and delete it.
3✔
690
        logBucket := chanBucket.NestedReadWriteBucket(
3✔
691
                revocationLogBucket,
3✔
692
        )
3✔
693
        if logBucket != nil {
6✔
694
                err := chanBucket.DeleteNestedBucket(revocationLogBucket)
3✔
695
                if err != nil {
3✔
696
                        return err
×
697
                }
×
698
        }
699

700
        // We also check whether the old revocation log bucket exists
701
        // and delete it if so.
702
        oldLogBucket := chanBucket.NestedReadWriteBucket(
3✔
703
                revocationLogBucketDeprecated,
3✔
704
        )
3✔
705
        if oldLogBucket != nil {
3✔
706
                err := chanBucket.DeleteNestedBucket(
×
707
                        revocationLogBucketDeprecated,
×
708
                )
×
709
                if err != nil {
×
710
                        return err
×
711
                }
×
712
        }
713

714
        return nil
3✔
715
}
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