mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 09:53:54 +01:00
contractcourt: specify deadline and budget for anchor output
This commit is contained in:
parent
f4035ade05
commit
6f0c2b5bab
@ -9,6 +9,7 @@ import (
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/fn"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/lightningnetwork/lnd/sweep"
|
||||
)
|
||||
@ -118,6 +119,14 @@ func (c *anchorResolver) Resolve() (ContractResolver, error) {
|
||||
Fee: sweep.FeeEstimateInfo{
|
||||
FeeRate: relayFeeRate,
|
||||
},
|
||||
// For normal anchor sweeping, the budget is 330 sats.
|
||||
Budget: btcutil.Amount(
|
||||
anchorInput.SignDesc().Output.Value,
|
||||
),
|
||||
|
||||
// There's no rush to sweep the anchor, so we use a nil
|
||||
// deadline here.
|
||||
DeadlineHeight: fn.None[int32](),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -37,12 +37,6 @@ var (
|
||||
)
|
||||
|
||||
const (
|
||||
// anchorSweepConfTarget is the conf target used when sweeping
|
||||
// commitment anchors. This value is only used when the commitment
|
||||
// transaction has no valid HTLCs for determining a confirmation
|
||||
// deadline.
|
||||
anchorSweepConfTarget = 144
|
||||
|
||||
// arbitratorBlockBufferSize is the size of the buffer we give to each
|
||||
// channel arbitrator.
|
||||
arbitratorBlockBufferSize = 20
|
||||
@ -1321,36 +1315,23 @@ func (c *ChannelArbitrator) sweepAnchors(anchors *lnwallet.AnchorResolutions,
|
||||
htlcs htlcSet, anchorPath string) error {
|
||||
|
||||
// Find the deadline for this specific anchor.
|
||||
deadlineOpt, _, err := c.findCommitmentDeadlineAndValue(
|
||||
deadline, value, err := c.findCommitmentDeadlineAndValue(
|
||||
heightHint, htlcs,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deadline := uint32(1)
|
||||
deadlineOpt.WhenSome(func(d int32) {
|
||||
deadline = uint32(d)
|
||||
})
|
||||
// If we cannot find a deadline, it means there's no HTLCs at
|
||||
// stake, which means we can relax our anchor sweeping as we
|
||||
// don't have any time sensitive outputs to sweep.
|
||||
if deadline.IsNone() {
|
||||
log.Infof("ChannelArbitrator(%v): no HTLCs at stake, "+
|
||||
"skipped anchor CPFP", c.cfg.ChanPoint)
|
||||
|
||||
// Create a force flag that's used to indicate whether we
|
||||
// should force sweeping this anchor.
|
||||
var force bool
|
||||
|
||||
// Check the deadline against the default value. If it's less
|
||||
// than the default value of 144, it means there is a deadline
|
||||
// and we will perform a CPFP for this commitment tx.
|
||||
if deadline < anchorSweepConfTarget {
|
||||
// Signal that this is a force sweep, so that the
|
||||
// anchor will be swept even if it isn't economical
|
||||
// purely based on the anchor value.
|
||||
force = true
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Debugf("ChannelArbitrator(%v): pre-confirmation sweep of "+
|
||||
"anchor of %s commit tx %v, force=%v", c.cfg.ChanPoint,
|
||||
anchorPath, anchor.CommitAnchor, force)
|
||||
|
||||
witnessType := input.CommitmentAnchor
|
||||
|
||||
// For taproot channels, we need to use the proper witness
|
||||
@ -1374,6 +1355,28 @@ func (c *ChannelArbitrator) sweepAnchors(anchors *lnwallet.AnchorResolutions,
|
||||
},
|
||||
)
|
||||
|
||||
// If we have a deadline, we'll use it to calculate the
|
||||
// deadline height, otherwise default to none.
|
||||
deadlineDesc := "None"
|
||||
deadlineHeight := fn.MapOption(func(d int32) int32 {
|
||||
deadlineDesc = fmt.Sprintf("%d", d)
|
||||
|
||||
return d + int32(heightHint)
|
||||
})(deadline)
|
||||
|
||||
// Calculate the budget based on the value under protection,
|
||||
// which is the sum of all HTLCs on this commitment subtracted
|
||||
// by their budgets.
|
||||
budget := calculateBudget(
|
||||
value, c.cfg.Budget.AnchorCPFPRatio,
|
||||
c.cfg.Budget.AnchorCPFP,
|
||||
)
|
||||
|
||||
log.Infof("ChannelArbitrator(%v): offering anchor from %s "+
|
||||
"commitment %v to sweeper with deadline=%v, budget=%v",
|
||||
c.cfg.ChanPoint, anchorPath, anchor.CommitAnchor,
|
||||
deadlineDesc, budget)
|
||||
|
||||
// Sweep anchor output with a confirmation target fee
|
||||
// preference. Because this is a cpfp-operation, the anchor
|
||||
// will only be attempted to sweep when the current fee
|
||||
@ -1382,11 +1385,9 @@ func (c *ChannelArbitrator) sweepAnchors(anchors *lnwallet.AnchorResolutions,
|
||||
_, err = c.cfg.Sweeper.SweepInput(
|
||||
&anchorInput,
|
||||
sweep.Params{
|
||||
Fee: sweep.FeeEstimateInfo{
|
||||
ConfTarget: deadline,
|
||||
},
|
||||
Force: force,
|
||||
ExclusiveGroup: &exclusiveGroup,
|
||||
Budget: budget,
|
||||
DeadlineHeight: deadlineHeight,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
@ -2138,6 +2139,20 @@ func (c *ChannelArbitrator) checkRemoteDiffActions(height uint32,
|
||||
continue
|
||||
}
|
||||
|
||||
preimageAvailable, err := c.isPreimageAvailable(htlc.RHash)
|
||||
if err != nil {
|
||||
log.Errorf("ChannelArbitrator(%v): failed to query "+
|
||||
"preimage for dangling htlc=%x from remote "+
|
||||
"commitments diff", c.cfg.ChanPoint,
|
||||
htlc.RHash[:])
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if preimageAvailable {
|
||||
continue
|
||||
}
|
||||
|
||||
actionMap[HtlcFailNowAction] = append(
|
||||
actionMap[HtlcFailNowAction], htlc,
|
||||
)
|
||||
|
@ -392,6 +392,8 @@ func createTestChannelArbitrator(t *testing.T, log ArbitratorLog,
|
||||
return nil
|
||||
},
|
||||
Budget: *DefaultBudgetConfig(),
|
||||
PreimageDB: newMockWitnessBeacon(),
|
||||
Registry: &mockRegistry{},
|
||||
}
|
||||
|
||||
// We'll use the resolvedChan to synchronize on call to
|
||||
@ -2405,6 +2407,8 @@ func TestSweepAnchors(t *testing.T) {
|
||||
htlcIndexBase := uint64(99)
|
||||
htlcExpiryBase := heightHint + uint32(10)
|
||||
|
||||
htlcAmt := lnwire.MilliSatoshi(1_000_000)
|
||||
|
||||
// Create three testing HTLCs.
|
||||
htlcDust := channeldb.HTLC{
|
||||
HtlcIndex: htlcIndexBase + 1,
|
||||
@ -2415,15 +2419,17 @@ func TestSweepAnchors(t *testing.T) {
|
||||
HtlcIndex: htlcIndexBase + 2,
|
||||
RefundTimeout: htlcExpiryBase + 2,
|
||||
RHash: rHash,
|
||||
Amt: htlcAmt,
|
||||
}
|
||||
htlcSmallExipry := channeldb.HTLC{
|
||||
HtlcIndex: htlcIndexBase + 3,
|
||||
RefundTimeout: htlcExpiryBase + 3,
|
||||
Amt: htlcAmt,
|
||||
}
|
||||
|
||||
// Setup our local HTLC set such that we will use the HTLC's CLTV from
|
||||
// the incoming HTLC set.
|
||||
expectedLocalDeadline := htlcWithPreimage.RefundTimeout - heightHint
|
||||
expectedLocalDeadline := htlcWithPreimage.RefundTimeout
|
||||
chanArb.activeHTLCs[LocalHtlcSet] = htlcSet{
|
||||
incomingHTLCs: map[uint64]channeldb.HTLC{
|
||||
htlcWithPreimage.HtlcIndex: htlcWithPreimage,
|
||||
@ -2442,8 +2448,7 @@ func TestSweepAnchors(t *testing.T) {
|
||||
}
|
||||
|
||||
// Setup our remote HTLC set such that no valid HTLCs can be used, thus
|
||||
// we default to anchorSweepConfTarget.
|
||||
expectedRemoteDeadline := anchorSweepConfTarget
|
||||
// the anchor sweeping is skipped.
|
||||
chanArb.activeHTLCs[RemoteHtlcSet] = htlcSet{
|
||||
incomingHTLCs: map[uint64]channeldb.HTLC{
|
||||
htlcSmallExipry.HtlcIndex: htlcSmallExipry,
|
||||
@ -2463,7 +2468,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 - heightHint
|
||||
expectedPendingDeadline := htlcSmallExipry.RefundTimeout
|
||||
chanArb.activeHTLCs[RemotePendingHtlcSet] = htlcSet{
|
||||
incomingHTLCs: map[uint64]channeldb.HTLC{
|
||||
htlcDust.HtlcIndex: htlcDust,
|
||||
@ -2506,20 +2511,22 @@ func TestSweepAnchors(t *testing.T) {
|
||||
|
||||
// Verify deadlines are used as expected.
|
||||
deadlines := chanArbCtx.sweeper.deadlines
|
||||
|
||||
// We should see two `SweepInput` calls.
|
||||
require.Len(t, deadlines, 2)
|
||||
|
||||
// Since there's no guarantee of the deadline orders, we sort it here
|
||||
// so they can be compared.
|
||||
sort.Ints(deadlines) // [12, 13, 144]
|
||||
sort.Ints(deadlines) // [12, 13]
|
||||
require.EqualValues(
|
||||
t, expectedLocalDeadline, deadlines[0],
|
||||
"local deadline not matched",
|
||||
"local deadline not matched, want %v, got %v",
|
||||
expectedLocalDeadline, deadlines[0],
|
||||
)
|
||||
require.EqualValues(
|
||||
t, expectedPendingDeadline, deadlines[1],
|
||||
"pending remote deadline not matched",
|
||||
)
|
||||
require.EqualValues(
|
||||
t, expectedRemoteDeadline, deadlines[2],
|
||||
"remote deadline not matched",
|
||||
"pending remote deadline not matched, want %v, got %v",
|
||||
expectedPendingDeadline, deadlines[1],
|
||||
)
|
||||
}
|
||||
|
||||
@ -2582,6 +2589,8 @@ func TestChannelArbitratorAnchors(t *testing.T) {
|
||||
heightHint := uint32(1000)
|
||||
chanArbCtx.chanArb.blocks <- int32(heightHint)
|
||||
|
||||
htlcAmt := lnwire.MilliSatoshi(1_000_000)
|
||||
|
||||
// Create testing HTLCs.
|
||||
htlcExpiryBase := heightHint + uint32(10)
|
||||
htlcWithPreimage := channeldb.HTLC{
|
||||
@ -2589,10 +2598,12 @@ func TestChannelArbitratorAnchors(t *testing.T) {
|
||||
RefundTimeout: htlcExpiryBase + 2,
|
||||
RHash: rHash,
|
||||
Incoming: true,
|
||||
Amt: htlcAmt,
|
||||
}
|
||||
htlc := channeldb.HTLC{
|
||||
HtlcIndex: 100,
|
||||
RefundTimeout: htlcExpiryBase + 3,
|
||||
Amt: htlcAmt,
|
||||
}
|
||||
|
||||
// We now send two HTLC updates, one for local HTLC set and the other
|
||||
@ -2600,9 +2611,9 @@ func TestChannelArbitratorAnchors(t *testing.T) {
|
||||
newUpdate := &ContractUpdate{
|
||||
HtlcKey: LocalHtlcSet,
|
||||
// This will make the deadline of the local anchor resolution
|
||||
// to be htlcWithPreimage's CLTV minus heightHint since the
|
||||
// incoming HTLC (toLocalHTLCs) has a lower CLTV value and is
|
||||
// preimage available.
|
||||
// to be htlcWithPreimage's CLTV since the incoming HTLC
|
||||
// (toLocalHTLCs) has a lower CLTV value and is preimage
|
||||
// available.
|
||||
Htlcs: []channeldb.HTLC{htlc, htlcWithPreimage},
|
||||
}
|
||||
chanArb.notifyContractUpdate(newUpdate)
|
||||
@ -2610,8 +2621,8 @@ func TestChannelArbitratorAnchors(t *testing.T) {
|
||||
newUpdate = &ContractUpdate{
|
||||
HtlcKey: RemoteHtlcSet,
|
||||
// This will make the deadline of the remote anchor resolution
|
||||
// to be htlcWithPreimage's CLTV minus heightHint because the
|
||||
// incoming HTLC (toRemoteHTLCs) has a lower CLTV.
|
||||
// to be htlcWithPreimage's CLTV because the incoming HTLC
|
||||
// (toRemoteHTLCs) has a lower CLTV.
|
||||
Htlcs: []channeldb.HTLC{htlc, htlcWithPreimage},
|
||||
}
|
||||
chanArb.notifyContractUpdate(newUpdate)
|
||||
@ -2734,14 +2745,14 @@ func TestChannelArbitratorAnchors(t *testing.T) {
|
||||
|
||||
// We expect two anchor inputs, the local and the remote to be swept.
|
||||
// Thus we should expect there are two deadlines used, both are equal
|
||||
// to htlcWithPreimage's CLTV minus current block height.
|
||||
// to htlcWithPreimage's CLTV.
|
||||
require.Equal(t, 2, len(chanArbCtx.sweeper.deadlines))
|
||||
require.EqualValues(t,
|
||||
htlcWithPreimage.RefundTimeout-heightHint,
|
||||
htlcWithPreimage.RefundTimeout,
|
||||
chanArbCtx.sweeper.deadlines[0],
|
||||
)
|
||||
require.EqualValues(t,
|
||||
htlcWithPreimage.RefundTimeout-heightHint,
|
||||
htlcWithPreimage.RefundTimeout,
|
||||
chanArbCtx.sweeper.deadlines[1],
|
||||
)
|
||||
}
|
||||
|
@ -143,6 +143,11 @@ func (s *mockSweeper) SweepInput(input input.Input, params sweep.Params) (
|
||||
}
|
||||
}
|
||||
|
||||
// Update the deadlines used if it's set.
|
||||
params.DeadlineHeight.WhenSome(func(d int32) {
|
||||
s.deadlines = append(s.deadlines, int(d))
|
||||
})
|
||||
|
||||
result := make(chan sweep.Result, 1)
|
||||
result <- sweep.Result{
|
||||
Tx: s.sweepTx,
|
||||
|
Loading…
Reference in New Issue
Block a user