mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-01-19 05:45:21 +01:00
contractcourt: calculate value left when searching for commit deadline
This commit changes `findCommitmentDeadline` to `findCommitmentDeadlineAndValue` to calculate the value left from all the time-sensitive HTLCs after subtracting their budgets. This value is then used to calculate the budget to be used when sweeping the anchor output.
This commit is contained in:
parent
cab180a52e
commit
f4035ade05
@ -1321,11 +1321,18 @@ func (c *ChannelArbitrator) sweepAnchors(anchors *lnwallet.AnchorResolutions,
|
|||||||
htlcs htlcSet, anchorPath string) error {
|
htlcs htlcSet, anchorPath string) error {
|
||||||
|
|
||||||
// Find the deadline for this specific anchor.
|
// Find the deadline for this specific anchor.
|
||||||
deadline, err := c.findCommitmentDeadline(heightHint, htlcs)
|
deadlineOpt, _, err := c.findCommitmentDeadlineAndValue(
|
||||||
|
heightHint, htlcs,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deadline := uint32(1)
|
||||||
|
deadlineOpt.WhenSome(func(d int32) {
|
||||||
|
deadline = uint32(d)
|
||||||
|
})
|
||||||
|
|
||||||
// Create a force flag that's used to indicate whether we
|
// Create a force flag that's used to indicate whether we
|
||||||
// should force sweeping this anchor.
|
// should force sweeping this anchor.
|
||||||
var force bool
|
var force bool
|
||||||
@ -1426,20 +1433,26 @@ func (c *ChannelArbitrator) sweepAnchors(anchors *lnwallet.AnchorResolutions,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// findCommitmentDeadline finds the deadline (relative block height) for a
|
// findCommitmentDeadlineAndValue finds the deadline (relative block height)
|
||||||
// commitment transaction by extracting the minimum CLTV from its HTLCs. From
|
// for a commitment transaction by extracting the minimum CLTV from its HTLCs.
|
||||||
// our PoV, the deadline is defined to be the smaller of,
|
// From our PoV, the deadline is defined to be the smaller of,
|
||||||
// - the least CLTV from outgoing HTLCs, or,
|
// - the least CLTV from outgoing HTLCs, or,
|
||||||
// - the least CLTV from incoming HTLCs if the preimage is available.
|
// - the least CLTV from incoming HTLCs if the preimage is available.
|
||||||
//
|
//
|
||||||
// Note: when the deadline turns out to be 0 blocks, we will replace it with 1
|
// 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
|
||||||
|
// then returns the value left after subtracting the budget used for sweeping
|
||||||
|
// the time-sensitive HTLCs.
|
||||||
|
//
|
||||||
|
// NOTE: when the deadline turns out to be 0 blocks, we will replace it with 1
|
||||||
// block because our fee estimator doesn't allow a 0 conf target. This also
|
// block because our fee estimator doesn't allow a 0 conf target. This also
|
||||||
// means we've left behind and should increase our fee to make the transaction
|
// means we've left behind and should increase our fee to make the transaction
|
||||||
// confirmed asap.
|
// confirmed asap.
|
||||||
func (c *ChannelArbitrator) findCommitmentDeadline(heightHint uint32,
|
func (c *ChannelArbitrator) findCommitmentDeadlineAndValue(heightHint uint32,
|
||||||
htlcs htlcSet) (uint32, error) {
|
htlcs htlcSet) (fn.Option[int32], btcutil.Amount, error) {
|
||||||
|
|
||||||
deadlineMinHeight := uint32(math.MaxUint32)
|
deadlineMinHeight := uint32(math.MaxUint32)
|
||||||
|
totalValue := btcutil.Amount(0)
|
||||||
|
|
||||||
// First, iterate through the outgoingHTLCs to find the lowest CLTV
|
// First, iterate through the outgoingHTLCs to find the lowest CLTV
|
||||||
// value.
|
// value.
|
||||||
@ -1453,11 +1466,15 @@ func (c *ChannelArbitrator) findCommitmentDeadline(heightHint uint32,
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value := htlc.Amt.ToSatoshis()
|
||||||
|
totalValue += value
|
||||||
|
|
||||||
if htlc.RefundTimeout < deadlineMinHeight {
|
if htlc.RefundTimeout < deadlineMinHeight {
|
||||||
deadlineMinHeight = htlc.RefundTimeout
|
deadlineMinHeight = htlc.RefundTimeout
|
||||||
|
|
||||||
log.Tracef("ChannelArbitrator(%v): outgoing HTLC has "+
|
log.Tracef("ChannelArbitrator(%v): outgoing HTLC has "+
|
||||||
"deadline: %v", c.cfg.ChanPoint,
|
"deadline=%v, value=%v", c.cfg.ChanPoint,
|
||||||
deadlineMinHeight)
|
deadlineMinHeight, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1477,18 +1494,22 @@ func (c *ChannelArbitrator) findCommitmentDeadline(heightHint uint32,
|
|||||||
// this HTLC.
|
// this HTLC.
|
||||||
preimageAvailable, err := c.isPreimageAvailable(htlc.RHash)
|
preimageAvailable, err := c.isPreimageAvailable(htlc.RHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return fn.None[int32](), 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !preimageAvailable {
|
if !preimageAvailable {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value := htlc.Amt.ToSatoshis()
|
||||||
|
totalValue += value
|
||||||
|
|
||||||
if htlc.RefundTimeout < deadlineMinHeight {
|
if htlc.RefundTimeout < deadlineMinHeight {
|
||||||
deadlineMinHeight = htlc.RefundTimeout
|
deadlineMinHeight = htlc.RefundTimeout
|
||||||
|
|
||||||
log.Tracef("ChannelArbitrator(%v): incoming HTLC has "+
|
log.Tracef("ChannelArbitrator(%v): incoming HTLC has "+
|
||||||
"deadline: %v", c.cfg.ChanPoint,
|
"deadline=%v, amt=%v", c.cfg.ChanPoint,
|
||||||
deadlineMinHeight)
|
deadlineMinHeight, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1502,9 +1523,9 @@ func (c *ChannelArbitrator) findCommitmentDeadline(heightHint uint32,
|
|||||||
deadline := deadlineMinHeight - heightHint
|
deadline := deadlineMinHeight - heightHint
|
||||||
switch {
|
switch {
|
||||||
// When we couldn't find a deadline height from our HTLCs, we will fall
|
// When we couldn't find a deadline height from our HTLCs, we will fall
|
||||||
// back to the default value.
|
// back to the default value as there's no time pressure here.
|
||||||
case deadlineMinHeight == math.MaxUint32:
|
case deadlineMinHeight == math.MaxUint32:
|
||||||
deadline = anchorSweepConfTarget
|
return fn.None[int32](), 0, nil
|
||||||
|
|
||||||
// When the deadline is passed, we will fall back to the smallest conf
|
// When the deadline is passed, we will fall back to the smallest conf
|
||||||
// target (1 block).
|
// target (1 block).
|
||||||
@ -1515,11 +1536,19 @@ func (c *ChannelArbitrator) findCommitmentDeadline(heightHint uint32,
|
|||||||
deadline = 1
|
deadline = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("ChannelArbitrator(%v): calculated deadline: %d, "+
|
// Calculate the value left after subtracting the budget used for
|
||||||
"using deadlineMinHeight=%d, heightHint=%d",
|
// sweeping the time-sensitive HTLCs.
|
||||||
c.cfg.ChanPoint, deadline, deadlineMinHeight, heightHint)
|
valueLeft := totalValue - calculateBudget(
|
||||||
|
totalValue, c.cfg.Budget.DeadlineHTLCRatio,
|
||||||
|
c.cfg.Budget.DeadlineHTLC,
|
||||||
|
)
|
||||||
|
|
||||||
return deadline, nil
|
log.Debugf("ChannelArbitrator(%v): calculated valueLeft=%v, "+
|
||||||
|
"deadline=%d, using deadlineMinHeight=%d, heightHint=%d",
|
||||||
|
c.cfg.ChanPoint, valueLeft, deadline, deadlineMinHeight,
|
||||||
|
heightHint)
|
||||||
|
|
||||||
|
return fn.Some(int32(deadline)), valueLeft, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// launchResolvers updates the activeResolvers list and starts the resolvers.
|
// launchResolvers updates the activeResolvers list and starts the resolvers.
|
||||||
|
@ -2226,9 +2226,10 @@ func TestRemoteCloseInitiator(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestFindCommitmentDeadline tests the logic used to determine confirmation
|
// TestFindCommitmentDeadlineAndValue tests the logic used to determine
|
||||||
// deadline is implemented as expected.
|
// confirmation deadline and total time-sensitive value is implemented as
|
||||||
func TestFindCommitmentDeadline(t *testing.T) {
|
// expected.
|
||||||
|
func TestFindCommitmentDeadlineAndValue(t *testing.T) {
|
||||||
// Create a testing channel arbitrator.
|
// Create a testing channel arbitrator.
|
||||||
log := &mockArbitratorLog{
|
log := &mockArbitratorLog{
|
||||||
state: StateDefault,
|
state: StateDefault,
|
||||||
@ -2251,29 +2252,36 @@ func TestFindCommitmentDeadline(t *testing.T) {
|
|||||||
heightHint := uint32(1000)
|
heightHint := uint32(1000)
|
||||||
htlcExpiryBase := heightHint + uint32(10)
|
htlcExpiryBase := heightHint + uint32(10)
|
||||||
|
|
||||||
|
htlcAmt := lnwire.MilliSatoshi(1_000_000)
|
||||||
|
|
||||||
// Create four testing HTLCs.
|
// Create four testing HTLCs.
|
||||||
htlcDust := channeldb.HTLC{
|
htlcDust := channeldb.HTLC{
|
||||||
HtlcIndex: htlcIndexBase + 1,
|
HtlcIndex: htlcIndexBase + 1,
|
||||||
RefundTimeout: htlcExpiryBase + 1,
|
RefundTimeout: htlcExpiryBase + 1,
|
||||||
OutputIndex: -1,
|
OutputIndex: -1,
|
||||||
|
Amt: htlcAmt,
|
||||||
}
|
}
|
||||||
htlcSmallExipry := channeldb.HTLC{
|
htlcSmallExipry := channeldb.HTLC{
|
||||||
HtlcIndex: htlcIndexBase + 2,
|
HtlcIndex: htlcIndexBase + 2,
|
||||||
RefundTimeout: htlcExpiryBase + 2,
|
RefundTimeout: htlcExpiryBase + 2,
|
||||||
|
Amt: htlcAmt,
|
||||||
}
|
}
|
||||||
|
|
||||||
htlcPreimage := channeldb.HTLC{
|
htlcPreimage := channeldb.HTLC{
|
||||||
HtlcIndex: htlcIndexBase + 3,
|
HtlcIndex: htlcIndexBase + 3,
|
||||||
RefundTimeout: htlcExpiryBase + 3,
|
RefundTimeout: htlcExpiryBase + 3,
|
||||||
RHash: rHash,
|
RHash: rHash,
|
||||||
|
Amt: htlcAmt,
|
||||||
}
|
}
|
||||||
htlcLargeExpiry := channeldb.HTLC{
|
htlcLargeExpiry := channeldb.HTLC{
|
||||||
HtlcIndex: htlcIndexBase + 4,
|
HtlcIndex: htlcIndexBase + 4,
|
||||||
RefundTimeout: htlcExpiryBase + 100,
|
RefundTimeout: htlcExpiryBase + 100,
|
||||||
|
Amt: htlcAmt,
|
||||||
}
|
}
|
||||||
htlcExpired := channeldb.HTLC{
|
htlcExpired := channeldb.HTLC{
|
||||||
HtlcIndex: htlcIndexBase + 5,
|
HtlcIndex: htlcIndexBase + 5,
|
||||||
RefundTimeout: heightHint,
|
RefundTimeout: heightHint,
|
||||||
|
Amt: htlcAmt,
|
||||||
}
|
}
|
||||||
|
|
||||||
makeHTLCSet := func(incoming, outgoing channeldb.HTLC) htlcSet {
|
makeHTLCSet := func(incoming, outgoing channeldb.HTLC) htlcSet {
|
||||||
@ -2288,51 +2296,68 @@ func TestFindCommitmentDeadline(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
htlcs htlcSet
|
htlcs htlcSet
|
||||||
err error
|
err error
|
||||||
deadline uint32
|
deadline fn.Option[int32]
|
||||||
|
expectedBudget btcutil.Amount
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
// When we have no HTLCs, the default value should be
|
// When we have no HTLCs, the default value should be
|
||||||
// used.
|
// used.
|
||||||
name: "use default conf target",
|
name: "use default conf target",
|
||||||
htlcs: htlcSet{},
|
htlcs: htlcSet{},
|
||||||
err: nil,
|
err: nil,
|
||||||
deadline: anchorSweepConfTarget,
|
deadline: fn.None[int32](),
|
||||||
|
expectedBudget: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// When we have a preimage available in the local HTLC
|
// When we have a preimage available in the local HTLC
|
||||||
// set, its CLTV should be used.
|
// set, its CLTV should be used. And the value left
|
||||||
name: "use htlc with preimage available",
|
// should be the sum of the HTLCs minus their budgets,
|
||||||
htlcs: makeHTLCSet(htlcPreimage, htlcLargeExpiry),
|
// which is exactly htlcAmt.
|
||||||
err: nil,
|
name: "use htlc with preimage available",
|
||||||
deadline: htlcPreimage.RefundTimeout - heightHint,
|
htlcs: makeHTLCSet(htlcPreimage, htlcLargeExpiry),
|
||||||
|
err: nil,
|
||||||
|
deadline: fn.Some(int32(
|
||||||
|
htlcPreimage.RefundTimeout - heightHint,
|
||||||
|
)),
|
||||||
|
expectedBudget: htlcAmt.ToSatoshis(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// When the HTLC in the local set is not preimage
|
// When the HTLC in the local set is not preimage
|
||||||
// available, we should not use its CLTV even its value
|
// available, we should not use its CLTV even its value
|
||||||
// is smaller.
|
// is smaller. And the value left should be half of
|
||||||
name: "use htlc with no preimage available",
|
// htlcAmt.
|
||||||
htlcs: makeHTLCSet(htlcSmallExipry, htlcLargeExpiry),
|
name: "use htlc with no preimage available",
|
||||||
err: nil,
|
htlcs: makeHTLCSet(htlcSmallExipry, htlcLargeExpiry),
|
||||||
deadline: htlcLargeExpiry.RefundTimeout - heightHint,
|
err: nil,
|
||||||
|
deadline: fn.Some(int32(
|
||||||
|
htlcLargeExpiry.RefundTimeout - heightHint,
|
||||||
|
)),
|
||||||
|
expectedBudget: htlcAmt.ToSatoshis() / 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// When we have dust HTLCs, their CLTVs should NOT be
|
// When we have dust HTLCs, their CLTVs should NOT be
|
||||||
// used even the values are smaller.
|
// used even the values are smaller. And the value left
|
||||||
name: "ignore dust HTLCs",
|
// should be half of htlcAmt.
|
||||||
htlcs: makeHTLCSet(htlcPreimage, htlcDust),
|
name: "ignore dust HTLCs",
|
||||||
err: nil,
|
htlcs: makeHTLCSet(htlcPreimage, htlcDust),
|
||||||
deadline: htlcPreimage.RefundTimeout - heightHint,
|
err: nil,
|
||||||
|
deadline: fn.Some(int32(
|
||||||
|
htlcPreimage.RefundTimeout - heightHint,
|
||||||
|
)),
|
||||||
|
expectedBudget: htlcAmt.ToSatoshis() / 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// When we've reached our deadline, use conf target of
|
// When we've reached our deadline, use conf target of
|
||||||
// 1 as our deadline.
|
// 1 as our deadline. And the value left should be
|
||||||
name: "use conf target 1",
|
// htlcAmt.
|
||||||
htlcs: makeHTLCSet(htlcPreimage, htlcExpired),
|
name: "use conf target 1",
|
||||||
err: nil,
|
htlcs: makeHTLCSet(htlcPreimage, htlcExpired),
|
||||||
deadline: 1,
|
err: nil,
|
||||||
|
deadline: fn.Some(int32(1)),
|
||||||
|
expectedBudget: htlcAmt.ToSatoshis(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2340,12 +2365,14 @@ func TestFindCommitmentDeadline(t *testing.T) {
|
|||||||
tc := tc
|
tc := tc
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
deadline, err := chanArb.findCommitmentDeadline(
|
deadline, budget, err := chanArb.
|
||||||
heightHint, tc.htlcs,
|
findCommitmentDeadlineAndValue(
|
||||||
)
|
heightHint, tc.htlcs,
|
||||||
|
)
|
||||||
|
|
||||||
require.Equal(t, tc.err, err)
|
require.Equal(t, tc.err, err)
|
||||||
require.Equal(t, tc.deadline, deadline)
|
require.Equal(t, tc.deadline, deadline)
|
||||||
|
require.Equal(t, tc.expectedBudget, budget)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user