mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-26 01:33:02 +01:00
contractcourt: share deadlines between CPFP anchors and HTLCs
This commit changes how the deadline is calculated for CPFP anchor sweeping. In order to sweep the second-level HTLCs, we need to first get the FC tx confirmed. If we use a larger conf target for CPFP, we'd end up having few blocks to sweep the HTLCs, as these two sweeping txns share the deadline of the HTLC, as shown below, ``` More aggressive on the CPFP part. |-CPFP-|-----HTLC-----| Share the deadlines evenly. |---CPFP---|---HTLC---| More aggressive on the HTLC part. |-----CPFP-----|-HTLC-| ``` In this commit, we decide to share the deadlines evenly as a starting point so neither side will have a short of deadlines.
This commit is contained in:
parent
2906b8b20c
commit
1470adbed2
@ -1436,9 +1436,13 @@ func (c *ChannelArbitrator) sweepAnchors(anchors *lnwallet.AnchorResolutions,
|
||||
|
||||
// findCommitmentDeadlineAndValue finds the deadline (relative block height)
|
||||
// for a commitment transaction by extracting the minimum CLTV from its HTLCs.
|
||||
// From our PoV, the deadline is defined to be the smaller of,
|
||||
// - the least CLTV from outgoing HTLCs, or,
|
||||
// - the least CLTV from incoming HTLCs if the preimage is available.
|
||||
// From our PoV, the deadline delta is defined to be the smaller of,
|
||||
// - half of the least CLTV from outgoing HTLCs' corresponding incoming
|
||||
// HTLCs, or,
|
||||
// - half of the least CLTV from incoming HTLCs if the preimage is available.
|
||||
//
|
||||
// We use half of the CTLV value to ensure that we have enough time to sweep
|
||||
// the second-level HTLCs.
|
||||
//
|
||||
// It also finds the total value that are time-sensitive, which is the sum of
|
||||
// all the outgoing HTLCs plus incoming HTLCs whose preimages are known. It
|
||||
@ -1468,10 +1472,24 @@ func (c *ChannelArbitrator) findCommitmentDeadlineAndValue(heightHint uint32,
|
||||
}
|
||||
|
||||
value := htlc.Amt.ToSatoshis()
|
||||
totalValue += value
|
||||
|
||||
if htlc.RefundTimeout < deadlineMinHeight {
|
||||
deadlineMinHeight = htlc.RefundTimeout
|
||||
// Find the expiry height for this outgoing HTLC's incoming
|
||||
// HTLC.
|
||||
deadlineOpt := c.cfg.FindOutgoingHTLCDeadline(htlc)
|
||||
|
||||
// The deadline is default to the current deadlineMinHeight,
|
||||
// and it's overwritten when it's not none.
|
||||
deadline := deadlineMinHeight
|
||||
deadlineOpt.WhenSome(func(d int32) {
|
||||
deadline = uint32(d)
|
||||
|
||||
// We only consider the value is under protection when
|
||||
// it's time-sensitive.
|
||||
totalValue += value
|
||||
})
|
||||
|
||||
if deadline < deadlineMinHeight {
|
||||
deadlineMinHeight = deadline
|
||||
|
||||
log.Tracef("ChannelArbitrator(%v): outgoing HTLC has "+
|
||||
"deadline=%v, value=%v", c.cfg.ChanPoint,
|
||||
@ -1521,7 +1539,7 @@ func (c *ChannelArbitrator) findCommitmentDeadlineAndValue(heightHint uint32,
|
||||
// * none of the HTLCs are preimageAvailable.
|
||||
// - when our deadlineMinHeight is no greater than the heightHint,
|
||||
// which means we are behind our schedule.
|
||||
deadline := deadlineMinHeight - heightHint
|
||||
var deadline uint32
|
||||
switch {
|
||||
// When we couldn't find a deadline height from our HTLCs, we will fall
|
||||
// back to the default value as there's no time pressure here.
|
||||
@ -1535,6 +1553,11 @@ func (c *ChannelArbitrator) findCommitmentDeadlineAndValue(heightHint uint32,
|
||||
"deadlineMinHeight=%d, heightHint=%d",
|
||||
c.cfg.ChanPoint, deadlineMinHeight, heightHint)
|
||||
deadline = 1
|
||||
|
||||
// Use half of the deadline delta, and leave the other half to be used
|
||||
// to sweep the HTLCs.
|
||||
default:
|
||||
deadline = (deadlineMinHeight - heightHint) / 2
|
||||
}
|
||||
|
||||
// Calculate the value left after subtracting the budget used for
|
||||
@ -2800,7 +2823,7 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32) {
|
||||
// state, so we'll get the most up to date signals to we can
|
||||
// properly do our job.
|
||||
case signalUpdate := <-c.signalUpdates:
|
||||
log.Tracef("ChannelArbitrator(%v) got new signal "+
|
||||
log.Tracef("ChannelArbitrator(%v): got new signal "+
|
||||
"update!", c.cfg.ChanPoint)
|
||||
|
||||
// We'll update the ShortChannelID.
|
||||
|
@ -2305,20 +2305,22 @@ func TestFindCommitmentDeadlineAndValue(t *testing.T) {
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
htlcs htlcSet
|
||||
err error
|
||||
deadline fn.Option[int32]
|
||||
expectedBudget btcutil.Amount
|
||||
name string
|
||||
htlcs htlcSet
|
||||
err error
|
||||
deadline fn.Option[int32]
|
||||
mockFindOutgoingHTLCDeadline func()
|
||||
expectedBudget btcutil.Amount
|
||||
}{
|
||||
{
|
||||
// When we have no HTLCs, the default value should be
|
||||
// used.
|
||||
name: "use default conf target",
|
||||
htlcs: htlcSet{},
|
||||
err: nil,
|
||||
deadline: fn.None[int32](),
|
||||
expectedBudget: 0,
|
||||
name: "use default conf target",
|
||||
htlcs: htlcSet{},
|
||||
err: nil,
|
||||
deadline: fn.None[int32](),
|
||||
mockFindOutgoingHTLCDeadline: func() {},
|
||||
expectedBudget: 0,
|
||||
},
|
||||
{
|
||||
// When we have a preimage available in the local HTLC
|
||||
@ -2329,8 +2331,17 @@ func TestFindCommitmentDeadlineAndValue(t *testing.T) {
|
||||
htlcs: makeHTLCSet(htlcPreimage, htlcLargeExpiry),
|
||||
err: nil,
|
||||
deadline: fn.Some(int32(
|
||||
htlcPreimage.RefundTimeout - heightHint,
|
||||
(htlcPreimage.RefundTimeout - heightHint) / 2,
|
||||
)),
|
||||
mockFindOutgoingHTLCDeadline: func() {
|
||||
chanArb.cfg.FindOutgoingHTLCDeadline = func(
|
||||
htlc channeldb.HTLC) fn.Option[int32] {
|
||||
|
||||
return fn.Some(int32(
|
||||
htlcLargeExpiry.RefundTimeout,
|
||||
))
|
||||
}
|
||||
},
|
||||
expectedBudget: htlcAmt.ToSatoshis(),
|
||||
},
|
||||
{
|
||||
@ -2342,8 +2353,18 @@ func TestFindCommitmentDeadlineAndValue(t *testing.T) {
|
||||
htlcs: makeHTLCSet(htlcSmallExipry, htlcLargeExpiry),
|
||||
err: nil,
|
||||
deadline: fn.Some(int32(
|
||||
htlcLargeExpiry.RefundTimeout - heightHint,
|
||||
(htlcLargeExpiry.RefundTimeout -
|
||||
heightHint) / 2,
|
||||
)),
|
||||
mockFindOutgoingHTLCDeadline: func() {
|
||||
chanArb.cfg.FindOutgoingHTLCDeadline = func(
|
||||
htlc channeldb.HTLC) fn.Option[int32] {
|
||||
|
||||
return fn.Some(int32(
|
||||
htlcLargeExpiry.RefundTimeout,
|
||||
))
|
||||
}
|
||||
},
|
||||
expectedBudget: htlcAmt.ToSatoshis() / 2,
|
||||
},
|
||||
{
|
||||
@ -2354,18 +2375,36 @@ func TestFindCommitmentDeadlineAndValue(t *testing.T) {
|
||||
htlcs: makeHTLCSet(htlcPreimage, htlcDust),
|
||||
err: nil,
|
||||
deadline: fn.Some(int32(
|
||||
htlcPreimage.RefundTimeout - heightHint,
|
||||
(htlcPreimage.RefundTimeout - heightHint) / 2,
|
||||
)),
|
||||
mockFindOutgoingHTLCDeadline: func() {
|
||||
chanArb.cfg.FindOutgoingHTLCDeadline = func(
|
||||
htlc channeldb.HTLC) fn.Option[int32] {
|
||||
|
||||
return fn.Some(int32(
|
||||
htlcDust.RefundTimeout,
|
||||
))
|
||||
}
|
||||
},
|
||||
expectedBudget: htlcAmt.ToSatoshis() / 2,
|
||||
},
|
||||
{
|
||||
// When we've reached our deadline, use conf target of
|
||||
// 1 as our deadline. And the value left should be
|
||||
// htlcAmt.
|
||||
name: "use conf target 1",
|
||||
htlcs: makeHTLCSet(htlcPreimage, htlcExpired),
|
||||
err: nil,
|
||||
deadline: fn.Some(int32(1)),
|
||||
name: "use conf target 1",
|
||||
htlcs: makeHTLCSet(htlcPreimage, htlcExpired),
|
||||
err: nil,
|
||||
deadline: fn.Some(int32(1)),
|
||||
mockFindOutgoingHTLCDeadline: func() {
|
||||
chanArb.cfg.FindOutgoingHTLCDeadline = func(
|
||||
htlc channeldb.HTLC) fn.Option[int32] {
|
||||
|
||||
return fn.Some(int32(
|
||||
htlcExpired.RefundTimeout,
|
||||
))
|
||||
}
|
||||
},
|
||||
expectedBudget: htlcAmt.ToSatoshis(),
|
||||
},
|
||||
}
|
||||
@ -2373,7 +2412,9 @@ func TestFindCommitmentDeadlineAndValue(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Mock the method `FindOutgoingHTLCDeadline`.
|
||||
tc.mockFindOutgoingHTLCDeadline()
|
||||
|
||||
deadline, budget, err := chanArb.
|
||||
findCommitmentDeadlineAndValue(
|
||||
heightHint, tc.htlcs,
|
||||
@ -2412,31 +2453,35 @@ func TestSweepAnchors(t *testing.T) {
|
||||
chanArbCtx.chanArb.blocks <- int32(heightHint)
|
||||
|
||||
htlcIndexBase := uint64(99)
|
||||
htlcExpiryBase := heightHint + uint32(10)
|
||||
deadlineDelta := uint32(10)
|
||||
|
||||
htlcAmt := lnwire.MilliSatoshi(1_000_000)
|
||||
|
||||
// Create three testing HTLCs.
|
||||
htlcDust := channeldb.HTLC{
|
||||
HtlcIndex: htlcIndexBase + 1,
|
||||
RefundTimeout: htlcExpiryBase + 1,
|
||||
RefundTimeout: heightHint + 1,
|
||||
OutputIndex: -1,
|
||||
}
|
||||
|
||||
deadlinePreimageDelta := deadlineDelta + 2
|
||||
htlcWithPreimage := channeldb.HTLC{
|
||||
HtlcIndex: htlcIndexBase + 2,
|
||||
RefundTimeout: htlcExpiryBase + 2,
|
||||
RefundTimeout: heightHint + deadlinePreimageDelta,
|
||||
RHash: rHash,
|
||||
Amt: htlcAmt,
|
||||
}
|
||||
|
||||
deadlineSmallDelta := deadlineDelta + 4
|
||||
htlcSmallExipry := channeldb.HTLC{
|
||||
HtlcIndex: htlcIndexBase + 3,
|
||||
RefundTimeout: htlcExpiryBase + 3,
|
||||
RefundTimeout: heightHint + deadlineSmallDelta,
|
||||
Amt: htlcAmt,
|
||||
}
|
||||
|
||||
// Setup our local HTLC set such that we will use the HTLC's CLTV from
|
||||
// the incoming HTLC set.
|
||||
expectedLocalDeadline := htlcWithPreimage.RefundTimeout
|
||||
expectedLocalDeadline := heightHint + deadlinePreimageDelta/2
|
||||
chanArb.activeHTLCs[LocalHtlcSet] = htlcSet{
|
||||
incomingHTLCs: map[uint64]channeldb.HTLC{
|
||||
htlcWithPreimage.HtlcIndex: htlcWithPreimage,
|
||||
@ -2475,7 +2520,7 @@ func TestSweepAnchors(t *testing.T) {
|
||||
|
||||
// Setup out pending remote HTLC set such that we will use the HTLC's
|
||||
// CLTV from the outgoing HTLC set.
|
||||
expectedPendingDeadline := htlcSmallExipry.RefundTimeout
|
||||
expectedPendingDeadline := heightHint + deadlineSmallDelta/2
|
||||
chanArb.activeHTLCs[RemotePendingHtlcSet] = htlcSet{
|
||||
incomingHTLCs: map[uint64]channeldb.HTLC{
|
||||
htlcDust.HtlcIndex: htlcDust,
|
||||
@ -2493,6 +2538,18 @@ func TestSweepAnchors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
// Mock FindOutgoingHTLCDeadline so the pending remote's outgoing HTLC
|
||||
// returns the small expiry value.
|
||||
chanArb.cfg.FindOutgoingHTLCDeadline = func(
|
||||
htlc channeldb.HTLC) fn.Option[int32] {
|
||||
|
||||
if htlc.RHash != htlcSmallExipry.RHash {
|
||||
return fn.None[int32]()
|
||||
}
|
||||
|
||||
return fn.Some(int32(htlcSmallExipry.RefundTimeout))
|
||||
}
|
||||
|
||||
// Create AnchorResolutions.
|
||||
anchors := &lnwallet.AnchorResolutions{
|
||||
Local: &lnwallet.AnchorResolution{
|
||||
@ -2599,17 +2656,20 @@ func TestChannelArbitratorAnchors(t *testing.T) {
|
||||
htlcAmt := lnwire.MilliSatoshi(1_000_000)
|
||||
|
||||
// Create testing HTLCs.
|
||||
htlcExpiryBase := heightHint + uint32(10)
|
||||
deadlineDelta := uint32(10)
|
||||
deadlinePreimageDelta := deadlineDelta + 2
|
||||
htlcWithPreimage := channeldb.HTLC{
|
||||
HtlcIndex: 99,
|
||||
RefundTimeout: htlcExpiryBase + 2,
|
||||
RefundTimeout: heightHint + deadlinePreimageDelta,
|
||||
RHash: rHash,
|
||||
Incoming: true,
|
||||
Amt: htlcAmt,
|
||||
}
|
||||
|
||||
deadlineHTLCdelta := deadlineDelta + 3
|
||||
htlc := channeldb.HTLC{
|
||||
HtlcIndex: 100,
|
||||
RefundTimeout: htlcExpiryBase + 3,
|
||||
RefundTimeout: heightHint + deadlineHTLCdelta,
|
||||
Amt: htlcAmt,
|
||||
}
|
||||
|
||||
@ -2755,11 +2815,11 @@ func TestChannelArbitratorAnchors(t *testing.T) {
|
||||
// to htlcWithPreimage's CLTV.
|
||||
require.Equal(t, 2, len(chanArbCtx.sweeper.deadlines))
|
||||
require.EqualValues(t,
|
||||
htlcWithPreimage.RefundTimeout,
|
||||
heightHint+deadlinePreimageDelta/2,
|
||||
chanArbCtx.sweeper.deadlines[0],
|
||||
)
|
||||
require.EqualValues(t,
|
||||
htlcWithPreimage.RefundTimeout,
|
||||
heightHint+deadlinePreimageDelta/2,
|
||||
chanArbCtx.sweeper.deadlines[1],
|
||||
)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user