From d78d3f50b9fe4ae769b14c980a0532e62a673985 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Thu, 23 May 2019 20:05:30 +0200 Subject: [PATCH] channeldb: move deprecated code to migration_09_legacy_serialization.go And unexport deprecated code. --- channeldb/legacy_serialization.go | 4 +- .../migration_09_legacy_serialization.go | 255 ++++++++++++++++++ channeldb/migrations_test.go | 20 +- channeldb/payments.go | 253 ----------------- channeldb/payments_test.go | 79 +----- 5 files changed, 272 insertions(+), 339 deletions(-) create mode 100644 channeldb/migration_09_legacy_serialization.go diff --git a/channeldb/legacy_serialization.go b/channeldb/legacy_serialization.go index 2abb3f049..3ca9b5ecf 100644 --- a/channeldb/legacy_serialization.go +++ b/channeldb/legacy_serialization.go @@ -1,6 +1,8 @@ package channeldb -import "io" +import ( + "io" +) // deserializeCloseChannelSummaryV6 reads the v6 database format for // ChannelCloseSummary. diff --git a/channeldb/migration_09_legacy_serialization.go b/channeldb/migration_09_legacy_serialization.go new file mode 100644 index 000000000..ae0d7382b --- /dev/null +++ b/channeldb/migration_09_legacy_serialization.go @@ -0,0 +1,255 @@ +package channeldb + +import ( + "bytes" + "encoding/binary" + "io" + + "github.com/coreos/bbolt" + "github.com/lightningnetwork/lnd/lnwire" +) + +var ( + // paymentBucket is the name of the bucket within the database that + // stores all data related to payments. + // + // Within the payments bucket, each invoice is keyed by its invoice ID + // which is a monotonically increasing uint64. BoltDB's sequence + // feature is used for generating monotonically increasing id. + // + // NOTE: Deprecated. Kept around for migration purposes. + paymentBucket = []byte("payments") + + // paymentStatusBucket is the name of the bucket within the database + // that stores the status of a payment indexed by the payment's + // preimage. + // + // NOTE: Deprecated. Kept around for migration purposes. + paymentStatusBucket = []byte("payment-status") +) + +// outgoingPayment represents a successful payment between the daemon and a +// remote node. Details such as the total fee paid, and the time of the payment +// are stored. +// +// NOTE: Deprecated. Kept around for migration purposes. +type outgoingPayment struct { + Invoice + + // Fee is the total fee paid for the payment in milli-satoshis. + Fee lnwire.MilliSatoshi + + // TotalTimeLock is the total cumulative time-lock in the HTLC extended + // from the second-to-last hop to the destination. + TimeLockLength uint32 + + // Path encodes the path the payment took through the network. The path + // excludes the outgoing node and consists of the hex-encoded + // compressed public key of each of the nodes involved in the payment. + Path [][33]byte + + // PaymentPreimage is the preImage of a successful payment. This is used + // to calculate the PaymentHash as well as serve as a proof of payment. + PaymentPreimage [32]byte +} + +// addPayment saves a successful payment to the database. It is assumed that +// all payment are sent using unique payment hashes. +// +// NOTE: Deprecated. Kept around for migration purposes. +func (db *DB) addPayment(payment *outgoingPayment) error { + // Validate the field of the inner voice within the outgoing payment, + // these must also adhere to the same constraints as regular invoices. + if err := validateInvoice(&payment.Invoice); err != nil { + return err + } + + // We first serialize the payment before starting the database + // transaction so we can avoid creating a DB payment in the case of a + // serialization error. + var b bytes.Buffer + if err := serializeOutgoingPayment(&b, payment); err != nil { + return err + } + paymentBytes := b.Bytes() + + return db.Batch(func(tx *bbolt.Tx) error { + payments, err := tx.CreateBucketIfNotExists(paymentBucket) + if err != nil { + return err + } + + // Obtain the new unique sequence number for this payment. + paymentID, err := payments.NextSequence() + if err != nil { + return err + } + + // We use BigEndian for keys as it orders keys in + // ascending order. This allows bucket scans to order payments + // in the order in which they were created. + paymentIDBytes := make([]byte, 8) + binary.BigEndian.PutUint64(paymentIDBytes, paymentID) + + return payments.Put(paymentIDBytes, paymentBytes) + }) +} + +// fetchAllPayments returns all outgoing payments in DB. +// +// NOTE: Deprecated. Kept around for migration purposes. +func (db *DB) fetchAllPayments() ([]*outgoingPayment, error) { + var payments []*outgoingPayment + + err := db.View(func(tx *bbolt.Tx) error { + bucket := tx.Bucket(paymentBucket) + if bucket == nil { + return ErrNoPaymentsCreated + } + + return bucket.ForEach(func(k, v []byte) error { + // If the value is nil, then we ignore it as it may be + // a sub-bucket. + if v == nil { + return nil + } + + r := bytes.NewReader(v) + payment, err := deserializeOutgoingPayment(r) + if err != nil { + return err + } + + payments = append(payments, payment) + return nil + }) + }) + if err != nil { + return nil, err + } + + return payments, nil +} + +// fetchPaymentStatus returns the payment status for outgoing payment. +// If status of the payment isn't found, it will default to "StatusGrounded". +// +// NOTE: Deprecated. Kept around for migration purposes. +func (db *DB) fetchPaymentStatus(paymentHash [32]byte) (PaymentStatus, error) { + var paymentStatus = StatusGrounded + err := db.View(func(tx *bbolt.Tx) error { + var err error + paymentStatus, err = fetchPaymentStatusTx(tx, paymentHash) + return err + }) + if err != nil { + return StatusGrounded, err + } + + return paymentStatus, nil +} + +// fetchPaymentStatusTx is a helper method that returns the payment status for +// outgoing payment. If status of the payment isn't found, it will default to +// "StatusGrounded". It accepts the boltdb transactions such that this method +// can be composed into other atomic operations. +// +// NOTE: Deprecated. Kept around for migration purposes. +func fetchPaymentStatusTx(tx *bbolt.Tx, paymentHash [32]byte) (PaymentStatus, error) { + // The default status for all payments that aren't recorded in database. + var paymentStatus = StatusGrounded + + bucket := tx.Bucket(paymentStatusBucket) + if bucket == nil { + return paymentStatus, nil + } + + paymentStatusBytes := bucket.Get(paymentHash[:]) + if paymentStatusBytes == nil { + return paymentStatus, nil + } + + paymentStatus.FromBytes(paymentStatusBytes) + + return paymentStatus, nil +} + +func serializeOutgoingPayment(w io.Writer, p *outgoingPayment) error { + var scratch [8]byte + + if err := serializeInvoice(w, &p.Invoice); err != nil { + return err + } + + byteOrder.PutUint64(scratch[:], uint64(p.Fee)) + if _, err := w.Write(scratch[:]); err != nil { + return err + } + + // First write out the length of the bytes to prefix the value. + pathLen := uint32(len(p.Path)) + byteOrder.PutUint32(scratch[:4], pathLen) + if _, err := w.Write(scratch[:4]); err != nil { + return err + } + + // Then with the path written, we write out the series of public keys + // involved in the path. + for _, hop := range p.Path { + if _, err := w.Write(hop[:]); err != nil { + return err + } + } + + byteOrder.PutUint32(scratch[:4], p.TimeLockLength) + if _, err := w.Write(scratch[:4]); err != nil { + return err + } + + if _, err := w.Write(p.PaymentPreimage[:]); err != nil { + return err + } + + return nil +} + +func deserializeOutgoingPayment(r io.Reader) (*outgoingPayment, error) { + var scratch [8]byte + + p := &outgoingPayment{} + + inv, err := deserializeInvoice(r) + if err != nil { + return nil, err + } + p.Invoice = inv + + if _, err := r.Read(scratch[:]); err != nil { + return nil, err + } + p.Fee = lnwire.MilliSatoshi(byteOrder.Uint64(scratch[:])) + + if _, err = r.Read(scratch[:4]); err != nil { + return nil, err + } + pathLen := byteOrder.Uint32(scratch[:4]) + + path := make([][33]byte, pathLen) + for i := uint32(0); i < pathLen; i++ { + if _, err := r.Read(path[i][:]); err != nil { + return nil, err + } + } + p.Path = path + + if _, err = r.Read(scratch[:4]); err != nil { + return nil, err + } + p.TimeLockLength = byteOrder.Uint32(scratch[:4]) + + if _, err := r.Read(p.PaymentPreimage[:]); err != nil { + return nil, err + } + + return p, nil +} diff --git a/channeldb/migrations_test.go b/channeldb/migrations_test.go index bd07951ed..d04a352c5 100644 --- a/channeldb/migrations_test.go +++ b/channeldb/migrations_test.go @@ -26,11 +26,11 @@ func TestPaymentStatusesMigration(t *testing.T) { // Add fake payment to test database, verifying that it was created, // that we have only one payment, and its status is not "Completed". beforeMigrationFunc := func(d *DB) { - if err := d.AddPayment(fakePayment); err != nil { + if err := d.addPayment(fakePayment); err != nil { t.Fatalf("unable to add payment: %v", err) } - payments, err := d.FetchAllPayments() + payments, err := d.fetchAllPayments() if err != nil { t.Fatalf("unable to fetch payments: %v", err) } @@ -40,7 +40,7 @@ func TestPaymentStatusesMigration(t *testing.T) { len(payments)) } - paymentStatus, err := d.FetchPaymentStatus(paymentHash) + paymentStatus, err := d.fetchPaymentStatus(paymentHash) if err != nil { t.Fatalf("unable to fetch payment status: %v", err) } @@ -141,7 +141,7 @@ func TestPaymentStatusesMigration(t *testing.T) { } // Check that our completed payments were migrated. - paymentStatus, err := d.FetchPaymentStatus(paymentHash) + paymentStatus, err := d.fetchPaymentStatus(paymentHash) if err != nil { t.Fatalf("unable to fetch payment status: %v", err) } @@ -160,7 +160,7 @@ func TestPaymentStatusesMigration(t *testing.T) { // Check that the locally sourced payment was transitioned to // InFlight. - paymentStatus, err = d.FetchPaymentStatus(inFlightHash) + paymentStatus, err = d.fetchPaymentStatus(inFlightHash) if err != nil { t.Fatalf("unable to fetch payment status: %v", err) } @@ -179,7 +179,7 @@ func TestPaymentStatusesMigration(t *testing.T) { // Check that non-locally sourced payments remain in the default // Grounded state. - paymentStatus, err = d.FetchPaymentStatus(groundedHash) + paymentStatus, err = d.fetchPaymentStatus(groundedHash) if err != nil { t.Fatalf("unable to fetch payment status: %v", err) } @@ -571,12 +571,12 @@ func TestOutgoingPaymentsMigration(t *testing.T) { t.Parallel() const numPayments = 4 - var oldPayments []*OutgoingPayment + var oldPayments []*outgoingPayment // Add fake payments to test database, verifying that it was created. beforeMigrationFunc := func(d *DB) { for i := 0; i < numPayments; i++ { - var p *OutgoingPayment + var p *outgoingPayment var err error // We fill the database with random payments. For the @@ -593,14 +593,14 @@ func TestOutgoingPaymentsMigration(t *testing.T) { p = oldPayments[0] } - if err := d.AddPayment(p); err != nil { + if err := d.addPayment(p); err != nil { t.Fatalf("unable to add payment: %v", err) } oldPayments = append(oldPayments, p) } - payments, err := d.FetchAllPayments() + payments, err := d.fetchAllPayments() if err != nil { t.Fatalf("unable to fetch payments: %v", err) } diff --git a/channeldb/payments.go b/channeldb/payments.go index 036f25ffa..816657011 100644 --- a/channeldb/payments.go +++ b/channeldb/payments.go @@ -84,18 +84,6 @@ var ( // paymentFailInfoKey is a key used in the payment's sub-bucket to // store information about the reason a payment failed. paymentFailInfoKey = []byte("payment-fail-info") - - // paymentBucket is the name of the bucket within the database that - // stores all data related to payments. - // - // Within the payments bucket, each invoice is keyed by its invoice ID - // which is a monotonically increasing uint64. BoltDB's sequence - // feature is used for generating monotonically increasing id. - paymentBucket = []byte("payments") - - // paymentStatusBucket is the name of the bucket within the database that - // stores the status of a payment indexed by the payment's preimage. - paymentStatusBucket = []byte("payment-status") ) // FailureReason encodes the reason a payment ultimately failed. @@ -171,247 +159,6 @@ func (ps PaymentStatus) String() string { } } -// OutgoingPayment represents a successful payment between the daemon and a -// remote node. Details such as the total fee paid, and the time of the payment -// are stored. -// -// NOTE: Deprecated. Kept around for migration purposes. -type OutgoingPayment struct { - Invoice - - // Fee is the total fee paid for the payment in milli-satoshis. - Fee lnwire.MilliSatoshi - - // TotalTimeLock is the total cumulative time-lock in the HTLC extended - // from the second-to-last hop to the destination. - TimeLockLength uint32 - - // Path encodes the path the payment took through the network. The path - // excludes the outgoing node and consists of the hex-encoded - // compressed public key of each of the nodes involved in the payment. - Path [][33]byte - - // PaymentPreimage is the preImage of a successful payment. This is used - // to calculate the PaymentHash as well as serve as a proof of payment. - PaymentPreimage [32]byte -} - -// AddPayment saves a successful payment to the database. It is assumed that -// all payment are sent using unique payment hashes. -// -// NOTE: Deprecated. Kept around for migration purposes. -func (db *DB) AddPayment(payment *OutgoingPayment) error { - // Validate the field of the inner voice within the outgoing payment, - // these must also adhere to the same constraints as regular invoices. - if err := validateInvoice(&payment.Invoice); err != nil { - return err - } - - // We first serialize the payment before starting the database - // transaction so we can avoid creating a DB payment in the case of a - // serialization error. - var b bytes.Buffer - if err := serializeOutgoingPayment(&b, payment); err != nil { - return err - } - paymentBytes := b.Bytes() - - return db.Batch(func(tx *bbolt.Tx) error { - payments, err := tx.CreateBucketIfNotExists(paymentBucket) - if err != nil { - return err - } - - // Obtain the new unique sequence number for this payment. - paymentID, err := payments.NextSequence() - if err != nil { - return err - } - - // We use BigEndian for keys as it orders keys in - // ascending order. This allows bucket scans to order payments - // in the order in which they were created. - paymentIDBytes := make([]byte, 8) - binary.BigEndian.PutUint64(paymentIDBytes, paymentID) - - return payments.Put(paymentIDBytes, paymentBytes) - }) -} - -// FetchAllPayments returns all outgoing payments in DB. -// -// NOTE: Deprecated. Kept around for migration purposes. -func (db *DB) FetchAllPayments() ([]*OutgoingPayment, error) { - var payments []*OutgoingPayment - - err := db.View(func(tx *bbolt.Tx) error { - bucket := tx.Bucket(paymentBucket) - if bucket == nil { - return ErrNoPaymentsCreated - } - - return bucket.ForEach(func(k, v []byte) error { - // If the value is nil, then we ignore it as it may be - // a sub-bucket. - if v == nil { - return nil - } - - r := bytes.NewReader(v) - payment, err := deserializeOutgoingPayment(r) - if err != nil { - return err - } - - payments = append(payments, payment) - return nil - }) - }) - if err != nil { - return nil, err - } - - return payments, nil -} - -// DeleteAllPayments deletes all payments from DB. -// -// NOTE: Deprecated. Kept around for migration purposes. -func (db *DB) DeleteAllPayments() error { - return db.Update(func(tx *bbolt.Tx) error { - err := tx.DeleteBucket(paymentBucket) - if err != nil && err != bbolt.ErrBucketNotFound { - return err - } - - _, err = tx.CreateBucket(paymentBucket) - return err - }) -} - -// FetchPaymentStatus returns the payment status for outgoing payment. -// If status of the payment isn't found, it will default to "StatusGrounded". -// -// NOTE: Deprecated. Kept around for migration purposes. -func (db *DB) FetchPaymentStatus(paymentHash [32]byte) (PaymentStatus, error) { - var paymentStatus = StatusGrounded - err := db.View(func(tx *bbolt.Tx) error { - var err error - paymentStatus, err = FetchPaymentStatusTx(tx, paymentHash) - return err - }) - if err != nil { - return StatusGrounded, err - } - - return paymentStatus, nil -} - -// FetchPaymentStatusTx is a helper method that returns the payment status for -// outgoing payment. If status of the payment isn't found, it will default to -// "StatusGrounded". It accepts the boltdb transactions such that this method -// can be composed into other atomic operations. -// -// NOTE: Deprecated. Kept around for migration purposes. -func FetchPaymentStatusTx(tx *bbolt.Tx, paymentHash [32]byte) (PaymentStatus, error) { - // The default status for all payments that aren't recorded in database. - var paymentStatus = StatusGrounded - - bucket := tx.Bucket(paymentStatusBucket) - if bucket == nil { - return paymentStatus, nil - } - - paymentStatusBytes := bucket.Get(paymentHash[:]) - if paymentStatusBytes == nil { - return paymentStatus, nil - } - - paymentStatus.FromBytes(paymentStatusBytes) - - return paymentStatus, nil -} - -func serializeOutgoingPayment(w io.Writer, p *OutgoingPayment) error { - var scratch [8]byte - - if err := serializeInvoice(w, &p.Invoice); err != nil { - return err - } - - byteOrder.PutUint64(scratch[:], uint64(p.Fee)) - if _, err := w.Write(scratch[:]); err != nil { - return err - } - - // First write out the length of the bytes to prefix the value. - pathLen := uint32(len(p.Path)) - byteOrder.PutUint32(scratch[:4], pathLen) - if _, err := w.Write(scratch[:4]); err != nil { - return err - } - - // Then with the path written, we write out the series of public keys - // involved in the path. - for _, hop := range p.Path { - if _, err := w.Write(hop[:]); err != nil { - return err - } - } - - byteOrder.PutUint32(scratch[:4], p.TimeLockLength) - if _, err := w.Write(scratch[:4]); err != nil { - return err - } - - if _, err := w.Write(p.PaymentPreimage[:]); err != nil { - return err - } - - return nil -} - -func deserializeOutgoingPayment(r io.Reader) (*OutgoingPayment, error) { - var scratch [8]byte - - p := &OutgoingPayment{} - - inv, err := deserializeInvoice(r) - if err != nil { - return nil, err - } - p.Invoice = inv - - if _, err := r.Read(scratch[:]); err != nil { - return nil, err - } - p.Fee = lnwire.MilliSatoshi(byteOrder.Uint64(scratch[:])) - - if _, err = r.Read(scratch[:4]); err != nil { - return nil, err - } - pathLen := byteOrder.Uint32(scratch[:4]) - - path := make([][33]byte, pathLen) - for i := uint32(0); i < pathLen; i++ { - if _, err := r.Read(path[i][:]); err != nil { - return nil, err - } - } - p.Path = path - - if _, err = r.Read(scratch[:4]); err != nil { - return nil, err - } - p.TimeLockLength = byteOrder.Uint32(scratch[:4]) - - if _, err := r.Read(p.PaymentPreimage[:]); err != nil { - return nil, err - } - - return p, nil -} - // PaymentCreationInfo is the information necessary to have ready when // initiating a payment, moving it into state InFlight. type PaymentCreationInfo struct { diff --git a/channeldb/payments_test.go b/channeldb/payments_test.go index 40ab76502..2be1f38b8 100644 --- a/channeldb/payments_test.go +++ b/channeldb/payments_test.go @@ -37,7 +37,7 @@ var ( } ) -func makeFakePayment() *OutgoingPayment { +func makeFakePayment() *outgoingPayment { fakeInvoice := &Invoice{ // Use single second precision to avoid false positive test // failures due to the monotonic time component. @@ -55,7 +55,7 @@ func makeFakePayment() *OutgoingPayment { copy(fakePath[i][:], bytes.Repeat([]byte{byte(i)}, 33)) } - fakePayment := &OutgoingPayment{ + fakePayment := &outgoingPayment{ Invoice: *fakeInvoice, Fee: 101, Path: fakePath, @@ -106,7 +106,7 @@ func randomBytes(minLen, maxLen int) ([]byte, error) { return randBuf, nil } -func makeRandomFakePayment() (*OutgoingPayment, error) { +func makeRandomFakePayment() (*outgoingPayment, error) { var err error fakeInvoice := &Invoice{ // Use single second precision to avoid false positive test @@ -147,7 +147,7 @@ func makeRandomFakePayment() (*OutgoingPayment, error) { copy(fakePath[i][:], b) } - fakePayment := &OutgoingPayment{ + fakePayment := &outgoingPayment{ Invoice: *fakeInvoice, Fee: lnwire.MilliSatoshi(rand.Intn(1001)), Path: fakePath, @@ -199,77 +199,6 @@ func TestSentPaymentSerialization(t *testing.T) { } -func TestOutgoingPaymentWorkflow(t *testing.T) { - t.Parallel() - - db, cleanUp, err := makeTestDB() - defer cleanUp() - if err != nil { - t.Fatalf("unable to make test db: %v", err) - } - - fakePayment := makeFakePayment() - if err = db.AddPayment(fakePayment); err != nil { - t.Fatalf("unable to put payment in DB: %v", err) - } - - payments, err := db.FetchAllPayments() - if err != nil { - t.Fatalf("unable to fetch payments from DB: %v", err) - } - - expectedPayments := []*OutgoingPayment{fakePayment} - if !reflect.DeepEqual(payments, expectedPayments) { - t.Fatalf("Wrong payments after reading from DB."+ - "Got %v, want %v", - spew.Sdump(payments), - spew.Sdump(expectedPayments), - ) - } - - // Make some random payments - for i := 0; i < 5; i++ { - randomPayment, err := makeRandomFakePayment() - if err != nil { - t.Fatalf("Internal error in tests: %v", err) - } - - if err = db.AddPayment(randomPayment); err != nil { - t.Fatalf("unable to put payment in DB: %v", err) - } - - expectedPayments = append(expectedPayments, randomPayment) - } - - payments, err = db.FetchAllPayments() - if err != nil { - t.Fatalf("Can't get payments from DB: %v", err) - } - - if !reflect.DeepEqual(payments, expectedPayments) { - t.Fatalf("Wrong payments after reading from DB."+ - "Got %v, want %v", - spew.Sdump(payments), - spew.Sdump(expectedPayments), - ) - } - - // Delete all payments. - if err = db.DeleteAllPayments(); err != nil { - t.Fatalf("unable to delete payments from DB: %v", err) - } - - // Check that there is no payments after deletion - paymentsAfterDeletion, err := db.FetchAllPayments() - if err != nil { - t.Fatalf("Can't get payments after deletion: %v", err) - } - if len(paymentsAfterDeletion) != 0 { - t.Fatalf("After deletion DB has %v payments, want %v", - len(paymentsAfterDeletion), 0) - } -} - func TestRouteSerialization(t *testing.T) { t.Parallel()