cnct+htlcswitch+invoices: move invoice parameter check out of link

This commit is the final step in making the link unaware of invoices. It
now purely offers the htlc to the invoice registry and follows
instructions from the invoice registry about how and when to respond to
the htlc.

The change also fixes a bug where upon restart, hodl htlcs were
subjected to the invoice minimum cltv delta requirement again. If the
block height has increased in the mean while, the htlc would be canceled
back.

Furthermore the invoice registry interaction is aligned between link and
contract resolvers.
This commit is contained in:
Joost Jager
2019-04-16 12:11:20 +02:00
parent e095819385
commit 064e8492de
16 changed files with 533 additions and 265 deletions

View File

@@ -135,28 +135,52 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
preimageSubscription := h.PreimageDB.SubscribeUpdates()
defer preimageSubscription.CancelSubscription()
// Create a buffered hodl chan to prevent deadlock.
hodlChan := make(chan interface{}, 1)
// Define closure to process hodl events either direct or triggered by
// later notifcation.
processHodlEvent := func(e invoices.HodlEvent) (ContractResolver,
error) {
// Notify registry that we are potentially settling as exit hop
// on-chain, so that we will get a hodl event when a corresponding hodl
// invoice is settled.
event, err := h.Registry.NotifyExitHopHtlc(h.payHash, h.htlcAmt, hodlChan)
if err != nil && err != channeldb.ErrInvoiceNotFound {
return nil, err
}
defer h.Registry.HodlUnsubscribeAll(hodlChan)
if e.Preimage == nil {
log.Infof("%T(%v): Exit hop HTLC canceled "+
"(expiry=%v, height=%v), abandoning", h,
h.htlcResolution.ClaimOutpoint,
h.htlcExpiry, currentHeight)
// If the htlc can be settled directly, we can progress to the inner
// resolver immediately.
if event != nil && event.Preimage != nil {
if err := applyPreimage(*event.Preimage); err != nil {
h.resolved = true
return nil, h.Checkpoint(h)
}
if err := applyPreimage(*e.Preimage); err != nil {
return nil, err
}
return &h.htlcSuccessResolver, nil
}
// Create a buffered hodl chan to prevent deadlock.
hodlChan := make(chan interface{}, 1)
// Notify registry that we are potentially resolving as an exit hop
// on-chain. If this HTLC indeed pays to an existing invoice, the
// invoice registry will tell us what to do with the HTLC. This is
// identical to HTLC resolution in the link.
event, err := h.Registry.NotifyExitHopHtlc(
h.payHash, h.htlcAmt, h.htlcExpiry, currentHeight,
hodlChan,
)
switch err {
case channeldb.ErrInvoiceNotFound:
case nil:
defer h.Registry.HodlUnsubscribeAll(hodlChan)
// Resolve the htlc directly if possible.
if event != nil {
return processHodlEvent(*event)
}
default:
return nil, err
}
// With the epochs and preimage subscriptions initialized, we'll query
// to see if we already know the preimage.
preimage, ok := h.PreimageDB.LookupPreimage(h.payHash)
@@ -193,16 +217,7 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
case hodlItem := <-hodlChan:
hodlEvent := hodlItem.(invoices.HodlEvent)
// Only process settle events.
if hodlEvent.Preimage == nil {
continue
}
if err := applyPreimage(*event.Preimage); err != nil {
return nil, err
}
return &h.htlcSuccessResolver, nil
return processHodlEvent(hodlEvent)
case newBlock, ok := <-blockEpochs.Epochs:
if !ok {
@@ -211,8 +226,7 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
// If this new height expires the HTLC, then this means
// we never found out the preimage, so we can mark
// resolved and
// exit.
// resolved and exit.
newHeight := uint32(newBlock.Height)
if newHeight >= h.htlcExpiry {
log.Infof("%T(%v): HTLC has timed out "+

View File

@@ -7,7 +7,6 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet"
@@ -78,9 +77,11 @@ func (h *htlcSuccessResolver) ResolverKey() []byte {
}
// Resolve attempts to resolve an unresolved incoming HTLC that we know the
// preimage to. If the HTLC is on the commitment of the remote party, then
// we'll simply sweep it directly. Otherwise, we'll hand this off to the utxo
// nursery to do its duty.
// preimage to. If the HTLC is on the commitment of the remote party, then we'll
// simply sweep it directly. Otherwise, we'll hand this off to the utxo nursery
// to do its duty. There is no need to make a call to the invoice registry
// anymore. Every HTLC has already passed through the incoming contest resolver
// and in there the invoice was already marked as settled.
//
// TODO(roasbeef): create multi to batch
//
@@ -177,19 +178,6 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
return nil, fmt.Errorf("quitting")
}
// With the HTLC claimed, we can attempt to settle its
// corresponding invoice if we were the original destination. As
// the htlc is already settled at this point, we don't need to
// read on the hodl channel.
hodlChan := make(chan interface{}, 1)
_, err = h.Registry.NotifyExitHopHtlc(
h.payHash, h.htlcAmt, hodlChan,
)
if err != nil && err != channeldb.ErrInvoiceNotFound {
log.Errorf("Unable to settle invoice with payment "+
"hash %x: %v", h.payHash, err)
}
// Once the transaction has received a sufficient number of
// confirmations, we'll mark ourselves as fully resolved and exit.
h.resolved = true
@@ -255,17 +243,6 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
return nil, fmt.Errorf("quitting")
}
// With the HTLC claimed, we can attempt to settle its corresponding
// invoice if we were the original destination. As the htlc is already
// settled at this point, we don't need to read on the hodl
// channel.
hodlChan := make(chan interface{}, 1)
_, err = h.Registry.NotifyExitHopHtlc(h.payHash, h.htlcAmt, hodlChan)
if err != nil && err != channeldb.ErrInvoiceNotFound {
log.Errorf("Unable to settle invoice with payment "+
"hash %x: %v", h.payHash, err)
}
h.resolved = true
return nil, h.Checkpoint(h)
}

View File

@@ -21,6 +21,7 @@ type Registry interface {
// htlc should be resolved. If the htlc cannot be resolved immediately,
// the resolution is sent on the passed in hodlChan later.
NotifyExitHopHtlc(payHash lntypes.Hash, paidAmount lnwire.MilliSatoshi,
expiry uint32, currentHeight int32,
hodlChan chan<- interface{}) (*invoices.HodlEvent, error)
// HodlUnsubscribeAll unsubscribes from all hodl events.