Skip to content

multi: RBF signalling #8213

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contractcourt/commit_sweep_resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func (s *mockSweeper) SweepInput(input input.Input, params sweep.Params) (
}

func (s *mockSweeper) CreateSweepTx(inputs []input.Input, feePref sweep.FeePreference,
currentBlockHeight uint32) (*wire.MsgTx, error) {
currentBlockHeight uint32, enableRBF bool) (*wire.MsgTx, error) {

// We will wait for the test to supply the sweep tx to return.
sweepTx := <-s.createSweepTxChan
Expand Down
3 changes: 2 additions & 1 deletion contractcourt/htlc_success_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,12 +437,13 @@ func (h *htlcSuccessResolver) resolveRemoteCommitOutput() (
// complete.
//
// TODO: Use time-based sweeper and result chan.

var err error
h.sweepTx, err = h.Sweeper.CreateSweepTx(
[]input.Input{inp},
sweep.FeePreference{
ConfTarget: sweepConfTarget,
}, 0,
}, 0, false,
)
if err != nil {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion contractcourt/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ type UtxoSweeper interface {
// that spends from them. This method also makes an accurate fee
// estimate before generating the required witnesses.
CreateSweepTx(inputs []input.Input, feePref sweep.FeePreference,
currentBlockHeight uint32) (*wire.MsgTx, error)
currentBlockHeight uint32, enableRBF bool) (*wire.MsgTx, error)

// RelayFeePerKW returns the minimum fee rate required for transactions
// to be relayed.
Expand Down
1 change: 1 addition & 0 deletions funding/batch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ func (h *testHarness) parseRequest(
RemoteCsvDelay: uint16(in.RemoteCsvDelay),
MinConfs: in.MinConfs,
MaxLocalCsv: uint16(in.MaxLocalCsv),
EnableRBF: true,
}, nil
}

Expand Down
6 changes: 6 additions & 0 deletions funding/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,10 @@ type InitFundingMsg struct {
// Private determines whether or not this channel will be private.
Private bool

// EnableRBF is a boolen which if true sets transaction inputs
// to signal RBF
EnableRBF bool

// MinHtlcIn is the minimum incoming HTLC that we accept.
MinHtlcIn lnwire.MilliSatoshi

Expand Down Expand Up @@ -1580,6 +1584,7 @@ func (f *Manager) fundeeProcessOpenChannel(peer lnpeer.Peer,
ZeroConf: zeroConf,
OptionScidAlias: scid,
ScidAliasFeature: scidFeatureVal,
EnableRBF: false,
}

reservation, err := f.cfg.Wallet.InitChannelReservation(req)
Expand Down Expand Up @@ -4564,6 +4569,7 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) {
OptionScidAlias: scid,
ScidAliasFeature: scidFeatureVal,
Memo: msg.Memo,
EnableRBF: msg.EnableRBF,
}

reservation, err := f.cfg.Wallet.InitChannelReservation(req)
Expand Down
25 changes: 20 additions & 5 deletions funding/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/mempool"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/chainreg"
Expand Down Expand Up @@ -744,7 +745,7 @@ func openChannel(t *testing.T, alice, bob *testNode, localFundingAmt,

publ := fundChannel(
t, alice, bob, localFundingAmt, pushAmt, false, 0, 0, numConfs,
updateChan, announceChan, chanType,
updateChan, announceChan, chanType, true,
)
fundingOutPoint := &wire.OutPoint{
Hash: publ.TxHash(),
Expand All @@ -771,7 +772,7 @@ func fundChannel(t *testing.T, alice, bob *testNode, localFundingAmt,
pushAmt btcutil.Amount, subtractFees bool, fundUpToMaxAmt,
minFundAmt btcutil.Amount, numConfs uint32,
updateChan chan *lnrpc.OpenStatusUpdate, announceChan bool,
chanType *lnwire.ChannelType) *wire.MsgTx {
chanType *lnwire.ChannelType, enableRBF bool) *wire.MsgTx {

// Create a funding request and start the workflow.
errChan := make(chan error, 1)
Expand All @@ -790,6 +791,7 @@ func fundChannel(t *testing.T, alice, bob *testNode, localFundingAmt,
ChannelType: chanType,
Updates: updateChan,
Err: errChan,
EnableRBF: enableRBF,
}

// If this is a taproot channel, then we want to force it to be a
Expand Down Expand Up @@ -3039,6 +3041,7 @@ func TestFundingManagerCustomChannelParameters(t *testing.T) {
Err: errChan,
BaseFee: &baseFee,
FeeRate: &feeRate,
EnableRBF: true,
}

alice.fundingMgr.InitFundingWorkflow(initReq)
Expand Down Expand Up @@ -3210,6 +3213,12 @@ func TestFundingManagerCustomChannelParameters(t *testing.T) {
resCtx, err := alice.fundingMgr.getReservationCtx(bobPubKey, chanID)
require.NoError(t, err, "unable to find ctx")

// Make sure that our funding tx inputs signal RBF
for _, in := range resCtx.reservation.FinalFundingTx().TxIn {
require.Equal(t, uint32(mempool.MaxRBFSequence), in.Sequence,
"input sequence does not signal RBF")
}

// Alice's CSV delay should be 4 since Bob sent the default value, and
// Bob's should be 67 since Alice sent the custom value.
if err := assertDelay(resCtx, 4, csvDelay); err != nil {
Expand Down Expand Up @@ -3332,6 +3341,12 @@ func TestFundingManagerCustomChannelParameters(t *testing.T) {
alice.fundingMgr.ProcessFundingMsg(channelReadyBob, bob)
bob.fundingMgr.ProcessFundingMsg(channelReadyAlice, alice)

// Make sure that the mined funding tx inputs signal RBF
for _, in := range fundingTx.TxIn {
require.Equal(t, uint32(mempool.MaxRBFSequence), in.Sequence,
"input sequence does not signal RBF")
}

// Make sure both fundingManagers send the expected channel
// announcements.
// Alice should advertise the default MinHTLC value of
Expand Down Expand Up @@ -3877,7 +3892,7 @@ func TestFundingManagerFundAll(t *testing.T) {
pushAmt := btcutil.Amount(0)
fundingTx := fundChannel(
t, alice, bob, test.spendAmt, pushAmt, true, 0, 0, 1,
updateChan, true, nil,
updateChan, true, nil, true,
)

// Check whether the expected change output is present.
Expand Down Expand Up @@ -4033,7 +4048,7 @@ func TestFundingManagerFundMax(t *testing.T) {
fundingTx := fundChannel(
t, alice, bob, 0, pushAmt, false,
test.fundUpToMaxAmt, test.minFundAmt, 1,
updateChan, true, nil,
updateChan, true, nil, true,
)

// Check whether the expected change output is present.
Expand Down Expand Up @@ -4518,7 +4533,7 @@ func testZeroConf(t *testing.T, chanType *lnwire.ChannelType) {
// Call fundChannel with the zero-conf ChannelType.
fundingTx := fundChannel(
t, alice, bob, fundingAmt, pushAmt, false, 0, 0, 1, updateChan,
true, &channelType,
true, &channelType, true,
)
fundingOp := &wire.OutPoint{
Hash: fundingTx.TxHash(),
Expand Down
2 changes: 1 addition & 1 deletion lnwallet/btcwallet/btcwallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -1028,7 +1028,7 @@ func (b *BtcWallet) CreateSimpleTx(outputs []*wire.TxOut,

return b.wallet.CreateSimpleTx(
nil, defaultAccount, outputs, minConfs, feeSatPerKB,
b.cfg.CoinSelectionStrategy, dryRun,
b.cfg.CoinSelectionStrategy, dryRun, base.WithRBF(),
)
}

Expand Down
2 changes: 1 addition & 1 deletion lnwallet/btcwallet/psbt.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func (b *BtcWallet) FundPsbt(packet *psbt.Packet, minConfs int32,
accountNum = account
}

var opts []wallet.TxCreateOption
opts := []wallet.TxCreateOption{wallet.WithRBF()}
if changeScope != nil {
opts = append(opts, wallet.WithCustomChangeScope(changeScope))
}
Expand Down
4 changes: 4 additions & 0 deletions lnwallet/chanfunding/assembler.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ type Request struct {
// output. By definition, this'll also use segwit v1 (taproot) for the
// funding output.
Musig2 bool

// EnableRBF is a boolen which if true sets transaction inputs
// to signal RBF
EnableRBF bool
}

// Intent is returned by an Assembler and represents the base functionality the
Expand Down
19 changes: 15 additions & 4 deletions lnwallet/chanfunding/wallet_assembler.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/btcutil/txsort"
"github.com/btcsuite/btcd/mempool"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/input"
Expand Down Expand Up @@ -45,6 +46,9 @@ type FullIntent struct {

// signer is the Assembler's instance of the Singer interface.
signer input.Signer

// the sequence assigned to inputs in CompileFundingTx (for RBF signalling)
inputSequence uint32
}

// BindKeys is a method unique to the FullIntent variant. This allows the
Expand Down Expand Up @@ -75,6 +79,7 @@ func (f *FullIntent) CompileFundingTx(extraInputs []*wire.TxIn,
for _, coin := range f.InputCoins {
fundingTx.AddTxIn(&wire.TxIn{
PreviousOutPoint: coin.OutPoint,
Sequence: f.inputSequence,
})
}
for _, theirInput := range extraInputs {
Expand Down Expand Up @@ -472,16 +477,22 @@ func (w *WalletAssembler) ProvisionChannel(r *Request) (Intent, error) {
w.cfg.CoinLocker.LockOutpoint(outpoint)
}

var inputSequence uint32
if r.EnableRBF {
inputSequence = mempool.MaxRBFSequence
}

newIntent := &FullIntent{
ShimIntent: ShimIntent{
localFundingAmt: localContributionAmt,
remoteFundingAmt: r.RemoteAmt,
musig2: r.Musig2,
},
InputCoins: selectedCoins,
coinLocker: w.cfg.CoinLocker,
coinSource: w.cfg.CoinSource,
signer: w.cfg.Signer,
InputCoins: selectedCoins,
coinLocker: w.cfg.CoinLocker,
coinSource: w.cfg.CoinSource,
signer: w.cfg.Signer,
inputSequence: inputSequence,
}

if changeOutput != nil {
Expand Down
7 changes: 6 additions & 1 deletion lnwallet/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ type InitFundingReserveMsg struct {
// negotiated.
ScidAliasFeature bool

// EnableRBF is a boolen which if true sets transaction inputs
// to signal RBF
EnableRBF bool

// Memo is any arbitrary information we wish to store locally about the
// channel that will be useful to our future selves.
Memo []byte
Expand Down Expand Up @@ -930,7 +934,8 @@ func (l *LightningWallet) handleFundingReserveRequest(req *InitFundingReserveMsg
TaprootPubkey, true, DefaultAccountName,
)
},
Musig2: req.CommitType == CommitmentTypeSimpleTaproot,
Musig2: req.CommitType == CommitmentTypeSimpleTaproot,
EnableRBF: req.EnableRBF,
}
fundingIntent, err = req.ChanFunder.ProvisionChannel(
fundingReq,
Expand Down
1 change: 1 addition & 0 deletions pilot.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ func (c *chanController) OpenChannel(target *btcec.PublicKey,
RemoteCsvDelay: 0,
MinConfs: c.minConfs,
MaxValueInFlight: 0,
EnableRBF: true,
}

updateStream, errChan := c.server.OpenChannel(req)
Expand Down
8 changes: 6 additions & 2 deletions rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1288,6 +1288,9 @@ func (r *rpcServer) SendCoins(ctx context.Context,
return nil, err
}

// Set input sequences of sweep tx to signal RBF
enableRBF := true

// With the sweeper instance created, we can now generate a
// transaction that will sweep ALL outputs from the wallet in a
// single transaction. This will be generated in a concurrent
Expand All @@ -1297,7 +1300,7 @@ func (r *rpcServer) SendCoins(ctx context.Context,
sweepTxPkg, err := sweep.CraftSweepAllTx(
maxFeeRate, feePerKw, uint32(bestHeight), nil,
targetAddr, wallet, wallet, wallet.WalletController,
r.server.cc.Signer, minConfs,
r.server.cc.Signer, minConfs, enableRBF,
)
if err != nil {
return nil, err
Expand Down Expand Up @@ -1351,7 +1354,7 @@ func (r *rpcServer) SendCoins(ctx context.Context,
maxFeeRate, feePerKw, uint32(bestHeight),
outputs, targetAddr, wallet, wallet,
wallet.WalletController,
r.server.cc.Signer, minConfs,
r.server.cc.Signer, minConfs, true,
)
if err != nil {
return nil, err
Expand Down Expand Up @@ -2218,6 +2221,7 @@ func (r *rpcServer) parseOpenChannelReq(in *lnrpc.OpenChannelRequest,
MinFundAmt: minFundAmt,
Memo: []byte(in.Memo),
Outpoints: outpoints,
EnableRBF: true,
}, nil
}

Expand Down
5 changes: 3 additions & 2 deletions sweep/sweeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -1152,6 +1152,7 @@ func (s *UtxoSweeper) sweep(inputs inputSet, feeRate chainfee.SatPerKWeight,
tx, err := createSweepTx(
inputs, nil, s.currentOutputScript, uint32(currentHeight),
feeRate, s.cfg.MaxFeeRate.FeePerKWeight(), s.cfg.Signer,
false,
)
if err != nil {
return fmt.Errorf("create sweep tx: %v", err)
Expand Down Expand Up @@ -1440,7 +1441,7 @@ func (s *UtxoSweeper) handleUpdateReq(req *updateReq, bestHeight int32) (
// - Thwart future possible fee sniping attempts.
// - Make us blend in with the bitcoind wallet.
func (s *UtxoSweeper) CreateSweepTx(inputs []input.Input, feePref FeePreference,
currentBlockHeight uint32) (*wire.MsgTx, error) {
currentBlockHeight uint32, enableRBF bool) (*wire.MsgTx, error) {

feePerKw, err := s.cfg.DetermineFeePerKw(s.cfg.FeeEstimator, feePref)
if err != nil {
Expand All @@ -1455,7 +1456,7 @@ func (s *UtxoSweeper) CreateSweepTx(inputs []input.Input, feePref FeePreference,

return createSweepTx(
inputs, nil, pkScript, currentBlockHeight, feePerKw,
s.cfg.MaxFeeRate.FeePerKWeight(), s.cfg.Signer,
s.cfg.MaxFeeRate.FeePerKWeight(), s.cfg.Signer, enableRBF,
)
}

Expand Down
13 changes: 11 additions & 2 deletions sweep/txgenerator.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/mempool"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/input"
Expand Down Expand Up @@ -140,7 +141,7 @@ func generateInputPartitionings(sweepableInputs []txInput,
func createSweepTx(inputs []input.Input, outputs []*wire.TxOut,
changePkScript []byte, currentBlockHeight uint32,
feePerKw, maxFeeRate chainfee.SatPerKWeight,
signer input.Signer) (*wire.MsgTx, error) {
signer input.Signer, enableRBF bool) (*wire.MsgTx, error) {

inputs, estimator, err := getWeightEstimate(
inputs, outputs, feePerKw, maxFeeRate, changePkScript,
Expand Down Expand Up @@ -198,6 +199,14 @@ func createSweepTx(inputs []input.Input, outputs []*wire.TxOut,
requiredOutput += btcutil.Amount(o.RequiredTxOut().Value)
}

inputSequence := func(in input.Input) (s uint32) {
s = in.BlocksToMaturity()
if enableRBF && s == 0 {
s = mempool.MaxRBFSequence
}
return
}

// Sum up the value contained in the remaining inputs, and add them to
// the sweep transaction.
for _, o := range inputs {
Expand All @@ -208,7 +217,7 @@ func createSweepTx(inputs []input.Input, outputs []*wire.TxOut,
idxs = append(idxs, o)
sweepTx.AddTxIn(&wire.TxIn{
PreviousOutPoint: *o.OutPoint(),
Sequence: o.BlocksToMaturity(),
Sequence: inputSequence(o),
})

if lt, ok := o.RequiredLockTime(); ok {
Expand Down
5 changes: 3 additions & 2 deletions sweep/walletsweep.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,8 @@ func CraftSweepAllTx(feeRate, maxFeeRate chainfee.SatPerKWeight,
blockHeight uint32, deliveryAddrs []DeliveryAddr,
changeAddr btcutil.Address, coinSelectLocker CoinSelectionLocker,
utxoSource UtxoSource, outpointLocker OutpointLocker,
signer input.Signer, minConfs int32) (*WalletSweepPackage, error) {
signer input.Signer, minConfs int32,
enableRBF bool) (*WalletSweepPackage, error) {

// TODO(roasbeef): turn off ATPL as well when available?

Expand Down Expand Up @@ -321,7 +322,7 @@ func CraftSweepAllTx(feeRate, maxFeeRate chainfee.SatPerKWeight,
// respects our fee preference and targets all the UTXOs of the wallet.
sweepTx, err := createSweepTx(
inputsToSweep, txOuts, changePkScript, blockHeight,
feeRate, maxFeeRate, signer,
feeRate, maxFeeRate, signer, enableRBF,
)
if err != nil {
unlockOutputs()
Expand Down
Loading