123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802 |
- package channeldb
- import (
- "bytes"
- "fmt"
- "math"
- "reflect"
- "testing"
- "time"
- "github.com/btcsuite/btcd/btcec/v2"
- "github.com/btcsuite/btcwallet/walletdb"
- "github.com/davecgh/go-spew/spew"
- "github.com/lightningnetwork/lnd/kvdb"
- "github.com/lightningnetwork/lnd/lntypes"
- "github.com/lightningnetwork/lnd/record"
- "github.com/lightningnetwork/lnd/routing/route"
- "github.com/stretchr/testify/require"
- )
- var (
- priv, _ = btcec.NewPrivateKey()
- pub = priv.PubKey()
- vertex = route.NewVertex(pub)
- testHop1 = &route.Hop{
- PubKeyBytes: vertex,
- ChannelID: 12345,
- OutgoingTimeLock: 111,
- AmtToForward: 555,
- CustomRecords: record.CustomSet{
- 65536: []byte{},
- 80001: []byte{},
- },
- MPP: record.NewMPP(32, [32]byte{0x42}),
- Metadata: []byte{1, 2, 3},
- }
- testHop2 = &route.Hop{
- PubKeyBytes: vertex,
- ChannelID: 12345,
- OutgoingTimeLock: 111,
- AmtToForward: 555,
- LegacyPayload: true,
- }
- testHop3 = &route.Hop{
- PubKeyBytes: route.NewVertex(pub),
- ChannelID: 12345,
- OutgoingTimeLock: 111,
- AmtToForward: 555,
- CustomRecords: record.CustomSet{
- 65536: []byte{},
- 80001: []byte{},
- },
- AMP: record.NewAMP([32]byte{0x69}, [32]byte{0x42}, 1),
- Metadata: []byte{1, 2, 3},
- }
- testRoute = route.Route{
- TotalTimeLock: 123,
- TotalAmount: 1234567,
- SourcePubKey: vertex,
- Hops: []*route.Hop{
- testHop3,
- testHop2,
- testHop1,
- },
- }
- testBlindedRoute = route.Route{
- TotalTimeLock: 150,
- TotalAmount: 1000,
- SourcePubKey: vertex,
- Hops: []*route.Hop{
- {
- PubKeyBytes: vertex,
- ChannelID: 9876,
- OutgoingTimeLock: 120,
- AmtToForward: 900,
- EncryptedData: []byte{1, 3, 3},
- BlindingPoint: pub,
- },
- {
- PubKeyBytes: vertex,
- EncryptedData: []byte{3, 2, 1},
- },
- {
- PubKeyBytes: vertex,
- Metadata: []byte{4, 5, 6},
- AmtToForward: 500,
- OutgoingTimeLock: 100,
- TotalAmtMsat: 500,
- },
- },
- }
- )
- func makeFakeInfo() (*PaymentCreationInfo, *HTLCAttemptInfo) {
- var preimg lntypes.Preimage
- copy(preimg[:], rev[:])
- hash := preimg.Hash()
- c := &PaymentCreationInfo{
- PaymentIdentifier: hash,
- Value: 1000,
- // Use single second precision to avoid false positive test
- // failures due to the monotonic time component.
- CreationTime: time.Unix(time.Now().Unix(), 0),
- PaymentRequest: []byte(""),
- }
- a := NewHtlcAttempt(
- 44, priv, testRoute, time.Unix(100, 0), &hash,
- )
- return c, &a.HTLCAttemptInfo
- }
- func TestSentPaymentSerialization(t *testing.T) {
- t.Parallel()
- c, s := makeFakeInfo()
- var b bytes.Buffer
- if err := serializePaymentCreationInfo(&b, c); err != nil {
- t.Fatalf("unable to serialize creation info: %v", err)
- }
- newCreationInfo, err := deserializePaymentCreationInfo(&b)
- require.NoError(t, err, "unable to deserialize creation info")
- if !reflect.DeepEqual(c, newCreationInfo) {
- t.Fatalf("Payments do not match after "+
- "serialization/deserialization %v vs %v",
- spew.Sdump(c), spew.Sdump(newCreationInfo),
- )
- }
- b.Reset()
- if err := serializeHTLCAttemptInfo(&b, s); err != nil {
- t.Fatalf("unable to serialize info: %v", err)
- }
- newWireInfo, err := deserializeHTLCAttemptInfo(&b)
- require.NoError(t, err, "unable to deserialize info")
- newWireInfo.AttemptID = s.AttemptID
- // First we verify all the records match up porperly, as they aren't
- // able to be properly compared using reflect.DeepEqual.
- err = assertRouteEqual(&s.Route, &newWireInfo.Route)
- if err != nil {
- t.Fatalf("Routes do not match after "+
- "serialization/deserialization: %v", err)
- }
- // Clear routes to allow DeepEqual to compare the remaining fields.
- newWireInfo.Route = route.Route{}
- s.Route = route.Route{}
- // Call session key method to set our cached session key so we can use
- // DeepEqual, and assert that our key equals the original key.
- require.Equal(t, s.cachedSessionKey, newWireInfo.SessionKey())
- if !reflect.DeepEqual(s, newWireInfo) {
- t.Fatalf("Payments do not match after "+
- "serialization/deserialization %v vs %v",
- spew.Sdump(s), spew.Sdump(newWireInfo),
- )
- }
- }
- // assertRouteEquals compares to routes for equality and returns an error if
- // they are not equal.
- func assertRouteEqual(a, b *route.Route) error {
- if !reflect.DeepEqual(a, b) {
- return fmt.Errorf("HTLCAttemptInfos don't match: %v vs %v",
- spew.Sdump(a), spew.Sdump(b))
- }
- return nil
- }
- // TestRouteSerialization tests serialization of a regular and blinded route.
- func TestRouteSerialization(t *testing.T) {
- t.Parallel()
- testSerializeRoute(t, testRoute)
- testSerializeRoute(t, testBlindedRoute)
- }
- func testSerializeRoute(t *testing.T, route route.Route) {
- var b bytes.Buffer
- err := SerializeRoute(&b, route)
- require.NoError(t, err)
- r := bytes.NewReader(b.Bytes())
- route2, err := DeserializeRoute(r)
- require.NoError(t, err)
- reflect.DeepEqual(route, route2)
- }
- // deletePayment removes a payment with paymentHash from the payments database.
- func deletePayment(t *testing.T, db *DB, paymentHash lntypes.Hash, seqNr uint64) {
- t.Helper()
- err := kvdb.Update(db, func(tx kvdb.RwTx) error {
- payments := tx.ReadWriteBucket(paymentsRootBucket)
- // Delete the payment bucket.
- err := payments.DeleteNestedBucket(paymentHash[:])
- if err != nil {
- return err
- }
- key := make([]byte, 8)
- byteOrder.PutUint64(key, seqNr)
- // Delete the index that references this payment.
- indexes := tx.ReadWriteBucket(paymentsIndexBucket)
- return indexes.Delete(key)
- }, func() {})
- if err != nil {
- t.Fatalf("could not delete "+
- "payment: %v", err)
- }
- }
- // TestQueryPayments tests retrieval of payments with forwards and reversed
- // queries.
- func TestQueryPayments(t *testing.T) {
- // Define table driven test for QueryPayments.
- // Test payments have sequence indices [1, 3, 4, 5, 6, 7].
- // Note that the payment with index 7 has the same payment hash as 6,
- // and is stored in a nested bucket within payment 6 rather than being
- // its own entry in the payments bucket. We do this to test retrieval
- // of legacy payments.
- tests := []struct {
- name string
- query PaymentsQuery
- firstIndex uint64
- lastIndex uint64
- // expectedSeqNrs contains the set of sequence numbers we expect
- // our query to return.
- expectedSeqNrs []uint64
- }{
- {
- name: "IndexOffset at the end of the payments range",
- query: PaymentsQuery{
- IndexOffset: 7,
- MaxPayments: 7,
- Reversed: false,
- IncludeIncomplete: true,
- },
- firstIndex: 0,
- lastIndex: 0,
- expectedSeqNrs: nil,
- },
- {
- name: "query in forwards order, start at beginning",
- query: PaymentsQuery{
- IndexOffset: 0,
- MaxPayments: 2,
- Reversed: false,
- IncludeIncomplete: true,
- },
- firstIndex: 1,
- lastIndex: 3,
- expectedSeqNrs: []uint64{1, 3},
- },
- {
- name: "query in forwards order, start at end, overflow",
- query: PaymentsQuery{
- IndexOffset: 6,
- MaxPayments: 2,
- Reversed: false,
- IncludeIncomplete: true,
- },
- firstIndex: 7,
- lastIndex: 7,
- expectedSeqNrs: []uint64{7},
- },
- {
- name: "start at offset index outside of payments",
- query: PaymentsQuery{
- IndexOffset: 20,
- MaxPayments: 2,
- Reversed: false,
- IncludeIncomplete: true,
- },
- firstIndex: 0,
- lastIndex: 0,
- expectedSeqNrs: nil,
- },
- {
- name: "overflow in forwards order",
- query: PaymentsQuery{
- IndexOffset: 4,
- MaxPayments: math.MaxUint64,
- Reversed: false,
- IncludeIncomplete: true,
- },
- firstIndex: 5,
- lastIndex: 7,
- expectedSeqNrs: []uint64{5, 6, 7},
- },
- {
- name: "start at offset index outside of payments, " +
- "reversed order",
- query: PaymentsQuery{
- IndexOffset: 9,
- MaxPayments: 2,
- Reversed: true,
- IncludeIncomplete: true,
- },
- firstIndex: 6,
- lastIndex: 7,
- expectedSeqNrs: []uint64{6, 7},
- },
- {
- name: "query in reverse order, start at end",
- query: PaymentsQuery{
- IndexOffset: 0,
- MaxPayments: 2,
- Reversed: true,
- IncludeIncomplete: true,
- },
- firstIndex: 6,
- lastIndex: 7,
- expectedSeqNrs: []uint64{6, 7},
- },
- {
- name: "query in reverse order, starting in middle",
- query: PaymentsQuery{
- IndexOffset: 4,
- MaxPayments: 2,
- Reversed: true,
- IncludeIncomplete: true,
- },
- firstIndex: 1,
- lastIndex: 3,
- expectedSeqNrs: []uint64{1, 3},
- },
- {
- name: "query in reverse order, starting in middle, " +
- "with underflow",
- query: PaymentsQuery{
- IndexOffset: 4,
- MaxPayments: 5,
- Reversed: true,
- IncludeIncomplete: true,
- },
- firstIndex: 1,
- lastIndex: 3,
- expectedSeqNrs: []uint64{1, 3},
- },
- {
- name: "all payments in reverse, order maintained",
- query: PaymentsQuery{
- IndexOffset: 0,
- MaxPayments: 7,
- Reversed: true,
- IncludeIncomplete: true,
- },
- firstIndex: 1,
- lastIndex: 7,
- expectedSeqNrs: []uint64{1, 3, 4, 5, 6, 7},
- },
- {
- name: "exclude incomplete payments",
- query: PaymentsQuery{
- IndexOffset: 0,
- MaxPayments: 7,
- Reversed: false,
- IncludeIncomplete: false,
- },
- firstIndex: 7,
- lastIndex: 7,
- expectedSeqNrs: []uint64{7},
- },
- {
- name: "query payments at index gap",
- query: PaymentsQuery{
- IndexOffset: 1,
- MaxPayments: 7,
- Reversed: false,
- IncludeIncomplete: true,
- },
- firstIndex: 3,
- lastIndex: 7,
- expectedSeqNrs: []uint64{3, 4, 5, 6, 7},
- },
- {
- name: "query payments reverse before index gap",
- query: PaymentsQuery{
- IndexOffset: 3,
- MaxPayments: 7,
- Reversed: true,
- IncludeIncomplete: true,
- },
- firstIndex: 1,
- lastIndex: 1,
- expectedSeqNrs: []uint64{1},
- },
- {
- name: "query payments reverse on index gap",
- query: PaymentsQuery{
- IndexOffset: 2,
- MaxPayments: 7,
- Reversed: true,
- IncludeIncomplete: true,
- },
- firstIndex: 1,
- lastIndex: 1,
- expectedSeqNrs: []uint64{1},
- },
- {
- name: "query payments forward on index gap",
- query: PaymentsQuery{
- IndexOffset: 2,
- MaxPayments: 2,
- Reversed: false,
- IncludeIncomplete: true,
- },
- firstIndex: 3,
- lastIndex: 4,
- expectedSeqNrs: []uint64{3, 4},
- },
- {
- name: "query in forwards order, with start creation " +
- "time",
- query: PaymentsQuery{
- IndexOffset: 0,
- MaxPayments: 2,
- Reversed: false,
- IncludeIncomplete: true,
- CreationDateStart: 5,
- },
- firstIndex: 5,
- lastIndex: 6,
- expectedSeqNrs: []uint64{5, 6},
- },
- {
- name: "query in forwards order, with start creation " +
- "time at end, overflow",
- query: PaymentsQuery{
- IndexOffset: 0,
- MaxPayments: 2,
- Reversed: false,
- IncludeIncomplete: true,
- CreationDateStart: 7,
- },
- firstIndex: 7,
- lastIndex: 7,
- expectedSeqNrs: []uint64{7},
- },
- {
- name: "query with start and end creation time",
- query: PaymentsQuery{
- IndexOffset: 9,
- MaxPayments: math.MaxUint64,
- Reversed: true,
- IncludeIncomplete: true,
- CreationDateStart: 3,
- CreationDateEnd: 5,
- },
- firstIndex: 3,
- lastIndex: 5,
- expectedSeqNrs: []uint64{3, 4, 5},
- },
- }
- for _, tt := range tests {
- tt := tt
- t.Run(tt.name, func(t *testing.T) {
- t.Parallel()
- db, err := MakeTestDB(t)
- if err != nil {
- t.Fatalf("unable to init db: %v", err)
- }
- // Make a preliminary query to make sure it's ok to
- // query when we have no payments.
- resp, err := db.QueryPayments(tt.query)
- require.NoError(t, err)
- require.Len(t, resp.Payments, 0)
- // Populate the database with a set of test payments.
- // We create 6 original payments, deleting the payment
- // at index 2 so that we cover the case where sequence
- // numbers are missing. We also add a duplicate payment
- // to the last payment added to test the legacy case
- // where we have duplicates in the nested duplicates
- // bucket.
- nonDuplicatePayments := 6
- pControl := NewPaymentControl(db)
- for i := 0; i < nonDuplicatePayments; i++ {
- // Generate a test payment.
- info, _, preimg, err := genInfo()
- if err != nil {
- t.Fatalf("unable to create test "+
- "payment: %v", err)
- }
- // Override creation time to allow for testing
- // of CreationDateStart and CreationDateEnd.
- info.CreationTime = time.Unix(int64(i+1), 0)
- // Create a new payment entry in the database.
- err = pControl.InitPayment(info.PaymentIdentifier, info)
- if err != nil {
- t.Fatalf("unable to initialize "+
- "payment in database: %v", err)
- }
- // Immediately delete the payment with index 2.
- if i == 1 {
- pmt, err := pControl.FetchPayment(
- info.PaymentIdentifier,
- )
- require.NoError(t, err)
- deletePayment(t, db, info.PaymentIdentifier,
- pmt.SequenceNum)
- }
- // If we are on the last payment entry, add a
- // duplicate payment with sequence number equal
- // to the parent payment + 1. Note that
- // duplicate payments will always be succeeded.
- if i == (nonDuplicatePayments - 1) {
- pmt, err := pControl.FetchPayment(
- info.PaymentIdentifier,
- )
- require.NoError(t, err)
- appendDuplicatePayment(
- t, pControl.db,
- info.PaymentIdentifier,
- pmt.SequenceNum+1,
- preimg,
- )
- }
- }
- // Fetch all payments in the database.
- allPayments, err := db.FetchPayments()
- if err != nil {
- t.Fatalf("payments could not be fetched from "+
- "database: %v", err)
- }
- if len(allPayments) != 6 {
- t.Fatalf("Number of payments received does not "+
- "match expected one. Got %v, want %v.",
- len(allPayments), 6)
- }
- querySlice, err := db.QueryPayments(tt.query)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if tt.firstIndex != querySlice.FirstIndexOffset ||
- tt.lastIndex != querySlice.LastIndexOffset {
- t.Errorf("First or last index does not match "+
- "expected index. Want (%d, %d), got (%d, %d).",
- tt.firstIndex, tt.lastIndex,
- querySlice.FirstIndexOffset,
- querySlice.LastIndexOffset)
- }
- if len(querySlice.Payments) != len(tt.expectedSeqNrs) {
- t.Errorf("expected: %v payments, got: %v",
- len(tt.expectedSeqNrs), len(querySlice.Payments))
- }
- for i, seqNr := range tt.expectedSeqNrs {
- q := querySlice.Payments[i]
- if seqNr != q.SequenceNum {
- t.Errorf("sequence numbers do not match, "+
- "got %v, want %v", q.SequenceNum, seqNr)
- }
- }
- })
- }
- }
- // TestFetchPaymentWithSequenceNumber tests lookup of payments with their
- // sequence number. It sets up one payment with no duplicates, and another with
- // two duplicates in its duplicates bucket then uses these payments to test the
- // case where a specific duplicate is not found and the duplicates bucket is not
- // present when we expect it to be.
- func TestFetchPaymentWithSequenceNumber(t *testing.T) {
- db, err := MakeTestDB(t)
- require.NoError(t, err)
- pControl := NewPaymentControl(db)
- // Generate a test payment which does not have duplicates.
- noDuplicates, _, _, err := genInfo()
- require.NoError(t, err)
- // Create a new payment entry in the database.
- err = pControl.InitPayment(noDuplicates.PaymentIdentifier, noDuplicates)
- require.NoError(t, err)
- // Fetch the payment so we can get its sequence nr.
- noDuplicatesPayment, err := pControl.FetchPayment(
- noDuplicates.PaymentIdentifier,
- )
- require.NoError(t, err)
- // Generate a test payment which we will add duplicates to.
- hasDuplicates, _, preimg, err := genInfo()
- require.NoError(t, err)
- // Create a new payment entry in the database.
- err = pControl.InitPayment(hasDuplicates.PaymentIdentifier, hasDuplicates)
- require.NoError(t, err)
- // Fetch the payment so we can get its sequence nr.
- hasDuplicatesPayment, err := pControl.FetchPayment(
- hasDuplicates.PaymentIdentifier,
- )
- require.NoError(t, err)
- // We declare the sequence numbers used here so that we can reference
- // them in tests.
- var (
- duplicateOneSeqNr = hasDuplicatesPayment.SequenceNum + 1
- duplicateTwoSeqNr = hasDuplicatesPayment.SequenceNum + 2
- )
- // Add two duplicates to our second payment.
- appendDuplicatePayment(
- t, db, hasDuplicates.PaymentIdentifier, duplicateOneSeqNr, preimg,
- )
- appendDuplicatePayment(
- t, db, hasDuplicates.PaymentIdentifier, duplicateTwoSeqNr, preimg,
- )
- tests := []struct {
- name string
- paymentHash lntypes.Hash
- sequenceNumber uint64
- expectedErr error
- }{
- {
- name: "lookup payment without duplicates",
- paymentHash: noDuplicates.PaymentIdentifier,
- sequenceNumber: noDuplicatesPayment.SequenceNum,
- expectedErr: nil,
- },
- {
- name: "lookup payment with duplicates",
- paymentHash: hasDuplicates.PaymentIdentifier,
- sequenceNumber: hasDuplicatesPayment.SequenceNum,
- expectedErr: nil,
- },
- {
- name: "lookup first duplicate",
- paymentHash: hasDuplicates.PaymentIdentifier,
- sequenceNumber: duplicateOneSeqNr,
- expectedErr: nil,
- },
- {
- name: "lookup second duplicate",
- paymentHash: hasDuplicates.PaymentIdentifier,
- sequenceNumber: duplicateTwoSeqNr,
- expectedErr: nil,
- },
- {
- name: "lookup non-existent duplicate",
- paymentHash: hasDuplicates.PaymentIdentifier,
- sequenceNumber: 999999,
- expectedErr: ErrDuplicateNotFound,
- },
- {
- name: "lookup duplicate, no duplicates bucket",
- paymentHash: noDuplicates.PaymentIdentifier,
- sequenceNumber: duplicateTwoSeqNr,
- expectedErr: ErrNoDuplicateBucket,
- },
- }
- for _, test := range tests {
- test := test
- t.Run(test.name, func(t *testing.T) {
- err := kvdb.Update(
- db, func(tx walletdb.ReadWriteTx) error {
- var seqNrBytes [8]byte
- byteOrder.PutUint64(
- seqNrBytes[:], test.sequenceNumber,
- )
- _, err := fetchPaymentWithSequenceNumber(
- tx, test.paymentHash, seqNrBytes[:],
- )
- return err
- }, func() {},
- )
- require.Equal(t, test.expectedErr, err)
- })
- }
- }
- // appendDuplicatePayment adds a duplicate payment to an existing payment. Note
- // that this function requires a unique sequence number.
- //
- // This code is *only* intended to replicate legacy duplicate payments in lnd,
- // our current schema does not allow duplicates.
- func appendDuplicatePayment(t *testing.T, db *DB, paymentHash lntypes.Hash,
- seqNr uint64, preImg lntypes.Preimage) {
- err := kvdb.Update(db, func(tx walletdb.ReadWriteTx) error {
- bucket, err := fetchPaymentBucketUpdate(
- tx, paymentHash,
- )
- if err != nil {
- return err
- }
- // Create the duplicates bucket if it is not
- // present.
- dup, err := bucket.CreateBucketIfNotExists(
- duplicatePaymentsBucket,
- )
- if err != nil {
- return err
- }
- var sequenceKey [8]byte
- byteOrder.PutUint64(sequenceKey[:], seqNr)
- // Create duplicate payments for the two dup
- // sequence numbers we've setup.
- putDuplicatePayment(t, dup, sequenceKey[:], paymentHash, preImg)
- // Finally, once we have created our entry we add an index for
- // it.
- err = createPaymentIndexEntry(tx, sequenceKey[:], paymentHash)
- require.NoError(t, err)
- return nil
- }, func() {})
- require.NoError(t, err, "could not create payment")
- }
- // putDuplicatePayment creates a duplicate payment in the duplicates bucket
- // provided with the minimal information required for successful reading.
- func putDuplicatePayment(t *testing.T, duplicateBucket kvdb.RwBucket,
- sequenceKey []byte, paymentHash lntypes.Hash,
- preImg lntypes.Preimage) {
- paymentBucket, err := duplicateBucket.CreateBucketIfNotExists(
- sequenceKey,
- )
- require.NoError(t, err)
- err = paymentBucket.Put(duplicatePaymentSequenceKey, sequenceKey)
- require.NoError(t, err)
- // Generate fake information for the duplicate payment.
- info, _, _, err := genInfo()
- require.NoError(t, err)
- // Write the payment info to disk under the creation info key. This code
- // is copied rather than using serializePaymentCreationInfo to ensure
- // we always write in the legacy format used by duplicate payments.
- var b bytes.Buffer
- var scratch [8]byte
- _, err = b.Write(paymentHash[:])
- require.NoError(t, err)
- byteOrder.PutUint64(scratch[:], uint64(info.Value))
- _, err = b.Write(scratch[:])
- require.NoError(t, err)
- err = serializeTime(&b, info.CreationTime)
- require.NoError(t, err)
- byteOrder.PutUint32(scratch[:4], 0)
- _, err = b.Write(scratch[:4])
- require.NoError(t, err)
- // Get the PaymentCreationInfo.
- err = paymentBucket.Put(duplicatePaymentCreationInfoKey, b.Bytes())
- require.NoError(t, err)
- // Duolicate payments are only stored for successes, so add the
- // preimage.
- err = paymentBucket.Put(duplicatePaymentSettleInfoKey, preImg[:])
- require.NoError(t, err)
- }
|