github.com/decred/dcrlnd@v0.7.6/channeldb/payments_test.go (about) 1 package channeldb 2 3 import ( 4 "bytes" 5 "fmt" 6 "math" 7 "reflect" 8 "testing" 9 "time" 10 11 "github.com/btcsuite/btcwallet/walletdb" 12 "github.com/davecgh/go-spew/spew" 13 "github.com/decred/dcrd/dcrec/secp256k1/v4" 14 "github.com/decred/dcrlnd/kvdb" 15 "github.com/decred/dcrlnd/lntypes" 16 "github.com/decred/dcrlnd/record" 17 "github.com/decred/dcrlnd/routing/route" 18 "github.com/stretchr/testify/require" 19 ) 20 21 var ( 22 priv, _ = secp256k1.GeneratePrivateKey() 23 pub = priv.PubKey() 24 25 testHop1 = &route.Hop{ 26 PubKeyBytes: route.NewVertex(pub), 27 ChannelID: 12345, 28 OutgoingTimeLock: 111, 29 AmtToForward: 555, 30 CustomRecords: record.CustomSet{ 31 65536: []byte{}, 32 80001: []byte{}, 33 }, 34 MPP: record.NewMPP(32, [32]byte{0x42}), 35 } 36 37 testHop2 = &route.Hop{ 38 PubKeyBytes: route.NewVertex(pub), 39 ChannelID: 12345, 40 OutgoingTimeLock: 111, 41 AmtToForward: 555, 42 LegacyPayload: true, 43 } 44 45 testRoute = route.Route{ 46 TotalTimeLock: 123, 47 TotalAmount: 1234567, 48 SourcePubKey: route.NewVertex(pub), 49 Hops: []*route.Hop{ 50 testHop2, 51 testHop1, 52 }, 53 } 54 ) 55 56 func makeFakeInfo() (*PaymentCreationInfo, *HTLCAttemptInfo) { 57 var preimg lntypes.Preimage 58 copy(preimg[:], rev[:]) 59 60 hash := preimg.Hash() 61 62 c := &PaymentCreationInfo{ 63 PaymentIdentifier: hash, 64 Value: 1000, 65 // Use single second precision to avoid false positive test 66 // failures due to the monotonic time component. 67 CreationTime: time.Unix(time.Now().Unix(), 0), 68 PaymentRequest: []byte(""), 69 } 70 71 a := NewHtlcAttemptInfo( 72 44, priv, testRoute, time.Unix(100, 0), &hash, 73 ) 74 75 return c, a 76 } 77 78 func TestSentPaymentSerialization(t *testing.T) { 79 t.Parallel() 80 81 c, s := makeFakeInfo() 82 83 var b bytes.Buffer 84 if err := serializePaymentCreationInfo(&b, c); err != nil { 85 t.Fatalf("unable to serialize creation info: %v", err) 86 } 87 88 newCreationInfo, err := deserializePaymentCreationInfo(&b) 89 if err != nil { 90 t.Fatalf("unable to deserialize creation info: %v", err) 91 } 92 93 if !reflect.DeepEqual(c, newCreationInfo) { 94 t.Fatalf("Payments do not match after "+ 95 "serialization/deserialization %v vs %v", 96 spew.Sdump(c), spew.Sdump(newCreationInfo), 97 ) 98 } 99 100 b.Reset() 101 if err := serializeHTLCAttemptInfo(&b, s); err != nil { 102 t.Fatalf("unable to serialize info: %v", err) 103 } 104 105 newWireInfo, err := deserializeHTLCAttemptInfo(&b) 106 if err != nil { 107 t.Fatalf("unable to deserialize info: %v", err) 108 } 109 newWireInfo.AttemptID = s.AttemptID 110 111 // First we verify all the records match up porperly, as they aren't 112 // able to be properly compared using reflect.DeepEqual. 113 err = assertRouteEqual(&s.Route, &newWireInfo.Route) 114 if err != nil { 115 t.Fatalf("Routes do not match after "+ 116 "serialization/deserialization: %v", err) 117 } 118 119 // Clear routes to allow DeepEqual to compare the remaining fields. 120 newWireInfo.Route = route.Route{} 121 s.Route = route.Route{} 122 123 // Call session key method to set our cached session key so we can use 124 // DeepEqual, and assert that our key equals the original key. 125 require.Equal(t, s.cachedSessionKey, newWireInfo.SessionKey()) 126 127 if !reflect.DeepEqual(s, newWireInfo) { 128 t.Fatalf("Payments do not match after "+ 129 "serialization/deserialization %v vs %v", 130 spew.Sdump(s), spew.Sdump(newWireInfo), 131 ) 132 } 133 } 134 135 // assertRouteEquals compares to routes for equality and returns an error if 136 // they are not equal. 137 func assertRouteEqual(a, b *route.Route) error { 138 if !reflect.DeepEqual(a, b) { 139 return fmt.Errorf("HTLCAttemptInfos don't match: %v vs %v", 140 spew.Sdump(a), spew.Sdump(b)) 141 } 142 143 return nil 144 } 145 146 func TestRouteSerialization(t *testing.T) { 147 t.Parallel() 148 149 var b bytes.Buffer 150 if err := SerializeRoute(&b, testRoute); err != nil { 151 t.Fatal(err) 152 } 153 154 r := bytes.NewReader(b.Bytes()) 155 route2, err := DeserializeRoute(r) 156 if err != nil { 157 t.Fatal(err) 158 } 159 160 // First we verify all the records match up porperly, as they aren't 161 // able to be properly compared using reflect.DeepEqual. 162 err = assertRouteEqual(&testRoute, &route2) 163 if err != nil { 164 t.Fatalf("routes not equal: \n%v vs \n%v", 165 spew.Sdump(testRoute), spew.Sdump(route2)) 166 } 167 } 168 169 // deletePayment removes a payment with paymentHash from the payments database. 170 func deletePayment(t *testing.T, db *DB, paymentHash lntypes.Hash, seqNr uint64) { 171 t.Helper() 172 173 err := kvdb.Update(db, func(tx kvdb.RwTx) error { 174 payments := tx.ReadWriteBucket(paymentsRootBucket) 175 176 // Delete the payment bucket. 177 err := payments.DeleteNestedBucket(paymentHash[:]) 178 if err != nil { 179 return err 180 } 181 182 key := make([]byte, 8) 183 byteOrder.PutUint64(key, seqNr) 184 185 // Delete the index that references this payment. 186 indexes := tx.ReadWriteBucket(paymentsIndexBucket) 187 return indexes.Delete(key) 188 }, func() {}) 189 190 if err != nil { 191 t.Fatalf("could not delete "+ 192 "payment: %v", err) 193 } 194 } 195 196 // TestQueryPayments tests retrieval of payments with forwards and reversed 197 // queries. 198 func TestQueryPayments(t *testing.T) { 199 // Define table driven test for QueryPayments. 200 // Test payments have sequence indices [1, 3, 4, 5, 6, 7]. 201 // Note that the payment with index 7 has the same payment hash as 6, 202 // and is stored in a nested bucket within payment 6 rather than being 203 // its own entry in the payments bucket. We do this to test retrieval 204 // of legacy payments. 205 tests := []struct { 206 name string 207 query PaymentsQuery 208 firstIndex uint64 209 lastIndex uint64 210 211 // expectedSeqNrs contains the set of sequence numbers we expect 212 // our query to return. 213 expectedSeqNrs []uint64 214 }{ 215 { 216 name: "IndexOffset at the end of the payments range", 217 query: PaymentsQuery{ 218 IndexOffset: 7, 219 MaxPayments: 7, 220 Reversed: false, 221 IncludeIncomplete: true, 222 }, 223 firstIndex: 0, 224 lastIndex: 0, 225 expectedSeqNrs: nil, 226 }, 227 { 228 name: "query in forwards order, start at beginning", 229 query: PaymentsQuery{ 230 IndexOffset: 0, 231 MaxPayments: 2, 232 Reversed: false, 233 IncludeIncomplete: true, 234 }, 235 firstIndex: 1, 236 lastIndex: 3, 237 expectedSeqNrs: []uint64{1, 3}, 238 }, 239 { 240 name: "query in forwards order, start at end, overflow", 241 query: PaymentsQuery{ 242 IndexOffset: 6, 243 MaxPayments: 2, 244 Reversed: false, 245 IncludeIncomplete: true, 246 }, 247 firstIndex: 7, 248 lastIndex: 7, 249 expectedSeqNrs: []uint64{7}, 250 }, 251 { 252 name: "start at offset index outside of payments", 253 query: PaymentsQuery{ 254 IndexOffset: 20, 255 MaxPayments: 2, 256 Reversed: false, 257 IncludeIncomplete: true, 258 }, 259 firstIndex: 0, 260 lastIndex: 0, 261 expectedSeqNrs: nil, 262 }, 263 { 264 name: "overflow in forwards order", 265 query: PaymentsQuery{ 266 IndexOffset: 4, 267 MaxPayments: math.MaxUint64, 268 Reversed: false, 269 IncludeIncomplete: true, 270 }, 271 firstIndex: 5, 272 lastIndex: 7, 273 expectedSeqNrs: []uint64{5, 6, 7}, 274 }, 275 { 276 name: "start at offset index outside of payments, " + 277 "reversed order", 278 query: PaymentsQuery{ 279 IndexOffset: 9, 280 MaxPayments: 2, 281 Reversed: true, 282 IncludeIncomplete: true, 283 }, 284 firstIndex: 6, 285 lastIndex: 7, 286 expectedSeqNrs: []uint64{6, 7}, 287 }, 288 { 289 name: "query in reverse order, start at end", 290 query: PaymentsQuery{ 291 IndexOffset: 0, 292 MaxPayments: 2, 293 Reversed: true, 294 IncludeIncomplete: true, 295 }, 296 firstIndex: 6, 297 lastIndex: 7, 298 expectedSeqNrs: []uint64{6, 7}, 299 }, 300 { 301 name: "query in reverse order, starting in middle", 302 query: PaymentsQuery{ 303 IndexOffset: 4, 304 MaxPayments: 2, 305 Reversed: true, 306 IncludeIncomplete: true, 307 }, 308 firstIndex: 1, 309 lastIndex: 3, 310 expectedSeqNrs: []uint64{1, 3}, 311 }, 312 { 313 name: "query in reverse order, starting in middle, " + 314 "with underflow", 315 query: PaymentsQuery{ 316 IndexOffset: 4, 317 MaxPayments: 5, 318 Reversed: true, 319 IncludeIncomplete: true, 320 }, 321 firstIndex: 1, 322 lastIndex: 3, 323 expectedSeqNrs: []uint64{1, 3}, 324 }, 325 { 326 name: "all payments in reverse, order maintained", 327 query: PaymentsQuery{ 328 IndexOffset: 0, 329 MaxPayments: 7, 330 Reversed: true, 331 IncludeIncomplete: true, 332 }, 333 firstIndex: 1, 334 lastIndex: 7, 335 expectedSeqNrs: []uint64{1, 3, 4, 5, 6, 7}, 336 }, 337 { 338 name: "exclude incomplete payments", 339 query: PaymentsQuery{ 340 IndexOffset: 0, 341 MaxPayments: 7, 342 Reversed: false, 343 IncludeIncomplete: false, 344 }, 345 firstIndex: 7, 346 lastIndex: 7, 347 expectedSeqNrs: []uint64{7}, 348 }, 349 { 350 name: "query payments at index gap", 351 query: PaymentsQuery{ 352 IndexOffset: 1, 353 MaxPayments: 7, 354 Reversed: false, 355 IncludeIncomplete: true, 356 }, 357 firstIndex: 3, 358 lastIndex: 7, 359 expectedSeqNrs: []uint64{3, 4, 5, 6, 7}, 360 }, 361 { 362 name: "query payments reverse before index gap", 363 query: PaymentsQuery{ 364 IndexOffset: 3, 365 MaxPayments: 7, 366 Reversed: true, 367 IncludeIncomplete: true, 368 }, 369 firstIndex: 1, 370 lastIndex: 1, 371 expectedSeqNrs: []uint64{1}, 372 }, 373 { 374 name: "query payments reverse on index gap", 375 query: PaymentsQuery{ 376 IndexOffset: 2, 377 MaxPayments: 7, 378 Reversed: true, 379 IncludeIncomplete: true, 380 }, 381 firstIndex: 1, 382 lastIndex: 1, 383 expectedSeqNrs: []uint64{1}, 384 }, 385 { 386 name: "query payments forward on index gap", 387 query: PaymentsQuery{ 388 IndexOffset: 2, 389 MaxPayments: 2, 390 Reversed: false, 391 IncludeIncomplete: true, 392 }, 393 firstIndex: 3, 394 lastIndex: 4, 395 expectedSeqNrs: []uint64{3, 4}, 396 }, 397 } 398 399 for _, tt := range tests { 400 tt := tt 401 t.Run(tt.name, func(t *testing.T) { 402 t.Parallel() 403 404 db, cleanup, err := MakeTestDB() 405 if err != nil { 406 t.Fatalf("unable to init db: %v", err) 407 } 408 defer cleanup() 409 410 // Make a preliminary query to make sure it's ok to 411 // query when we have no payments. 412 resp, err := db.QueryPayments(tt.query) 413 require.NoError(t, err) 414 require.Len(t, resp.Payments, 0) 415 416 // Populate the database with a set of test payments. 417 // We create 6 original payments, deleting the payment 418 // at index 2 so that we cover the case where sequence 419 // numbers are missing. We also add a duplicate payment 420 // to the last payment added to test the legacy case 421 // where we have duplicates in the nested duplicates 422 // bucket. 423 nonDuplicatePayments := 6 424 pControl := NewPaymentControl(db) 425 426 for i := 0; i < nonDuplicatePayments; i++ { 427 // Generate a test payment. 428 info, _, preimg, err := genInfo() 429 if err != nil { 430 t.Fatalf("unable to create test "+ 431 "payment: %v", err) 432 } 433 434 // Create a new payment entry in the database. 435 err = pControl.InitPayment(info.PaymentIdentifier, info) 436 if err != nil { 437 t.Fatalf("unable to initialize "+ 438 "payment in database: %v", err) 439 } 440 441 // Immediately delete the payment with index 2. 442 if i == 1 { 443 pmt, err := pControl.FetchPayment( 444 info.PaymentIdentifier, 445 ) 446 require.NoError(t, err) 447 448 deletePayment(t, db, info.PaymentIdentifier, 449 pmt.SequenceNum) 450 } 451 452 // If we are on the last payment entry, add a 453 // duplicate payment with sequence number equal 454 // to the parent payment + 1. Note that 455 // duplicate payments will always be succeeded. 456 if i == (nonDuplicatePayments - 1) { 457 pmt, err := pControl.FetchPayment( 458 info.PaymentIdentifier, 459 ) 460 require.NoError(t, err) 461 462 appendDuplicatePayment( 463 t, pControl.db, 464 info.PaymentIdentifier, 465 pmt.SequenceNum+1, 466 preimg, 467 ) 468 } 469 } 470 471 // Fetch all payments in the database. 472 allPayments, err := db.FetchPayments() 473 if err != nil { 474 t.Fatalf("payments could not be fetched from "+ 475 "database: %v", err) 476 } 477 478 if len(allPayments) != 6 { 479 t.Fatalf("Number of payments received does not "+ 480 "match expected one. Got %v, want %v.", 481 len(allPayments), 6) 482 } 483 484 querySlice, err := db.QueryPayments(tt.query) 485 if err != nil { 486 t.Fatalf("unexpected error: %v", err) 487 } 488 if tt.firstIndex != querySlice.FirstIndexOffset || 489 tt.lastIndex != querySlice.LastIndexOffset { 490 t.Errorf("First or last index does not match "+ 491 "expected index. Want (%d, %d), got (%d, %d).", 492 tt.firstIndex, tt.lastIndex, 493 querySlice.FirstIndexOffset, 494 querySlice.LastIndexOffset) 495 } 496 497 if len(querySlice.Payments) != len(tt.expectedSeqNrs) { 498 t.Errorf("expected: %v payments, got: %v", 499 len(tt.expectedSeqNrs), len(querySlice.Payments)) 500 } 501 502 for i, seqNr := range tt.expectedSeqNrs { 503 q := querySlice.Payments[i] 504 if seqNr != q.SequenceNum { 505 t.Errorf("sequence numbers do not match, "+ 506 "got %v, want %v", q.SequenceNum, seqNr) 507 } 508 } 509 }) 510 } 511 } 512 513 // TestFetchPaymentWithSequenceNumber tests lookup of payments with their 514 // sequence number. It sets up one payment with no duplicates, and another with 515 // two duplicates in its duplicates bucket then uses these payments to test the 516 // case where a specific duplicate is not found and the duplicates bucket is not 517 // present when we expect it to be. 518 func TestFetchPaymentWithSequenceNumber(t *testing.T) { 519 db, cleanup, err := MakeTestDB() 520 require.NoError(t, err) 521 522 defer cleanup() 523 524 pControl := NewPaymentControl(db) 525 526 // Generate a test payment which does not have duplicates. 527 noDuplicates, _, _, err := genInfo() 528 require.NoError(t, err) 529 530 // Create a new payment entry in the database. 531 err = pControl.InitPayment(noDuplicates.PaymentIdentifier, noDuplicates) 532 require.NoError(t, err) 533 534 // Fetch the payment so we can get its sequence nr. 535 noDuplicatesPayment, err := pControl.FetchPayment( 536 noDuplicates.PaymentIdentifier, 537 ) 538 require.NoError(t, err) 539 540 // Generate a test payment which we will add duplicates to. 541 hasDuplicates, _, preimg, err := genInfo() 542 require.NoError(t, err) 543 544 // Create a new payment entry in the database. 545 err = pControl.InitPayment(hasDuplicates.PaymentIdentifier, hasDuplicates) 546 require.NoError(t, err) 547 548 // Fetch the payment so we can get its sequence nr. 549 hasDuplicatesPayment, err := pControl.FetchPayment( 550 hasDuplicates.PaymentIdentifier, 551 ) 552 require.NoError(t, err) 553 554 // We declare the sequence numbers used here so that we can reference 555 // them in tests. 556 var ( 557 duplicateOneSeqNr = hasDuplicatesPayment.SequenceNum + 1 558 duplicateTwoSeqNr = hasDuplicatesPayment.SequenceNum + 2 559 ) 560 561 // Add two duplicates to our second payment. 562 appendDuplicatePayment( 563 t, db, hasDuplicates.PaymentIdentifier, duplicateOneSeqNr, preimg, 564 ) 565 appendDuplicatePayment( 566 t, db, hasDuplicates.PaymentIdentifier, duplicateTwoSeqNr, preimg, 567 ) 568 569 tests := []struct { 570 name string 571 paymentHash lntypes.Hash 572 sequenceNumber uint64 573 expectedErr error 574 }{ 575 { 576 name: "lookup payment without duplicates", 577 paymentHash: noDuplicates.PaymentIdentifier, 578 sequenceNumber: noDuplicatesPayment.SequenceNum, 579 expectedErr: nil, 580 }, 581 { 582 name: "lookup payment with duplicates", 583 paymentHash: hasDuplicates.PaymentIdentifier, 584 sequenceNumber: hasDuplicatesPayment.SequenceNum, 585 expectedErr: nil, 586 }, 587 { 588 name: "lookup first duplicate", 589 paymentHash: hasDuplicates.PaymentIdentifier, 590 sequenceNumber: duplicateOneSeqNr, 591 expectedErr: nil, 592 }, 593 { 594 name: "lookup second duplicate", 595 paymentHash: hasDuplicates.PaymentIdentifier, 596 sequenceNumber: duplicateTwoSeqNr, 597 expectedErr: nil, 598 }, 599 { 600 name: "lookup non-existent duplicate", 601 paymentHash: hasDuplicates.PaymentIdentifier, 602 sequenceNumber: 999999, 603 expectedErr: ErrDuplicateNotFound, 604 }, 605 { 606 name: "lookup duplicate, no duplicates bucket", 607 paymentHash: noDuplicates.PaymentIdentifier, 608 sequenceNumber: duplicateTwoSeqNr, 609 expectedErr: ErrNoDuplicateBucket, 610 }, 611 } 612 613 for _, test := range tests { 614 test := test 615 616 t.Run(test.name, func(t *testing.T) { 617 err := kvdb.Update(db, 618 func(tx walletdb.ReadWriteTx) error { 619 620 var seqNrBytes [8]byte 621 byteOrder.PutUint64( 622 seqNrBytes[:], test.sequenceNumber, 623 ) 624 625 _, err := fetchPaymentWithSequenceNumber( 626 tx, test.paymentHash, seqNrBytes[:], 627 ) 628 return err 629 }, func() {}) 630 require.Equal(t, test.expectedErr, err) 631 }) 632 } 633 } 634 635 // appendDuplicatePayment adds a duplicate payment to an existing payment. Note 636 // that this function requires a unique sequence number. 637 // 638 // This code is *only* intended to replicate legacy duplicate payments in lnd, 639 // our current schema does not allow duplicates. 640 func appendDuplicatePayment(t *testing.T, db *DB, paymentHash lntypes.Hash, 641 seqNr uint64, preImg lntypes.Preimage) { 642 643 err := kvdb.Update(db, func(tx walletdb.ReadWriteTx) error { 644 bucket, err := fetchPaymentBucketUpdate( 645 tx, paymentHash, 646 ) 647 if err != nil { 648 return err 649 } 650 651 // Create the duplicates bucket if it is not 652 // present. 653 dup, err := bucket.CreateBucketIfNotExists( 654 duplicatePaymentsBucket, 655 ) 656 if err != nil { 657 return err 658 } 659 660 var sequenceKey [8]byte 661 byteOrder.PutUint64(sequenceKey[:], seqNr) 662 663 // Create duplicate payments for the two dup 664 // sequence numbers we've setup. 665 putDuplicatePayment(t, dup, sequenceKey[:], paymentHash, preImg) 666 667 // Finally, once we have created our entry we add an index for 668 // it. 669 err = createPaymentIndexEntry(tx, sequenceKey[:], paymentHash) 670 require.NoError(t, err) 671 672 return nil 673 }, func() {}) 674 if err != nil { 675 t.Fatalf("could not create payment: %v", err) 676 } 677 } 678 679 // putDuplicatePayment creates a duplicate payment in the duplicates bucket 680 // provided with the minimal information required for successful reading. 681 func putDuplicatePayment(t *testing.T, duplicateBucket kvdb.RwBucket, 682 sequenceKey []byte, paymentHash lntypes.Hash, 683 preImg lntypes.Preimage) { 684 685 paymentBucket, err := duplicateBucket.CreateBucketIfNotExists( 686 sequenceKey, 687 ) 688 require.NoError(t, err) 689 690 err = paymentBucket.Put(duplicatePaymentSequenceKey, sequenceKey) 691 require.NoError(t, err) 692 693 // Generate fake information for the duplicate payment. 694 info, _, _, err := genInfo() 695 require.NoError(t, err) 696 697 // Write the payment info to disk under the creation info key. This code 698 // is copied rather than using serializePaymentCreationInfo to ensure 699 // we always write in the legacy format used by duplicate payments. 700 var b bytes.Buffer 701 var scratch [8]byte 702 _, err = b.Write(paymentHash[:]) 703 require.NoError(t, err) 704 705 byteOrder.PutUint64(scratch[:], uint64(info.Value)) 706 _, err = b.Write(scratch[:]) 707 require.NoError(t, err) 708 709 err = serializeTime(&b, info.CreationTime) 710 require.NoError(t, err) 711 712 byteOrder.PutUint32(scratch[:4], 0) 713 _, err = b.Write(scratch[:4]) 714 require.NoError(t, err) 715 716 // Get the PaymentCreationInfo. 717 err = paymentBucket.Put(duplicatePaymentCreationInfoKey, b.Bytes()) 718 require.NoError(t, err) 719 720 // Duolicate payments are only stored for successes, so add the 721 // preimage. 722 err = paymentBucket.Put(duplicatePaymentSettleInfoKey, preImg[:]) 723 require.NoError(t, err) 724 }