lnd.xprv/channeldb/payments.go
BitfuryLightning 1c7f87c3f1
channeldb: refactor payments code
Go-fmt files. Refactored code according to the guidelines.
Enhanced payment test: add error checking
and individual context for each API call.
Add Timestamp field to payment struct.
2016-12-27 16:42:56 -08:00

249 lines
5.3 KiB
Go

package channeldb
import (
"bytes"
"encoding/binary"
"github.com/boltdb/bolt"
"github.com/roasbeef/btcd/wire"
"github.com/roasbeef/btcutil"
"io"
"time"
)
var (
// invoiceBucket 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 sequence feature is used for generating
// monotonically increasing id.
paymentBucket = []byte("payments")
)
// OutgoingPayment represents payment from given node.
type OutgoingPayment struct {
Invoice
// Total fee paid.
Fee btcutil.Amount
// Path including starting and ending nodes.
Path [][33]byte
// Timelock length.
TimeLockLength uint32
// RHash value used for payment.
// We need RHash because we start payment knowing only RHash
RHash [32]byte
// Timestamp is time when payment was created.
Timestamp time.Time
}
// AddPayment adds payment to DB.
// There is no checking that payment with the same hash already exist.
func (db *DB) AddPayment(p *OutgoingPayment) error {
err := validateInvoice(&p.Invoice)
if err != nil {
return err
}
// We serialize before writing to database
// so no db access in the case of serialization errors
b := new(bytes.Buffer)
err = serializeOutgoingPayment(b, p)
if err != nil {
return err
}
paymentBytes := b.Bytes()
return db.Update(func(tx *bolt.Tx) error {
payments, err := tx.CreateBucketIfNotExists(paymentBucket)
if err != nil {
return err
}
paymentId, err := payments.NextSequence()
if err != nil {
return err
}
// We use BigEndian for keys because
// it orders keys in ascending order
paymentIdBytes := make([]byte, 8)
binary.BigEndian.PutUint64(paymentIdBytes, paymentId)
err = payments.Put(paymentIdBytes, paymentBytes)
if err != nil {
return err
}
return nil
})
}
// 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
}
err := bucket.ForEach(func(k, v []byte) error {
// Value can be nil if it is a sub-backet
// so simply ignore it.
if v == nil {
return nil
}
r := bytes.NewReader(v)
payment, err := deserializeOutgoingPayment(r)
if err != nil {
return err
}
payments = append(payments, payment)
return nil
})
return err
})
if err != nil {
return nil, err
}
return payments, nil
}
// DeleteAllPayments deletes all payments from DB.
// If payments bucket does not exist it will create
// new bucket without error.
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)
if err != nil {
return err
}
return err
})
}
func serializeOutgoingPayment(w io.Writer, p *OutgoingPayment) error {
err := serializeInvoice(w, &p.Invoice)
if err != nil {
return err
}
// Serialize fee.
feeBytes := make([]byte, 8)
byteOrder.PutUint64(feeBytes, uint64(p.Fee))
_, err = w.Write(feeBytes)
if err != nil {
return err
}
// Serialize path.
pathLen := uint32(len(p.Path))
pathLenBytes := make([]byte, 4)
// Write length of the path
byteOrder.PutUint32(pathLenBytes, pathLen)
_, err = w.Write(pathLenBytes)
if err != nil {
return err
}
// Serialize each element of the path
for i := uint32(0); i < pathLen; i++ {
_, err := w.Write(p.Path[i][:])
if err != nil {
return err
}
}
// Serialize TimeLockLength
timeLockLengthBytes := make([]byte, 4)
byteOrder.PutUint32(timeLockLengthBytes, p.TimeLockLength)
_, err = w.Write(timeLockLengthBytes)
if err != nil {
return err
}
// Serialize RHash
_, err = w.Write(p.RHash[:])
if err != nil {
return err
}
// Serialize Timestamp.
tBytes, err := p.Timestamp.MarshalBinary()
if err != nil {
return err
}
err = wire.WriteVarBytes(w, 0, tBytes)
if err != nil {
return err
}
return nil
}
func deserializeOutgoingPayment(r io.Reader) (*OutgoingPayment, error) {
p := &OutgoingPayment{}
// Deserialize invoice
inv, err := deserializeInvoice(r)
if err != nil {
return nil, err
}
p.Invoice = *inv
// Deserialize fee
feeBytes := make([]byte, 8)
_, err = r.Read(feeBytes)
if err != nil {
return nil, err
}
p.Fee = btcutil.Amount(byteOrder.Uint64(feeBytes))
// Deserialize path
pathLenBytes := make([]byte, 4)
_, err = r.Read(pathLenBytes)
if err != nil {
return nil, err
}
pathLen := byteOrder.Uint32(pathLenBytes)
path := make([][33]byte, pathLen)
for i := uint32(0); i < pathLen; i++ {
_, err := r.Read(path[i][:])
if err != nil {
return nil, err
}
}
p.Path = path
// Deserialize TimeLockLength
timeLockLengthBytes := make([]byte, 4)
_, err = r.Read(timeLockLengthBytes)
if err != nil {
return nil, err
}
p.TimeLockLength = byteOrder.Uint32(timeLockLengthBytes)
// Deserialize RHash
_, err = r.Read(p.RHash[:])
if err != nil {
return nil, err
}
// Deserialize Timestamp
tBytes, err := wire.ReadVarBytes(r, 0, 100,
"OutgoingPayment.Timestamp")
if err != nil {
return nil, err
}
err = p.Timestamp.UnmarshalBinary(tBytes)
if err != nil {
return nil, err
}
return p, nil
}