mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-08-28 06:32:18 +02:00
htlcswitch: hodl invoice
This commit modifies the invoice registry to handle invoices for which the preimage is not known yet (hodl invoices). In that case, the resolution channel passed in from links and resolvers is stored until we either learn the preimage or want to cancel the htlc.
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
package contractcourt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/invoices"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
)
|
||||
@@ -70,11 +72,18 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
||||
return nil, h.Checkpoint(h)
|
||||
}
|
||||
|
||||
// applyPreimage is a helper function that will populate our internal
|
||||
// tryApplyPreimage is a helper function that will populate our internal
|
||||
// resolver with the preimage we learn of. This should be called once
|
||||
// the preimage is revealed so the inner resolver can properly complete
|
||||
// its duties.
|
||||
applyPreimage := func(preimage lntypes.Preimage) {
|
||||
// its duties. The boolean return value indicates whether the preimage
|
||||
// was properly applied.
|
||||
tryApplyPreimage := func(preimage lntypes.Preimage) bool {
|
||||
// Check to see if this preimage matches our htlc.
|
||||
if !preimage.Matches(h.payHash) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Update htlcResolution with the matching preimage.
|
||||
h.htlcResolution.Preimage = preimage
|
||||
|
||||
log.Infof("%T(%v): extracted preimage=%v from beacon!", h,
|
||||
@@ -93,6 +102,8 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
||||
// preimage.
|
||||
h.htlcResolution.SignedSuccessTx.TxIn[0].Witness[3] = preimage[:]
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// If the HTLC hasn't expired yet, then we may still be able to claim
|
||||
@@ -112,6 +123,26 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
||||
blockEpochs.Cancel()
|
||||
}()
|
||||
|
||||
// Create a buffered hodl chan to prevent deadlock.
|
||||
hodlChan := make(chan interface{}, 1)
|
||||
|
||||
// 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 the htlc can be settled directly, we can progress to the inner
|
||||
// resolver immediately.
|
||||
if event != nil && event.Preimage != nil {
|
||||
if tryApplyPreimage(*event.Preimage) {
|
||||
return &h.htlcSuccessResolver, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
@@ -119,26 +150,35 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
||||
// If we do, then this means we can claim the HTLC! However,
|
||||
// we don't know how to ourselves, so we'll return our inner
|
||||
// resolver which has the knowledge to do so.
|
||||
applyPreimage(preimage)
|
||||
return &h.htlcSuccessResolver, nil
|
||||
if tryApplyPreimage(preimage) {
|
||||
return &h.htlcSuccessResolver, nil
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
|
||||
select {
|
||||
case preimage := <-preimageSubscription.WitnessUpdates:
|
||||
// If this isn't our preimage, then we'll continue
|
||||
// onwards.
|
||||
hash := preimage.Hash()
|
||||
preimageMatches := bytes.Equal(hash[:], h.payHash[:])
|
||||
if !preimageMatches {
|
||||
if !tryApplyPreimage(preimage) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Otherwise, we've learned of the preimage! We'll add
|
||||
// this information to our inner resolver, then return
|
||||
// it so it can continue contract resolution.
|
||||
applyPreimage(preimage)
|
||||
// We've learned of the preimage and this information
|
||||
// has been added to our inner resolver. We return it so
|
||||
// it can continue contract resolution.
|
||||
return &h.htlcSuccessResolver, nil
|
||||
|
||||
case hodlItem := <-hodlChan:
|
||||
hodlEvent := hodlItem.(invoices.HodlEvent)
|
||||
|
||||
// Only process settle events.
|
||||
if hodlEvent.Preimage == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if !tryApplyPreimage(*hodlEvent.Preimage) {
|
||||
continue
|
||||
}
|
||||
return &h.htlcSuccessResolver, nil
|
||||
|
||||
case newBlock, ok := <-blockEpochs.Epochs:
|
||||
|
@@ -180,8 +180,11 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
|
||||
// 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
|
||||
// process the result.
|
||||
_, err = h.Registry.NotifyExitHopHtlc(h.payHash, h.htlcAmt)
|
||||
// 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)
|
||||
@@ -254,8 +257,10 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
|
||||
|
||||
// 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 the result.
|
||||
_, err = h.Registry.NotifyExitHopHtlc(h.payHash, h.htlcAmt)
|
||||
// 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)
|
||||
|
Reference in New Issue
Block a user