mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 18:10:34 +01:00
211 lines
5.2 KiB
Go
211 lines
5.2 KiB
Go
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.
|
|
paymentBucket = []byte("payments")
|
|
)
|
|
|
|
// 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.
|
|
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.
|
|
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 *bolt.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.
|
|
func (db *DB) FetchAllPayments() ([]*OutgoingPayment, error) {
|
|
var payments []*OutgoingPayment
|
|
|
|
err := db.View(func(tx *bolt.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.
|
|
func (db *DB) DeleteAllPayments() error {
|
|
return db.Update(func(tx *bolt.Tx) error {
|
|
err := tx.DeleteBucket(paymentBucket)
|
|
if err != nil && err != bolt.ErrBucketNotFound {
|
|
return err
|
|
}
|
|
|
|
_, err = tx.CreateBucket(paymentBucket)
|
|
return err
|
|
})
|
|
}
|
|
|
|
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
|
|
}
|