lnd: extend the invoiceRegistry to wrap on-disk invoices with an in-memory cache
This commit extends the existing invoiceRegistry functionality to wrap the on-disk invoices available via the channeldb with an in-memory cache on invoices. Currently the in-memory cache is only reserved for the storage of special “debug” invoices which all nodes are able to settle immediately.
This commit is contained in:
parent
39e6217860
commit
0c4293ba82
@ -3,75 +3,115 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/fastsha256"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/roasbeef/btcd/wire"
|
||||
"github.com/roasbeef/btcutil"
|
||||
)
|
||||
|
||||
// invoice represents a payment invoice which will be dispatched via the
|
||||
// Lightning Network.
|
||||
type invoice struct {
|
||||
value btcutil.Amount
|
||||
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, _ = wire.NewShaHash(bytes.Repeat([]byte{1}, 32))
|
||||
|
||||
paymentHash wire.ShaHash
|
||||
paymentPreimage wire.ShaHash
|
||||
|
||||
// TODO(roasbeef): other contract stuff
|
||||
}
|
||||
debugHash = wire.ShaHash(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
|
||||
invoiceIndex map[wire.ShaHash]*invoice
|
||||
|
||||
cdb *channeldb.DB
|
||||
|
||||
// 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[wire.ShaHash]*channeldb.Invoice
|
||||
}
|
||||
|
||||
// newInvoiceRegistry creates a new invoice registry.
|
||||
func newInvoiceRegistry() *invoiceRegistry {
|
||||
// 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{
|
||||
invoiceIndex: make(map[wire.ShaHash]*invoice),
|
||||
cdb: cdb,
|
||||
debugInvoices: make(map[wire.ShaHash]*channeldb.Invoice),
|
||||
}
|
||||
}
|
||||
|
||||
// addInvoice adds an 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) addInvoice(amt btcutil.Amount, preimage wire.ShaHash) {
|
||||
// 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 wire.ShaHash) {
|
||||
paymentHash := wire.ShaHash(fastsha256.Sum256(preimage[:]))
|
||||
|
||||
i.Lock()
|
||||
i.invoiceIndex[paymentHash] = &invoice{
|
||||
value: amt,
|
||||
paymentHash: paymentHash,
|
||||
paymentPreimage: preimage,
|
||||
i.debugInvoices[paymentHash] = &channeldb.Invoice{
|
||||
CreationDate: time.Now(),
|
||||
Terms: channeldb.ContractTerm{
|
||||
Value: amt,
|
||||
PaymentPreimage: preimage,
|
||||
},
|
||||
}
|
||||
i.Unlock()
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// TODO(roasbeef): also check in memory for quick lookups/settles?
|
||||
return i.cdb.AddInvoice(invoice)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (i *invoiceRegistry) lookupInvoice(hash wire.ShaHash) (*invoice, bool) {
|
||||
// TODO(roasbeef): ignore if settled?
|
||||
func (i *invoiceRegistry) LookupInvoice(rHash wire.ShaHash) (*channeldb.Invoice, error) {
|
||||
// First check the in-memory debug invoice index to see if this is an
|
||||
// existing invoice added for debugging.
|
||||
i.RLock()
|
||||
inv, ok := i.invoiceIndex[hash]
|
||||
invoice, ok := i.debugInvoices[rHash]
|
||||
i.RUnlock()
|
||||
|
||||
return inv, ok
|
||||
}
|
||||
|
||||
var (
|
||||
debugPre, _ = wire.NewShaHash(bytes.Repeat([]byte{1}, 32))
|
||||
debugHash = wire.ShaHash(fastsha256.Sum256(debugPre[:]))
|
||||
)
|
||||
|
||||
// debugInvoice is a fake invoice created for debugging purposes within the
|
||||
// daemon.
|
||||
func (i *invoiceRegistry) debugInvoice() *invoice {
|
||||
return &invoice{
|
||||
value: btcutil.Amount(100000 * 1e8),
|
||||
paymentPreimage: *debugPre,
|
||||
paymentHash: debugHash,
|
||||
// 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 wire.ShaHash) error {
|
||||
// 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).
|
||||
return i.cdb.SettleInvoice(rHash)
|
||||
}
|
||||
|
21
peer.go
21
peer.go
@ -858,7 +858,7 @@ type pendingPayment struct {
|
||||
type commitmentState struct {
|
||||
// htlcsToSettle is a list of preimages which allow us to settle one or
|
||||
// many of the pending HTLC's we've received from the upstream peer.
|
||||
htlcsToSettle map[uint32]invoice
|
||||
htlcsToSettle map[uint32]*channeldb.Invoice
|
||||
|
||||
// TODO(roasbeef): use once trickle+batch logic is in
|
||||
pendingBatch []*pendingPayment
|
||||
@ -920,7 +920,7 @@ func (p *peer) htlcManager(channel *lnwallet.LightningChannel,
|
||||
channel: channel,
|
||||
chanPoint: channel.ChannelPoint(),
|
||||
clearedHTCLs: make(map[uint32]*pendingPayment),
|
||||
htlcsToSettle: make(map[uint32]invoice),
|
||||
htlcsToSettle: make(map[uint32]*channeldb.Invoice),
|
||||
switchChan: htlcPlex,
|
||||
}
|
||||
|
||||
@ -1055,13 +1055,13 @@ func (p *peer) handleUpstreamMsg(state *commitmentState, msg lnwire.Message) {
|
||||
index := state.channel.ReceiveHTLC(htlcPkt)
|
||||
|
||||
rHash := htlcPkt.RedemptionHashes[0]
|
||||
if invoice, found := p.server.invoices.lookupInvoice(rHash); found {
|
||||
invoice, err := p.server.invoices.LookupInvoice(rHash)
|
||||
if err == nil {
|
||||
// TODO(roasbeef): check value
|
||||
// * onion layer strip should also be before invoice lookup
|
||||
// * also can immediately send the settle msg
|
||||
invCopy := *invoice
|
||||
invCopy.value = btcutil.Amount(htlcPkt.Amount)
|
||||
state.htlcsToSettle[index] = invCopy
|
||||
state.htlcsToSettle[index] = invoice
|
||||
} else if err != channeldb.ErrInvoiceNotFound {
|
||||
peerLog.Errorf("unable to query for invoice: %v", err)
|
||||
}
|
||||
case *lnwire.HTLCSettleRequest:
|
||||
// TODO(roasbeef): this assumes no "multi-sig"
|
||||
@ -1159,7 +1159,8 @@ func (p *peer) handleUpstreamMsg(state *commitmentState, msg lnwire.Message) {
|
||||
// Otherwise, we settle this HTLC within our local
|
||||
// state update log, then send the update entry to the
|
||||
// remote party.
|
||||
logIndex, err := state.channel.SettleHTLC(invoice.paymentPreimage)
|
||||
preimage := invoice.Terms.PaymentPreimage
|
||||
logIndex, err := state.channel.SettleHTLC(preimage)
|
||||
if err != nil {
|
||||
peerLog.Errorf("unable to settle htlc: %v", err)
|
||||
p.Disconnect()
|
||||
@ -1169,12 +1170,12 @@ func (p *peer) handleUpstreamMsg(state *commitmentState, msg lnwire.Message) {
|
||||
settleMsg := &lnwire.HTLCSettleRequest{
|
||||
ChannelPoint: state.chanPoint,
|
||||
HTLCKey: lnwire.HTLCKey(logIndex),
|
||||
RedemptionProofs: [][32]byte{invoice.paymentPreimage},
|
||||
RedemptionProofs: [][32]byte{preimage},
|
||||
}
|
||||
p.queueMsg(settleMsg, nil)
|
||||
delete(state.htlcsToSettle, htlc.Index)
|
||||
|
||||
bandwidthUpdate += invoice.value
|
||||
bandwidthUpdate += invoice.Terms.Value
|
||||
|
||||
numSettled++
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ func newServer(listenAddrs []string, notifier chainntnfs.ChainNotifier,
|
||||
chanDB: chanDB,
|
||||
fundingMgr: newFundingManager(wallet),
|
||||
htlcSwitch: newHtlcSwitch(),
|
||||
invoices: newInvoiceRegistry(),
|
||||
invoices: newInvoiceRegistry(chanDB),
|
||||
lnwallet: wallet,
|
||||
identityPriv: privKey,
|
||||
lightningID: fastsha256.Sum256(serializedPubKey),
|
||||
@ -104,8 +104,6 @@ func newServer(listenAddrs []string, notifier chainntnfs.ChainNotifier,
|
||||
quit: make(chan struct{}),
|
||||
}
|
||||
|
||||
// TODO(roasbeef): remove
|
||||
s.invoices.addInvoice(1000*1e8, *debugPre)
|
||||
|
||||
s.utxoNursery = newUtxoNursery(notifier, wallet)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user