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  }