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

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

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