lnd/invoiceregistry.go
Olaoluwa Osuntokun 5affed38fc
multi: update btcsuite API's to latest upstream changes
This commit makes a large number of minor changes concerning API usage
within the deamon to match the latest version on the upstream btcsuite
libraries.

The major changes are the switch from wire.ShaHash to chainhash.Hash,
and that wire.NewMsgTx() now takes a paramter indicating the version of
the transaction to be created.
2017-01-05 13:56:34 -08:00

219 lines
6.7 KiB
Go

package main
import (
"bytes"
"sync"
"time"
"github.com/btcsuite/fastsha256"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcutil"
)
var (
// debugPre is the default debug preimage which is inserted into the
// invoice registry if the --debughtlc flag is activated on start up.
// All nodes initialize with the flag active will immediately settle
// any incoming HTLC whose rHash is corresponds with the debug
// preimage.
debugPre, _ = chainhash.NewHash(bytes.Repeat([]byte{1}, 32))
debugHash = chainhash.Hash(fastsha256.Sum256(debugPre[:]))
)
// invoiceRegistry is a central registry of all the outstanding invoices
// created by the daemon. The registry is a thin wrapper around a map in order
// to ensure that all updates/reads are thread safe.
type invoiceRegistry struct {
sync.RWMutex
cdb *channeldb.DB
clientMtx sync.Mutex
nextClientID uint32
notificationClients map[uint32]*invoiceSubscription
// debugInvoices is a mp which stores special "debug" invoices which
// should be only created/used when manual tests require an invoice
// that *all* nodes are able to fully settle.
debugInvoices map[chainhash.Hash]*channeldb.Invoice
}
// newInvoiceRegistry creates a new invoice registry. The invoice registry
// wraps the persistent on-disk invoice storage with an additional in-memory
// layer. The in-memory layer is in pace such that debug invoices can be added
// which are volatile yet available system wide within the daemon.
func newInvoiceRegistry(cdb *channeldb.DB) *invoiceRegistry {
return &invoiceRegistry{
cdb: cdb,
debugInvoices: make(map[chainhash.Hash]*channeldb.Invoice),
notificationClients: make(map[uint32]*invoiceSubscription),
}
}
// addDebugInvoice adds a debug invoice for the specified amount, identified
// by the passed preimage. Once this invoice is added, sub-systems within the
// daemon add/forward HTLC's are able to obtain the proper preimage required
// for redemption in the case that we're the final destination.
func (i *invoiceRegistry) AddDebugInvoice(amt btcutil.Amount, preimage chainhash.Hash) {
paymentHash := chainhash.Hash(fastsha256.Sum256(preimage[:]))
invoice := &channeldb.Invoice{
CreationDate: time.Now(),
Terms: channeldb.ContractTerm{
Value: amt,
PaymentPreimage: preimage,
},
}
i.Lock()
i.debugInvoices[paymentHash] = invoice
i.Unlock()
ltndLog.Debugf("Adding debug invoice %v", newLogClosure(func() string {
return spew.Sdump(invoice)
}))
}
// AddInvoice adds a regular invoice for the specified amount, identified by
// the passed preimage. Additionally, any memo or recipt data provided will
// also be stored on-disk. Once this invoice is added, sub-systems within the
// daemon add/forward HTLC's are able to obtain the proper preimage required
// for redemption in the case that we're the final destination.
func (i *invoiceRegistry) AddInvoice(invoice *channeldb.Invoice) error {
ltndLog.Debugf("Adding invoice %v", newLogClosure(func() string {
return spew.Sdump(invoice)
}))
// TODO(roasbeef): also check in memory for quick lookups/settles?
if err := i.cdb.AddInvoice(invoice); err != nil {
return err
}
// TODO(roasbeef): re-enable?
//go i.notifyClients(invoice, false)
return nil
}
// lookupInvoice looks up an invoice by it's payment hash (R-Hash), if found
// then we're able to pull the funds pending within an HTLC.
// TODO(roasbeef): ignore if settled?
func (i *invoiceRegistry) LookupInvoice(rHash chainhash.Hash) (*channeldb.Invoice, error) {
// First check the in-memory debug invoice index to see if this is an
// existing invoice added for debugging.
i.RLock()
invoice, ok := i.debugInvoices[rHash]
i.RUnlock()
// If found, then simply return the invoice directly.
if ok {
return invoice, nil
}
// Otherwise, we'll check the database to see if there's an existing
// matching invoice.
return i.cdb.LookupInvoice(rHash)
}
// SettleInvoice attempts to mark an invoice as settled. If the invoice is a
// dbueg invoice, then this method is a nooop as debug invoices are never fully
// settled.
func (i *invoiceRegistry) SettleInvoice(rHash chainhash.Hash) error {
ltndLog.Debugf("Settling invoice %x", rHash[:])
// First check the in-memory debug invoice index to see if this is an
// existing invoice added for debugging.
i.RLock()
if _, ok := i.debugInvoices[rHash]; ok {
// Debug invoices are never fully settled, so we simply return
// immediately in this case.
i.RUnlock()
return nil
}
i.RUnlock()
// If this isn't a debug invoice, then we'll attempt to settle an
// invoice matching this rHash on disk (if one exists).
if err := i.cdb.SettleInvoice(rHash); err != nil {
return err
}
// Launch a new goroutine to notify any/all registered invoice
// notification clients.
go func() {
invoice, err := i.cdb.LookupInvoice(rHash)
if err != nil {
ltndLog.Errorf("unable to find invoice: %v", err)
return
}
i.notifyClients(invoice, true)
}()
return nil
}
// notifyClients notifies all currently registered invoice notification clients
// of a newly added/settled invoice.
func (i *invoiceRegistry) notifyClients(invoice *channeldb.Invoice, settle bool) {
i.clientMtx.Lock()
defer i.clientMtx.Unlock()
for _, client := range i.notificationClients {
var eventChan chan *channeldb.Invoice
if settle {
eventChan = client.SettledInvoices
} else {
eventChan = client.NewInvoices
}
go func() {
eventChan <- invoice
}()
}
}
// invoiceSubscription represents an intent to receive updates for newly added
// or settled invoices. For each newly added invoice, a copy of the invoice
// will be sent over the NewInvoices channel. Similarly, for each newly settled
// invoice, a copy of the invoice will be sent over the SettledInvoices
// channel.
type invoiceSubscription struct {
NewInvoices chan *channeldb.Invoice
SettledInvoices chan *channeldb.Invoice
inv *invoiceRegistry
id uint32
}
// Cancel unregisters the invoiceSubscription, freeing any previously allocate
// resources.
func (i *invoiceSubscription) Cancel() {
i.inv.clientMtx.Lock()
delete(i.inv.notificationClients, i.id)
i.inv.clientMtx.Unlock()
}
// SubscribeNotifications returns an invoiceSubscription which allows the
// caller to receive async notifications when any invoices are settled or
// added.
func (i *invoiceRegistry) SubscribeNotifications() *invoiceSubscription {
client := &invoiceSubscription{
NewInvoices: make(chan *channeldb.Invoice),
SettledInvoices: make(chan *channeldb.Invoice),
inv: i,
}
i.clientMtx.Lock()
i.notificationClients[i.nextClientID] = client
client.id = i.nextClientID
i.nextClientID++
i.clientMtx.Unlock()
return client
}