mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 01:43:16 +01:00
contractcourt: add Launch
method to incoming contest resolver
A minor refactor is done to support implementing `Launch`.
This commit is contained in:
parent
6c786e5637
commit
da4ce49c49
@ -78,6 +78,37 @@ func (h *htlcIncomingContestResolver) processFinalHtlcFail() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Launch will call the inner resolver's launch method if the preimage can be
|
||||
// found, otherwise it's a no-op.
|
||||
func (h *htlcIncomingContestResolver) Launch() error {
|
||||
// NOTE: we don't mark this resolver as launched as the inner resolver
|
||||
// will set it when it's launched.
|
||||
if h.launched {
|
||||
h.log.Tracef("already launched")
|
||||
return nil
|
||||
}
|
||||
|
||||
h.log.Debugf("launching contest resolver...")
|
||||
|
||||
// Query the preimage and apply it if we already know it.
|
||||
applied, err := h.findAndapplyPreimage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// No preimage found, leave it to be handled by the resolver.
|
||||
if !applied {
|
||||
return nil
|
||||
}
|
||||
|
||||
h.log.Debugf("found preimage for htlc=%x, launching success resolver",
|
||||
h.htlc.RHash)
|
||||
|
||||
// Once we've applied the preimage, we'll launch the inner resolver to
|
||||
// attempt to claim the HTLC.
|
||||
return h.htlcSuccessResolver.Launch()
|
||||
}
|
||||
|
||||
// Resolve attempts to resolve this contract. As we don't yet know of the
|
||||
// preimage for the contract, we'll wait for one of two things to happen:
|
||||
//
|
||||
@ -94,6 +125,7 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
||||
// If we're already full resolved, then we don't have anything further
|
||||
// to do.
|
||||
if h.resolved {
|
||||
h.log.Errorf("already resolved")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@ -112,8 +144,7 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
||||
// now.
|
||||
payload, nextHopOnionBlob, err := h.decodePayload()
|
||||
if err != nil {
|
||||
log.Debugf("ChannelArbitrator(%v): cannot decode payload of "+
|
||||
"htlc %v", h.ChanPoint, h.HtlcPoint())
|
||||
h.log.Debugf("cannot decode payload of htlc %v", h.HtlcPoint())
|
||||
|
||||
// If we've locked in an htlc with an invalid payload on our
|
||||
// commitment tx, we don't need to resolve it. The other party
|
||||
@ -188,65 +219,6 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
||||
return nil, h.Checkpoint(h, report)
|
||||
}
|
||||
|
||||
// applyPreimage 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. The error return value indicates whether the preimage
|
||||
// was properly applied.
|
||||
applyPreimage := func(preimage lntypes.Preimage) error {
|
||||
// Sanity check to see if this preimage matches our htlc. At
|
||||
// this point it should never happen that it does not match.
|
||||
if !preimage.Matches(h.htlc.RHash) {
|
||||
return errors.New("preimage does not match hash")
|
||||
}
|
||||
|
||||
// Update htlcResolution with the matching preimage.
|
||||
h.htlcResolution.Preimage = preimage
|
||||
|
||||
log.Infof("%T(%v): applied preimage=%v", h,
|
||||
h.htlcResolution.ClaimOutpoint, preimage)
|
||||
|
||||
isSecondLevel := h.htlcResolution.SignedSuccessTx != nil
|
||||
|
||||
// If we didn't have to go to the second level to claim (this
|
||||
// is the remote commitment transaction), then we don't need to
|
||||
// modify our canned witness.
|
||||
if !isSecondLevel {
|
||||
return nil
|
||||
}
|
||||
|
||||
isTaproot := txscript.IsPayToTaproot(
|
||||
h.htlcResolution.SignedSuccessTx.TxOut[0].PkScript,
|
||||
)
|
||||
|
||||
// If this is our commitment transaction, then we'll need to
|
||||
// populate the witness for the second-level HTLC transaction.
|
||||
switch {
|
||||
// For taproot channels, the witness for sweeping with success
|
||||
// looks like:
|
||||
// - <sender sig> <receiver sig> <preimage> <success_script>
|
||||
// <control_block>
|
||||
//
|
||||
// So we'll insert it at the 3rd index of the witness.
|
||||
case isTaproot:
|
||||
//nolint:lll
|
||||
h.htlcResolution.SignedSuccessTx.TxIn[0].Witness[2] = preimage[:]
|
||||
|
||||
// Within the witness for the success transaction, the
|
||||
// preimage is the 4th element as it looks like:
|
||||
//
|
||||
// * <0> <sender sig> <recvr sig> <preimage> <witness script>
|
||||
//
|
||||
// We'll populate it within the witness, as since this
|
||||
// was a "contest" resolver, we didn't yet know of the
|
||||
// preimage.
|
||||
case !isTaproot:
|
||||
h.htlcResolution.SignedSuccessTx.TxIn[0].Witness[3] = preimage[:]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Define a closure to process htlc resolutions either directly or
|
||||
// triggered by future notifications.
|
||||
processHtlcResolution := func(e invoices.HtlcResolution) (
|
||||
@ -258,7 +230,7 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
||||
// If the htlc resolution was a settle, apply the
|
||||
// preimage and return a success resolver.
|
||||
case *invoices.HtlcSettleResolution:
|
||||
err := applyPreimage(resolution.Preimage)
|
||||
err := h.applyPreimage(resolution.Preimage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -380,7 +352,9 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
||||
// However, we don't know how to ourselves, so we'll
|
||||
// return our inner resolver which has the knowledge to
|
||||
// do so.
|
||||
if err := applyPreimage(preimage); err != nil {
|
||||
h.log.Debugf("Found preimage for htlc=%x", h.htlc.RHash)
|
||||
|
||||
if err := h.applyPreimage(preimage); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -399,7 +373,10 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := applyPreimage(preimage); err != nil {
|
||||
h.log.Debugf("Received preimage for htlc=%x",
|
||||
h.htlc.RHash)
|
||||
|
||||
if err := h.applyPreimage(preimage); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -446,6 +423,76 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// applyPreimage 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. The error
|
||||
// return value indicates whether the preimage was properly applied.
|
||||
func (h *htlcIncomingContestResolver) applyPreimage(
|
||||
preimage lntypes.Preimage) error {
|
||||
|
||||
// Sanity check to see if this preimage matches our htlc. At this point
|
||||
// it should never happen that it does not match.
|
||||
if !preimage.Matches(h.htlc.RHash) {
|
||||
return errors.New("preimage does not match hash")
|
||||
}
|
||||
|
||||
// We may already have the preimage since both the `Launch` and
|
||||
// `Resolve` methods will look for it.
|
||||
if h.htlcResolution.Preimage != lntypes.ZeroHash {
|
||||
h.log.Debugf("already applied preimage for htlc=%x",
|
||||
h.htlc.RHash)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update htlcResolution with the matching preimage.
|
||||
h.htlcResolution.Preimage = preimage
|
||||
|
||||
log.Infof("%T(%v): applied preimage=%v", h,
|
||||
h.htlcResolution.ClaimOutpoint, preimage)
|
||||
|
||||
isSecondLevel := h.htlcResolution.SignedSuccessTx != nil
|
||||
|
||||
// If we didn't have to go to the second level to claim (this
|
||||
// is the remote commitment transaction), then we don't need to
|
||||
// modify our canned witness.
|
||||
if !isSecondLevel {
|
||||
return nil
|
||||
}
|
||||
|
||||
isTaproot := txscript.IsPayToTaproot(
|
||||
h.htlcResolution.SignedSuccessTx.TxOut[0].PkScript,
|
||||
)
|
||||
|
||||
// If this is our commitment transaction, then we'll need to
|
||||
// populate the witness for the second-level HTLC transaction.
|
||||
switch {
|
||||
// For taproot channels, the witness for sweeping with success
|
||||
// looks like:
|
||||
// - <sender sig> <receiver sig> <preimage> <success_script>
|
||||
// <control_block>
|
||||
//
|
||||
// So we'll insert it at the 3rd index of the witness.
|
||||
case isTaproot:
|
||||
//nolint:lll
|
||||
h.htlcResolution.SignedSuccessTx.TxIn[0].Witness[2] = preimage[:]
|
||||
|
||||
// Within the witness for the success transaction, the
|
||||
// preimage is the 4th element as it looks like:
|
||||
//
|
||||
// * <0> <sender sig> <recvr sig> <preimage> <witness script>
|
||||
//
|
||||
// We'll populate it within the witness, as since this
|
||||
// was a "contest" resolver, we didn't yet know of the
|
||||
// preimage.
|
||||
case !isTaproot:
|
||||
//nolint:lll
|
||||
h.htlcResolution.SignedSuccessTx.TxIn[0].Witness[3] = preimage[:]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// report returns a report on the resolution state of the contract.
|
||||
func (h *htlcIncomingContestResolver) report() *ContractReport {
|
||||
// No locking needed as these values are read-only.
|
||||
@ -472,6 +519,7 @@ func (h *htlcIncomingContestResolver) report() *ContractReport {
|
||||
//
|
||||
// NOTE: Part of the ContractResolver interface.
|
||||
func (h *htlcIncomingContestResolver) Stop() {
|
||||
h.log.Debugf("stopping...")
|
||||
close(h.quit)
|
||||
}
|
||||
|
||||
@ -571,3 +619,77 @@ func (h *htlcIncomingContestResolver) decodePayload() (*hop.Payload,
|
||||
// A compile time assertion to ensure htlcIncomingContestResolver meets the
|
||||
// ContractResolver interface.
|
||||
var _ htlcContractResolver = (*htlcIncomingContestResolver)(nil)
|
||||
|
||||
// findAndapplyPreimage attempts to find the preimage for the incoming HTLC. If
|
||||
// found, it will be applied.
|
||||
func (h *htlcIncomingContestResolver) findAndapplyPreimage() (bool, error) {
|
||||
// Query to see if we already know the preimage.
|
||||
preimage, ok := h.PreimageDB.LookupPreimage(h.htlc.RHash)
|
||||
|
||||
// If the preimage is known, we'll apply it.
|
||||
if ok {
|
||||
if err := h.applyPreimage(preimage); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Successfully applied the preimage, we can now return.
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// First try to parse the payload.
|
||||
payload, _, err := h.decodePayload()
|
||||
if err != nil {
|
||||
h.log.Errorf("Cannot decode payload of htlc %v", h.HtlcPoint())
|
||||
|
||||
// If we cannot decode the payload, we will return a nil error
|
||||
// and let it to be handled in `Resolve`.
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Exit early if this is not the exit hop, which means we are not the
|
||||
// payment receiver and don't have preimage.
|
||||
if payload.FwdInfo.NextHop != hop.Exit {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
circuitKey := models.CircuitKey{
|
||||
ChanID: h.ShortChanID,
|
||||
HtlcID: h.htlc.HtlcIndex,
|
||||
}
|
||||
|
||||
// Try get the resolution - if it doesn't give us a resolution
|
||||
// immediately, we'll assume we don't know it yet and let the `Resolve`
|
||||
// handle the waiting.
|
||||
//
|
||||
// NOTE: we use a nil subscriber here and a zero current height as we
|
||||
// are only interested in the settle resolution.
|
||||
//
|
||||
// TODO(yy): move this logic to link and let the preimage be accessed
|
||||
// via the preimage beacon.
|
||||
resolution, err := h.Registry.NotifyExitHopHtlc(
|
||||
h.htlc.RHash, h.htlc.Amt, h.htlcExpiry, 0,
|
||||
circuitKey, nil, nil, payload,
|
||||
)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
res, ok := resolution.(*invoices.HtlcSettleResolution)
|
||||
|
||||
// Exit early if it's not a settle resolution.
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Otherwise we have a settle resolution, apply the preimage.
|
||||
err = h.applyPreimage(res.Preimage)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
@ -5,11 +5,13 @@ import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
sphinx "github.com/lightningnetwork/lightning-onion"
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/lightningnetwork/lnd/invoices"
|
||||
"github.com/lightningnetwork/lnd/kvdb"
|
||||
"github.com/lightningnetwork/lnd/lnmock"
|
||||
@ -356,6 +358,7 @@ func newIncomingResolverTestContext(t *testing.T, isExit bool) *incomingResolver
|
||||
|
||||
return nil
|
||||
},
|
||||
Sweeper: newMockSweeper(),
|
||||
},
|
||||
PutResolverReport: func(_ kvdb.RwTx,
|
||||
_ *channeldb.ResolverReport) error {
|
||||
@ -374,10 +377,16 @@ func newIncomingResolverTestContext(t *testing.T, isExit bool) *incomingResolver
|
||||
},
|
||||
}
|
||||
|
||||
res := lnwallet.IncomingHtlcResolution{
|
||||
SweepSignDesc: input.SignDescriptor{
|
||||
Output: &wire.TxOut{},
|
||||
},
|
||||
}
|
||||
|
||||
c.resolver = &htlcIncomingContestResolver{
|
||||
htlcSuccessResolver: &htlcSuccessResolver{
|
||||
contractResolverKit: *newContractResolverKit(cfg),
|
||||
htlcResolution: lnwallet.IncomingHtlcResolution{},
|
||||
htlcResolution: res,
|
||||
htlc: channeldb.HTLC{
|
||||
Amt: lnwire.MilliSatoshi(testHtlcAmount),
|
||||
RHash: testResHash,
|
||||
@ -386,6 +395,7 @@ func newIncomingResolverTestContext(t *testing.T, isExit bool) *incomingResolver
|
||||
},
|
||||
htlcExpiry: testHtlcExpiry,
|
||||
}
|
||||
c.resolver.initLogger("htlcIncomingContestResolver")
|
||||
|
||||
return c
|
||||
}
|
||||
@ -395,6 +405,10 @@ func (i *incomingResolverTestContext) resolve() {
|
||||
i.resolveErr = make(chan error, 1)
|
||||
go func() {
|
||||
var err error
|
||||
|
||||
err = i.resolver.Launch()
|
||||
require.NoError(i.t, err)
|
||||
|
||||
i.nextResolver, err = i.resolver.Resolve()
|
||||
i.resolveErr <- err
|
||||
}()
|
||||
|
@ -1806,6 +1806,10 @@ func (i *InvoiceRegistry) notifyHodlSubscribers(htlcResolution HtlcResolution) {
|
||||
func (i *InvoiceRegistry) hodlSubscribe(subscriber chan<- interface{},
|
||||
circuitKey CircuitKey) {
|
||||
|
||||
if subscriber == nil {
|
||||
return
|
||||
}
|
||||
|
||||
i.hodlSubscriptionsMux.Lock()
|
||||
defer i.hodlSubscriptionsMux.Unlock()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user