multi: replace htlcResolution with an interface

This commit repalces the htlcResolution struct with an interface.
This interface is implemeted by failure, settle and accept resolution
structs. Only settles and fails are exported because the existing
code that handles htlc resolutions uses a nil resolution to indicate
that a htlc was accepted. The accept resolution is used internally
to report on the resolution result of the accepted htlc, but a nil
resolution is surfaced. Further refactoring of all the functions
that call NotifyExitHopHtlc to handle a htlc accept case (rather than
having a nil check) is required.
This commit is contained in:
carla
2020-02-06 19:35:10 +02:00
parent 2cd26d7556
commit 2569b4d08a
11 changed files with 477 additions and 257 deletions

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"github.com/btcsuite/btcutil"
@@ -172,7 +173,23 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
processHtlcResolution := func(e invoices.HtlcResolution) (
ContractResolver, error) {
if e.Preimage == nil {
// Take action based on the type of resolution we have
// received.
switch resolution := e.(type) {
// If the htlc resolution was a settle, apply the
// preimage and return a success resolver.
case *invoices.HtlcSettleResolution:
err := applyPreimage(resolution.Preimage)
if err != nil {
return nil, err
}
return &h.htlcSuccessResolver, nil
// If the htlc was failed, mark the htlc as
// resolved.
case *invoices.HtlcFailResolution:
log.Infof("%T(%v): Exit hop HTLC canceled "+
"(expiry=%v, height=%v), abandoning", h,
h.htlcResolution.ClaimOutpoint,
@@ -180,13 +197,13 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
h.resolved = true
return nil, h.Checkpoint(h)
}
if err := applyPreimage(*e.Preimage); err != nil {
return nil, err
// Error if the resolution type is unknown, we are only
// expecting settles and fails.
default:
return nil, fmt.Errorf("unknown resolution"+
" type: %v", e)
}
return &h.htlcSuccessResolver, nil
}
// Create a buffered hodl chan to prevent deadlock.
@@ -211,14 +228,29 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
defer h.Registry.HodlUnsubscribeAll(hodlChan)
// If the resolution is non-nil (indicating that a settle or cancel has
// occurred), and the invoice is known to the registry (indicating that
// the htlc is paying one of our invoices and is not a forward), try to
// resolve it directly.
if resolution != nil &&
resolution.Outcome != invoices.ResultInvoiceNotFound {
// Take action based on the resolution we received. If the htlc was
// settled, or a htlc for a known invoice failed we can resolve it
// directly. If the resolution is nil, the htlc was neither accepted
// nor failed, so we cannot take action yet.
switch res := resolution.(type) {
case *invoices.HtlcFailResolution:
// In the case where the htlc failed, but the invoice was known
// to the registry, we can directly resolve the htlc.
if res.Outcome != invoices.ResultInvoiceNotFound {
return processHtlcResolution(resolution)
}
return processHtlcResolution(*resolution)
// If we settled the htlc, we can resolve it.
case *invoices.HtlcSettleResolution:
return processHtlcResolution(resolution)
// If the resolution is nil, the htlc was neither settled nor failed so
// we cannot take action at present.
case nil:
default:
return nil, fmt.Errorf("unknown htlc resolution type: %T",
resolution)
}
// With the epochs and preimage subscriptions initialized, we'll query
@@ -256,7 +288,6 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
case hodlItem := <-hodlChan:
htlcResolution := hodlItem.(invoices.HtlcResolution)
return processHtlcResolution(htlcResolution)
case newBlock, ok := <-blockEpochs.Epochs:

View File

@@ -35,7 +35,7 @@ func TestHtlcIncomingResolverFwdPreimageKnown(t *testing.T) {
defer timeout(t)()
ctx := newIncomingResolverTestContext(t)
ctx.registry.notifyResolution = invoices.NewFailureResolution(
ctx.registry.notifyResolution = invoices.NewFailResolution(
testResCircuitKey, testHtlcExpiry,
invoices.ResultInvoiceNotFound,
)
@@ -52,7 +52,7 @@ func TestHtlcIncomingResolverFwdContestedSuccess(t *testing.T) {
defer timeout(t)()
ctx := newIncomingResolverTestContext(t)
ctx.registry.notifyResolution = invoices.NewFailureResolution(
ctx.registry.notifyResolution = invoices.NewFailResolution(
testResCircuitKey, testHtlcExpiry,
invoices.ResultInvoiceNotFound,
)
@@ -72,7 +72,7 @@ func TestHtlcIncomingResolverFwdContestedTimeout(t *testing.T) {
defer timeout(t)()
ctx := newIncomingResolverTestContext(t)
ctx.registry.notifyResolution = invoices.NewFailureResolution(
ctx.registry.notifyResolution = invoices.NewFailResolution(
testResCircuitKey, testHtlcExpiry,
invoices.ResultInvoiceNotFound,
)
@@ -91,7 +91,7 @@ func TestHtlcIncomingResolverFwdTimeout(t *testing.T) {
defer timeout(t)()
ctx := newIncomingResolverTestContext(t)
ctx.registry.notifyResolution = invoices.NewFailureResolution(
ctx.registry.notifyResolution = invoices.NewFailResolution(
testResCircuitKey, testHtlcExpiry,
invoices.ResultInvoiceNotFound,
)
@@ -139,7 +139,7 @@ func TestHtlcIncomingResolverExitCancel(t *testing.T) {
defer timeout(t)()
ctx := newIncomingResolverTestContext(t)
ctx.registry.notifyResolution = invoices.NewFailureResolution(
ctx.registry.notifyResolution = invoices.NewFailResolution(
testResCircuitKey, testAcceptHeight,
invoices.ResultInvoiceAlreadyCanceled,
)
@@ -158,7 +158,7 @@ func TestHtlcIncomingResolverExitSettleHodl(t *testing.T) {
ctx.resolve()
notifyData := <-ctx.registry.notifyChan
notifyData.hodlChan <- *invoices.NewSettleResolution(
notifyData.hodlChan <- invoices.NewSettleResolution(
testResPreimage, testResCircuitKey, testAcceptHeight,
invoices.ResultSettled,
)
@@ -187,7 +187,7 @@ func TestHtlcIncomingResolverExitCancelHodl(t *testing.T) {
ctx := newIncomingResolverTestContext(t)
ctx.resolve()
notifyData := <-ctx.registry.notifyChan
notifyData.hodlChan <- *invoices.NewFailureResolution(
notifyData.hodlChan <- invoices.NewFailResolution(
testResCircuitKey, testAcceptHeight, invoices.ResultCanceled,
)

View File

@@ -27,7 +27,7 @@ type Registry interface {
NotifyExitHopHtlc(payHash lntypes.Hash, paidAmount lnwire.MilliSatoshi,
expiry uint32, currentHeight int32,
circuitKey channeldb.CircuitKey, hodlChan chan<- interface{},
payload invoices.Payload) (*invoices.HtlcResolution, error)
payload invoices.Payload) (invoices.HtlcResolution, error)
// HodlUnsubscribeAll unsubscribes from all htlc resolutions.
HodlUnsubscribeAll(subscriber chan<- interface{})

View File

@@ -18,13 +18,13 @@ type notifyExitHopData struct {
type mockRegistry struct {
notifyChan chan notifyExitHopData
notifyErr error
notifyResolution *invoices.HtlcResolution
notifyResolution invoices.HtlcResolution
}
func (r *mockRegistry) NotifyExitHopHtlc(payHash lntypes.Hash,
paidAmount lnwire.MilliSatoshi, expiry uint32, currentHeight int32,
circuitKey channeldb.CircuitKey, hodlChan chan<- interface{},
payload invoices.Payload) (*invoices.HtlcResolution, error) {
payload invoices.Payload) (invoices.HtlcResolution, error) {
r.notifyChan <- notifyExitHopData{
hodlChan: hodlChan,