Skip to content

Commit ea050d0

Browse files
authored
Merge pull request #9610 from lightningnetwork/rbf-staging
multi: integrate rbf changes from staging branch
2 parents de1ed93 + 42fa837 commit ea050d0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+5863
-2756
lines changed

channeldb/channel.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4104,6 +4104,34 @@ func (c *OpenChannel) AbsoluteThawHeight() (uint32, error) {
41044104
return c.ThawHeight, nil
41054105
}
41064106

4107+
// DeriveHeightHint derives the block height for the channel opening.
4108+
func (c *OpenChannel) DeriveHeightHint() uint32 {
4109+
// As a height hint, we'll try to use the opening height, but if the
4110+
// channel isn't yet open, then we'll use the height it was broadcast
4111+
// at. This may be an unconfirmed zero-conf channel.
4112+
heightHint := c.ShortChanID().BlockHeight
4113+
if heightHint == 0 {
4114+
heightHint = c.BroadcastHeight()
4115+
}
4116+
4117+
// Since no zero-conf state is stored in a channel backup, the below
4118+
// logic will not be triggered for restored, zero-conf channels. Set
4119+
// the height hint for zero-conf channels.
4120+
if c.IsZeroConf() {
4121+
if c.ZeroConfConfirmed() {
4122+
// If the zero-conf channel is confirmed, we'll use the
4123+
// confirmed SCID's block height.
4124+
heightHint = c.ZeroConfRealScid().BlockHeight
4125+
} else {
4126+
// The zero-conf channel is unconfirmed. We'll need to
4127+
// use the FundingBroadcastHeight.
4128+
heightHint = c.BroadcastHeight()
4129+
}
4130+
}
4131+
4132+
return heightHint
4133+
}
4134+
41074135
func putChannelCloseSummary(tx kvdb.RwTx, chanID []byte,
41084136
summary *ChannelCloseSummary, lastChanState *OpenChannel) error {
41094137

contractcourt/chain_watcher.go

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ func newChainWatcher(cfg chainWatcherConfig) (*chainWatcher, error) {
281281
}
282282

283283
// Get the channel opening block height.
284-
heightHint := deriveHeightHint(chanState)
284+
heightHint := chanState.DeriveHeightHint()
285285

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

1331-
// deriveHeightHint derives the block height for the channel opening.
1332-
func deriveHeightHint(chanState *channeldb.OpenChannel) uint32 {
1333-
// As a height hint, we'll try to use the opening height, but if the
1334-
// channel isn't yet open, then we'll use the height it was broadcast
1335-
// at. This may be an unconfirmed zero-conf channel.
1336-
heightHint := chanState.ShortChanID().BlockHeight
1337-
if heightHint == 0 {
1338-
heightHint = chanState.BroadcastHeight()
1339-
}
1340-
1341-
// Since no zero-conf state is stored in a channel backup, the below
1342-
// logic will not be triggered for restored, zero-conf channels. Set
1343-
// the height hint for zero-conf channels.
1344-
if chanState.IsZeroConf() {
1345-
if chanState.ZeroConfConfirmed() {
1346-
// If the zero-conf channel is confirmed, we'll use the
1347-
// confirmed SCID's block height.
1348-
heightHint = chanState.ZeroConfRealScid().BlockHeight
1349-
} else {
1350-
// The zero-conf channel is unconfirmed. We'll need to
1351-
// use the FundingBroadcastHeight.
1352-
heightHint = chanState.BroadcastHeight()
1353-
}
1354-
}
1355-
1356-
return heightHint
1357-
}
1358-
13591331
// handleCommitSpend takes a spending tx of the funding output and handles the
13601332
// channel close based on the closure type.
13611333
func (c *chainWatcher) handleCommitSpend(

docs/release-notes/release-notes-0.19.0.md

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,27 @@
9393

9494
# New Features
9595

96+
* Add support for [archiving channel backup](https://github.com/lightningnetwork/lnd/pull/9232)
97+
in a designated folder which allows for easy referencing in the future. A new
98+
config is added `disable-backup-archive`, with default set to false, to
99+
determine if previous channel backups should be archived or not.
100+
101+
## Protocol Updates
102+
103+
* `lnd` now [supports the new RBF cooperative close
104+
flow](https://github.com/lightningnetwork/lnd/pull/9610). Unlike the old flow,
105+
this version now uses RBF to enable either side to increase their fee rate using
106+
their _own_ channel funds. This removes the old "negotiation" logic that could
107+
fail, with a version where either side can increase the fee on their coop close
108+
transaction using their channel balance.
109+
110+
This new feature can be activated with a new config flag:
111+
`--protocol.rbf-coop-close`.
112+
113+
With this new co-op close type, users can issue multiple `lncli closechannnel`
114+
commands with increasing fee rates to use RBF to bump an existing signed co-op
115+
close transaction.
116+
96117
* [Support](https://github.com/lightningnetwork/lnd/pull/8390) for
97118
[experimental endorsement](https://github.com/lightning/blips/pull/27)
98119
signal relay was added. This signal has *no impact* on routing, and
@@ -106,10 +127,7 @@
106127
initial historical sync may be blocked due to a race condition in handling the
107128
syncer's internal state.
108129

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

114132
* [The max fee rate](https://github.com/lightningnetwork/lnd/pull/9491) is now
115133
respected when a coop close is initiated. Before the max fee rate would only
@@ -432,6 +450,7 @@ The underlying functionality between those two options remain the same.
432450
* Keagan McClelland
433451
* Nishant Bansal
434452
* Oliver Gugger
453+
* Olaoluwa Osuntokun
435454
* Pins
436455
* Viktor Tigerström
437456
* Yong Yu

feature/default_sets.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,8 @@ var defaultSetDesc = setDesc{
103103
lnwire.ExperimentalEndorsementOptional: {
104104
SetNodeAnn: {}, // N
105105
},
106+
lnwire.RbfCoopCloseOptionalStaging: {
107+
SetInit: {}, // I
108+
SetNodeAnn: {}, // N
109+
},
106110
}

feature/manager.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ type Config struct {
7373
// forwarding experimental endorsement.
7474
NoExperimentalEndorsement bool
7575

76+
// NoRbfCoopClose unsets any bits that signal support for using RBF for
77+
// coop close.
78+
NoRbfCoopClose bool
79+
7680
// CustomFeatures is a set of custom features to advertise in each
7781
// set.
7882
CustomFeatures map[Set][]lnwire.FeatureBit
@@ -209,11 +213,13 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) {
209213
raw.Unset(lnwire.SimpleTaprootOverlayChansOptional)
210214
raw.Unset(lnwire.SimpleTaprootOverlayChansRequired)
211215
}
212-
213216
if cfg.NoExperimentalEndorsement {
214217
raw.Unset(lnwire.ExperimentalEndorsementOptional)
215218
raw.Unset(lnwire.ExperimentalEndorsementRequired)
216219
}
220+
if cfg.NoRbfCoopClose {
221+
raw.Unset(lnwire.RbfCoopCloseOptionalStaging)
222+
}
217223

218224
for _, custom := range cfg.CustomFeatures[set] {
219225
if custom > set.Maximum() {

htlcswitch/interfaces.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ type FlushHookID uint64
196196

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

201201
const (
202202
// Incoming is the direction from the remote peer to our node.

htlcswitch/switch.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package htlcswitch
22

33
import (
44
"bytes"
5+
"context"
56
"errors"
67
"fmt"
78
"math/rand"
@@ -125,6 +126,9 @@ type ChanClose struct {
125126

126127
// Err is used by request creator to receive request execution error.
127128
Err chan error
129+
130+
// Ctx is a context linked to the lifetime of the caller.
131+
Ctx context.Context //nolint:containedctx
128132
}
129133

130134
// Config defines the configuration for the service. ALL elements within the
@@ -1413,7 +1417,7 @@ func (s *Switch) teardownCircuit(pkt *htlcPacket) error {
14131417
// targetFeePerKw parameter should be the ideal fee-per-kw that will be used as
14141418
// a starting point for close negotiation. The deliveryScript parameter is an
14151419
// optional parameter which sets a user specified script to close out to.
1416-
func (s *Switch) CloseLink(chanPoint *wire.OutPoint,
1420+
func (s *Switch) CloseLink(ctx context.Context, chanPoint *wire.OutPoint,
14171421
closeType contractcourt.ChannelCloseType,
14181422
targetFeePerKw, maxFee chainfee.SatPerKWeight,
14191423
deliveryScript lnwire.DeliveryAddress) (chan interface{}, chan error) {
@@ -1427,9 +1431,10 @@ func (s *Switch) CloseLink(chanPoint *wire.OutPoint,
14271431
ChanPoint: chanPoint,
14281432
Updates: updateChan,
14291433
TargetFeePerKw: targetFeePerKw,
1430-
MaxFee: maxFee,
14311434
DeliveryScript: deliveryScript,
14321435
Err: errChan,
1436+
MaxFee: maxFee,
1437+
Ctx: ctx,
14331438
}
14341439

14351440
select {
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package input
2+
3+
import (
4+
"testing"
5+
6+
"github.com/btcsuite/btcd/chaincfg"
7+
"github.com/btcsuite/btcd/txscript"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
// FuzzScriptIsOpReturn fuzzes the ScriptIsOpReturn function.
12+
func FuzzScriptIsOpReturn(f *testing.F) {
13+
// Seed the corpus with some representative inputs.
14+
f.Add([]byte{})
15+
f.Add([]byte{txscript.OP_RETURN})
16+
17+
// Use our canonical ScriptBuilder to produce a valid OP_RETURN script.
18+
builder := txscript.NewScriptBuilder()
19+
builder.AddOp(txscript.OP_RETURN)
20+
builder.AddData([]byte("valid data"))
21+
script, err := builder.Script()
22+
require.NoError(f, err)
23+
f.Add(script)
24+
25+
// An example of a script that does not start with OP_RETURN.
26+
f.Add([]byte{txscript.OP_DUP, txscript.OP_RETURN})
27+
28+
f.Fuzz(func(t *testing.T, pkScript []byte) {
29+
result := ScriptIsOpReturn(pkScript)
30+
31+
var expected bool
32+
if len(script) == 0 || script[0] != txscript.OP_RETURN {
33+
expected = false
34+
} else {
35+
scriptClass, _, _, err := txscript.ExtractPkScriptAddrs(
36+
pkScript, &chaincfg.MainNetParams,
37+
)
38+
if err != nil {
39+
t.Fatalf("unable to extract pk script "+
40+
"addresses: %v", err)
41+
}
42+
43+
expected = scriptClass == txscript.NullDataTy
44+
}
45+
46+
if result != expected {
47+
t.Fatalf("for script %x, expected ScriptIsOpReturn=%v;"+
48+
" got %v", script, expected, result)
49+
}
50+
})
51+
}

input/script_utils.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3239,3 +3239,36 @@ func ComputeCommitmentPoint(commitSecret []byte) *btcec.PublicKey {
32393239
_, pubKey := btcec.PrivKeyFromBytes(commitSecret)
32403240
return pubKey
32413241
}
3242+
3243+
// ScriptIsOpReturn returns true if the passed script is an OP_RETURN script.
3244+
//
3245+
// Lifted from the txscript package:
3246+
// https://github.com/btcsuite/btcd/blob/cc26860b40265e1332cca8748c5dbaf3c81cc094/txscript/standard.go#L493-L526.
3247+
//
3248+
//nolint:ll
3249+
func ScriptIsOpReturn(script []byte) bool {
3250+
// A null script is of the form:
3251+
// OP_RETURN <optional data>
3252+
//
3253+
// Thus, it can either be a single OP_RETURN or an OP_RETURN followed by
3254+
// a data push up to MaxDataCarrierSize bytes.
3255+
3256+
// The script can't possibly be a null data script if it doesn't start
3257+
// with OP_RETURN. Fail fast to avoid more work below.
3258+
if len(script) < 1 || script[0] != txscript.OP_RETURN {
3259+
return false
3260+
}
3261+
3262+
// Single OP_RETURN.
3263+
if len(script) == 1 {
3264+
return true
3265+
}
3266+
3267+
// OP_RETURN followed by data push up to MaxDataCarrierSize bytes.
3268+
tokenizer := txscript.MakeScriptTokenizer(0, script[1:])
3269+
3270+
return tokenizer.Next() && tokenizer.Done() &&
3271+
(txscript.IsSmallInt(tokenizer.Opcode()) ||
3272+
tokenizer.Opcode() <= txscript.OP_PUSHDATA4) &&
3273+
len(tokenizer.Data()) <= txscript.MaxDataCarrierSize
3274+
}

itest/list_on_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,10 @@ var allTestCases = []*lntest.TestCase{
678678
Name: "access perm",
679679
TestFunc: testAccessPerm,
680680
},
681+
{
682+
Name: "rbf coop close",
683+
TestFunc: testCoopCloseRbf,
684+
},
681685
}
682686

683687
// appendPrefixed is used to add a prefix to each test name in the subtests

0 commit comments

Comments
 (0)