/ channeldb / revocation_log_test.go
revocation_log_test.go
  1  package channeldb
  2  
  3  import (
  4  	"bytes"
  5  	"encoding/binary"
  6  	"io"
  7  	"math"
  8  	"math/rand"
  9  	"testing"
 10  
 11  	"github.com/btcsuite/btcd/btcutil"
 12  	"github.com/lightningnetwork/lnd/fn/v2"
 13  	"github.com/lightningnetwork/lnd/kvdb"
 14  	"github.com/lightningnetwork/lnd/lntest/channels"
 15  	"github.com/lightningnetwork/lnd/lnwire"
 16  	"github.com/lightningnetwork/lnd/tlv"
 17  	"github.com/stretchr/testify/require"
 18  )
 19  
 20  const (
 21  	// testType is used for creating a testing tlv record type. We use 10
 22  	// here so it's easier to be recognized by its hex value, 0xa.
 23  	testType tlv.Type = 10
 24  )
 25  
 26  var (
 27  	// testValue is used for creating tlv record.
 28  	testValue = uint8(255) // 0xff
 29  
 30  	// testValueBytes is the tlv encoded testValue.
 31  	testValueBytes = []byte{
 32  		0x3,  // total length = 3
 33  		0xa,  // type = 10
 34  		0x1,  // length = 1
 35  		0xff, // value = 255
 36  	}
 37  
 38  	customRecords = lnwire.CustomRecords{
 39  		lnwire.MinCustomRecordsTlvType + 1: []byte("custom data"),
 40  	}
 41  
 42  	blobBytes = []byte{
 43  		// Corresponds to the encoded version of the above custom
 44  		// records.
 45  		0xfe, 0x00, 0x01, 0x00, 0x01, 0x0b, 0x63, 0x75, 0x73, 0x74,
 46  		0x6f, 0x6d, 0x20, 0x64, 0x61, 0x74, 0x61,
 47  	}
 48  
 49  	testHTLCEntry = HTLCEntry{
 50  		RefundTimeout: tlv.NewPrimitiveRecord[tlv.TlvType1, uint32](
 51  			740_000,
 52  		),
 53  		OutputIndex: tlv.NewPrimitiveRecord[tlv.TlvType2, uint16](
 54  			10,
 55  		),
 56  		Incoming: tlv.NewPrimitiveRecord[tlv.TlvType3](true),
 57  		Amt: tlv.NewRecordT[tlv.TlvType4](
 58  			tlv.NewBigSizeT(btcutil.Amount(1_000_000)),
 59  		),
 60  		CustomBlob: tlv.SomeRecordT(
 61  			tlv.NewPrimitiveRecord[tlv.TlvType5](blobBytes),
 62  		),
 63  		HtlcIndex: tlv.SomeRecordT(tlv.NewRecordT[tlv.TlvType6](
 64  			tlv.NewBigSizeT(uint64(0x33)),
 65  		)),
 66  	}
 67  	testHTLCEntryBytes = []byte{
 68  		// Body length 44.
 69  		0x2c,
 70  		// Rhash tlv.
 71  		0x0, 0x0,
 72  		// RefundTimeout tlv.
 73  		0x1, 0x4, 0x0, 0xb, 0x4a, 0xa0,
 74  		// OutputIndex tlv.
 75  		0x2, 0x2, 0x0, 0xa,
 76  		// Incoming tlv.
 77  		0x3, 0x1, 0x1,
 78  		// Amt tlv.
 79  		0x4, 0x5, 0xfe, 0x0, 0xf, 0x42, 0x40,
 80  		// Custom blob tlv.
 81  		0x5, 0x11, 0xfe, 0x00, 0x01, 0x00, 0x01, 0x0b, 0x63, 0x75, 0x73,
 82  		0x74, 0x6f, 0x6d, 0x20, 0x64, 0x61, 0x74, 0x61,
 83  		// HTLC index tlv.
 84  		0x6, 0x1, 0x33,
 85  	}
 86  
 87  	testHTLCEntryHash = HTLCEntry{
 88  		RHash: tlv.NewPrimitiveRecord[tlv.TlvType0](NewSparsePayHash(
 89  			[32]byte{0x33, 0x44, 0x55},
 90  		)),
 91  		RefundTimeout: tlv.NewPrimitiveRecord[tlv.TlvType1, uint32](
 92  			740_000,
 93  		),
 94  		OutputIndex: tlv.NewPrimitiveRecord[tlv.TlvType2, uint16](
 95  			10,
 96  		),
 97  		Incoming: tlv.NewPrimitiveRecord[tlv.TlvType3](true),
 98  		Amt: tlv.NewRecordT[tlv.TlvType4](
 99  			tlv.NewBigSizeT(btcutil.Amount(1_000_000)),
100  		),
101  	}
102  	testHTLCEntryHashBytes = []byte{
103  		// Body length 54.
104  		0x36,
105  		// Rhash tlv.
106  		0x0, 0x20,
107  		0x33, 0x44, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00,
108  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
109  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
111  		// RefundTimeout tlv.
112  		0x1, 0x4, 0x0, 0xb, 0x4a, 0xa0,
113  		// OutputIndex tlv.
114  		0x2, 0x2, 0x0, 0xa,
115  		// Incoming tlv.
116  		0x3, 0x1, 0x1,
117  		// Amt tlv.
118  		0x4, 0x5, 0xfe, 0x0, 0xf, 0x42, 0x40,
119  	}
120  
121  	localBalance  = lnwire.MilliSatoshi(9000)
122  	remoteBalance = lnwire.MilliSatoshi(3000)
123  
124  	testChannelCommit = ChannelCommitment{
125  		CommitHeight:  999,
126  		LocalBalance:  localBalance,
127  		RemoteBalance: remoteBalance,
128  		CommitFee:     btcutil.Amount(rand.Int63()),
129  		FeePerKw:      btcutil.Amount(5000),
130  		CommitTx:      channels.TestFundingTx,
131  		CommitSig:     bytes.Repeat([]byte{1}, 71),
132  		Htlcs: []HTLC{{
133  			RefundTimeout: testHTLCEntry.RefundTimeout.Val,
134  			OutputIndex:   int32(testHTLCEntry.OutputIndex.Val),
135  			HtlcIndex: uint64(
136  				testHTLCEntry.HtlcIndex.ValOpt().
137  					UnsafeFromSome().Int(),
138  			),
139  			Incoming: testHTLCEntry.Incoming.Val,
140  			Amt: lnwire.NewMSatFromSatoshis(
141  				testHTLCEntry.Amt.Val.Int(),
142  			),
143  			CustomRecords: customRecords,
144  		}},
145  		CustomBlob: fn.Some(blobBytes),
146  	}
147  
148  	testRevocationLogNoAmts = NewRevocationLog(
149  		0, 1, testChannelCommit.CommitTx.TxHash(),
150  		fn.None[lnwire.MilliSatoshi](), fn.None[lnwire.MilliSatoshi](),
151  		[]*HTLCEntry{&testHTLCEntry}, fn.Some(blobBytes),
152  	)
153  	testRevocationLogNoAmtsBytes = []byte{
154  		// Body length 61.
155  		0x3d,
156  		// OurOutputIndex tlv.
157  		0x0, 0x2, 0x0, 0x0,
158  		// TheirOutputIndex tlv.
159  		0x1, 0x2, 0x0, 0x1,
160  		// CommitTxHash tlv.
161  		0x2, 0x20,
162  		0x28, 0x76, 0x2, 0x59, 0x1d, 0x9d, 0x64, 0x86,
163  		0x6e, 0x60, 0x29, 0x23, 0x1d, 0x5e, 0xc5, 0xe6,
164  		0xbd, 0xf7, 0xd3, 0x9b, 0x16, 0x7d, 0x0, 0xff,
165  		0xc8, 0x22, 0x51, 0xb1, 0x5b, 0xa0, 0xbf, 0xd,
166  		// Custom blob tlv.
167  		0x5, 0x11, 0xfe, 0x00, 0x01, 0x00, 0x01, 0x0b, 0x63, 0x75, 0x73,
168  		0x74, 0x6f, 0x6d, 0x20, 0x64, 0x61, 0x74, 0x61,
169  	}
170  
171  	testRevocationLogWithAmts = NewRevocationLog(
172  		0, 1, testChannelCommit.CommitTx.TxHash(),
173  		fn.Some(localBalance), fn.Some(remoteBalance),
174  		[]*HTLCEntry{&testHTLCEntry}, fn.Some(blobBytes),
175  	)
176  	testRevocationLogWithAmtsBytes = []byte{
177  		// Body length 71.
178  		0x47,
179  		// OurOutputIndex tlv.
180  		0x0, 0x2, 0x0, 0x0,
181  		// TheirOutputIndex tlv.
182  		0x1, 0x2, 0x0, 0x1,
183  		// CommitTxHash tlv.
184  		0x2, 0x20,
185  		0x28, 0x76, 0x2, 0x59, 0x1d, 0x9d, 0x64, 0x86,
186  		0x6e, 0x60, 0x29, 0x23, 0x1d, 0x5e, 0xc5, 0xe6,
187  		0xbd, 0xf7, 0xd3, 0x9b, 0x16, 0x7d, 0x0, 0xff,
188  		0xc8, 0x22, 0x51, 0xb1, 0x5b, 0xa0, 0xbf, 0xd,
189  		// OurBalance.
190  		0x3, 0x3, 0xfd, 0x23, 0x28,
191  		// Remote Balance.
192  		0x4, 0x3, 0xfd, 0x0b, 0xb8,
193  		// Custom blob tlv.
194  		0x5, 0x11, 0xfe, 0x00, 0x01, 0x00, 0x01, 0x0b, 0x63, 0x75, 0x73,
195  		0x74, 0x6f, 0x6d, 0x20, 0x64, 0x61, 0x74, 0x61,
196  	}
197  )
198  
199  func TestWriteTLVStream(t *testing.T) {
200  	t.Parallel()
201  
202  	// Create a dummy tlv stream for testing.
203  	ts, err := tlv.NewStream(
204  		tlv.MakePrimitiveRecord(testType, &testValue),
205  	)
206  	require.NoError(t, err)
207  
208  	// Write the tlv stream.
209  	buf := bytes.NewBuffer([]byte{})
210  	err = writeTlvStream(buf, ts)
211  	require.NoError(t, err)
212  
213  	// Check the bytes are written as expected.
214  	require.Equal(t, testValueBytes, buf.Bytes())
215  }
216  
217  func TestReadTLVStream(t *testing.T) {
218  	t.Parallel()
219  
220  	var valueRead uint8
221  
222  	// Create a dummy tlv stream for testing.
223  	ts, err := tlv.NewStream(
224  		tlv.MakePrimitiveRecord(testType, &valueRead),
225  	)
226  	require.NoError(t, err)
227  
228  	// Read the tlv stream.
229  	buf := bytes.NewBuffer(testValueBytes)
230  	_, err = readTlvStream(buf, ts)
231  	require.NoError(t, err)
232  
233  	// Check the bytes are read as expected.
234  	require.Equal(t, testValue, valueRead)
235  }
236  
237  func TestReadTLVStreamErr(t *testing.T) {
238  	t.Parallel()
239  
240  	var valueRead uint8
241  
242  	// Create a dummy tlv stream for testing.
243  	ts, err := tlv.NewStream(
244  		tlv.MakePrimitiveRecord(testType, &valueRead),
245  	)
246  	require.NoError(t, err)
247  
248  	// Use empty bytes to cause an EOF.
249  	b := []byte{}
250  
251  	// Read the tlv stream.
252  	buf := bytes.NewBuffer(b)
253  	_, err = readTlvStream(buf, ts)
254  	require.ErrorIs(t, err, io.ErrUnexpectedEOF)
255  
256  	// Check the bytes are not read.
257  	require.Zero(t, valueRead)
258  }
259  
260  func TestSerializeHTLCEntriesEmptyRHash(t *testing.T) {
261  	t.Parallel()
262  
263  	// Copy the testHTLCEntry.
264  	entry := testHTLCEntry
265  
266  	// Write the tlv stream.
267  	buf := bytes.NewBuffer([]byte{})
268  	err := serializeHTLCEntries(buf, []*HTLCEntry{&entry})
269  	require.NoError(t, err)
270  
271  	// Check the bytes are read as expected.
272  	require.Equal(t, testHTLCEntryBytes, buf.Bytes())
273  }
274  
275  func TestSerializeHTLCEntriesWithRHash(t *testing.T) {
276  	t.Parallel()
277  
278  	// Copy the testHTLCEntry.
279  	entry := testHTLCEntryHash
280  
281  	// Write the tlv stream.
282  	buf := bytes.NewBuffer([]byte{})
283  	err := serializeHTLCEntries(buf, []*HTLCEntry{&entry})
284  	require.NoError(t, err)
285  
286  	// Check the bytes are read as expected.
287  	require.Equal(t, testHTLCEntryHashBytes, buf.Bytes())
288  }
289  
290  func TestSerializeHTLCEntries(t *testing.T) {
291  	t.Parallel()
292  
293  	// Copy the testHTLCEntry.
294  	entry := testHTLCEntry
295  
296  	// Create a fake rHash.
297  	rHashBytes := bytes.Repeat([]byte{10}, 32)
298  	copy(entry.RHash.Val[:], rHashBytes)
299  
300  	// Construct the serialized bytes.
301  	//
302  	// Exclude the first 3 bytes, which are total length, RHash type and
303  	// RHash length(0).
304  	partialBytes := testHTLCEntryBytes[3:]
305  
306  	// Write the total length and RHash tlv.
307  	expectedBytes := []byte{0x4c, 0x0, 0x20}
308  	expectedBytes = append(expectedBytes, rHashBytes...)
309  
310  	// Append the rest.
311  	expectedBytes = append(expectedBytes, partialBytes...)
312  
313  	buf := bytes.NewBuffer([]byte{})
314  	err := serializeHTLCEntries(buf, []*HTLCEntry{&entry})
315  	require.NoError(t, err)
316  
317  	// Check the bytes are read as expected.
318  	require.Equal(t, expectedBytes, buf.Bytes())
319  }
320  
321  // TestSerializeAndDeserializeRevLog tests the serialization and deserialization
322  // of various forms of the revocation log.
323  func TestSerializeAndDeserializeRevLog(t *testing.T) {
324  	t.Parallel()
325  
326  	tests := []struct {
327  		name        string
328  		revLog      RevocationLog
329  		revLogBytes []byte
330  	}{
331  		{
332  			name:        "with no amount fields",
333  			revLog:      testRevocationLogNoAmts,
334  			revLogBytes: testRevocationLogNoAmtsBytes,
335  		},
336  		{
337  			name:        "with amount fields",
338  			revLog:      testRevocationLogWithAmts,
339  			revLogBytes: testRevocationLogWithAmtsBytes,
340  		},
341  	}
342  
343  	for _, test := range tests {
344  		test := test
345  		t.Run(test.name, func(t *testing.T) {
346  			t.Parallel()
347  
348  			testSerializeRevocationLog(
349  				t, &test.revLog, test.revLogBytes,
350  			)
351  
352  			testDeserializeRevocationLog(
353  				t, &test.revLog, test.revLogBytes,
354  			)
355  		})
356  	}
357  }
358  
359  func testSerializeRevocationLog(t *testing.T, rl *RevocationLog,
360  	revLogBytes []byte) {
361  
362  	// Copy the testRevocationLogWithAmts and testHTLCEntry.
363  	htlc := testHTLCEntry
364  	rl.HTLCEntries = []*HTLCEntry{&htlc}
365  
366  	// Write the tlv stream.
367  	buf := bytes.NewBuffer([]byte{})
368  	err := serializeRevocationLog(buf, rl)
369  	require.NoError(t, err)
370  
371  	// Check the expected bytes on the body of the revocation log.
372  	bodyIndex := buf.Len() - len(testHTLCEntryBytes)
373  	require.Equal(t, revLogBytes, buf.Bytes()[:bodyIndex])
374  }
375  
376  func testDeserializeRevocationLog(t *testing.T, revLog *RevocationLog,
377  	revLogBytes []byte) {
378  
379  	// Construct the full bytes.
380  	revLogBytes = append(revLogBytes, testHTLCEntryBytes...)
381  
382  	// Read the tlv stream.
383  	buf := bytes.NewBuffer(revLogBytes)
384  	rl, err := deserializeRevocationLog(buf)
385  	require.NoError(t, err)
386  
387  	// Check the bytes are read as expected.
388  	require.Len(t, rl.HTLCEntries, 1)
389  	require.Equal(t, *revLog, rl)
390  }
391  
392  func TestDeserializeHTLCEntriesEmptyRHash(t *testing.T) {
393  	t.Parallel()
394  
395  	// Read the tlv stream.
396  	buf := bytes.NewBuffer(testHTLCEntryBytes)
397  	htlcs, err := deserializeHTLCEntries(buf)
398  	require.NoError(t, err)
399  
400  	// Check the bytes are read as expected.
401  	require.Len(t, htlcs, 1)
402  	require.Equal(t, &testHTLCEntry, htlcs[0])
403  }
404  
405  func TestDeserializeHTLCEntries(t *testing.T) {
406  	t.Parallel()
407  
408  	// Copy the testHTLCEntry.
409  	entry := testHTLCEntry
410  
411  	// Create a fake rHash.
412  	rHashBytes := bytes.Repeat([]byte{10}, 32)
413  	copy(entry.RHash.Val[:], rHashBytes)
414  
415  	// Construct the serialized bytes.
416  	//
417  	// Exclude the first 3 bytes, which are total length, RHash type and
418  	// RHash length(0).
419  	partialBytes := testHTLCEntryBytes[3:]
420  
421  	// Write the total length and RHash tlv.
422  	testBytes := append([]byte{0x4d, 0x0, 0x20}, rHashBytes...)
423  
424  	// Append the rest.
425  	testBytes = append(testBytes, partialBytes...)
426  
427  	// Read the tlv stream.
428  	buf := bytes.NewBuffer(testBytes)
429  	htlcs, err := deserializeHTLCEntries(buf)
430  	require.NoError(t, err)
431  
432  	// Check the bytes are read as expected.
433  	require.Len(t, htlcs, 1)
434  	require.Equal(t, &entry, htlcs[0])
435  }
436  
437  func TestFetchLogBucket(t *testing.T) {
438  	t.Parallel()
439  
440  	fullDB, err := MakeTestDB(t)
441  	require.NoError(t, err)
442  
443  	backend := fullDB.ChannelStateDB().backend
444  
445  	// Test that when neither of the buckets exists, an error is returned.
446  	err = kvdb.Update(backend, func(tx kvdb.RwTx) error {
447  		chanBucket, err := tx.CreateTopLevelBucket(openChannelBucket)
448  		require.NoError(t, err)
449  
450  		// Check an error is returned when there's no sub bucket.
451  		_, err = fetchLogBucket(chanBucket)
452  		return err
453  	}, func() {})
454  	require.ErrorIs(t, err, ErrNoPastDeltas)
455  
456  	// Test a successful fetch.
457  	err = kvdb.Update(backend, func(tx kvdb.RwTx) error {
458  		chanBucket, err := tx.CreateTopLevelBucket(openChannelBucket)
459  		require.NoError(t, err)
460  
461  		_, err = chanBucket.CreateBucket(revocationLogBucket)
462  		require.NoError(t, err)
463  
464  		// Check an error is returned when there's no sub bucket.
465  		_, err = fetchLogBucket(chanBucket)
466  		return err
467  	}, func() {})
468  	require.NoError(t, err)
469  }
470  
471  func TestDeleteLogBucket(t *testing.T) {
472  	t.Parallel()
473  
474  	fullDB, err := MakeTestDB(t)
475  	require.NoError(t, err)
476  
477  	backend := fullDB.ChannelStateDB().backend
478  
479  	err = kvdb.Update(backend, func(tx kvdb.RwTx) error {
480  		// Create the buckets.
481  		chanBucket, _, err := createTestRevocationLogBuckets(tx)
482  		require.NoError(t, err)
483  
484  		// Create the buckets again should give us an error.
485  		_, _, err = createTestRevocationLogBuckets(tx)
486  		require.ErrorIs(t, err, kvdb.ErrBucketExists)
487  
488  		// Delete both buckets.
489  		err = deleteLogBucket(chanBucket)
490  		require.NoError(t, err)
491  
492  		// Create the buckets again should give us NO error.
493  		_, _, err = createTestRevocationLogBuckets(tx)
494  		return err
495  	}, func() {})
496  	require.NoError(t, err)
497  }
498  
499  func TestPutRevocationLog(t *testing.T) {
500  	t.Parallel()
501  
502  	// Create a test commit that has a large htlc output index.
503  	testHtlc := HTLC{OutputIndex: math.MaxUint16 + 1}
504  	testCommit := testChannelCommit
505  	testCommit.Htlcs = []HTLC{testHtlc}
506  
507  	// Create a test commit that has a dust HTLC.
508  	testHtlcDust := HTLC{OutputIndex: -1}
509  	testCommitDust := testChannelCommit
510  	testCommitDust.Htlcs = append(testCommitDust.Htlcs, testHtlcDust)
511  
512  	testCases := []struct {
513  		name        string
514  		commit      ChannelCommitment
515  		ourIndex    uint32
516  		theirIndex  uint32
517  		noAmtData   bool
518  		expectedErr error
519  		expectedLog RevocationLog
520  	}{
521  		{
522  			// Test a normal put operation.
523  			name:        "successful put with amount data",
524  			commit:      testChannelCommit,
525  			ourIndex:    0,
526  			theirIndex:  1,
527  			expectedErr: nil,
528  			expectedLog: testRevocationLogWithAmts,
529  		},
530  		{
531  			// Test a normal put operation.
532  			name:        "successful put with no amount data",
533  			commit:      testChannelCommit,
534  			ourIndex:    0,
535  			theirIndex:  1,
536  			noAmtData:   true,
537  			expectedErr: nil,
538  			expectedLog: testRevocationLogNoAmts,
539  		},
540  		{
541  			// Test our index too big.
542  			name:        "our index too big",
543  			commit:      testChannelCommit,
544  			ourIndex:    math.MaxUint16 + 1,
545  			theirIndex:  1,
546  			expectedErr: ErrOutputIndexTooBig,
547  			expectedLog: RevocationLog{},
548  		},
549  		{
550  			// Test their index too big.
551  			name:        "their index too big",
552  			commit:      testChannelCommit,
553  			ourIndex:    0,
554  			theirIndex:  math.MaxUint16 + 1,
555  			expectedErr: ErrOutputIndexTooBig,
556  			expectedLog: RevocationLog{},
557  		},
558  		{
559  			// Test htlc output index too big.
560  			name:        "htlc index too big",
561  			commit:      testCommit,
562  			ourIndex:    0,
563  			theirIndex:  1,
564  			expectedErr: ErrOutputIndexTooBig,
565  			expectedLog: RevocationLog{},
566  		},
567  		{
568  			// Test dust htlc is not saved.
569  			name:        "dust htlc not saved with amount data",
570  			commit:      testCommitDust,
571  			ourIndex:    0,
572  			theirIndex:  1,
573  			expectedErr: nil,
574  			expectedLog: testRevocationLogWithAmts,
575  		},
576  		{
577  			// Test dust htlc is not saved.
578  			name:        "dust htlc not saved with no amount data",
579  			commit:      testCommitDust,
580  			ourIndex:    0,
581  			theirIndex:  1,
582  			noAmtData:   true,
583  			expectedErr: nil,
584  			expectedLog: testRevocationLogNoAmts,
585  		},
586  	}
587  
588  	for _, tc := range testCases {
589  		tc := tc
590  
591  		fullDB, err := MakeTestDB(t)
592  		require.NoError(t, err)
593  
594  		backend := fullDB.ChannelStateDB().backend
595  
596  		// Construct the testing db transaction.
597  		dbTx := func(tx kvdb.RwTx) (RevocationLog, error) {
598  			// Create the buckets.
599  			_, bucket, err := createTestRevocationLogBuckets(tx)
600  			require.NoError(t, err)
601  
602  			// Save the log.
603  			err = putRevocationLog(
604  				bucket, &tc.commit, tc.ourIndex, tc.theirIndex,
605  				tc.noAmtData,
606  			)
607  			if err != nil {
608  				return RevocationLog{}, err
609  			}
610  
611  			// Read the saved log.
612  			return fetchRevocationLog(
613  				bucket, tc.commit.CommitHeight,
614  			)
615  		}
616  
617  		t.Run(tc.name, func(t *testing.T) {
618  			var rl RevocationLog
619  			err := kvdb.Update(backend, func(tx kvdb.RwTx) error {
620  				record, err := dbTx(tx)
621  				rl = record
622  				return err
623  			}, func() {})
624  
625  			require.Equal(t, tc.expectedErr, err)
626  			require.Equal(t, tc.expectedLog, rl)
627  		})
628  	}
629  }
630  
631  func TestFetchRevocationLogCompatible(t *testing.T) {
632  	t.Parallel()
633  
634  	knownHeight := testChannelCommit.CommitHeight
635  	unknownHeight := knownHeight + 1
636  	logKey := makeLogKey(knownHeight)
637  
638  	testCases := []struct {
639  		name         string
640  		updateNum    uint64
641  		expectedErr  error
642  		createRl     bool
643  		createCommit bool
644  		expectRl     bool
645  		expectCommit bool
646  	}{
647  		{
648  			// Test we can fetch the new log.
649  			name:        "fetch new log",
650  			updateNum:   knownHeight,
651  			expectedErr: nil,
652  			createRl:    true,
653  			expectRl:    true,
654  		},
655  		{
656  			// Test we can fetch the legacy log.
657  			name:         "fetch legacy log",
658  			updateNum:    knownHeight,
659  			expectedErr:  nil,
660  			createCommit: true,
661  			expectCommit: true,
662  		},
663  		{
664  			// Test we only fetch the new log when both logs exist.
665  			name:         "fetch new log only",
666  			updateNum:    knownHeight,
667  			expectedErr:  nil,
668  			createRl:     true,
669  			createCommit: true,
670  			expectRl:     true,
671  		},
672  		{
673  			// Test no past deltas when the buckets do not exist.
674  			name:        "no buckets created",
675  			updateNum:   unknownHeight,
676  			expectedErr: ErrNoPastDeltas,
677  		},
678  		{
679  			// Test no logs found when the height is unknown.
680  			name:         "no log found",
681  			updateNum:    unknownHeight,
682  			expectedErr:  ErrLogEntryNotFound,
683  			createRl:     true,
684  			createCommit: true,
685  		},
686  	}
687  
688  	for _, tc := range testCases {
689  		tc := tc
690  
691  		fullDB, err := MakeTestDB(t)
692  		require.NoError(t, err)
693  
694  		backend := fullDB.ChannelStateDB().backend
695  
696  		var (
697  			rl     *RevocationLog
698  			commit *ChannelCommitment
699  		)
700  
701  		// Setup the buckets and fill the test data if specified.
702  		err = kvdb.Update(backend, func(tx kvdb.RwTx) error {
703  			// Create the root bucket.
704  			cb, err := tx.CreateTopLevelBucket(openChannelBucket)
705  			require.NoError(t, err)
706  
707  			// Create the revocation log if specified.
708  			if tc.createRl {
709  				lb, err := cb.CreateBucket(revocationLogBucket)
710  				require.NoError(t, err)
711  
712  				err = putRevocationLog(
713  					lb, &testChannelCommit, 0, 1, false,
714  				)
715  				require.NoError(t, err)
716  			}
717  
718  			// Create the channel commit if specified.
719  			if tc.createCommit {
720  				legacyBucket, err := cb.CreateBucket(
721  					revocationLogBucketDeprecated,
722  				)
723  				require.NoError(t, err)
724  
725  				buf := bytes.NewBuffer([]byte{})
726  				err = serializeChanCommit(
727  					buf, &testChannelCommit,
728  				)
729  				require.NoError(t, err)
730  
731  				err = legacyBucket.Put(logKey[:], buf.Bytes())
732  				require.NoError(t, err)
733  			}
734  
735  			return nil
736  		}, func() {})
737  
738  		// Construct the testing db transaction.
739  		dbTx := func(tx kvdb.RTx) error {
740  			cb := tx.ReadBucket(openChannelBucket)
741  
742  			rl, commit, err = fetchRevocationLogCompatible(
743  				cb, tc.updateNum,
744  			)
745  			return err
746  		}
747  
748  		t.Run(tc.name, func(t *testing.T) {
749  			err := kvdb.View(backend, dbTx, func() {})
750  			require.Equal(t, tc.expectedErr, err)
751  
752  			// Check the expected revocation log is returned.
753  			if tc.expectRl {
754  				require.NotNil(t, rl)
755  			} else {
756  				require.Nil(t, rl)
757  			}
758  
759  			// Check the expected channel commit is returned.
760  			if tc.expectCommit {
761  				require.NotNil(t, commit)
762  			} else {
763  				require.Nil(t, commit)
764  			}
765  		})
766  	}
767  }
768  
769  func createTestRevocationLogBuckets(tx kvdb.RwTx) (kvdb.RwBucket,
770  	kvdb.RwBucket, error) {
771  
772  	chanBucket, err := tx.CreateTopLevelBucket(openChannelBucket)
773  	if err != nil {
774  		return nil, nil, err
775  	}
776  
777  	logBucket, err := chanBucket.CreateBucket(revocationLogBucket)
778  	if err != nil {
779  		return nil, nil, err
780  	}
781  
782  	_, err = chanBucket.CreateBucket(revocationLogBucketDeprecated)
783  	if err != nil {
784  		return nil, nil, err
785  	}
786  
787  	return chanBucket, logBucket, nil
788  }
789  
790  // TestDeserializeHTLCEntriesLegacy checks that the legacy encoding of the
791  // HtlcIndex can be correctly read as BigSizeT. The field `HtlcIndex` was
792  // encoded using `uint16` and should now be deserialized into BigSizeT
793  // on-the-fly.
794  func TestDeserializeHTLCEntriesLegacy(t *testing.T) {
795  	t.Parallel()
796  
797  	// rawBytes defines the bytes read from the disk for the testing HTLC
798  	// entry.
799  	rawBytes := []byte{
800  		// Body length 45.
801  		0x2d,
802  		// Rhash tlv.
803  		0x0, 0x0,
804  		// RefundTimeout tlv.
805  		0x1, 0x4, 0x0, 0xb, 0x4a, 0xa0,
806  		// OutputIndex tlv.
807  		0x2, 0x2, 0x0, 0xa,
808  		// Incoming tlv.
809  		0x3, 0x1, 0x1,
810  		// Amt tlv.
811  		0x4, 0x5, 0xfe, 0x0, 0xf, 0x42, 0x40,
812  		// Custom blob tlv.
813  		0x5, 0x11, 0xfe, 0x00, 0x01, 0x00, 0x01, 0x0b, 0x63, 0x75, 0x73,
814  		0x74, 0x6f, 0x6d, 0x20, 0x64, 0x61, 0x74, 0x61,
815  
816  		// HTLC index tlv.
817  		//
818  		// NOTE: We are missing two bytes in the end, which is appended
819  		// below.
820  		0x6, 0x2,
821  	}
822  
823  	// Iterate through all possible values encoded using uint16. They should
824  	// be correctly read as uint16 and converted to BigSizeT.
825  	for i := range math.MaxUint16 + 1 {
826  		// Encode the index using two bytes.
827  		rawHtlcIndex := make([]byte, 2)
828  		binary.BigEndian.PutUint16(rawHtlcIndex, uint16(i))
829  
830  		// Copy the raw bytes and append the htlc index.
831  		rawEntry := bytes.Clone(rawBytes)
832  		rawEntry = append(rawEntry, rawHtlcIndex...)
833  
834  		// Read the tlv stream.
835  		buf := bytes.NewBuffer(rawEntry)
836  		htlcs, err := deserializeHTLCEntries(buf)
837  		require.NoError(t, err)
838  
839  		// Check the bytes are read as expected.
840  		require.Len(t, htlcs, 1)
841  
842  		// Assert that the uint16 is converted to BigSizeT.
843  		record := tlv.SomeRecordT(tlv.NewRecordT[tlv.TlvType6](
844  			tlv.NewBigSizeT(uint64(i)),
845  		))
846  		require.Equal(t, record, htlcs[0].HtlcIndex)
847  	}
848  }