invoices: add subscription to invoice updates
This commit adds the ability for clients within the daemon to register for notifications that are dispatched once invoices are settled, or new invoices are added. Such notifications can prove useful when synchronizing higher level primitives, or implementing workflow within desktop/mobile UI’s.
This commit is contained in:
parent
3247a160c5
commit
cfd9f8f6f0
@ -31,6 +31,10 @@ type invoiceRegistry struct {
|
|||||||
|
|
||||||
cdb *channeldb.DB
|
cdb *channeldb.DB
|
||||||
|
|
||||||
|
clientMtx sync.Mutex
|
||||||
|
nextClientID uint32
|
||||||
|
notificationClients map[uint32]*invoiceSubscription
|
||||||
|
|
||||||
// debugInvoices is a mp which stores special "debug" invoices which
|
// debugInvoices is a mp which stores special "debug" invoices which
|
||||||
// should be only created/used when manual tests require an invoice
|
// should be only created/used when manual tests require an invoice
|
||||||
// that *all* nodes are able to fully settle.
|
// that *all* nodes are able to fully settle.
|
||||||
@ -45,6 +49,7 @@ func newInvoiceRegistry(cdb *channeldb.DB) *invoiceRegistry {
|
|||||||
return &invoiceRegistry{
|
return &invoiceRegistry{
|
||||||
cdb: cdb,
|
cdb: cdb,
|
||||||
debugInvoices: make(map[wire.ShaHash]*channeldb.Invoice),
|
debugInvoices: make(map[wire.ShaHash]*channeldb.Invoice),
|
||||||
|
notificationClients: make(map[uint32]*invoiceSubscription),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +88,14 @@ func (i *invoiceRegistry) AddInvoice(invoice *channeldb.Invoice) error {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
// TODO(roasbeef): also check in memory for quick lookups/settles?
|
// TODO(roasbeef): also check in memory for quick lookups/settles?
|
||||||
return i.cdb.AddInvoice(invoice)
|
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
|
// lookupInvoice looks up an invoice by it's payment hash (R-Hash), if found
|
||||||
@ -126,5 +138,80 @@ func (i *invoiceRegistry) SettleInvoice(rHash wire.ShaHash) error {
|
|||||||
|
|
||||||
// If this isn't a debug invoice, then we'll attempt to settle an
|
// If this isn't a debug invoice, then we'll attempt to settle an
|
||||||
// invoice matching this rHash on disk (if one exists).
|
// invoice matching this rHash on disk (if one exists).
|
||||||
return i.cdb.SettleInvoice(rHash)
|
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 notificaiton 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 unregisteres the invoiceSubscription, freeing any previoulsy 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),
|
||||||
|
}
|
||||||
|
|
||||||
|
i.clientMtx.Lock()
|
||||||
|
i.notificationClients[i.nextClientID] = client
|
||||||
|
client.id = i.nextClientID
|
||||||
|
i.nextClientID++
|
||||||
|
i.clientMtx.Unlock()
|
||||||
|
|
||||||
|
return client
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user