Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
752fc86
lnwire: add feature bits for new rbf coop close
Roasbeef Jan 31, 2024
82ac5fa
feature: set optional bit for rbf-coop close by default
Roasbeef Jan 31, 2024
01382f4
htlcswitch: change LinkDirection to be a type alias
Roasbeef Feb 1, 2024
1d2c22d
protofsm: add String() method to ProtocolState
Roasbeef Feb 8, 2025
c32d4f9
peer: add an implementation of chancloser.ChanObserver
Roasbeef Feb 1, 2024
daed552
peer: make a DaemonAdapter impl for lnd
Roasbeef Feb 1, 2024
a4064c7
protofsm: don't return error on broadcast fail
Roasbeef Mar 8, 2024
29afeb4
lnd: register protofsm logger
Roasbeef Mar 8, 2024
127d3be
input: add new ScriptIsOpReturn helper func
Roasbeef Feb 11, 2025
bf3007a
multi: thread thru RPC caller context from CloseChannel
Roasbeef Feb 11, 2025
ed8a672
protofsm: update StateMachine to meet new msgmux.Endpoint interface
Roasbeef Feb 27, 2025
b2794b0
lnwallet/chancloser: enforce pubkey binding for msg mapper
Roasbeef Feb 8, 2025
8432e70
multi: extract new DeriveHeightHint() function, use for new rbf closer
Roasbeef Feb 11, 2025
baf0d83
peer: update chooseDeliveryScript to gen script if needed
Roasbeef Feb 26, 2025
8b2ade7
lnwallet+protofsm: fix logging
yyforyongyu Mar 3, 2025
27b24ab
lnwallet/chancloser: add fee rate to ClosePending
Roasbeef Mar 8, 2024
835d762
lnwallet/chancloser: ignore spurious channel flushed events
Roasbeef Mar 8, 2024
14eca44
lnwire: update closing_complete and closing_sig to latest spec draft
Roasbeef Feb 8, 2025
e6d7a1a
protofsm: update state machine w/ new spec flow
Roasbeef Feb 8, 2025
333492e
lnwallet: implement special case for OP_RETURN in rbf-coop
Roasbeef Feb 8, 2025
7370617
lnwallet/chancloser: update RBF state machine to handle early offer case
Roasbeef Feb 27, 2025
84b6d95
lnwallet/chancloser: increase test coverage of state machine
Roasbeef Feb 27, 2025
fd05199
protofsm: add an upfront check for SendWhen predicates
Roasbeef Mar 5, 2025
047d353
peer: move existing chan closer init logic to new method
Roasbeef Jan 31, 2024
23a9a76
peer: add initial awareness of new rbf coop closer
Roasbeef Feb 1, 2024
2dae0ba
peer: add new composite chanCloserFsm type
Roasbeef Feb 1, 2024
4fff0d8
peer: create ErrorReporter implementation for rbf-fsm
Roasbeef Mar 5, 2024
f22cba9
peer: conditionally create new RBF chan closer
Roasbeef Feb 1, 2024
6364e98
peer: conditionally create rbf coop close fsm based on feature bits
Roasbeef Mar 5, 2024
a9e538e
feature: add new NoRbfCoopClose option
Roasbeef Mar 5, 2024
47af913
lncfg: add new protocol option - RbfCoopClose
Roasbeef Mar 5, 2024
429bbf3
server: thread through new NoRbfCoopClose option
Roasbeef Mar 5, 2024
413cc3d
itest: update async coop close itests to also use new rbf flow
Roasbeef Mar 5, 2024
029fbd2
peer: make activeChanCloses a SyncMap
Roasbeef Mar 8, 2024
282c50e
peer: attempt to unregister endpoint before registering
Roasbeef Mar 8, 2024
b32f576
lnrpc: add fee rate and local close bool to PendingUpdate
Roasbeef Mar 8, 2024
81d34c4
peer+rpc: set new rbf coop close rbf update fields
Roasbeef Mar 8, 2024
2899a75
lntest: fix error message in WaitForChannelCloseEvent
Roasbeef Mar 8, 2024
fbc67f7
lntest+itest: extend CloseChannelAssertPending
Roasbeef Mar 8, 2024
80b48fb
peer: update rbf close client logic w/ error and iteration awareness
Roasbeef Feb 8, 2025
3a18bf0
multi: enable RBF co-op bumps after reconnection
Roasbeef Feb 8, 2025
971ac5a
itest: add new RBF coop close itest
Roasbeef Feb 8, 2025
fccd984
multi: extract new DeriveHeightHint() function, use for new rbf closer
Roasbeef Feb 11, 2025
8d9ed0c
docs/release-notes: add rbf coop close section
Roasbeef Feb 12, 2025
720d98c
msgmux: fix arg expectation for mock in unit test
Roasbeef Mar 5, 2025
8df58a9
lnwallet/chancloser: add docs for new rbf chan closer
Roasbeef Mar 12, 2025
3681ba6
peer: for RBF state machine block req if RBF iterating is outstanding
Roasbeef Mar 17, 2025
42fa837
docs/release-notes: update release notes for RBF close
Roasbeef Mar 18, 2025
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
28 changes: 28 additions & 0 deletions channeldb/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -4104,6 +4104,34 @@ func (c *OpenChannel) AbsoluteThawHeight() (uint32, error) {
return c.ThawHeight, nil
}

// DeriveHeightHint derives the block height for the channel opening.
func (c *OpenChannel) DeriveHeightHint() uint32 {
// As a height hint, we'll try to use the opening height, but if the
// channel isn't yet open, then we'll use the height it was broadcast
// at. This may be an unconfirmed zero-conf channel.
heightHint := c.ShortChanID().BlockHeight
if heightHint == 0 {
heightHint = c.BroadcastHeight()
}

// Since no zero-conf state is stored in a channel backup, the below
// logic will not be triggered for restored, zero-conf channels. Set
// the height hint for zero-conf channels.
if c.IsZeroConf() {
if c.ZeroConfConfirmed() {
// If the zero-conf channel is confirmed, we'll use the
// confirmed SCID's block height.
heightHint = c.ZeroConfRealScid().BlockHeight
} else {
// The zero-conf channel is unconfirmed. We'll need to
// use the FundingBroadcastHeight.
heightHint = c.BroadcastHeight()
}
}

return heightHint
}

func putChannelCloseSummary(tx kvdb.RwTx, chanID []byte,
summary *ChannelCloseSummary, lastChanState *OpenChannel) error {

Expand Down
30 changes: 1 addition & 29 deletions contractcourt/chain_watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ func newChainWatcher(cfg chainWatcherConfig) (*chainWatcher, error) {
}

// Get the channel opening block height.
heightHint := deriveHeightHint(chanState)
heightHint := chanState.DeriveHeightHint()

// We'll register for a notification to be dispatched if the funding
// output is spent.
Expand Down Expand Up @@ -1328,34 +1328,6 @@ func deriveFundingPkScript(chanState *channeldb.OpenChannel) ([]byte, error) {
return fundingPkScript, nil
}

// deriveHeightHint derives the block height for the channel opening.
func deriveHeightHint(chanState *channeldb.OpenChannel) uint32 {
// As a height hint, we'll try to use the opening height, but if the
// channel isn't yet open, then we'll use the height it was broadcast
// at. This may be an unconfirmed zero-conf channel.
heightHint := chanState.ShortChanID().BlockHeight
if heightHint == 0 {
heightHint = chanState.BroadcastHeight()
}

// Since no zero-conf state is stored in a channel backup, the below
// logic will not be triggered for restored, zero-conf channels. Set
// the height hint for zero-conf channels.
if chanState.IsZeroConf() {
if chanState.ZeroConfConfirmed() {
// If the zero-conf channel is confirmed, we'll use the
// confirmed SCID's block height.
heightHint = chanState.ZeroConfRealScid().BlockHeight
} else {
// The zero-conf channel is unconfirmed. We'll need to
// use the FundingBroadcastHeight.
heightHint = chanState.BroadcastHeight()
}
}

return heightHint
}

// handleCommitSpend takes a spending tx of the funding output and handles the
// channel close based on the closure type.
func (c *chainWatcher) handleCommitSpend(
Expand Down
27 changes: 23 additions & 4 deletions docs/release-notes/release-notes-0.19.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,27 @@

# New Features

* Add support for [archiving channel backup](https://github.com/lightningnetwork/lnd/pull/9232)
in a designated folder which allows for easy referencing in the future. A new
config is added `disable-backup-archive`, with default set to false, to
determine if previous channel backups should be archived or not.

## Protocol Updates

* `lnd` now [supports the new RBF cooperative close
flow](https://github.com/lightningnetwork/lnd/pull/9610). Unlike the old flow,
this version now uses RBF to enable either side to increase their fee rate using
their _own_ channel funds. This removes the old "negotiation" logic that could
fail, with a version where either side can increase the fee on their coop close
transaction using their channel balance.

This new feature can be activated with a new config flag:
`--protocol.rbf-coop-close`.

With this new co-op close type, users can issue multiple `lncli closechannnel`
commands with increasing fee rates to use RBF to bump an existing signed co-op
close transaction.

* [Support](https://github.com/lightningnetwork/lnd/pull/8390) for
[experimental endorsement](https://github.com/lightning/blips/pull/27)
signal relay was added. This signal has *no impact* on routing, and
Expand All @@ -103,10 +124,7 @@
initial historical sync may be blocked due to a race condition in handling the
syncer's internal state.

* Add support for [archiving channel backup](https://github.com/lightningnetwork/lnd/pull/9232)
in a designated folder which allows for easy referencing in the future. A new
config is added `disable-backup-archive`, with default set to false, to
determine if previous channel backups should be archived or not.


* [The max fee rate](https://github.com/lightningnetwork/lnd/pull/9491) is now
respected when a coop close is initiated. Before the max fee rate would only
Expand Down Expand Up @@ -428,6 +446,7 @@ The underlying functionality between those two options remain the same.
* Keagan McClelland
* Nishant Bansal
* Oliver Gugger
* Olaoluwa Osuntokun
* Pins
* Viktor Tigerström
* Yong Yu
Expand Down
4 changes: 4 additions & 0 deletions feature/default_sets.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,8 @@ var defaultSetDesc = setDesc{
lnwire.ExperimentalEndorsementOptional: {
SetNodeAnn: {}, // N
},
lnwire.RbfCoopCloseOptionalStaging: {
SetInit: {}, // I
SetNodeAnn: {}, // N
},
}
8 changes: 7 additions & 1 deletion feature/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ type Config struct {
// forwarding experimental endorsement.
NoExperimentalEndorsement bool

// NoRbfCoopClose unsets any bits that signal support for using RBF for
// coop close.
NoRbfCoopClose bool

// CustomFeatures is a set of custom features to advertise in each
// set.
CustomFeatures map[Set][]lnwire.FeatureBit
Expand Down Expand Up @@ -209,11 +213,13 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) {
raw.Unset(lnwire.SimpleTaprootOverlayChansOptional)
raw.Unset(lnwire.SimpleTaprootOverlayChansRequired)
}

if cfg.NoExperimentalEndorsement {
raw.Unset(lnwire.ExperimentalEndorsementOptional)
raw.Unset(lnwire.ExperimentalEndorsementRequired)
}
if cfg.NoRbfCoopClose {
raw.Unset(lnwire.RbfCoopCloseOptionalStaging)
}

for _, custom := range cfg.CustomFeatures[set] {
if custom > set.Maximum() {
Expand Down
2 changes: 1 addition & 1 deletion htlcswitch/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ type FlushHookID uint64

// LinkDirection is used to query and change any link state on a per-direction
// basis.
type LinkDirection bool
type LinkDirection = bool

const (
// Incoming is the direction from the remote peer to our node.
Expand Down
9 changes: 7 additions & 2 deletions htlcswitch/switch.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package htlcswitch

import (
"bytes"
"context"
"errors"
"fmt"
"math/rand"
Expand Down Expand Up @@ -125,6 +126,9 @@ type ChanClose struct {

// Err is used by request creator to receive request execution error.
Err chan error

// Ctx is a context linked to the lifetime of the caller.
Ctx context.Context //nolint:containedctx
}

// Config defines the configuration for the service. ALL elements within the
Expand Down Expand Up @@ -1413,7 +1417,7 @@ func (s *Switch) teardownCircuit(pkt *htlcPacket) error {
// targetFeePerKw parameter should be the ideal fee-per-kw that will be used as
// a starting point for close negotiation. The deliveryScript parameter is an
// optional parameter which sets a user specified script to close out to.
func (s *Switch) CloseLink(chanPoint *wire.OutPoint,
func (s *Switch) CloseLink(ctx context.Context, chanPoint *wire.OutPoint,
closeType contractcourt.ChannelCloseType,
targetFeePerKw, maxFee chainfee.SatPerKWeight,
deliveryScript lnwire.DeliveryAddress) (chan interface{}, chan error) {
Expand All @@ -1427,9 +1431,10 @@ func (s *Switch) CloseLink(chanPoint *wire.OutPoint,
ChanPoint: chanPoint,
Updates: updateChan,
TargetFeePerKw: targetFeePerKw,
MaxFee: maxFee,
DeliveryScript: deliveryScript,
Err: errChan,
MaxFee: maxFee,
Ctx: ctx,
}

select {
Expand Down
51 changes: 51 additions & 0 deletions input/fuzz_script_is_op_return_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package input

import (
"testing"

"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/txscript"
"github.com/stretchr/testify/require"
)

// FuzzScriptIsOpReturn fuzzes the ScriptIsOpReturn function.
func FuzzScriptIsOpReturn(f *testing.F) {
// Seed the corpus with some representative inputs.
f.Add([]byte{})
f.Add([]byte{txscript.OP_RETURN})

// Use our canonical ScriptBuilder to produce a valid OP_RETURN script.
builder := txscript.NewScriptBuilder()
builder.AddOp(txscript.OP_RETURN)
builder.AddData([]byte("valid data"))
script, err := builder.Script()
require.NoError(f, err)
f.Add(script)

// An example of a script that does not start with OP_RETURN.
f.Add([]byte{txscript.OP_DUP, txscript.OP_RETURN})

f.Fuzz(func(t *testing.T, pkScript []byte) {
result := ScriptIsOpReturn(pkScript)

var expected bool
if len(script) == 0 || script[0] != txscript.OP_RETURN {
expected = false
} else {
scriptClass, _, _, err := txscript.ExtractPkScriptAddrs(
pkScript, &chaincfg.MainNetParams,
)
if err != nil {
t.Fatalf("unable to extract pk script "+
"addresses: %v", err)
}

expected = scriptClass == txscript.NullDataTy
}

if result != expected {
t.Fatalf("for script %x, expected ScriptIsOpReturn=%v;"+
" got %v", script, expected, result)
}
})
}
33 changes: 33 additions & 0 deletions input/script_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3239,3 +3239,36 @@ func ComputeCommitmentPoint(commitSecret []byte) *btcec.PublicKey {
_, pubKey := btcec.PrivKeyFromBytes(commitSecret)
return pubKey
}

// ScriptIsOpReturn returns true if the passed script is an OP_RETURN script.
//
// Lifted from the txscript package:
// https://github.com/btcsuite/btcd/blob/cc26860b40265e1332cca8748c5dbaf3c81cc094/txscript/standard.go#L493-L526.
//
//nolint:ll
func ScriptIsOpReturn(script []byte) bool {
// A null script is of the form:
// OP_RETURN <optional data>
//
// Thus, it can either be a single OP_RETURN or an OP_RETURN followed by
// a data push up to MaxDataCarrierSize bytes.

// The script can't possibly be a null data script if it doesn't start
// with OP_RETURN. Fail fast to avoid more work below.
if len(script) < 1 || script[0] != txscript.OP_RETURN {
return false
}

// Single OP_RETURN.
if len(script) == 1 {
return true
}

// OP_RETURN followed by data push up to MaxDataCarrierSize bytes.
tokenizer := txscript.MakeScriptTokenizer(0, script[1:])

return tokenizer.Next() && tokenizer.Done() &&
(txscript.IsSmallInt(tokenizer.Opcode()) ||
tokenizer.Opcode() <= txscript.OP_PUSHDATA4) &&
len(tokenizer.Data()) <= txscript.MaxDataCarrierSize
}
4 changes: 4 additions & 0 deletions itest/list_on_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,10 @@ var allTestCases = []*lntest.TestCase{
Name: "access perm",
TestFunc: testAccessPerm,
},
{
Name: "rbf coop close",
TestFunc: testCoopCloseRbf,
},
}

// appendPrefixed is used to add a prefix to each test name in the subtests
Expand Down
Loading
Loading