Merge pull request #3594 from joostjager/resolver-onion-decode
cnct: decode onion for incoming contest resolver
This commit is contained in:
14 files changed
+290
-193
No files matched your search
@@ -161,9 +161,9 @@ func assertResolversEqual(t *testing.T, originalResolver ContractResolver,
|
|||||||
t.Fatalf("expected %v, got %v",
|
t.Fatalf("expected %v, got %v",
|
||||||
ogRes.broadcastHeight, diskRes.broadcastHeight)
|
ogRes.broadcastHeight, diskRes.broadcastHeight)
|
||||||
}
|
}
|
||||||
if ogRes.htlcIndex != diskRes.htlcIndex {
|
if ogRes.htlc.HtlcIndex != diskRes.htlc.HtlcIndex {
|
||||||
t.Fatalf("expected %v, got %v", ogRes.htlcIndex,
|
t.Fatalf("expected %v, got %v", ogRes.htlc.HtlcIndex,
|
||||||
diskRes.htlcIndex)
|
diskRes.htlc.HtlcIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,9 +184,9 @@ func assertResolversEqual(t *testing.T, originalResolver ContractResolver,
|
|||||||
t.Fatalf("expected %v, got %v",
|
t.Fatalf("expected %v, got %v",
|
||||||
ogRes.broadcastHeight, diskRes.broadcastHeight)
|
ogRes.broadcastHeight, diskRes.broadcastHeight)
|
||||||
}
|
}
|
||||||
if ogRes.payHash != diskRes.payHash {
|
if ogRes.htlc.RHash != diskRes.htlc.RHash {
|
||||||
t.Fatalf("expected %v, got %v", ogRes.payHash,
|
t.Fatalf("expected %v, got %v", ogRes.htlc.RHash,
|
||||||
diskRes.payHash)
|
diskRes.htlc.RHash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,7 +265,9 @@ func TestContractInsertionRetrieval(t *testing.T) {
|
|||||||
outputIncubating: true,
|
outputIncubating: true,
|
||||||
resolved: true,
|
resolved: true,
|
||||||
broadcastHeight: 102,
|
broadcastHeight: 102,
|
||||||
htlcIndex: 12,
|
htlc: channeldb.HTLC{
|
||||||
|
HtlcIndex: 12,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
successResolver := htlcSuccessResolver{
|
successResolver := htlcSuccessResolver{
|
||||||
htlcResolution: lnwallet.IncomingHtlcResolution{
|
htlcResolution: lnwallet.IncomingHtlcResolution{
|
||||||
@@ -278,8 +280,10 @@ func TestContractInsertionRetrieval(t *testing.T) {
|
|||||||
outputIncubating: true,
|
outputIncubating: true,
|
||||||
resolved: true,
|
resolved: true,
|
||||||
broadcastHeight: 109,
|
broadcastHeight: 109,
|
||||||
payHash: testPreimage,
|
htlc: channeldb.HTLC{
|
||||||
sweepTx: nil,
|
RHash: testPreimage,
|
||||||
|
},
|
||||||
|
sweepTx: nil,
|
||||||
}
|
}
|
||||||
resolvers := []ContractResolver{
|
resolvers := []ContractResolver{
|
||||||
&timeoutResolver,
|
&timeoutResolver,
|
||||||
@@ -395,7 +399,9 @@ func TestContractResolution(t *testing.T) {
|
|||||||
outputIncubating: true,
|
outputIncubating: true,
|
||||||
resolved: true,
|
resolved: true,
|
||||||
broadcastHeight: 192,
|
broadcastHeight: 192,
|
||||||
htlcIndex: 9912,
|
htlc: channeldb.HTLC{
|
||||||
|
HtlcIndex: 9912,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// First, we'll insert the resolver into the database and ensure that
|
// First, we'll insert the resolver into the database and ensure that
|
||||||
@@ -454,7 +460,9 @@ func TestContractSwapping(t *testing.T) {
|
|||||||
outputIncubating: true,
|
outputIncubating: true,
|
||||||
resolved: true,
|
resolved: true,
|
||||||
broadcastHeight: 102,
|
broadcastHeight: 102,
|
||||||
htlcIndex: 12,
|
htlc: channeldb.HTLC{
|
||||||
|
HtlcIndex: 12,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
contestResolver := &htlcOutgoingContestResolver{
|
contestResolver := &htlcOutgoingContestResolver{
|
||||||
htlcTimeoutResolver: timeoutResolver,
|
htlcTimeoutResolver: timeoutResolver,
|
||||||
|
|||||||
@@ -151,6 +151,10 @@ type ChainArbitratorConfig struct {
|
|||||||
// NotifyClosedChannel is a function closure that the ChainArbitrator
|
// NotifyClosedChannel is a function closure that the ChainArbitrator
|
||||||
// will use to notify the ChannelNotifier about a newly closed channel.
|
// will use to notify the ChannelNotifier about a newly closed channel.
|
||||||
NotifyClosedChannel func(wire.OutPoint)
|
NotifyClosedChannel func(wire.OutPoint)
|
||||||
|
|
||||||
|
// OnionProcessor is used to decode onion payloads for on-chain
|
||||||
|
// resolution.
|
||||||
|
OnionProcessor OnionProcessor
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChainArbitrator is a sub-system that oversees the on-chain resolution of all
|
// ChainArbitrator is a sub-system that oversees the on-chain resolution of all
|
||||||
|
|||||||
@@ -501,9 +501,20 @@ func (c *ChannelArbitrator) relaunchResolvers(commitSet *CommitSet) error {
|
|||||||
"resolvers", c.cfg.ChanPoint, len(unresolvedContracts))
|
"resolvers", c.cfg.ChanPoint, len(unresolvedContracts))
|
||||||
|
|
||||||
for _, resolver := range unresolvedContracts {
|
for _, resolver := range unresolvedContracts {
|
||||||
if err := c.supplementResolver(resolver, htlcMap); err != nil {
|
htlcResolver, ok := resolver.(htlcContractResolver)
|
||||||
return err
|
if !ok {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
htlcPoint := htlcResolver.HtlcPoint()
|
||||||
|
htlc, ok := htlcMap[htlcPoint]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"htlc resolver %T unavailable", resolver,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
htlcResolver.Supplement(*htlc)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.launchResolvers(unresolvedContracts)
|
c.launchResolvers(unresolvedContracts)
|
||||||
@@ -511,89 +522,6 @@ func (c *ChannelArbitrator) relaunchResolvers(commitSet *CommitSet) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// supplementResolver takes a resolver as it is restored from the log and fills
|
|
||||||
// in missing data from the htlcMap.
|
|
||||||
func (c *ChannelArbitrator) supplementResolver(resolver ContractResolver,
|
|
||||||
htlcMap map[wire.OutPoint]*channeldb.HTLC) error {
|
|
||||||
|
|
||||||
switch r := resolver.(type) {
|
|
||||||
|
|
||||||
case *htlcSuccessResolver:
|
|
||||||
return c.supplementSuccessResolver(r, htlcMap)
|
|
||||||
|
|
||||||
case *htlcIncomingContestResolver:
|
|
||||||
return c.supplementIncomingContestResolver(r, htlcMap)
|
|
||||||
|
|
||||||
case *htlcTimeoutResolver:
|
|
||||||
return c.supplementTimeoutResolver(r, htlcMap)
|
|
||||||
|
|
||||||
case *htlcOutgoingContestResolver:
|
|
||||||
return c.supplementTimeoutResolver(
|
|
||||||
&r.htlcTimeoutResolver, htlcMap,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// supplementSuccessResolver takes a htlcIncomingContestResolver as it is
|
|
||||||
// restored from the log and fills in missing data from the htlcMap.
|
|
||||||
func (c *ChannelArbitrator) supplementIncomingContestResolver(
|
|
||||||
r *htlcIncomingContestResolver,
|
|
||||||
htlcMap map[wire.OutPoint]*channeldb.HTLC) error {
|
|
||||||
|
|
||||||
res := r.htlcResolution
|
|
||||||
htlcPoint := res.HtlcPoint()
|
|
||||||
htlc, ok := htlcMap[htlcPoint]
|
|
||||||
if !ok {
|
|
||||||
return errors.New(
|
|
||||||
"htlc for incoming contest resolver unavailable",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
r.htlcAmt = htlc.Amt
|
|
||||||
r.circuitKey = channeldb.CircuitKey{
|
|
||||||
ChanID: c.cfg.ShortChanID,
|
|
||||||
HtlcID: htlc.HtlcIndex,
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// supplementSuccessResolver takes a htlcSuccessResolver as it is restored from
|
|
||||||
// the log and fills in missing data from the htlcMap.
|
|
||||||
func (c *ChannelArbitrator) supplementSuccessResolver(r *htlcSuccessResolver,
|
|
||||||
htlcMap map[wire.OutPoint]*channeldb.HTLC) error {
|
|
||||||
|
|
||||||
res := r.htlcResolution
|
|
||||||
htlcPoint := res.HtlcPoint()
|
|
||||||
htlc, ok := htlcMap[htlcPoint]
|
|
||||||
if !ok {
|
|
||||||
return errors.New(
|
|
||||||
"htlc for success resolver unavailable",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
r.htlcAmt = htlc.Amt
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// supplementTimeoutResolver takes a htlcSuccessResolver as it is restored from
|
|
||||||
// the log and fills in missing data from the htlcMap.
|
|
||||||
func (c *ChannelArbitrator) supplementTimeoutResolver(r *htlcTimeoutResolver,
|
|
||||||
htlcMap map[wire.OutPoint]*channeldb.HTLC) error {
|
|
||||||
|
|
||||||
res := r.htlcResolution
|
|
||||||
htlcPoint := res.HtlcPoint()
|
|
||||||
htlc, ok := htlcMap[htlcPoint]
|
|
||||||
if !ok {
|
|
||||||
return errors.New(
|
|
||||||
"htlc for timeout resolver unavailable",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
r.htlcAmt = htlc.Amt
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Report returns htlc reports for the active resolvers.
|
// Report returns htlc reports for the active resolvers.
|
||||||
func (c *ChannelArbitrator) Report() []*ContractReport {
|
func (c *ChannelArbitrator) Report() []*ContractReport {
|
||||||
c.activeResolversLock.RLock()
|
c.activeResolversLock.RLock()
|
||||||
@@ -1224,8 +1152,10 @@ func (c *ChannelArbitrator) checkCommitChainActions(height uint32,
|
|||||||
// * race condition if adding and we broadcast, etc
|
// * race condition if adding and we broadcast, etc
|
||||||
// * or would make each instance sync?
|
// * or would make each instance sync?
|
||||||
|
|
||||||
log.Debugf("ChannelArbitrator(%v): checking chain actions at "+
|
log.Debugf("ChannelArbitrator(%v): checking commit chain actions at "+
|
||||||
"height=%v", c.cfg.ChanPoint, height)
|
"height=%v, in_htlc_count=%v, out_htlc_count=%v",
|
||||||
|
c.cfg.ChanPoint, height,
|
||||||
|
len(htlcs.incomingHTLCs), len(htlcs.outgoingHTLCs))
|
||||||
|
|
||||||
actionMap := make(ChainActionMap)
|
actionMap := make(ChainActionMap)
|
||||||
|
|
||||||
@@ -1719,6 +1649,8 @@ func (c *ChannelArbitrator) prepContractResolutions(
|
|||||||
// claim the HTLC (second-level or directly), then add the pre
|
// claim the HTLC (second-level or directly), then add the pre
|
||||||
case HtlcClaimAction:
|
case HtlcClaimAction:
|
||||||
for _, htlc := range htlcs {
|
for _, htlc := range htlcs {
|
||||||
|
htlc := htlc
|
||||||
|
|
||||||
htlcOp := wire.OutPoint{
|
htlcOp := wire.OutPoint{
|
||||||
Hash: commitHash,
|
Hash: commitHash,
|
||||||
Index: uint32(htlc.OutputIndex),
|
Index: uint32(htlc.OutputIndex),
|
||||||
@@ -1734,8 +1666,7 @@ func (c *ChannelArbitrator) prepContractResolutions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
resolver := newSuccessResolver(
|
resolver := newSuccessResolver(
|
||||||
resolution, height,
|
resolution, height, htlc, resolverCfg,
|
||||||
htlc.RHash, htlc.Amt, resolverCfg,
|
|
||||||
)
|
)
|
||||||
htlcResolvers = append(htlcResolvers, resolver)
|
htlcResolvers = append(htlcResolvers, resolver)
|
||||||
}
|
}
|
||||||
@@ -1745,6 +1676,8 @@ func (c *ChannelArbitrator) prepContractResolutions(
|
|||||||
// backwards.
|
// backwards.
|
||||||
case HtlcTimeoutAction:
|
case HtlcTimeoutAction:
|
||||||
for _, htlc := range htlcs {
|
for _, htlc := range htlcs {
|
||||||
|
htlc := htlc
|
||||||
|
|
||||||
htlcOp := wire.OutPoint{
|
htlcOp := wire.OutPoint{
|
||||||
Hash: commitHash,
|
Hash: commitHash,
|
||||||
Index: uint32(htlc.OutputIndex),
|
Index: uint32(htlc.OutputIndex),
|
||||||
@@ -1758,8 +1691,7 @@ func (c *ChannelArbitrator) prepContractResolutions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
resolver := newTimeoutResolver(
|
resolver := newTimeoutResolver(
|
||||||
resolution, height, htlc.HtlcIndex,
|
resolution, height, htlc, resolverCfg,
|
||||||
htlc.Amt, resolverCfg,
|
|
||||||
)
|
)
|
||||||
htlcResolvers = append(htlcResolvers, resolver)
|
htlcResolvers = append(htlcResolvers, resolver)
|
||||||
}
|
}
|
||||||
@@ -1769,6 +1701,8 @@ func (c *ChannelArbitrator) prepContractResolutions(
|
|||||||
// learn of the pre-image, or let the remote party time out.
|
// learn of the pre-image, or let the remote party time out.
|
||||||
case HtlcIncomingWatchAction:
|
case HtlcIncomingWatchAction:
|
||||||
for _, htlc := range htlcs {
|
for _, htlc := range htlcs {
|
||||||
|
htlc := htlc
|
||||||
|
|
||||||
htlcOp := wire.OutPoint{
|
htlcOp := wire.OutPoint{
|
||||||
Hash: commitHash,
|
Hash: commitHash,
|
||||||
Index: uint32(htlc.OutputIndex),
|
Index: uint32(htlc.OutputIndex),
|
||||||
@@ -1785,15 +1719,9 @@ func (c *ChannelArbitrator) prepContractResolutions(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
circuitKey := channeldb.CircuitKey{
|
|
||||||
HtlcID: htlc.HtlcIndex,
|
|
||||||
ChanID: c.cfg.ShortChanID,
|
|
||||||
}
|
|
||||||
|
|
||||||
resolver := newIncomingContestResolver(
|
resolver := newIncomingContestResolver(
|
||||||
htlc.RefundTimeout, circuitKey,
|
resolution, height, htlc,
|
||||||
resolution, height, htlc.RHash,
|
resolverCfg,
|
||||||
htlc.Amt, resolverCfg,
|
|
||||||
)
|
)
|
||||||
htlcResolvers = append(htlcResolvers, resolver)
|
htlcResolvers = append(htlcResolvers, resolver)
|
||||||
}
|
}
|
||||||
@@ -1803,6 +1731,8 @@ func (c *ChannelArbitrator) prepContractResolutions(
|
|||||||
// backwards), or just timeout.
|
// backwards), or just timeout.
|
||||||
case HtlcOutgoingWatchAction:
|
case HtlcOutgoingWatchAction:
|
||||||
for _, htlc := range htlcs {
|
for _, htlc := range htlcs {
|
||||||
|
htlc := htlc
|
||||||
|
|
||||||
htlcOp := wire.OutPoint{
|
htlcOp := wire.OutPoint{
|
||||||
Hash: commitHash,
|
Hash: commitHash,
|
||||||
Index: uint32(htlc.OutputIndex),
|
Index: uint32(htlc.OutputIndex),
|
||||||
@@ -1817,8 +1747,7 @@ func (c *ChannelArbitrator) prepContractResolutions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
resolver := newOutgoingContestResolver(
|
resolver := newOutgoingContestResolver(
|
||||||
resolution, height, htlc.HtlcIndex,
|
resolution, height, htlc, resolverCfg,
|
||||||
htlc.Amt, resolverCfg,
|
|
||||||
)
|
)
|
||||||
htlcResolvers = append(htlcResolvers, resolver)
|
htlcResolvers = append(htlcResolvers, resolver)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -308,6 +308,7 @@ func createTestChannelArbitrator(t *testing.T, log ArbitratorLog) (*chanArbTestC
|
|||||||
incubateChan <- struct{}{}
|
incubateChan <- struct{}{}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
OnionProcessor: &mockOnionProcessor{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll use the resolvedChan to synchronize on call to
|
// We'll use the resolvedChan to synchronize on call to
|
||||||
@@ -858,10 +859,10 @@ func TestChannelArbitratorLocalForceClosePendingHtlc(t *testing.T) {
|
|||||||
resolver)
|
resolver)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The resolver should have its htlcAmt field populated as it.
|
// The resolver should have its htlc amt field populated as it.
|
||||||
if int64(outgoingResolver.htlcAmt) != int64(htlcAmt) {
|
if int64(outgoingResolver.htlc.Amt) != int64(htlcAmt) {
|
||||||
t.Fatalf("wrong htlc amount: expected %v, got %v,",
|
t.Fatalf("wrong htlc amount: expected %v, got %v,",
|
||||||
htlcAmt, int64(outgoingResolver.htlcAmt))
|
htlcAmt, int64(outgoingResolver.htlc.Amt))
|
||||||
}
|
}
|
||||||
|
|
||||||
// htlcOutgoingContestResolver is now active and waiting for the HTLC to
|
// htlcOutgoingContestResolver is now active and waiting for the HTLC to
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/wire"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -51,6 +54,18 @@ type ContractResolver interface {
|
|||||||
Stop()
|
Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// htlcContractResolver is the required interface for htlc resolvers.
|
||||||
|
type htlcContractResolver interface {
|
||||||
|
ContractResolver
|
||||||
|
|
||||||
|
// HtlcPoint returns the htlc's outpoint on the commitment tx.
|
||||||
|
HtlcPoint() wire.OutPoint
|
||||||
|
|
||||||
|
// Supplement adds additional information to the resolver that is
|
||||||
|
// required before Resolve() is called.
|
||||||
|
Supplement(htlc channeldb.HTLC)
|
||||||
|
}
|
||||||
|
|
||||||
// reportingContractResolver is a ContractResolver that also exposes a report on
|
// reportingContractResolver is a ContractResolver that also exposes a report on
|
||||||
// the resolution state of the contract.
|
// the resolution state of the contract.
|
||||||
type reportingContractResolver interface {
|
type reportingContractResolver interface {
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
package contractcourt
|
package contractcourt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||||
"github.com/lightningnetwork/lnd/invoices"
|
"github.com/lightningnetwork/lnd/invoices"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// htlcIncomingContestResolver is a ContractResolver that's able to resolve an
|
// htlcIncomingContestResolver is a ContractResolver that's able to resolve an
|
||||||
@@ -27,28 +28,22 @@ type htlcIncomingContestResolver struct {
|
|||||||
// successfully.
|
// successfully.
|
||||||
htlcExpiry uint32
|
htlcExpiry uint32
|
||||||
|
|
||||||
// circuitKey describes the incoming htlc that is being resolved.
|
|
||||||
circuitKey channeldb.CircuitKey
|
|
||||||
|
|
||||||
// htlcSuccessResolver is the inner resolver that may be utilized if we
|
// htlcSuccessResolver is the inner resolver that may be utilized if we
|
||||||
// learn of the preimage.
|
// learn of the preimage.
|
||||||
htlcSuccessResolver
|
htlcSuccessResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
// newIncomingContestResolver instantiates a new incoming htlc contest resolver.
|
// newIncomingContestResolver instantiates a new incoming htlc contest resolver.
|
||||||
func newIncomingContestResolver(htlcExpiry uint32,
|
func newIncomingContestResolver(
|
||||||
circuitKey channeldb.CircuitKey, res lnwallet.IncomingHtlcResolution,
|
res lnwallet.IncomingHtlcResolution, broadcastHeight uint32,
|
||||||
broadcastHeight uint32, payHash lntypes.Hash,
|
htlc channeldb.HTLC, resCfg ResolverConfig) *htlcIncomingContestResolver {
|
||||||
htlcAmt lnwire.MilliSatoshi,
|
|
||||||
resCfg ResolverConfig) *htlcIncomingContestResolver {
|
|
||||||
|
|
||||||
success := newSuccessResolver(
|
success := newSuccessResolver(
|
||||||
res, broadcastHeight, payHash, htlcAmt, resCfg,
|
res, broadcastHeight, htlc, resCfg,
|
||||||
)
|
)
|
||||||
|
|
||||||
return &htlcIncomingContestResolver{
|
return &htlcIncomingContestResolver{
|
||||||
htlcExpiry: htlcExpiry,
|
htlcExpiry: htlc.RefundTimeout,
|
||||||
circuitKey: circuitKey,
|
|
||||||
htlcSuccessResolver: *success,
|
htlcSuccessResolver: *success,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,6 +67,22 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// First try to parse the payload. If that fails, we can stop resolution
|
||||||
|
// now.
|
||||||
|
payload, err := h.decodePayload()
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("ChannelArbitrator(%v): cannot decode payload of "+
|
||||||
|
"htlc %v", h.ChanPoint, 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
|
||||||
|
// will time it out and get their funds back. This situation can
|
||||||
|
// present itself when we crash before processRemoteAdds in the
|
||||||
|
// link has ran.
|
||||||
|
h.resolved = true
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Register for block epochs. After registration, the current height
|
// Register for block epochs. After registration, the current height
|
||||||
// will be sent on the channel immediately.
|
// will be sent on the channel immediately.
|
||||||
blockEpochs, err := h.Notifier.RegisterBlockEpochNtfn(nil)
|
blockEpochs, err := h.Notifier.RegisterBlockEpochNtfn(nil)
|
||||||
@@ -119,7 +130,7 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
|||||||
applyPreimage := func(preimage lntypes.Preimage) error {
|
applyPreimage := func(preimage lntypes.Preimage) error {
|
||||||
// Sanity check to see if this preimage matches our htlc. At
|
// Sanity check to see if this preimage matches our htlc. At
|
||||||
// this point it should never happen that it does not match.
|
// this point it should never happen that it does not match.
|
||||||
if !preimage.Matches(h.payHash) {
|
if !preimage.Matches(h.htlc.RHash) {
|
||||||
return errors.New("preimage does not match hash")
|
return errors.New("preimage does not match hash")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,9 +196,14 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
|||||||
// on-chain. If this HTLC indeed pays to an existing invoice, the
|
// 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
|
// invoice registry will tell us what to do with the HTLC. This is
|
||||||
// identical to HTLC resolution in the link.
|
// identical to HTLC resolution in the link.
|
||||||
|
circuitKey := channeldb.CircuitKey{
|
||||||
|
ChanID: h.ShortChanID,
|
||||||
|
HtlcID: h.htlc.HtlcIndex,
|
||||||
|
}
|
||||||
|
|
||||||
event, err := h.Registry.NotifyExitHopHtlc(
|
event, err := h.Registry.NotifyExitHopHtlc(
|
||||||
h.payHash, h.htlcAmt, h.htlcExpiry, currentHeight,
|
h.htlc.RHash, h.htlc.Amt, h.htlcExpiry, currentHeight,
|
||||||
h.circuitKey, hodlChan, nil,
|
circuitKey, hodlChan, payload,
|
||||||
)
|
)
|
||||||
switch err {
|
switch err {
|
||||||
case channeldb.ErrInvoiceNotFound:
|
case channeldb.ErrInvoiceNotFound:
|
||||||
@@ -204,7 +220,7 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
|||||||
|
|
||||||
// With the epochs and preimage subscriptions initialized, we'll query
|
// With the epochs and preimage subscriptions initialized, we'll query
|
||||||
// to see if we already know the preimage.
|
// to see if we already know the preimage.
|
||||||
preimage, ok := h.PreimageDB.LookupPreimage(h.payHash)
|
preimage, ok := h.PreimageDB.LookupPreimage(h.htlc.RHash)
|
||||||
if ok {
|
if ok {
|
||||||
// If we do, then this means we can claim the HTLC! However,
|
// 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
|
// we don't know how to ourselves, so we'll return our inner
|
||||||
@@ -222,7 +238,7 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
|||||||
case preimage := <-preimageSubscription.WitnessUpdates:
|
case preimage := <-preimageSubscription.WitnessUpdates:
|
||||||
// We receive all new preimages, so we need to ignore
|
// We receive all new preimages, so we need to ignore
|
||||||
// all except the preimage we are waiting for.
|
// all except the preimage we are waiting for.
|
||||||
if !preimage.Matches(h.payHash) {
|
if !preimage.Matches(h.htlc.RHash) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,7 +284,7 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
|
|||||||
func (h *htlcIncomingContestResolver) report() *ContractReport {
|
func (h *htlcIncomingContestResolver) report() *ContractReport {
|
||||||
// No locking needed as these values are read-only.
|
// No locking needed as these values are read-only.
|
||||||
|
|
||||||
finalAmt := h.htlcAmt.ToSatoshis()
|
finalAmt := h.htlc.Amt.ToSatoshis()
|
||||||
if h.htlcResolution.SignedSuccessTx != nil {
|
if h.htlcResolution.SignedSuccessTx != nil {
|
||||||
finalAmt = btcutil.Amount(
|
finalAmt = btcutil.Amount(
|
||||||
h.htlcResolution.SignedSuccessTx.TxOut[0].Value,
|
h.htlcResolution.SignedSuccessTx.TxOut[0].Value,
|
||||||
@@ -338,6 +354,28 @@ func newIncomingContestResolverFromReader(r io.Reader, resCfg ResolverConfig) (
|
|||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Supplement adds additional information to the resolver that is required
|
||||||
|
// before Resolve() is called.
|
||||||
|
//
|
||||||
|
// NOTE: Part of the htlcContractResolver interface.
|
||||||
|
func (h *htlcIncomingContestResolver) Supplement(htlc channeldb.HTLC) {
|
||||||
|
h.htlc = htlc
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodePayload (re)decodes the hop payload of a received htlc.
|
||||||
|
func (h *htlcIncomingContestResolver) decodePayload() (*hop.Payload, error) {
|
||||||
|
|
||||||
|
onionReader := bytes.NewReader(h.htlc.OnionBlob)
|
||||||
|
iterator, err := h.OnionProcessor.ReconstructHopIterator(
|
||||||
|
onionReader, h.htlc.RHash[:],
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return iterator.HopPayload()
|
||||||
|
}
|
||||||
|
|
||||||
// A compile time assertion to ensure htlcIncomingContestResolver meets the
|
// A compile time assertion to ensure htlcIncomingContestResolver meets the
|
||||||
// ContractResolver interface.
|
// ContractResolver interface.
|
||||||
var _ ContractResolver = (*htlcIncomingContestResolver)(nil)
|
var _ htlcContractResolver = (*htlcIncomingContestResolver)(nil)
|
||||||
@@ -2,9 +2,12 @@ package contractcourt
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||||
"github.com/lightningnetwork/lnd/invoices"
|
"github.com/lightningnetwork/lnd/invoices"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
|
|
||||||
@@ -21,6 +24,7 @@ var (
|
|||||||
testResPreimage = lntypes.Preimage{1, 2, 3}
|
testResPreimage = lntypes.Preimage{1, 2, 3}
|
||||||
testResHash = testResPreimage.Hash()
|
testResHash = testResPreimage.Hash()
|
||||||
testResCircuitKey = channeldb.CircuitKey{}
|
testResCircuitKey = channeldb.CircuitKey{}
|
||||||
|
testOnionBlob = []byte{4, 5, 6}
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestHtlcIncomingResolverFwdPreimageKnown tests resolution of a forwarded htlc
|
// TestHtlcIncomingResolverFwdPreimageKnown tests resolution of a forwarded htlc
|
||||||
@@ -107,6 +111,12 @@ func TestHtlcIncomingResolverExitSettle(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx.waitForResult(true)
|
ctx.waitForResult(true)
|
||||||
|
|
||||||
|
if !bytes.Equal(
|
||||||
|
ctx.onionProcessor.offeredOnionBlob, testOnionBlob,
|
||||||
|
) {
|
||||||
|
t.Fatal("unexpected onion blob")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestHtlcIncomingResolverExitCancel tests resolution of an exit hop htlc for
|
// TestHtlcIncomingResolverExitCancel tests resolution of an exit hop htlc for
|
||||||
@@ -168,14 +178,39 @@ func TestHtlcIncomingResolverExitCancelHodl(t *testing.T) {
|
|||||||
ctx.waitForResult(false)
|
ctx.waitForResult(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type mockHopIterator struct {
|
||||||
|
hop.Iterator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *mockHopIterator) HopPayload() (*hop.Payload, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockOnionProcessor struct {
|
||||||
|
offeredOnionBlob []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *mockOnionProcessor) ReconstructHopIterator(r io.Reader, rHash []byte) (
|
||||||
|
hop.Iterator, error) {
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
o.offeredOnionBlob = data
|
||||||
|
|
||||||
|
return &mockHopIterator{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
type incomingResolverTestContext struct {
|
type incomingResolverTestContext struct {
|
||||||
registry *mockRegistry
|
registry *mockRegistry
|
||||||
witnessBeacon *mockWitnessBeacon
|
witnessBeacon *mockWitnessBeacon
|
||||||
resolver *htlcIncomingContestResolver
|
resolver *htlcIncomingContestResolver
|
||||||
notifier *mockNotifier
|
notifier *mockNotifier
|
||||||
resolveErr chan error
|
onionProcessor *mockOnionProcessor
|
||||||
nextResolver ContractResolver
|
resolveErr chan error
|
||||||
t *testing.T
|
nextResolver ContractResolver
|
||||||
|
t *testing.T
|
||||||
}
|
}
|
||||||
|
|
||||||
func newIncomingResolverTestContext(t *testing.T) *incomingResolverTestContext {
|
func newIncomingResolverTestContext(t *testing.T) *incomingResolverTestContext {
|
||||||
@@ -189,13 +224,16 @@ func newIncomingResolverTestContext(t *testing.T) *incomingResolverTestContext {
|
|||||||
notifyChan: make(chan notifyExitHopData, 1),
|
notifyChan: make(chan notifyExitHopData, 1),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onionProcessor := &mockOnionProcessor{}
|
||||||
|
|
||||||
checkPointChan := make(chan struct{}, 1)
|
checkPointChan := make(chan struct{}, 1)
|
||||||
|
|
||||||
chainCfg := ChannelArbitratorConfig{
|
chainCfg := ChannelArbitratorConfig{
|
||||||
ChainArbitratorConfig: ChainArbitratorConfig{
|
ChainArbitratorConfig: ChainArbitratorConfig{
|
||||||
Notifier: notifier,
|
Notifier: notifier,
|
||||||
PreimageDB: witnessBeacon,
|
PreimageDB: witnessBeacon,
|
||||||
Registry: registry,
|
Registry: registry,
|
||||||
|
OnionProcessor: onionProcessor,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,17 +248,21 @@ func newIncomingResolverTestContext(t *testing.T) *incomingResolverTestContext {
|
|||||||
htlcSuccessResolver: htlcSuccessResolver{
|
htlcSuccessResolver: htlcSuccessResolver{
|
||||||
contractResolverKit: *newContractResolverKit(cfg),
|
contractResolverKit: *newContractResolverKit(cfg),
|
||||||
htlcResolution: lnwallet.IncomingHtlcResolution{},
|
htlcResolution: lnwallet.IncomingHtlcResolution{},
|
||||||
payHash: testResHash,
|
htlc: channeldb.HTLC{
|
||||||
|
RHash: testResHash,
|
||||||
|
OnionBlob: testOnionBlob,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
htlcExpiry: testHtlcExpiry,
|
htlcExpiry: testHtlcExpiry,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &incomingResolverTestContext{
|
return &incomingResolverTestContext{
|
||||||
registry: registry,
|
registry: registry,
|
||||||
witnessBeacon: witnessBeacon,
|
witnessBeacon: witnessBeacon,
|
||||||
resolver: resolver,
|
resolver: resolver,
|
||||||
notifier: notifier,
|
notifier: notifier,
|
||||||
t: t,
|
onionProcessor: onionProcessor,
|
||||||
|
t: t,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// htlcOutgoingContestResolver is a ContractResolver that's able to resolve an
|
// htlcOutgoingContestResolver is a ContractResolver that's able to resolve an
|
||||||
@@ -23,11 +23,11 @@ type htlcOutgoingContestResolver struct {
|
|||||||
// newOutgoingContestResolver instantiates a new outgoing contested htlc
|
// newOutgoingContestResolver instantiates a new outgoing contested htlc
|
||||||
// resolver.
|
// resolver.
|
||||||
func newOutgoingContestResolver(res lnwallet.OutgoingHtlcResolution,
|
func newOutgoingContestResolver(res lnwallet.OutgoingHtlcResolution,
|
||||||
broadcastHeight uint32, htlcIndex uint64, htlcAmt lnwire.MilliSatoshi,
|
broadcastHeight uint32, htlc channeldb.HTLC,
|
||||||
resCfg ResolverConfig) *htlcOutgoingContestResolver {
|
resCfg ResolverConfig) *htlcOutgoingContestResolver {
|
||||||
|
|
||||||
timeout := newTimeoutResolver(
|
timeout := newTimeoutResolver(
|
||||||
res, broadcastHeight, htlcIndex, htlcAmt, resCfg,
|
res, broadcastHeight, htlc, resCfg,
|
||||||
)
|
)
|
||||||
|
|
||||||
return &htlcOutgoingContestResolver{
|
return &htlcOutgoingContestResolver{
|
||||||
@@ -157,7 +157,7 @@ func (h *htlcOutgoingContestResolver) Resolve() (ContractResolver, error) {
|
|||||||
func (h *htlcOutgoingContestResolver) report() *ContractReport {
|
func (h *htlcOutgoingContestResolver) report() *ContractReport {
|
||||||
// No locking needed as these values are read-only.
|
// No locking needed as these values are read-only.
|
||||||
|
|
||||||
finalAmt := h.htlcAmt.ToSatoshis()
|
finalAmt := h.htlc.Amt.ToSatoshis()
|
||||||
if h.htlcResolution.SignedTimeoutTx != nil {
|
if h.htlcResolution.SignedTimeoutTx != nil {
|
||||||
finalAmt = btcutil.Amount(
|
finalAmt = btcutil.Amount(
|
||||||
h.htlcResolution.SignedTimeoutTx.TxOut[0].Value,
|
h.htlcResolution.SignedTimeoutTx.TxOut[0].Value,
|
||||||
@@ -215,4 +215,4 @@ func newOutgoingContestResolverFromReader(r io.Reader, resCfg ResolverConfig) (
|
|||||||
|
|
||||||
// A compile time assertion to ensure htlcOutgoingContestResolver meets the
|
// A compile time assertion to ensure htlcOutgoingContestResolver meets the
|
||||||
// ContractResolver interface.
|
// ContractResolver interface.
|
||||||
var _ ContractResolver = (*htlcOutgoingContestResolver)(nil)
|
var _ htlcContractResolver = (*htlcOutgoingContestResolver)(nil)
|
||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
@@ -98,6 +99,8 @@ func newOutgoingResolverTestContext(t *testing.T) *outgoingResolverTestContext {
|
|||||||
|
|
||||||
preimageDB := newMockWitnessBeacon()
|
preimageDB := newMockWitnessBeacon()
|
||||||
|
|
||||||
|
onionProcessor := &mockOnionProcessor{}
|
||||||
|
|
||||||
chainCfg := ChannelArbitratorConfig{
|
chainCfg := ChannelArbitratorConfig{
|
||||||
ChainArbitratorConfig: ChainArbitratorConfig{
|
ChainArbitratorConfig: ChainArbitratorConfig{
|
||||||
Notifier: notifier,
|
Notifier: notifier,
|
||||||
@@ -112,6 +115,7 @@ func newOutgoingResolverTestContext(t *testing.T) *outgoingResolverTestContext {
|
|||||||
resolutionChan <- msgs[0]
|
resolutionChan <- msgs[0]
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
OnionProcessor: onionProcessor,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,6 +138,10 @@ func newOutgoingResolverTestContext(t *testing.T) *outgoingResolverTestContext {
|
|||||||
htlcTimeoutResolver: htlcTimeoutResolver{
|
htlcTimeoutResolver: htlcTimeoutResolver{
|
||||||
contractResolverKit: *newContractResolverKit(cfg),
|
contractResolverKit: *newContractResolverKit(cfg),
|
||||||
htlcResolution: outgoingRes,
|
htlcResolution: outgoingRes,
|
||||||
|
htlc: channeldb.HTLC{
|
||||||
|
RHash: testResHash,
|
||||||
|
OnionBlob: testOnionBlob,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,9 @@ import (
|
|||||||
|
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
|
||||||
"github.com/lightningnetwork/lnd/sweep"
|
"github.com/lightningnetwork/lnd/sweep"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -38,9 +37,6 @@ type htlcSuccessResolver struct {
|
|||||||
// historical queries to the chain for spends/confirmations.
|
// historical queries to the chain for spends/confirmations.
|
||||||
broadcastHeight uint32
|
broadcastHeight uint32
|
||||||
|
|
||||||
// payHash is the payment hash of the original HTLC extended to us.
|
|
||||||
payHash lntypes.Hash
|
|
||||||
|
|
||||||
// sweepTx will be non-nil if we've already crafted a transaction to
|
// sweepTx will be non-nil if we've already crafted a transaction to
|
||||||
// sweep a direct HTLC output. This is only a concern if we're sweeping
|
// sweep a direct HTLC output. This is only a concern if we're sweeping
|
||||||
// from the commitment transaction of the remote party.
|
// from the commitment transaction of the remote party.
|
||||||
@@ -48,25 +44,22 @@ type htlcSuccessResolver struct {
|
|||||||
// TODO(roasbeef): send off to utxobundler
|
// TODO(roasbeef): send off to utxobundler
|
||||||
sweepTx *wire.MsgTx
|
sweepTx *wire.MsgTx
|
||||||
|
|
||||||
// htlcAmt is the original amount of the htlc, not taking into
|
// htlc contains information on the htlc that we are resolving on-chain.
|
||||||
// account any fees that may have to be paid if it goes on chain.
|
htlc channeldb.HTLC
|
||||||
htlcAmt lnwire.MilliSatoshi
|
|
||||||
|
|
||||||
contractResolverKit
|
contractResolverKit
|
||||||
}
|
}
|
||||||
|
|
||||||
// newSuccessResolver instanties a new htlc success resolver.
|
// newSuccessResolver instanties a new htlc success resolver.
|
||||||
func newSuccessResolver(res lnwallet.IncomingHtlcResolution,
|
func newSuccessResolver(res lnwallet.IncomingHtlcResolution,
|
||||||
broadcastHeight uint32, payHash lntypes.Hash,
|
broadcastHeight uint32, htlc channeldb.HTLC,
|
||||||
htlcAmt lnwire.MilliSatoshi,
|
|
||||||
resCfg ResolverConfig) *htlcSuccessResolver {
|
resCfg ResolverConfig) *htlcSuccessResolver {
|
||||||
|
|
||||||
return &htlcSuccessResolver{
|
return &htlcSuccessResolver{
|
||||||
contractResolverKit: *newContractResolverKit(resCfg),
|
contractResolverKit: *newContractResolverKit(resCfg),
|
||||||
htlcResolution: res,
|
htlcResolution: res,
|
||||||
broadcastHeight: broadcastHeight,
|
broadcastHeight: broadcastHeight,
|
||||||
payHash: payHash,
|
htlc: htlc,
|
||||||
htlcAmt: htlcAmt,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +107,7 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
|
|||||||
if h.sweepTx == nil {
|
if h.sweepTx == nil {
|
||||||
log.Infof("%T(%x): crafting sweep tx for "+
|
log.Infof("%T(%x): crafting sweep tx for "+
|
||||||
"incoming+remote htlc confirmed", h,
|
"incoming+remote htlc confirmed", h,
|
||||||
h.payHash[:])
|
h.htlc.RHash[:])
|
||||||
|
|
||||||
// Before we can craft out sweeping transaction, we
|
// Before we can craft out sweeping transaction, we
|
||||||
// need to create an input which contains all the items
|
// need to create an input which contains all the items
|
||||||
@@ -148,7 +141,7 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("%T(%x): crafted sweep tx=%v", h,
|
log.Infof("%T(%x): crafted sweep tx=%v", h,
|
||||||
h.payHash[:], spew.Sdump(h.sweepTx))
|
h.htlc.RHash[:], spew.Sdump(h.sweepTx))
|
||||||
|
|
||||||
// With the sweep transaction signed, we'll now
|
// With the sweep transaction signed, we'll now
|
||||||
// Checkpoint our state.
|
// Checkpoint our state.
|
||||||
@@ -164,7 +157,7 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
|
|||||||
err := h.PublishTx(h.sweepTx)
|
err := h.PublishTx(h.sweepTx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Infof("%T(%x): unable to publish tx: %v",
|
log.Infof("%T(%x): unable to publish tx: %v",
|
||||||
h, h.payHash[:], err)
|
h, h.htlc.RHash[:], err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,7 +173,7 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("%T(%x): waiting for sweep tx (txid=%v) to be "+
|
log.Infof("%T(%x): waiting for sweep tx (txid=%v) to be "+
|
||||||
"confirmed", h, h.payHash[:], sweepTXID)
|
"confirmed", h, h.htlc.RHash[:], sweepTXID)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case _, ok := <-confNtfn.Confirmed:
|
case _, ok := <-confNtfn.Confirmed:
|
||||||
@@ -199,7 +192,7 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("%T(%x): broadcasting second-layer transition tx: %v",
|
log.Infof("%T(%x): broadcasting second-layer transition tx: %v",
|
||||||
h, h.payHash[:], spew.Sdump(h.htlcResolution.SignedSuccessTx))
|
h, h.htlc.RHash[:], spew.Sdump(h.htlcResolution.SignedSuccessTx))
|
||||||
|
|
||||||
// We'll now broadcast the second layer transaction so we can kick off
|
// We'll now broadcast the second layer transaction so we can kick off
|
||||||
// the claiming process.
|
// the claiming process.
|
||||||
@@ -215,7 +208,7 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
|
|||||||
// done so.
|
// done so.
|
||||||
if !h.outputIncubating {
|
if !h.outputIncubating {
|
||||||
log.Infof("%T(%x): incubating incoming htlc output",
|
log.Infof("%T(%x): incubating incoming htlc output",
|
||||||
h, h.payHash[:])
|
h, h.htlc.RHash[:])
|
||||||
|
|
||||||
err := h.IncubateOutputs(
|
err := h.IncubateOutputs(
|
||||||
h.ChanPoint, nil, nil, &h.htlcResolution,
|
h.ChanPoint, nil, nil, &h.htlcResolution,
|
||||||
@@ -245,7 +238,7 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("%T(%x): waiting for second-level HTLC output to be spent "+
|
log.Infof("%T(%x): waiting for second-level HTLC output to be spent "+
|
||||||
"after csv_delay=%v", h, h.payHash[:], h.htlcResolution.CsvDelay)
|
"after csv_delay=%v", h, h.htlc.RHash[:], h.htlcResolution.CsvDelay)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case _, ok := <-spendNtfn.Spend:
|
case _, ok := <-spendNtfn.Spend:
|
||||||
@@ -298,7 +291,7 @@ func (h *htlcSuccessResolver) Encode(w io.Writer) error {
|
|||||||
if err := binary.Write(w, endian, h.broadcastHeight); err != nil {
|
if err := binary.Write(w, endian, h.broadcastHeight); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err := w.Write(h.payHash[:]); err != nil {
|
if _, err := w.Write(h.htlc.RHash[:]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,13 +324,28 @@ func newSuccessResolverFromReader(r io.Reader, resCfg ResolverConfig) (
|
|||||||
if err := binary.Read(r, endian, &h.broadcastHeight); err != nil {
|
if err := binary.Read(r, endian, &h.broadcastHeight); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if _, err := io.ReadFull(r, h.payHash[:]); err != nil {
|
if _, err := io.ReadFull(r, h.htlc.RHash[:]); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Supplement adds additional information to the resolver that is required
|
||||||
|
// before Resolve() is called.
|
||||||
|
//
|
||||||
|
// NOTE: Part of the htlcContractResolver interface.
|
||||||
|
func (h *htlcSuccessResolver) Supplement(htlc channeldb.HTLC) {
|
||||||
|
h.htlc = htlc
|
||||||
|
}
|
||||||
|
|
||||||
|
// HtlcPoint returns the htlc's outpoint on the commitment tx.
|
||||||
|
//
|
||||||
|
// NOTE: Part of the htlcContractResolver interface.
|
||||||
|
func (h *htlcSuccessResolver) HtlcPoint() wire.OutPoint {
|
||||||
|
return h.htlcResolution.HtlcPoint()
|
||||||
|
}
|
||||||
|
|
||||||
// A compile time assertion to ensure htlcSuccessResolver meets the
|
// A compile time assertion to ensure htlcSuccessResolver meets the
|
||||||
// ContractResolver interface.
|
// ContractResolver interface.
|
||||||
var _ ContractResolver = (*htlcSuccessResolver)(nil)
|
var _ htlcContractResolver = (*htlcSuccessResolver)(nil)
|
||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
@@ -40,29 +41,22 @@ type htlcTimeoutResolver struct {
|
|||||||
// TODO(roasbeef): wrap above into definite resolution embedding?
|
// TODO(roasbeef): wrap above into definite resolution embedding?
|
||||||
broadcastHeight uint32
|
broadcastHeight uint32
|
||||||
|
|
||||||
// htlcIndex is the index of this HTLC within the trace of the
|
// htlc contains information on the htlc that we are resolving on-chain.
|
||||||
// additional commitment state machine.
|
htlc channeldb.HTLC
|
||||||
htlcIndex uint64
|
|
||||||
|
|
||||||
// htlcAmt is the original amount of the htlc, not taking into
|
|
||||||
// account any fees that may have to be paid if it goes on chain.
|
|
||||||
htlcAmt lnwire.MilliSatoshi
|
|
||||||
|
|
||||||
contractResolverKit
|
contractResolverKit
|
||||||
}
|
}
|
||||||
|
|
||||||
// newTimeoutResolver instantiates a new timeout htlc resolver.
|
// newTimeoutResolver instantiates a new timeout htlc resolver.
|
||||||
func newTimeoutResolver(res lnwallet.OutgoingHtlcResolution,
|
func newTimeoutResolver(res lnwallet.OutgoingHtlcResolution,
|
||||||
broadcastHeight uint32, htlcIndex uint64,
|
broadcastHeight uint32, htlc channeldb.HTLC,
|
||||||
htlcAmt lnwire.MilliSatoshi,
|
|
||||||
resCfg ResolverConfig) *htlcTimeoutResolver {
|
resCfg ResolverConfig) *htlcTimeoutResolver {
|
||||||
|
|
||||||
return &htlcTimeoutResolver{
|
return &htlcTimeoutResolver{
|
||||||
contractResolverKit: *newContractResolverKit(resCfg),
|
contractResolverKit: *newContractResolverKit(resCfg),
|
||||||
htlcResolution: res,
|
htlcResolution: res,
|
||||||
broadcastHeight: broadcastHeight,
|
broadcastHeight: broadcastHeight,
|
||||||
htlcIndex: htlcIndex,
|
htlc: htlc,
|
||||||
htlcAmt: htlcAmt,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +151,7 @@ func (h *htlcTimeoutResolver) claimCleanUp(
|
|||||||
// resolved, then exit.
|
// resolved, then exit.
|
||||||
if err := h.DeliverResolutionMsg(ResolutionMsg{
|
if err := h.DeliverResolutionMsg(ResolutionMsg{
|
||||||
SourceChan: h.ShortChanID,
|
SourceChan: h.ShortChanID,
|
||||||
HtlcIndex: h.htlcIndex,
|
HtlcIndex: h.htlc.HtlcIndex,
|
||||||
PreImage: &pre,
|
PreImage: &pre,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -352,7 +346,7 @@ func (h *htlcTimeoutResolver) Resolve() (ContractResolver, error) {
|
|||||||
failureMsg := &lnwire.FailPermanentChannelFailure{}
|
failureMsg := &lnwire.FailPermanentChannelFailure{}
|
||||||
if err := h.DeliverResolutionMsg(ResolutionMsg{
|
if err := h.DeliverResolutionMsg(ResolutionMsg{
|
||||||
SourceChan: h.ShortChanID,
|
SourceChan: h.ShortChanID,
|
||||||
HtlcIndex: h.htlcIndex,
|
HtlcIndex: h.htlc.HtlcIndex,
|
||||||
Failure: failureMsg,
|
Failure: failureMsg,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -414,7 +408,7 @@ func (h *htlcTimeoutResolver) Encode(w io.Writer) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := binary.Write(w, endian, h.htlcIndex); err != nil {
|
if err := binary.Write(w, endian, h.htlc.HtlcIndex); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -449,13 +443,28 @@ func newTimeoutResolverFromReader(r io.Reader, resCfg ResolverConfig) (
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := binary.Read(r, endian, &h.htlcIndex); err != nil {
|
if err := binary.Read(r, endian, &h.htlc.HtlcIndex); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Supplement adds additional information to the resolver that is required
|
||||||
|
// before Resolve() is called.
|
||||||
|
//
|
||||||
|
// NOTE: Part of the htlcContractResolver interface.
|
||||||
|
func (h *htlcTimeoutResolver) Supplement(htlc channeldb.HTLC) {
|
||||||
|
h.htlc = htlc
|
||||||
|
}
|
||||||
|
|
||||||
|
// HtlcPoint returns the htlc's outpoint on the commitment tx.
|
||||||
|
//
|
||||||
|
// NOTE: Part of the htlcContractResolver interface.
|
||||||
|
func (h *htlcTimeoutResolver) HtlcPoint() wire.OutPoint {
|
||||||
|
return h.htlcResolution.HtlcPoint()
|
||||||
|
}
|
||||||
|
|
||||||
// A compile time assertion to ensure htlcTimeoutResolver meets the
|
// A compile time assertion to ensure htlcTimeoutResolver meets the
|
||||||
// ContractResolver interface.
|
// ContractResolver interface.
|
||||||
var _ ContractResolver = (*htlcTimeoutResolver)(nil)
|
var _ htlcContractResolver = (*htlcTimeoutResolver)(nil)
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
package contractcourt
|
package contractcourt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||||
"github.com/lightningnetwork/lnd/invoices"
|
"github.com/lightningnetwork/lnd/invoices"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
@@ -26,3 +29,10 @@ type Registry interface {
|
|||||||
// HodlUnsubscribeAll unsubscribes from all hodl events.
|
// HodlUnsubscribeAll unsubscribes from all hodl events.
|
||||||
HodlUnsubscribeAll(subscriber chan<- interface{})
|
HodlUnsubscribeAll(subscriber chan<- interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OnionProcessor is an interface used to decode onion blobs.
|
||||||
|
type OnionProcessor interface {
|
||||||
|
// ReconstructHopIterator attempts to decode a valid sphinx packet from
|
||||||
|
// the passed io.Reader instance.
|
||||||
|
ReconstructHopIterator(r io.Reader, rHash []byte) (hop.Iterator, error)
|
||||||
|
}
|
||||||
@@ -184,6 +184,30 @@ func (p *OnionProcessor) DecodeHopIterator(r io.Reader, rHash []byte,
|
|||||||
return makeSphinxHopIterator(onionPkt, sphinxPacket), lnwire.CodeNone
|
return makeSphinxHopIterator(onionPkt, sphinxPacket), lnwire.CodeNone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReconstructHopIterator attempts to decode a valid sphinx packet from the passed io.Reader
|
||||||
|
// instance using the rHash as the associated data when checking the relevant
|
||||||
|
// MACs during the decoding process.
|
||||||
|
func (p *OnionProcessor) ReconstructHopIterator(r io.Reader, rHash []byte) (
|
||||||
|
Iterator, error) {
|
||||||
|
|
||||||
|
onionPkt := &sphinx.OnionPacket{}
|
||||||
|
if err := onionPkt.Decode(r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to process the Sphinx packet. We include the payment hash of
|
||||||
|
// the HTLC as it's authenticated within the Sphinx packet itself as
|
||||||
|
// associated data in order to thwart attempts a replay attacks. In the
|
||||||
|
// case of a replay, an attacker is *forced* to use the same payment
|
||||||
|
// hash twice, thereby losing their money entirely.
|
||||||
|
sphinxPacket, err := p.router.ReconstructOnionPacket(onionPkt, rHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return makeSphinxHopIterator(onionPkt, sphinxPacket), nil
|
||||||
|
}
|
||||||
|
|
||||||
// DecodeHopIteratorRequest encapsulates all date necessary to process an onion
|
// DecodeHopIteratorRequest encapsulates all date necessary to process an onion
|
||||||
// packet, perform sphinx replay detection, and schedule the entry for garbage
|
// packet, perform sphinx replay detection, and schedule the entry for garbage
|
||||||
// collection.
|
// collection.
|
||||||
|
|||||||
@@ -906,6 +906,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB,
|
|||||||
Sweeper: s.sweeper,
|
Sweeper: s.sweeper,
|
||||||
Registry: s.invoices,
|
Registry: s.invoices,
|
||||||
NotifyClosedChannel: s.channelNotifier.NotifyClosedChannelEvent,
|
NotifyClosedChannel: s.channelNotifier.NotifyClosedChannelEvent,
|
||||||
|
OnionProcessor: s.sphinx,
|
||||||
}, chanDB)
|
}, chanDB)
|
||||||
|
|
||||||
s.breachArbiter = newBreachArbiter(&BreachConfig{
|
s.breachArbiter = newBreachArbiter(&BreachConfig{
|
||||||
|
|||||||
Reference in new issue
Block a user