Skip to content
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
14 changes: 9 additions & 5 deletions itest/lnd_coop_close_rbf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func testCoopCloseRbf(ht *lntest.HarnessTest) {
alicePendingUpdate := aliceCloseUpdate.GetClosePending()
require.NotNil(ht, aliceCloseUpdate)
require.Equal(
ht, int64(aliceFeeRate), alicePendingUpdate.FeePerVbyte,
ht, int64(aliceFeeRate), int64(alicePendingUpdate.FeePerKw/250),
)
require.True(ht, alicePendingUpdate.LocalCloseTx)

Expand All @@ -57,7 +57,9 @@ func testCoopCloseRbf(ht *lntest.HarnessTest) {
// Confirm that this new update was at 10 sat/vb.
bobPendingUpdate := bobCloseUpdate.GetClosePending()
require.NotNil(ht, bobCloseUpdate)
require.Equal(ht, bobPendingUpdate.FeePerVbyte, int64(bobFeeRate))
require.Equal(
ht, int64(bobPendingUpdate.FeePerKw/250), int64(bobFeeRate),
)
require.True(ht, bobPendingUpdate.LocalCloseTx)

var err error
Expand All @@ -68,7 +70,9 @@ func testCoopCloseRbf(ht *lntest.HarnessTest) {
require.NoError(ht, err)
alicePendingUpdate = aliceCloseUpdate.GetClosePending()
require.NotNil(ht, aliceCloseUpdate)
require.Equal(ht, alicePendingUpdate.FeePerVbyte, int64(bobFeeRate))
require.Equal(
ht, int64(alicePendingUpdate.FeePerKw/250), int64(bobFeeRate),
)
require.False(ht, alicePendingUpdate.LocalCloseTx)

// We'll now attempt to make a fee update that increases Alice's fee
Expand All @@ -84,7 +88,7 @@ func testCoopCloseRbf(ht *lntest.HarnessTest) {
alicePendingUpdate = aliceCloseUpdate.GetClosePending()
require.NotNil(ht, aliceCloseUpdate)
require.Equal(
ht, alicePendingUpdate.FeePerVbyte,
ht, int64(alicePendingUpdate.FeePerKw/250),
int64(aliceRejectedFeeRate),
)
require.True(ht, alicePendingUpdate.LocalCloseTx)
Expand Down Expand Up @@ -118,7 +122,7 @@ func testCoopCloseRbf(ht *lntest.HarnessTest) {
alicePendingUpdate = aliceCloseUpdate.GetClosePending()
require.NotNil(ht, aliceCloseUpdate)
require.Equal(
ht, alicePendingUpdate.FeePerVbyte, int64(aliceFeeRate),
ht, int64(alicePendingUpdate.FeePerKw/250), int64(aliceFeeRate),
)
require.True(ht, alicePendingUpdate.LocalCloseTx)

Expand Down
4,732 changes: 2,371 additions & 2,361 deletions lnrpc/lightning.pb.go

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions lnrpc/lightning.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2243,6 +2243,7 @@ message PendingUpdate {
uint32 output_index = 2;
int64 fee_per_vbyte = 3;
bool local_close_tx = 4;
uint64 fee_per_kw = 5;
}

message InstantUpdate {
Expand Down
4 changes: 4 additions & 0 deletions lnrpc/lightning.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -7129,6 +7129,10 @@
},
"local_close_tx": {
"type": "boolean"
},
"fee_per_kw": {
"type": "string",
"format": "uint64"
}
}
},
Expand Down
9 changes: 7 additions & 2 deletions lntest/harness.go
Original file line number Diff line number Diff line change
Expand Up @@ -1343,9 +1343,14 @@ func (h *HarnessTest) CloseChannelAssertPending(hn *node.HarnessNode,
continue
}

notifyRate := pendingClose.ClosePending.FeePerVbyte
// Close pending nottification in FeePerKw overwrites
// FeePerVbyte.
notifyRate := uint64(pendingClose.ClosePending.FeePerVbyte)
if pendingClose.ClosePending.FeePerKw != 0 {
notifyRate = pendingClose.ClosePending.FeePerKw / 250
}
if closeOpts.localTxOnly &&
notifyRate != int64(closeReq.SatPerVbyte) {
notifyRate != closeReq.SatPerVbyte {

continue
}
Expand Down
14 changes: 7 additions & 7 deletions lnwallet/chancloser/rbf_coop_states.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ type SendShutdown struct {

// IdealFeeRate is the ideal fee rate we'd like to use for the closing
// attempt.
IdealFeeRate chainfee.SatPerVByte
IdealFeeRate chainfee.SatPerKWeight
}

// protocolSealed indicates that this struct is a ProtocolEvent instance.
Expand Down Expand Up @@ -182,7 +182,7 @@ func (c *ChannelFlushed) protocolSealed() {}
// - toState: LocalOfferSent
type SendOfferEvent struct {
// TargetFeeRate is the fee rate we'll use for the closing transaction.
TargetFeeRate chainfee.SatPerVByte
TargetFeeRate chainfee.SatPerKWeight
}

// protocolSealed indicates that this struct is a ProtocolEvent instance.
Expand Down Expand Up @@ -304,7 +304,7 @@ type Environment struct {
// DefaultFeeRate is the fee we'll use for the closing transaction if
// the user didn't specify an ideal fee rate. This may happen if the
// remote party is the one that initiates the co-op close.
DefaultFeeRate chainfee.SatPerVByte
DefaultFeeRate chainfee.SatPerKWeight

// ThawHeight is the height at which the channel will be thawed. If
// this is None, then co-op close can occur at any moment.
Expand Down Expand Up @@ -453,7 +453,7 @@ type ShutdownPending struct {

// IdealFeeRate is the ideal fee rate we'd like to use for the closing
// attempt.
IdealFeeRate fn.Option[chainfee.SatPerVByte]
IdealFeeRate fn.Option[chainfee.SatPerKWeight]

// EarlyRemoteOffer is the offer we received from the remote party
// before we received their shutdown message. We'll stash it to process
Expand Down Expand Up @@ -498,7 +498,7 @@ type ChannelFlushing struct {
// IdealFeeRate is the ideal fee rate we'd like to use for the closing
// transaction. Once the channel has been flushed, we'll use this as
// our target fee rate.
IdealFeeRate fn.Option[chainfee.SatPerVByte]
IdealFeeRate fn.Option[chainfee.SatPerKWeight]
}

// String returns the name of the state for ChannelFlushing.
Expand Down Expand Up @@ -732,7 +732,7 @@ type LocalOfferSent struct {
ProposedFee btcutil.Amount

// ProposedFeeRate is the fee rate we proposed to the remote party.
ProposedFeeRate chainfee.SatPerVByte
ProposedFeeRate chainfee.SatPerKWeight

// LocalSig is the signature we sent to the remote party.
LocalSig lnwire.Sig
Expand Down Expand Up @@ -781,7 +781,7 @@ type ClosePending struct {
*CloseChannelTerms

// FeeRate is the fee rate of the closing transaction.
FeeRate chainfee.SatPerVByte
FeeRate chainfee.SatPerKWeight

// Party indicates which party is at this state. This is used to
// implement the state transition properly, based on ShouldRouteTo.
Expand Down
20 changes: 10 additions & 10 deletions lnwallet/chancloser/rbf_coop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,7 @@ func newRbfCloserTestHarness(t *testing.T,
ChanPoint: chanPoint,
ChanID: chanID,
Scid: scid,
DefaultFeeRate: defaultFeeRate.FeePerVByte(),
DefaultFeeRate: defaultFeeRate,
ThawHeight: cfg.thawHeight,
RemoteUpfrontShutdown: cfg.remoteUpfrontAddr,
LocalUpfrontShutdown: cfg.localUpfrontAddr,
Expand Down Expand Up @@ -854,7 +854,7 @@ func TestRbfChannelActiveTransitions(t *testing.T) {
localAddr := lnwire.DeliveryAddress(bytes.Repeat([]byte{0x01}, 20))
remoteAddr := lnwire.DeliveryAddress(bytes.Repeat([]byte{0x02}, 20))

feeRate := chainfee.SatPerVByte(1000)
feeRate := chainfee.SatPerKWeight(250_000)

// Test that if a spend event is received, the FSM transitions to the
// CloseFin terminal state.
Expand Down Expand Up @@ -1050,7 +1050,7 @@ func TestRbfShutdownPendingTransitions(t *testing.T) {
t.Run("initiator_shutdown_recv_ok", func(t *testing.T) {
firstState := *startingState
firstState.IdealFeeRate = fn.Some(
chainfee.FeePerKwFloor.FeePerVByte(),
chainfee.FeePerKwFloor,
)
firstState.ShutdownScripts = ShutdownScripts{
LocalDeliveryScript: localAddr,
Expand Down Expand Up @@ -1097,7 +1097,7 @@ func TestRbfShutdownPendingTransitions(t *testing.T) {
t.Run("responder_complete", func(t *testing.T) {
firstState := *startingState
firstState.IdealFeeRate = fn.Some(
chainfee.FeePerKwFloor.FeePerVByte(),
chainfee.FeePerKwFloor,
)
firstState.ShutdownScripts = ShutdownScripts{
LocalDeliveryScript: localAddr,
Expand Down Expand Up @@ -1128,7 +1128,7 @@ func TestRbfShutdownPendingTransitions(t *testing.T) {
t.Run("early_remote_offer_shutdown_complete", func(t *testing.T) {
firstState := *startingState
firstState.IdealFeeRate = fn.Some(
chainfee.FeePerKwFloor.FeePerVByte(),
chainfee.FeePerKwFloor,
)
firstState.ShutdownScripts = ShutdownScripts{
LocalDeliveryScript: localAddr,
Expand Down Expand Up @@ -1175,7 +1175,7 @@ func TestRbfShutdownPendingTransitions(t *testing.T) {
t.Run("early_remote_offer_shutdown_received", func(t *testing.T) {
firstState := *startingState
firstState.IdealFeeRate = fn.Some(
chainfee.FeePerKwFloor.FeePerVByte(),
chainfee.FeePerKwFloor,
)
firstState.ShutdownScripts = ShutdownScripts{
LocalDeliveryScript: localAddr,
Expand Down Expand Up @@ -1433,7 +1433,7 @@ func TestRbfCloseClosingNegotiationLocal(t *testing.T) {
}

sendOfferEvent := &SendOfferEvent{
TargetFeeRate: chainfee.FeePerKwFloor.FeePerVByte(),
TargetFeeRate: chainfee.FeePerKwFloor,
}

balanceAfterClose := localBalance.ToSatoshis() - absoluteFee
Expand Down Expand Up @@ -1614,7 +1614,7 @@ func TestRbfCloseClosingNegotiationLocal(t *testing.T) {
// Next, we'll send in a new SendOfferEvent event which
// simulates the user requesting a RBF fee bump. We'll use 10x
// the fee we used in the last iteration.
rbfFeeBump := chainfee.FeePerKwFloor.FeePerVByte() * 10
rbfFeeBump := chainfee.FeePerKwFloor * 10
localOffer := &SendOfferEvent{
TargetFeeRate: rbfFeeBump,
}
Expand Down Expand Up @@ -1649,7 +1649,7 @@ func TestRbfCloseClosingNegotiationLocal(t *testing.T) {
// the amount we have in the channel.
closeHarness.expectFeeEstimate(btcutil.SatoshiPerBitcoin, 1)

rbfFeeBump := chainfee.FeePerKwFloor.FeePerVByte()
rbfFeeBump := chainfee.FeePerKwFloor
localOffer := &SendOfferEvent{
TargetFeeRate: rbfFeeBump,
}
Expand Down Expand Up @@ -1997,7 +1997,7 @@ func TestRbfCloseErr(t *testing.T) {
})
defer closeHarness.stopAndAssert()

rbfFeeBump := chainfee.FeePerKwFloor.FeePerVByte()
rbfFeeBump := chainfee.FeePerKwFloor
localOffer := &SendOfferEvent{
TargetFeeRate: rbfFeeBump,
}
Expand Down
16 changes: 11 additions & 5 deletions lnwallet/chancloser/rbf_coop_transitions.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"fmt"

"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
Expand Down Expand Up @@ -509,7 +510,7 @@ func (c *ChannelFlushing) ProcessEvent(event ProtocolEvent,
localTxOut, remoteTxOut := closeTerms.DeriveCloseTxOuts()
absoluteFee := env.FeeEstimator.EstimateFee(
env.ChanType, localTxOut, remoteTxOut,
idealFeeRate.FeePerKWeight(),
idealFeeRate,
)

chancloserLog.Infof("ChannelPoint(%v): using ideal_fee=%v, "+
Expand Down Expand Up @@ -752,7 +753,7 @@ func (l *LocalCloseStart) ProcessEvent(event ProtocolEvent,
localTxOut, remoteTxOut := l.DeriveCloseTxOuts()
absoluteFee := env.FeeEstimator.EstimateFee(
env.ChanType, localTxOut, remoteTxOut,
msg.TargetFeeRate.FeePerKWeight(),
msg.TargetFeeRate,
)

// If we can't actually pay for fees here, then we'll just do a
Expand Down Expand Up @@ -1128,11 +1129,16 @@ func (l *RemoteCloseStart) ProcessEvent(event ProtocolEvent,
// We'll also compute the final fee rate that the remote party
// paid based off the absolute fee and the size of the closing
// transaction.
vSize := mempool.GetTxVirtualSize(btcutil.NewTx(closeTx))
feeRate := chainfee.SatPerVByte(
int64(msg.SigMsg.FeeSatoshis) / vSize,
weight := blockchain.GetTransactionWeight(
btcutil.NewTx(closeTx),
)

// Convert absolute fee to a fee rate in sat/kw, rounding up.
fee := int64(msg.SigMsg.FeeSatoshis)
rate := ((fee * 1000) + weight - 1) / weight

feeRate := chainfee.SatPerKWeight(rate)

// Now that we've extracted the signature, we'll transition to
// the next state where we'll sign+broadcast the sig.
return &CloseStateTransition{
Expand Down
14 changes: 7 additions & 7 deletions peer/brontide.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,10 @@ type PendingUpdate struct {
// transaction.
OutputIndex uint32

// FeePerVByte is an optional field, that is set only when the new RBF
// FeePerKw is an optional field, that is set only when the new RBF
// coop close flow is used. This indicates the new closing fee rate on
// the closing transaction.
FeePerVbyte fn.Option[chainfee.SatPerVByte]
FeePerKw fn.Option[chainfee.SatPerKWeight]

// IsLocalCloseTx is an optional field that indicates if this update is
// sent for our local close txn, or the close txn of the remote party.
Expand Down Expand Up @@ -3611,7 +3611,7 @@ func (p *Brontide) observeRbfCloseUpdates(chanCloser *chancloser.RbfChanCloser,

var (
lastTxids lntypes.Dual[chainhash.Hash]
lastFeeRates lntypes.Dual[chainfee.SatPerVByte]
lastFeeRates lntypes.Dual[chainfee.SatPerKWeight]
)

maybeNotifyTxBroadcast := func(state chancloser.AsymmetricPeerState,
Expand Down Expand Up @@ -3670,8 +3670,8 @@ func (p *Brontide) observeRbfCloseUpdates(chanCloser *chancloser.RbfChanCloser,
if closeReq != nil && closingTxid != lastTxid {
select {
case closeReq.Updates <- &PendingUpdate{
Txid: closingTxid[:],
FeePerVbyte: fn.Some(closePending.FeeRate),
Txid: closingTxid[:],
FeePerKw: fn.Some(closePending.FeeRate),
IsLocalCloseTx: fn.Some(
party == lntypes.Local,
),
Expand Down Expand Up @@ -3928,7 +3928,7 @@ func (p *Brontide) initRbfChanCloser(
ChanID: chanID,
Scid: scid,
ChanType: channel.ChanType(),
DefaultFeeRate: defaultFeePerKw.FeePerVByte(),
DefaultFeeRate: defaultFeePerKw,
ThawHeight: fn.Some(thawHeight),
RemoteUpfrontShutdown: ChooseAddr(
channel.RemoteUpfrontShutdownScript(),
Expand Down Expand Up @@ -4189,7 +4189,7 @@ func (p *Brontide) startRbfChanCloser(shutdown shutdownInit,
}

ctx, _ := p.cg.Create(context.Background())
feeRate := defaultFeePerKw.FeePerVByte()
feeRate := defaultFeePerKw

// Depending on the state of the state machine, we'll either
// kick things off by sending shutdown, or attempt to send a new
Expand Down
4 changes: 2 additions & 2 deletions rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -3137,8 +3137,8 @@ func createRPCCloseUpdate(
u.IsLocalCloseTx.WhenSome(func(isLocal bool) {
upd.LocalCloseTx = isLocal
})
u.FeePerVbyte.WhenSome(func(feeRate chainfee.SatPerVByte) {
upd.FeePerVbyte = int64(feeRate)
u.FeePerKw.WhenSome(func(feeRate chainfee.SatPerKWeight) {
upd.FeePerKw = uint64(feeRate)
})

return &lnrpc.CloseStatusUpdate{
Expand Down
Loading