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 }