channeldb: move deprecated code to migration_09_legacy_serialization.go

And unexport deprecated code.
This commit is contained in:
Johan T. Halseth 2019-05-23 20:05:30 +02:00
parent 1b788904f0
commit d78d3f50b9
No known key found for this signature in database
GPG Key ID: 15BAADA29DA20D26
5 changed files with 272 additions and 339 deletions

@ -1,6 +1,8 @@
package channeldb package channeldb
import "io" import (
"io"
)
// deserializeCloseChannelSummaryV6 reads the v6 database format for // deserializeCloseChannelSummaryV6 reads the v6 database format for
// ChannelCloseSummary. // ChannelCloseSummary.

@ -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
}

@ -26,11 +26,11 @@ func TestPaymentStatusesMigration(t *testing.T) {
// Add fake payment to test database, verifying that it was created, // Add fake payment to test database, verifying that it was created,
// that we have only one payment, and its status is not "Completed". // that we have only one payment, and its status is not "Completed".
beforeMigrationFunc := func(d *DB) { 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) t.Fatalf("unable to add payment: %v", err)
} }
payments, err := d.FetchAllPayments() payments, err := d.fetchAllPayments()
if err != nil { if err != nil {
t.Fatalf("unable to fetch payments: %v", err) t.Fatalf("unable to fetch payments: %v", err)
} }
@ -40,7 +40,7 @@ func TestPaymentStatusesMigration(t *testing.T) {
len(payments)) len(payments))
} }
paymentStatus, err := d.FetchPaymentStatus(paymentHash) paymentStatus, err := d.fetchPaymentStatus(paymentHash)
if err != nil { if err != nil {
t.Fatalf("unable to fetch payment status: %v", err) 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. // Check that our completed payments were migrated.
paymentStatus, err := d.FetchPaymentStatus(paymentHash) paymentStatus, err := d.fetchPaymentStatus(paymentHash)
if err != nil { if err != nil {
t.Fatalf("unable to fetch payment status: %v", err) 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 // Check that the locally sourced payment was transitioned to
// InFlight. // InFlight.
paymentStatus, err = d.FetchPaymentStatus(inFlightHash) paymentStatus, err = d.fetchPaymentStatus(inFlightHash)
if err != nil { if err != nil {
t.Fatalf("unable to fetch payment status: %v", err) 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 // Check that non-locally sourced payments remain in the default
// Grounded state. // Grounded state.
paymentStatus, err = d.FetchPaymentStatus(groundedHash) paymentStatus, err = d.fetchPaymentStatus(groundedHash)
if err != nil { if err != nil {
t.Fatalf("unable to fetch payment status: %v", err) t.Fatalf("unable to fetch payment status: %v", err)
} }
@ -571,12 +571,12 @@ func TestOutgoingPaymentsMigration(t *testing.T) {
t.Parallel() t.Parallel()
const numPayments = 4 const numPayments = 4
var oldPayments []*OutgoingPayment var oldPayments []*outgoingPayment
// Add fake payments to test database, verifying that it was created. // Add fake payments to test database, verifying that it was created.
beforeMigrationFunc := func(d *DB) { beforeMigrationFunc := func(d *DB) {
for i := 0; i < numPayments; i++ { for i := 0; i < numPayments; i++ {
var p *OutgoingPayment var p *outgoingPayment
var err error var err error
// We fill the database with random payments. For the // We fill the database with random payments. For the
@ -593,14 +593,14 @@ func TestOutgoingPaymentsMigration(t *testing.T) {
p = oldPayments[0] 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) t.Fatalf("unable to add payment: %v", err)
} }
oldPayments = append(oldPayments, p) oldPayments = append(oldPayments, p)
} }
payments, err := d.FetchAllPayments() payments, err := d.fetchAllPayments()
if err != nil { if err != nil {
t.Fatalf("unable to fetch payments: %v", err) t.Fatalf("unable to fetch payments: %v", err)
} }

@ -84,18 +84,6 @@ var (
// paymentFailInfoKey is a key used in the payment's sub-bucket to // paymentFailInfoKey is a key used in the payment's sub-bucket to
// store information about the reason a payment failed. // store information about the reason a payment failed.
paymentFailInfoKey = []byte("payment-fail-info") 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. // 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 // PaymentCreationInfo is the information necessary to have ready when
// initiating a payment, moving it into state InFlight. // initiating a payment, moving it into state InFlight.
type PaymentCreationInfo struct { type PaymentCreationInfo struct {

@ -37,7 +37,7 @@ var (
} }
) )
func makeFakePayment() *OutgoingPayment { func makeFakePayment() *outgoingPayment {
fakeInvoice := &Invoice{ fakeInvoice := &Invoice{
// Use single second precision to avoid false positive test // Use single second precision to avoid false positive test
// failures due to the monotonic time component. // failures due to the monotonic time component.
@ -55,7 +55,7 @@ func makeFakePayment() *OutgoingPayment {
copy(fakePath[i][:], bytes.Repeat([]byte{byte(i)}, 33)) copy(fakePath[i][:], bytes.Repeat([]byte{byte(i)}, 33))
} }
fakePayment := &OutgoingPayment{ fakePayment := &outgoingPayment{
Invoice: *fakeInvoice, Invoice: *fakeInvoice,
Fee: 101, Fee: 101,
Path: fakePath, Path: fakePath,
@ -106,7 +106,7 @@ func randomBytes(minLen, maxLen int) ([]byte, error) {
return randBuf, nil return randBuf, nil
} }
func makeRandomFakePayment() (*OutgoingPayment, error) { func makeRandomFakePayment() (*outgoingPayment, error) {
var err error var err error
fakeInvoice := &Invoice{ fakeInvoice := &Invoice{
// Use single second precision to avoid false positive test // Use single second precision to avoid false positive test
@ -147,7 +147,7 @@ func makeRandomFakePayment() (*OutgoingPayment, error) {
copy(fakePath[i][:], b) copy(fakePath[i][:], b)
} }
fakePayment := &OutgoingPayment{ fakePayment := &outgoingPayment{
Invoice: *fakeInvoice, Invoice: *fakeInvoice,
Fee: lnwire.MilliSatoshi(rand.Intn(1001)), Fee: lnwire.MilliSatoshi(rand.Intn(1001)),
Path: fakePath, 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) { func TestRouteSerialization(t *testing.T) {
t.Parallel() t.Parallel()