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
17 changes: 15 additions & 2 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -1944,7 +1944,8 @@ func (c *Config) ImplementationConfig(
c, ltndLog, interceptor,
c.RemoteSigner.MigrateWatchOnly,
)
return &ImplementationCfg{

implCfg := &ImplementationCfg{
GrpcRegistrar: rpcImpl,
RestRegistrar: rpcImpl,
ExternalValidator: rpcImpl,
Expand All @@ -1954,17 +1955,29 @@ func (c *Config) ImplementationConfig(
WalletConfigBuilder: rpcImpl,
ChainControlBuilder: rpcImpl,
}

if c.Dev.NeedMockAuxChanCloser() {
//nolint:ll
implCfg.AuxChanCloser = c.Dev.GetMockAuxChanCloserValueForTest()
}

return implCfg
}

defaultImpl := NewDefaultWalletImpl(c, ltndLog, interceptor, false)
return &ImplementationCfg{
implCfg := &ImplementationCfg{
GrpcRegistrar: defaultImpl,
RestRegistrar: defaultImpl,
ExternalValidator: defaultImpl,
DatabaseBuilder: NewDefaultDatabaseBuilder(c, ltndLog),
WalletConfigBuilder: defaultImpl,
ChainControlBuilder: defaultImpl,
}
if c.Dev.NeedMockAuxChanCloser() {
implCfg.AuxChanCloser = c.Dev.GetMockAuxChanCloserValueForTest()
}
Comment on lines +1976 to +1978
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This if block is a duplicate of the one on lines 1959-1962. To improve maintainability, you could refactor ImplementationConfig to avoid this code repetition. One way is to declare implCfg before the main if/else block, initialize it within each branch, and then have a single block to set AuxChanCloser before returning.


return implCfg
}

// CleanAndExpandPath expands environment variables and leading ~ in the
Expand Down
2 changes: 2 additions & 0 deletions docs/release-notes/release-notes-0.22.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@

## Functional Enhancements

* [Extend the RBF cooperative close flow to support auxiliary channel closers](https://github.com/lightningnetwork/lnd/pull/10817) by propagating shutdown custom records, deriving and preserving auxiliary close outputs across fee updates, exposing final close output details in close notifications.

## RPC Additions

* The `routerrpc.EstimateRouteFee` RPC now supports [restricting fee estimates
Expand Down
4 changes: 4 additions & 0 deletions itest/list_on_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,10 @@ var allTestCases = []*lntest.TestCase{
Name: "rbf coop close",
TestFunc: testCoopCloseRbf,
},
{
Name: "rbf coop close with aux close outputs",
TestFunc: testCoopCloseRbfWithAuxCloseOutputs,
},
{
Name: "rbf coop close disconnect",
TestFunc: testRBFCoopCloseDisconnect,
Expand Down
79 changes: 79 additions & 0 deletions itest/lnd_coop_close_rbf_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package itest

import (
"bytes"
"fmt"
"testing"

"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lntest"
"github.com/lightningnetwork/lnd/lntest/node"
Expand Down Expand Up @@ -206,6 +208,83 @@ func testCoopCloseRbf(ht *lntest.HarnessTest) {
}
}

// testCoopCloseRbfWithAuxCloseOutputs tests that
// AuxCloseOutputs are included in the TxOuts when AuxChanCloser exists
// and are preserved across fee updates.
func testCoopCloseRbfWithAuxCloseOutputs(ht *lntest.HarnessTest) {
ht.SetFeeEstimate(250)
ht.SetFeeEstimateWithConf(250, 6)

rbfCoopFlags := []string{
"--protocol.rbf-coop-close",
"--dev.mock-aux-chan-closer"}
params := lntest.OpenChannelParams{
Amt: btcutil.Amount(10_000_000),
PushAmt: btcutil.Amount(5_000_000),
}
cfgs := [][]string{rbfCoopFlags, rbfCoopFlags}
chanPoints, nodes := ht.CreateSimpleNetwork(cfgs, params)
alice, bob := nodes[0], nodes[1]
chanPoint := chanPoints[0]

aliceFeeRate := chainfee.SatPerVByte(5)
aliceCloseStream, aliceCloseUpdate := ht.CloseChannelAssertPending(
alice, chanPoint, false,
lntest.WithCoopCloseFeeRate(aliceFeeRate),
lntest.WithLocalTxNotify(),
)

alicePendingUpdate := aliceCloseUpdate.GetClosePending()
checkAdditionalOutputs(ht, chainhash.Hash(alicePendingUpdate.Txid))

bobFeeRate := aliceFeeRate * 2
_, bobCloseUpdate := ht.CloseChannelAssertPending(
bob, chanPoint, false, lntest.WithCoopCloseFeeRate(bobFeeRate),
lntest.WithLocalTxNotify(),
)

bobPendingUpdate := bobCloseUpdate.GetClosePending()
checkAdditionalOutputs(ht, chainhash.Hash(bobPendingUpdate.Txid))

aliceCloseUpdate, err := ht.ReceiveCloseChannelUpdate(aliceCloseStream)
require.NoError(ht, err)
alicePendingUpdate = aliceCloseUpdate.GetClosePending()
checkAdditionalOutputs(ht, chainhash.Hash(alicePendingUpdate.Txid))

block := ht.MineBlocksAndAssertNumTxes(1, 1)[0]

aliceClosingTxid := ht.WaitForChannelCloseEvent(aliceCloseStream)
checkAdditionalOutputs(ht, aliceClosingTxid)
ht.AssertTxInBlock(block, aliceClosingTxid)
}

func checkAdditionalOutputs(ht *lntest.HarnessTest, txid chainhash.Hash) {
tx := ht.Miner().GetRawTransaction(txid)
txOuts := tx.MsgTx().TxOut
require.Equal(ht, 3, len(txOuts))

expectedTxOut := wire.TxOut{
Value: 50_000,
PkScript: []byte{
0x00, 0x14, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11,
},
}
contains := false
for _, txOut := range txOuts {
if txOut.Value == expectedTxOut.Value &&
bytes.Equal(
txOut.PkScript,
expectedTxOut.PkScript,
) {

contains = true
}
}
require.True(ht, contains)
}

// testRBFCoopCloseDisconnect tests that when a node disconnects that the node
// is properly disconnected.
func testRBFCoopCloseDisconnect(ht *lntest.HarnessTest) {
Expand Down
13 changes: 13 additions & 0 deletions lncfg/dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"time"

"github.com/lightningnetwork/lnd/fn/v2"
"github.com/lightningnetwork/lnd/lnwallet/chancloser"
"github.com/lightningnetwork/lnd/lnwallet/chanfunding"
)

Expand All @@ -21,6 +22,18 @@ func IsDevBuild() bool {
// should always remain empty.
type DevConfig struct{}

// NeedMockAuxChanCloser returns the config value for MockAuxChanCloser,
// which is always false for production build.
func (d *DevConfig) NeedMockAuxChanCloser() bool {
return false
}

// GetMockAuxChanCloserValueForTest returns the mock AuxChanCloser value
// which is always None for production build
func (d *DevConfig) GetMockAuxChanCloserValueForTest() fn.Option[chancloser.AuxChanCloser] {
return fn.None[chancloser.AuxChanCloser]()
}

// ChannelReadyWait returns the config value, which is always 0 for production
// build.
func (d *DevConfig) ChannelReadyWait() time.Duration {
Expand Down
62 changes: 62 additions & 0 deletions lncfg/dev_integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ package lncfg
import (
"time"

"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/fn/v2"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/chancloser"
"github.com/lightningnetwork/lnd/lnwallet/chanfunding"
"github.com/lightningnetwork/lnd/lnwallet/types"
"github.com/lightningnetwork/lnd/lnwire"
)

// IsDevBuild returns a bool to indicate whether we are in a development
Expand All @@ -30,6 +35,63 @@ type DevConfig struct {
UnsafeConnect bool `long:"unsafeconnect" description:"Allow the rpcserver to connect to a peer even if there's already a connection."`
ForceChannelCloseConfs uint32 `long:"force-channel-close-confs" description:"Force a specific number of confirmations for channel closes (dev/test only)"`
MinFwdHistoryAge time.Duration `long:"min-fwd-history-age" description:"Minimum age of forwarding events before they can be deleted via DeleteForwardingHistory (dev/test only, default: 1h)"`
MockAuxChanCloser bool `long:"mock-aux-chan-closer" description:"Set the mock AuxChanCloser for tests."`
}

// NeedMockAuxChanCloser returns the config value for MockAuxChanCloser,
// indicating whether the integration test needs a mock AuxChanCloser.
func (d *DevConfig) NeedMockAuxChanCloser() bool {
return d.MockAuxChanCloser
}

// GetMockAuxChanCloserValueForTest returns the mock AuxChanCloser value
// that can be used in integration tests.
//
//nolint:ll
func (d *DevConfig) GetMockAuxChanCloserValueForTest() fn.Option[chancloser.AuxChanCloser] {
return fn.Some[chancloser.AuxChanCloser](&mockAuxChanCloser{})
}

// Mock implementation for AuxChanCloser.
type mockAuxChanCloser struct{}

func (m *mockAuxChanCloser) ShutdownBlob(
req types.AuxShutdownReq,
) (fn.Option[lnwire.CustomRecords], error) {

return fn.None[lnwire.CustomRecords](), nil
}

func (m *mockAuxChanCloser) AuxCloseOutputs(
desc types.AuxCloseDesc,
) (fn.Option[chancloser.AuxCloseOutputs], error) {

return fn.Some[chancloser.AuxCloseOutputs](
chancloser.AuxCloseOutputs{
ExtraCloseOutputs: []lnwallet.CloseOutput{
{
TxOut: wire.TxOut{
Value: 50_000,
PkScript: []byte{
0x00, 0x14, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11,
0x11, 0x11,
},
},
Comment on lines +73 to +83
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This hardcoded TxOut is also defined in itest/lnd_coop_close_rbf_test.go and lnwallet/chancloser/rbf_coop_test.go. To improve maintainability and avoid inconsistencies, consider defining this as a shared constant or variable in a test helper package that can be imported by all three files.

IsLocal: desc.Initiator,
},
},
},
), nil
}

func (m *mockAuxChanCloser) FinalizeClose(desc types.AuxCloseDesc,
closeTx *wire.MsgTx) error {

return nil
}

// ChannelReadyWait returns the config value `ProcessChannelReadyWait`.
Expand Down
1 change: 1 addition & 0 deletions lnwallet/chancloser/rbf_coop_msg_mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ func (r *RbfMsgMapper) MapMsg(wireMsg msgmux.PeerMsg) fn.Option[ProtocolEvent] {
return someEvent(&ShutdownReceived{
BlockHeight: r.bestHeight(),
ShutdownScript: msg.Address,
CustomRecords: msg.CustomRecords,
RemoteShutdownNonce: remoteShutdownNonce,
})

Expand Down
Loading
Loading