contractcourt: specify deadline and budget for htlc timeout
This commit is contained in:
8 files changed
+214
-13
No files matched your search
@@ -3,6 +3,7 @@ package contractcourt
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@@ -14,6 +15,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/clock"
|
"github.com/lightningnetwork/lnd/clock"
|
||||||
|
"github.com/lightningnetwork/lnd/fn"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/kvdb"
|
"github.com/lightningnetwork/lnd/kvdb"
|
||||||
"github.com/lightningnetwork/lnd/labels"
|
"github.com/lightningnetwork/lnd/labels"
|
||||||
@@ -384,6 +386,11 @@ func newActiveChannelArbitrator(channel *channeldb.OpenChannel,
|
|||||||
chanStateDB := c.chanSource.ChannelStateDB()
|
chanStateDB := c.chanSource.ChannelStateDB()
|
||||||
return chanStateDB.FetchHistoricalChannel(&chanPoint)
|
return chanStateDB.FetchHistoricalChannel(&chanPoint)
|
||||||
},
|
},
|
||||||
|
FindOutgoingHTLCDeadline: func(
|
||||||
|
rHash chainhash.Hash) fn.Option[int32] {
|
||||||
|
|
||||||
|
return c.FindOutgoingHTLCDeadline(chanPoint, rHash)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// The final component needed is an arbitrator log that the arbitrator
|
// The final component needed is an arbitrator log that the arbitrator
|
||||||
@@ -578,6 +585,7 @@ func (c *ChainArbitrator) Start() error {
|
|||||||
// corresponding more restricted resolver, as we don't have to watch
|
// corresponding more restricted resolver, as we don't have to watch
|
||||||
// the chain any longer, only resolve the contracts on the confirmed
|
// the chain any longer, only resolve the contracts on the confirmed
|
||||||
// commitment.
|
// commitment.
|
||||||
|
//nolint:lll
|
||||||
for _, closeChanInfo := range closingChannels {
|
for _, closeChanInfo := range closingChannels {
|
||||||
// We can leave off the CloseContract and ForceCloseChan
|
// We can leave off the CloseContract and ForceCloseChan
|
||||||
// methods as the channel is already closed at this point.
|
// methods as the channel is already closed at this point.
|
||||||
@@ -601,6 +609,11 @@ func (c *ChainArbitrator) Start() error {
|
|||||||
chanStateDB := c.chanSource.ChannelStateDB()
|
chanStateDB := c.chanSource.ChannelStateDB()
|
||||||
return chanStateDB.FetchHistoricalChannel(&chanPoint)
|
return chanStateDB.FetchHistoricalChannel(&chanPoint)
|
||||||
},
|
},
|
||||||
|
FindOutgoingHTLCDeadline: func(
|
||||||
|
rHash chainhash.Hash) fn.Option[int32] {
|
||||||
|
|
||||||
|
return c.FindOutgoingHTLCDeadline(chanPoint, rHash)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
chanLog, err := newBoltArbitratorLog(
|
chanLog, err := newBoltArbitratorLog(
|
||||||
c.chanSource.Backend, arbCfg, c.cfg.ChainHash, chanPoint,
|
c.chanSource.Backend, arbCfg, c.cfg.ChainHash, chanPoint,
|
||||||
@@ -1204,5 +1217,63 @@ func (c *ChainArbitrator) SubscribeChannelEvents(
|
|||||||
return watcher.SubscribeChannelEvents(), nil
|
return watcher.SubscribeChannelEvents(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindOutgoingHTLCDeadline returns the deadline in absolute block height for
|
||||||
|
// the specified outgoing HTLC. For an outgoing HTLC, its deadline is defined
|
||||||
|
// by the timeout height of its corresponding incoming HTLC - this is the
|
||||||
|
// expiry height the that remote peer can spend his/her outgoing HTLC via the
|
||||||
|
// timeout path.
|
||||||
|
func (c *ChainArbitrator) FindOutgoingHTLCDeadline(chanPoint wire.OutPoint,
|
||||||
|
rHash chainhash.Hash) fn.Option[int32] {
|
||||||
|
|
||||||
|
// minRefundTimeout tracks the minimal refund timeout found using the
|
||||||
|
// rHash. It's possible that we find multiple HTLCs living in different
|
||||||
|
// channels sharing the same rHash if an MPP is routed by us. In this
|
||||||
|
// case, we'll use the smallest refund timeout as the deadline.
|
||||||
|
//
|
||||||
|
// TODO(yy): can instead query the circuit map to find the exact HTLC.
|
||||||
|
minRefundTimeout := uint32(math.MaxInt32)
|
||||||
|
|
||||||
|
// Iterate over all active channels to find the HTLC with the matching
|
||||||
|
// rHash.
|
||||||
|
for cp, channelArb := range c.activeChannels {
|
||||||
|
// Skip the targeted channel as the incoming HTLC is not here.
|
||||||
|
if cp == chanPoint {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate all the known HTLCs to find the targeted incoming
|
||||||
|
// HTLC.
|
||||||
|
for _, htlcs := range channelArb.activeHTLCs {
|
||||||
|
for _, htlc := range htlcs.incomingHTLCs {
|
||||||
|
if htlc.RHash != rHash {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("ChannelArbitrator(%v): found "+
|
||||||
|
"incoming HTLC in channel=%v using "+
|
||||||
|
"rHash=%v, refundTimeout=%v", chanPoint,
|
||||||
|
cp, rHash, htlc.RefundTimeout)
|
||||||
|
|
||||||
|
// Update the value if it's smaller.
|
||||||
|
if minRefundTimeout > htlc.RefundTimeout {
|
||||||
|
minRefundTimeout = htlc.RefundTimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the refund timeout value if found.
|
||||||
|
if minRefundTimeout != math.MaxInt32 {
|
||||||
|
return fn.Some(int32(minRefundTimeout))
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's no incoming HTLC found, it means we are the first hop. In
|
||||||
|
// this case, we can relax the deadline.
|
||||||
|
log.Infof("ChannelArbitrator(%v): incoming HTLC not found for "+
|
||||||
|
"rHash=%v, using default deadline instead", chanPoint, rHash)
|
||||||
|
|
||||||
|
return fn.None[int32]()
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(roasbeef): arbitration reports
|
// TODO(roasbeef): arbitration reports
|
||||||
// * types: contested, waiting for success conf, etc
|
// * types: contested, waiting for success conf, etc
|
||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/channeldb/models"
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
|
"github.com/lightningnetwork/lnd/fn"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/invoices"
|
"github.com/lightningnetwork/lnd/invoices"
|
||||||
@@ -170,6 +171,13 @@ type ChannelArbitratorConfig struct {
|
|||||||
// additional information required for proper contract resolution.
|
// additional information required for proper contract resolution.
|
||||||
FetchHistoricalChannel func() (*channeldb.OpenChannel, error)
|
FetchHistoricalChannel func() (*channeldb.OpenChannel, error)
|
||||||
|
|
||||||
|
// FindOutgoingHTLCDeadline returns the deadline in absolute block
|
||||||
|
// height for the specified outgoing HTLC. For an outgoing HTLC, its
|
||||||
|
// deadline is defined by the timeout height of its corresponding
|
||||||
|
// incoming HTLC - this is the expiry height the that remote peer can
|
||||||
|
// spend his/her outgoing HTLC via the timeout path.
|
||||||
|
FindOutgoingHTLCDeadline func(rHash chainhash.Hash) fn.Option[int32]
|
||||||
|
|
||||||
ChainArbitratorConfig
|
ChainArbitratorConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,6 +765,14 @@ func (c *ChannelArbitrator) relaunchResolvers(commitSet *CommitSet,
|
|||||||
}
|
}
|
||||||
|
|
||||||
htlcResolver.Supplement(*htlc)
|
htlcResolver.Supplement(*htlc)
|
||||||
|
|
||||||
|
// If this is an outgoing HTLC, we will also need to supplement
|
||||||
|
// the resolver with the expiry block height of its
|
||||||
|
// corresponding incoming HTLC.
|
||||||
|
if !htlc.Incoming {
|
||||||
|
deadline := c.cfg.FindOutgoingHTLCDeadline(htlc.RHash)
|
||||||
|
htlcResolver.SupplementDeadline(deadline)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The anchor resolver is stateless and can always be re-instantiated.
|
// The anchor resolver is stateless and can always be re-instantiated.
|
||||||
@@ -1733,8 +1749,15 @@ func (c *ChannelArbitrator) checkCommitChainActions(height uint32,
|
|||||||
for _, htlc := range htlcs.outgoingHTLCs {
|
for _, htlc := range htlcs.outgoingHTLCs {
|
||||||
// We'll need to go on-chain for an outgoing HTLC if it was
|
// We'll need to go on-chain for an outgoing HTLC if it was
|
||||||
// never resolved downstream, and it's "close" to timing out.
|
// never resolved downstream, and it's "close" to timing out.
|
||||||
toChain := c.shouldGoOnChain(htlc, c.cfg.OutgoingBroadcastDelta,
|
//
|
||||||
height,
|
// TODO(yy): If there's no corresponding incoming HTLC, it
|
||||||
|
// means we are the first hop, hence the payer. This is a
|
||||||
|
// tricky case - unlike a forwarding hop, we don't have an
|
||||||
|
// incoming HTLC that will time out, which means as long as we
|
||||||
|
// can learn the preimage, we can settle the invoice (before it
|
||||||
|
// expires?).
|
||||||
|
toChain := c.shouldGoOnChain(
|
||||||
|
htlc, c.cfg.OutgoingBroadcastDelta, height,
|
||||||
)
|
)
|
||||||
|
|
||||||
if toChain {
|
if toChain {
|
||||||
@@ -2349,6 +2372,16 @@ func (c *ChannelArbitrator) prepContractResolutions(
|
|||||||
if chanState != nil {
|
if chanState != nil {
|
||||||
resolver.SupplementState(chanState)
|
resolver.SupplementState(chanState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For outgoing HTLCs, we will also need to
|
||||||
|
// supplement the resolver with the expiry
|
||||||
|
// block height of its corresponding incoming
|
||||||
|
// HTLC.
|
||||||
|
deadline := c.cfg.FindOutgoingHTLCDeadline(
|
||||||
|
htlc.RHash,
|
||||||
|
)
|
||||||
|
resolver.SupplementDeadline(deadline)
|
||||||
|
|
||||||
htlcResolvers = append(htlcResolvers, resolver)
|
htlcResolvers = append(htlcResolvers, resolver)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2441,6 +2474,16 @@ func (c *ChannelArbitrator) prepContractResolutions(
|
|||||||
if chanState != nil {
|
if chanState != nil {
|
||||||
resolver.SupplementState(chanState)
|
resolver.SupplementState(chanState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For outgoing HTLCs, we will also need to
|
||||||
|
// supplement the resolver with the expiry
|
||||||
|
// block height of its corresponding incoming
|
||||||
|
// HTLC.
|
||||||
|
deadline := c.cfg.FindOutgoingHTLCDeadline(
|
||||||
|
htlc.RHash,
|
||||||
|
)
|
||||||
|
resolver.SupplementDeadline(deadline)
|
||||||
|
|
||||||
htlcResolvers = append(htlcResolvers, resolver)
|
htlcResolvers = append(htlcResolvers, resolver)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/clock"
|
"github.com/lightningnetwork/lnd/clock"
|
||||||
|
"github.com/lightningnetwork/lnd/fn"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/kvdb"
|
"github.com/lightningnetwork/lnd/kvdb"
|
||||||
"github.com/lightningnetwork/lnd/lntest/mock"
|
"github.com/lightningnetwork/lnd/lntest/mock"
|
||||||
@@ -425,6 +426,11 @@ func createTestChannelArbitrator(t *testing.T, log ArbitratorLog,
|
|||||||
FetchHistoricalChannel: func() (*channeldb.OpenChannel, error) {
|
FetchHistoricalChannel: func() (*channeldb.OpenChannel, error) {
|
||||||
return &channeldb.OpenChannel{}, nil
|
return &channeldb.OpenChannel{}, nil
|
||||||
},
|
},
|
||||||
|
FindOutgoingHTLCDeadline: func(
|
||||||
|
rHash chainhash.Hash) fn.Option[int32] {
|
||||||
|
|
||||||
|
return fn.None[int32]()
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
testOpts := &testChanArbOpts{
|
testOpts := &testChanArbOpts{
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/btcsuite/btclog"
|
"github.com/btcsuite/btclog"
|
||||||
"github.com/lightningnetwork/lnd/build"
|
"github.com/lightningnetwork/lnd/build"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/fn"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -20,10 +21,6 @@ const (
|
|||||||
// sweepConfTarget is the default number of blocks that we'll use as a
|
// sweepConfTarget is the default number of blocks that we'll use as a
|
||||||
// confirmation target when sweeping.
|
// confirmation target when sweeping.
|
||||||
sweepConfTarget = 6
|
sweepConfTarget = 6
|
||||||
|
|
||||||
// secondLevelConfTarget is the confirmation target we'll use when
|
|
||||||
// adding fees to our second-level HTLC transactions.
|
|
||||||
secondLevelConfTarget = 6
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ContractResolver is an interface which packages a state machine which is
|
// ContractResolver is an interface which packages a state machine which is
|
||||||
@@ -75,6 +72,10 @@ type htlcContractResolver interface {
|
|||||||
// Supplement adds additional information to the resolver that is
|
// Supplement adds additional information to the resolver that is
|
||||||
// required before Resolve() is called.
|
// required before Resolve() is called.
|
||||||
Supplement(htlc channeldb.HTLC)
|
Supplement(htlc channeldb.HTLC)
|
||||||
|
|
||||||
|
// SupplementDeadline gives the deadline height for the HTLC output.
|
||||||
|
// This is only useful for outgoing HTLCs.
|
||||||
|
SupplementDeadline(deadlineHeight fn.Option[int32])
|
||||||
}
|
}
|
||||||
|
|
||||||
// reportingContractResolver is a ContractResolver that also exposes a report on
|
// reportingContractResolver is a ContractResolver that also exposes a report on
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/channeldb/models"
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
|
"github.com/lightningnetwork/lnd/fn"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
"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"
|
||||||
@@ -516,6 +517,12 @@ func (h *htlcIncomingContestResolver) Supplement(htlc channeldb.HTLC) {
|
|||||||
h.htlc = htlc
|
h.htlc = htlc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SupplementDeadline does nothing for an incoming htlc resolver.
|
||||||
|
//
|
||||||
|
// NOTE: Part of the htlcContractResolver interface.
|
||||||
|
func (h *htlcIncomingContestResolver) SupplementDeadline(_ fn.Option[int32]) {
|
||||||
|
}
|
||||||
|
|
||||||
// decodePayload (re)decodes the hop payload of a received htlc.
|
// decodePayload (re)decodes the hop payload of a received htlc.
|
||||||
func (h *htlcIncomingContestResolver) decodePayload() (*hop.Payload,
|
func (h *htlcIncomingContestResolver) decodePayload() (*hop.Payload,
|
||||||
[]byte, error) {
|
[]byte, error) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/fn"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -196,6 +197,12 @@ func (h *htlcOutgoingContestResolver) Encode(w io.Writer) error {
|
|||||||
return h.htlcTimeoutResolver.Encode(w)
|
return h.htlcTimeoutResolver.Encode(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SupplementDeadline does nothing for an incoming htlc resolver.
|
||||||
|
//
|
||||||
|
// NOTE: Part of the htlcContractResolver interface.
|
||||||
|
func (h *htlcOutgoingContestResolver) SupplementDeadline(_ fn.Option[int32]) {
|
||||||
|
}
|
||||||
|
|
||||||
// newOutgoingContestResolverFromReader attempts to decode an encoded ContractResolver
|
// newOutgoingContestResolverFromReader attempts to decode an encoded ContractResolver
|
||||||
// from the passed Reader instance, returning an active ContractResolver
|
// from the passed Reader instance, returning an active ContractResolver
|
||||||
// instance.
|
// instance.
|
||||||
|
|||||||
@@ -737,6 +737,12 @@ func (h *htlcSuccessResolver) HtlcPoint() wire.OutPoint {
|
|||||||
return h.htlcResolution.HtlcPoint()
|
return h.htlcResolution.HtlcPoint()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SupplementDeadline does nothing for an incoming htlc resolver.
|
||||||
|
//
|
||||||
|
// NOTE: Part of the htlcContractResolver interface.
|
||||||
|
func (h *htlcSuccessResolver) SupplementDeadline(_ fn.Option[int32]) {
|
||||||
|
}
|
||||||
|
|
||||||
// A compile time assertion to ensure htlcSuccessResolver meets the
|
// A compile time assertion to ensure htlcSuccessResolver meets the
|
||||||
// ContractResolver interface.
|
// ContractResolver interface.
|
||||||
var _ htlcContractResolver = (*htlcSuccessResolver)(nil)
|
var _ htlcContractResolver = (*htlcSuccessResolver)(nil)
|
||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"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/channeldb"
|
||||||
|
"github.com/lightningnetwork/lnd/fn"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/lnutils"
|
"github.com/lightningnetwork/lnd/lnutils"
|
||||||
@@ -61,6 +62,11 @@ type htlcTimeoutResolver struct {
|
|||||||
contractResolverKit
|
contractResolverKit
|
||||||
|
|
||||||
htlcLeaseResolver
|
htlcLeaseResolver
|
||||||
|
|
||||||
|
// incomingHTLCExpiryHeight is the absolute block height at which the
|
||||||
|
// incoming HTLC will expire. This is used as the deadline height as
|
||||||
|
// the outgoing HTLC must be swept before its incoming HTLC expires.
|
||||||
|
incomingHTLCExpiryHeight fn.Option[int32]
|
||||||
}
|
}
|
||||||
|
|
||||||
// newTimeoutResolver instantiates a new timeout htlc resolver.
|
// newTimeoutResolver instantiates a new timeout htlc resolver.
|
||||||
@@ -483,13 +489,45 @@ func (h *htlcTimeoutResolver) sweepSecondLevelTx() error {
|
|||||||
h.broadcastHeight,
|
h.broadcastHeight,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate the budget.
|
||||||
|
//
|
||||||
|
// TODO(yy): the budget is twice the output's value, which is needed as
|
||||||
|
// we don't force sweep the output now. To prevent cascading force
|
||||||
|
// closes, we use all its output value plus a wallet input as the
|
||||||
|
// budget. This is a temporary solution until we can optionally cancel
|
||||||
|
// the incoming HTLC, more details in,
|
||||||
|
// - https://github.com/lightningnetwork/lnd/issues/7969
|
||||||
|
budget := calculateBudget(
|
||||||
|
btcutil.Amount(inp.SignDesc().Output.Value), 2, 0,
|
||||||
|
)
|
||||||
|
|
||||||
|
// For an outgoing HTLC, it must be swept before the RefundTimeout of
|
||||||
|
// its incoming HTLC is reached.
|
||||||
|
//
|
||||||
|
// TODO(yy): we may end up mixing inputs with different time locks.
|
||||||
|
// Suppose we have two outgoing HTLCs,
|
||||||
|
// - HTLC1: nLocktime is 800000, CLTV delta is 80.
|
||||||
|
// - HTLC2: nLocktime is 800001, CLTV delta is 79.
|
||||||
|
// This means they would both have an incoming HTLC that expires at
|
||||||
|
// 800080, hence they share the same deadline but different locktimes.
|
||||||
|
// However, with current design, when we are at block 800000, HTLC1 is
|
||||||
|
// offered to the sweeper. When block 800001 is reached, HTLC1's
|
||||||
|
// sweeping process is already started, while HTLC2 is being offered to
|
||||||
|
// the sweeper, so they won't be mixed. This can become an issue tho,
|
||||||
|
// if we decide to sweep per X blocks. Or the contractcourt sees the
|
||||||
|
// block first while the sweeper is only aware of the last block. To
|
||||||
|
// properly fix it, we need `blockbeat` to make sure subsystems are in
|
||||||
|
// sync.
|
||||||
|
log.Infof("%T(%x): offering second-level HTLC timeout tx to sweeper "+
|
||||||
|
"with deadline=%v, budget=%v", h, h.htlc.RHash[:],
|
||||||
|
h.incomingHTLCExpiryHeight, budget)
|
||||||
|
|
||||||
_, err := h.Sweeper.SweepInput(
|
_, err := h.Sweeper.SweepInput(
|
||||||
inp,
|
inp,
|
||||||
sweep.Params{
|
sweep.Params{
|
||||||
Fee: sweep.FeeEstimateInfo{
|
Budget: budget,
|
||||||
ConfTarget: secondLevelConfTarget,
|
DeadlineHeight: h.incomingHTLCExpiryHeight,
|
||||||
},
|
|
||||||
Force: true,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -699,12 +737,26 @@ func (h *htlcTimeoutResolver) handleCommitSpend(
|
|||||||
h.htlcResolution.CsvDelay, h.broadcastHeight,
|
h.htlcResolution.CsvDelay, h.broadcastHeight,
|
||||||
h.htlc.RHash,
|
h.htlc.RHash,
|
||||||
)
|
)
|
||||||
|
// Calculate the budget for this sweep.
|
||||||
|
budget := calculateBudget(
|
||||||
|
btcutil.Amount(inp.SignDesc().Output.Value),
|
||||||
|
h.Budget.NoDeadlineHTLCRatio,
|
||||||
|
h.Budget.NoDeadlineHTLC,
|
||||||
|
)
|
||||||
|
|
||||||
|
log.Infof("%T(%x): offering second-level timeout tx output to "+
|
||||||
|
"sweeper with no deadline and budget=%v", h,
|
||||||
|
h.htlc.RHash[:], budget)
|
||||||
|
|
||||||
_, err = h.Sweeper.SweepInput(
|
_, err = h.Sweeper.SweepInput(
|
||||||
inp,
|
inp,
|
||||||
sweep.Params{
|
sweep.Params{
|
||||||
Fee: sweep.FeeEstimateInfo{
|
Budget: budget,
|
||||||
ConfTarget: sweepConfTarget,
|
|
||||||
},
|
// For second level success tx, there's no rush
|
||||||
|
// to get it confirmed, so we use a nil
|
||||||
|
// deadline.
|
||||||
|
DeadlineHeight: fn.None[int32](),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -918,6 +970,14 @@ func (h *htlcTimeoutResolver) HtlcPoint() wire.OutPoint {
|
|||||||
return h.htlcResolution.HtlcPoint()
|
return h.htlcResolution.HtlcPoint()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SupplementDeadline sets the incomingHTLCExpiryHeight for this outgoing htlc
|
||||||
|
// resolver.
|
||||||
|
//
|
||||||
|
// NOTE: Part of the htlcContractResolver interface.
|
||||||
|
func (h *htlcTimeoutResolver) SupplementDeadline(d fn.Option[int32]) {
|
||||||
|
h.incomingHTLCExpiryHeight = d
|
||||||
|
}
|
||||||
|
|
||||||
// A compile time assertion to ensure htlcTimeoutResolver meets the
|
// A compile time assertion to ensure htlcTimeoutResolver meets the
|
||||||
// ContractResolver interface.
|
// ContractResolver interface.
|
||||||
var _ htlcContractResolver = (*htlcTimeoutResolver)(nil)
|
var _ htlcContractResolver = (*htlcTimeoutResolver)(nil)
|
||||||
|
|||||||
Reference in new issue
Block a user