channeldb: store optional invoice fields as variable length byte arrays

This commit modifies the on-disk storage of invoices to stop the
optional fields (memo+receipt) on-disk as variable length byte arrays.
This change saves space as the optional fields now only take up as much
space as is strictly needed, rather than always being padded out to max
size (1KB).
This commit is contained in:
Olaoluwa Osuntokun 2016-09-23 15:15:22 -07:00
parent 6ff11ba65c
commit 0b841ec686
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2
3 changed files with 26 additions and 12 deletions

@ -25,8 +25,8 @@ func randInvoice(value btcutil.Amount) (*Invoice, error) {
Value: value,
},
}
copy(i.Memo[:], []byte("memo"))
copy(i.Receipt[:], []byte("recipt"))
i.Memo = []byte("memo")
i.Receipt = []byte("recipt")
return i, nil
}
@ -43,8 +43,8 @@ func TestInvoiceWorkflow(t *testing.T) {
fakeInvoice := &Invoice{
CreationDate: time.Now(),
}
copy(fakeInvoice.Memo[:], []byte("memo"))
copy(fakeInvoice.Receipt[:], []byte("recipt"))
fakeInvoice.Memo = []byte("memo")
fakeInvoice.Receipt = []byte("recipt")
copy(fakeInvoice.Terms.PaymentPreimage[:], rev[:])
fakeInvoice.Terms.Value = btcutil.Amount(10000)

@ -2,6 +2,7 @@ package channeldb
import (
"bytes"
"fmt"
"io"
"time"
@ -77,13 +78,13 @@ type Invoice struct {
// Memo is an optional memo to be stored along side an invoice. The
// memo may contain further details pertaining to the invoice itself,
// or any other message which fits within the size constraints.
Memo [MaxMemoSize]byte
Memo []byte
// Receipt is an optional field dedicated for storing a
// cryptographically binding receipt of payment.
//
// TODO(roasbeef): document scheme.
Receipt [MaxReceiptSize]byte
Receipt []byte
// CreationDate is the exact time the invoice was created.
CreationDate time.Time
@ -102,6 +103,16 @@ type Invoice struct {
// insertion will be aborted and rejected due to the strict policy banning any
// duplicate payment hashes.
func (d *DB) AddInvoice(i *Invoice) error {
if len(i.Memo) > MaxMemoSize {
return fmt.Errorf("max length a memo is %v, and invoice "+
"of length %v was provided", MaxMemoSize, len(i.Memo))
}
if len(i.Receipt) > MaxReceiptSize {
return fmt.Errorf("max length a receipt is %v, and invoice "+
"of length %v was provided", MaxReceiptSize,
len(i.Receipt))
}
return d.store.Update(func(tx *bolt.Tx) error {
invoices, err := tx.CreateBucketIfNotExists(invoiceBucket)
if err != nil {
@ -283,10 +294,10 @@ func putInvoice(invoices *bolt.Bucket, invoiceIndex *bolt.Bucket,
}
func serializeInvoice(w io.Writer, i *Invoice) error {
if _, err := w.Write(i.Memo[:]); err != nil {
if err := wire.WriteVarBytes(w, 0, i.Memo[:]); err != nil {
return err
}
if _, err := w.Write(i.Receipt[:]); err != nil {
if err := wire.WriteVarBytes(w, 0, i.Receipt[:]); err != nil {
return err
}
@ -331,13 +342,16 @@ func fetchInvoice(invoiceNum []byte, invoices *bolt.Bucket) (*Invoice, error) {
}
func deserializeInvoice(r io.Reader) (*Invoice, error) {
var err error
invoice := &Invoice{}
// TODO(roasbeef): use read full everywhere
if _, err := io.ReadFull(r, invoice.Memo[:]); err != nil {
invoice.Memo, err = wire.ReadVarBytes(r, 0, MaxMemoSize, "")
if err != nil {
return nil, err
}
if _, err := io.ReadFull(r, invoice.Receipt[:]); err != nil {
invoice.Receipt, err = wire.ReadVarBytes(r, 0, MaxReceiptSize, "")
if err != nil {
return nil, err
}

@ -613,12 +613,12 @@ func (r *rpcServer) AddInvoice(ctx context.Context,
i := &channeldb.Invoice{
CreationDate: time.Now(),
Memo: []byte(invoice.Memo),
Receipt: invoice.Receipt,
Terms: channeldb.ContractTerm{
Value: btcutil.Amount(invoice.Value),
},
}
copy(i.Memo[:], invoice.Memo)
copy(i.Receipt[:], invoice.Receipt)
copy(i.Terms.PaymentPreimage[:], preImage)
if err := r.server.invoices.AddInvoice(i); err != nil {