cnct: pre-confirmation anchor sweep

Start anchor sweep attempts immediately after the commitment transaction
has been published. This makes the anchor known to the sweeper and
allows the user to bump the fee on it to get their commitment
transaction confirmed in case the fee committed too is insufficient for
timely confirmation.
This commit is contained in:
Joost Jager
2020-03-10 14:01:39 +01:00
parent e8e99c6533
commit d2b6472843
4 changed files with 221 additions and 15 deletions

View File

@@ -13,9 +13,11 @@ import (
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/sweep"
)
var (
@@ -74,6 +76,10 @@ type ArbChannel interface {
// returned summary contains all items needed to eventually resolve all
// outputs on chain.
ForceCloseChan() (*lnwallet.LocalForceCloseSummary, error)
// NewAnchorResolutions returns the anchor resolutions for currently
// valid commitment transactions.
NewAnchorResolutions() ([]*lnwallet.AnchorResolution, error)
}
// ChannelArbitratorConfig contains all the functionality that the
@@ -847,9 +853,31 @@ func (c *ChannelArbitrator) stateStep(
// to be confirmed.
case StateCommitmentBroadcasted:
switch trigger {
// We are waiting for a commitment to be confirmed, so any
// other trigger will be ignored.
// We are waiting for a commitment to be confirmed.
case chainTrigger, userTrigger:
// The commitment transaction has been broadcast, but it
// doesn't necessarily need to be the commitment
// transaction version that is going to be confirmed. To
// be sure that any of those versions can be anchored
// down, we now submit all anchor resolutions to the
// sweeper. The sweeper will keep trying to sweep all of
// them.
//
// Note that the sweeper is idempotent. If we ever
// happen to end up at this point in the code again, no
// harm is done by re-offering the anchors to the
// sweeper.
anchors, err := c.cfg.Channel.NewAnchorResolutions()
if err != nil {
return StateError, closeTx, err
}
err = c.sweepAnchors(anchors, triggerHeight)
if err != nil {
return StateError, closeTx, err
}
nextState = StateCommitmentBroadcasted
// If this state advance was triggered by any of the
@@ -979,6 +1007,54 @@ func (c *ChannelArbitrator) stateStep(
return nextState, closeTx, nil
}
// sweepAnchors offers all given anchor resolutions to the sweeper. It requests
// sweeping at the minimum fee rate. This fee rate can be upped manually by the
// user via the BumpFee rpc.
func (c *ChannelArbitrator) sweepAnchors(anchors []*lnwallet.AnchorResolution,
heightHint uint32) error {
// Use the chan id as the exclusive group. This prevents any of the
// anchors from being batched together.
exclusiveGroup := c.cfg.ShortChanID.ToUint64()
// Retrieve the current minimum fee rate from the sweeper.
minFeeRate := c.cfg.Sweeper.RelayFeePerKW()
for _, anchor := range anchors {
log.Debugf("ChannelArbitrator(%v): pre-confirmation sweep of "+
"anchor of tx %v", c.cfg.ChanPoint, anchor.CommitAnchor)
// Prepare anchor output for sweeping.
anchorInput := input.MakeBaseInput(
&anchor.CommitAnchor,
input.CommitmentAnchor,
&anchor.AnchorSignDescriptor,
heightHint,
)
// Sweep anchor output with the minimum fee rate. This usually
// (up to a min relay fee of 3 sat/b) means that the anchor
// sweep will be economical. Also signal that this is a force
// sweep. If the user decides to bump the fee on the anchor
// sweep, it will be swept even if it isn't economical.
_, err := c.cfg.Sweeper.SweepInput(
&anchorInput,
sweep.Params{
Fee: sweep.FeePreference{
FeeRate: minFeeRate,
},
Force: true,
ExclusiveGroup: &exclusiveGroup,
},
)
if err != nil {
return err
}
}
return nil
}
// launchResolvers updates the activeResolvers list and starts the resolvers.
func (c *ChannelArbitrator) launchResolvers(resolvers []ContractResolver) {
c.activeResolversLock.Lock()