diff --git a/invoiceregistry.go b/invoiceregistry.go index 67ae5fcc..ac65f606 100644 --- a/invoiceregistry.go +++ b/invoiceregistry.go @@ -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) } diff --git a/peer.go b/peer.go index c9a81ae6..4719efb2 100644 --- a/peer.go +++ b/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++ } diff --git a/server.go b/server.go index 19391b38..4d8dd6c4 100644 --- a/server.go +++ b/server.go @@ -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)