From 8a825a468792f2a197fe05e5d74d77680ecb316b Mon Sep 17 00:00:00 2001 From: ziggie Date: Sun, 21 Jan 2024 16:30:32 +0000 Subject: [PATCH] multi: Add itest for a failed funding flow. This adds an itest for a failed funding flow by our peer. --- itest/list_on_test.go | 4 ++++ itest/lnd_psbt_test.go | 48 +++++++++++++++++++++++++++++++++++++ lncfg/config.go | 9 +++++++ lncfg/dev.go | 14 ++++++++++- lncfg/dev_integration.go | 24 ++++++++++++++++++- lntest/harness_assertion.go | 10 ++++++++ server.go | 17 +++++++++++-- 7 files changed, 122 insertions(+), 4 deletions(-) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index d5664d3df..8bf8b0923 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -550,4 +550,8 @@ var allTestCases = []*lntest.TestCase{ Name: "update pending open channels", TestFunc: testUpdateOnPendingOpenChannels, }, + { + Name: "fail funding flow psbt", + TestFunc: testPsbtChanFundingFailFlow, + }, } diff --git a/itest/lnd_psbt_test.go b/itest/lnd_psbt_test.go index 3bf1cfa04..6c9d92d60 100644 --- a/itest/lnd_psbt_test.go +++ b/itest/lnd_psbt_test.go @@ -21,6 +21,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntest/node" + "github.com/lightningnetwork/lnd/lnwallet/chanfunding" "github.com/stretchr/testify/require" ) @@ -1340,3 +1341,50 @@ func sendAllCoinsToAddrType(ht *lntest.HarnessTest, ht.MineBlocksAndAssertNumTxes(1, 1) ht.WaitForBlockchainSync(hn) } + +// testPsbtChanFundingFailFlow tests the failing of a funding flow by the +// remote peer and that the initiator receives the expected error and aborts +// the channel opening. The psbt funding flow is used to simulate this behavior +// because we can easily let the remote peer run into the timeout. +func testPsbtChanFundingFailFlow(ht *lntest.HarnessTest) { + const chanSize = funding.MaxBtcFundingAmount + + // Decrease the timeout window for the remote peer to accelerate the + // funding fail process. + args := []string{ + "--dev.reservationtimeout=1s", + "--dev.zombiesweeperinterval=1s", + } + ht.RestartNodeWithExtraArgs(ht.Bob, args) + + // Before we start the test, we'll ensure both sides are connected so + // the funding flow can be properly executed. + alice := ht.Alice + bob := ht.Bob + ht.EnsureConnected(alice, bob) + + // At this point, we can begin our PSBT channel funding workflow. We'll + // start by generating a pending channel ID externally that will be used + // to track this new funding type. + pendingChanID := ht.Random32Bytes() + + // Now that we have the pending channel ID, Alice will open the channel + // by specifying a PSBT shim. + chanUpdates, _ := ht.OpenChannelPsbt( + alice, bob, lntest.OpenChannelParams{ + Amt: chanSize, + FundingShim: &lnrpc.FundingShim{ + Shim: &lnrpc.FundingShim_PsbtShim{ + PsbtShim: &lnrpc.PsbtShim{ + PendingChanId: pendingChanID, + }, + }, + }, + }, + ) + + // We received the AcceptChannel msg from our peer but we are not going + // to fund this channel but instead wait for our peer to fail the + // funding workflow with an internal error. + ht.ReceiveOpenChannelError(chanUpdates, chanfunding.ErrRemoteCanceled) +} diff --git a/lncfg/config.go b/lncfg/config.go index 2f3e014c8..07c37875d 100644 --- a/lncfg/config.go +++ b/lncfg/config.go @@ -5,6 +5,7 @@ import ( "os/user" "path/filepath" "strings" + "time" ) const ( @@ -67,6 +68,14 @@ const ( // peer and a block arriving during that round trip to trigger force // closure. DefaultOutgoingCltvRejectDelta = DefaultOutgoingBroadcastDelta + 3 + + // DefaultReservationTimeout is the default time we wait until we remove + // an unfinished (zombiestate) open channel flow from memory. + DefaultReservationTimeout = 10 * time.Minute + + // DefaultZombieSweeperInterval is the default time interval at which + // unfinished (zombiestate) open channel flows are purged from memory. + DefaultZombieSweeperInterval = 1 * time.Minute ) // CleanAndExpandPath expands environment variables and leading ~ in the diff --git a/lncfg/dev.go b/lncfg/dev.go index ac47b0f5e..0bc305cff 100644 --- a/lncfg/dev.go +++ b/lncfg/dev.go @@ -2,7 +2,9 @@ package lncfg -import "time" +import ( + "time" +) // IsDevBuild returns a bool to indicate whether we are in a development // environment. @@ -21,3 +23,13 @@ type DevConfig struct{} func (d *DevConfig) ChannelReadyWait() time.Duration { return 0 } + +// GetReservationTimeout returns the config value for `ReservationTimeout`. +func (d *DevConfig) GetReservationTimeout() time.Duration { + return DefaultReservationTimeout +} + +// GetZombieSweeperInterval returns the config value for`ZombieSweeperInterval`. +func (d *DevConfig) GetZombieSweeperInterval() time.Duration { + return DefaultZombieSweeperInterval +} diff --git a/lncfg/dev_integration.go b/lncfg/dev_integration.go index c55b2efa7..526c86b6a 100644 --- a/lncfg/dev_integration.go +++ b/lncfg/dev_integration.go @@ -2,7 +2,9 @@ package lncfg -import "time" +import ( + "time" +) // IsDevBuild returns a bool to indicate whether we are in a development // environment. @@ -18,9 +20,29 @@ func IsDevBuild() bool { //nolint:lll type DevConfig struct { ProcessChannelReadyWait time.Duration `long:"processchannelreadywait" description:"Time to sleep before processing remote node's channel_ready message."` + ReservationTimeout time.Duration `long:"reservationtimeout" description:"The maximum time we keep a pending channel open flow in memory."` + ZombieSweeperInterval time.Duration `long:"zombiesweeperinterval" description:"The time interval at which channel opening flows are evaluated for zombie status."` } // ChannelReadyWait returns the config value `ProcessChannelReadyWait`. func (d *DevConfig) ChannelReadyWait() time.Duration { return d.ProcessChannelReadyWait } + +// GetReservationTimeout returns the config value for `ReservationTimeout`. +func (d *DevConfig) GetReservationTimeout() time.Duration { + if d.ReservationTimeout == 0 { + return DefaultReservationTimeout + } + + return d.ReservationTimeout +} + +// GetZombieSweeperInterval returns the config value for`ZombieSweeperInterval`. +func (d *DevConfig) GetZombieSweeperInterval() time.Duration { + if d.ZombieSweeperInterval == 0 { + return DefaultZombieSweeperInterval + } + + return d.ZombieSweeperInterval +} diff --git a/lntest/harness_assertion.go b/lntest/harness_assertion.go index 6ead7d277..cb65d55a6 100644 --- a/lntest/harness_assertion.go +++ b/lntest/harness_assertion.go @@ -288,6 +288,16 @@ func (h *HarnessTest) ReceiveOpenChannelUpdate( return update } +// ReceiveOpenChannelError waits for the expected error during the open channel +// flow from the peer or times out. +func (h *HarnessTest) ReceiveOpenChannelError( + stream rpc.OpenChanClient, expectedErr error) { + + _, err := h.receiveOpenChannelUpdate(stream) + require.Contains(h, err.Error(), expectedErr.Error(), + "error not matched") +} + // receiveOpenChannelUpdate waits until a message or an error is received on // the stream or the timeout is reached. // diff --git a/server.go b/server.go index b59f873cf..e371da5ac 100644 --- a/server.go +++ b/server.go @@ -1277,6 +1277,12 @@ func newServer(cfg *Config, listenAddrs []net.Addr, return ourPolicy, err } + // For the reservationTimeout and the zombieSweeperInterval different + // values are set in case we are in a dev environment so enhance test + // capacilities. + reservationTimeout := lncfg.DefaultReservationTimeout + zombieSweeperInterval := lncfg.DefaultZombieSweeperInterval + // Get the development config for funding manager. If we are not in // development mode, this would be nil. var devCfg *funding.DevConfig @@ -1284,6 +1290,13 @@ func newServer(cfg *Config, listenAddrs []net.Addr, devCfg = &funding.DevConfig{ ProcessChannelReadyWait: cfg.Dev.ChannelReadyWait(), } + + reservationTimeout = cfg.Dev.GetReservationTimeout() + zombieSweeperInterval = cfg.Dev.GetZombieSweeperInterval() + + srvrLog.Debugf("Using the dev config for the fundingMgr: %v, "+ + "reservationTimeout=%v, zombieSweeperInterval=%v", + devCfg, reservationTimeout, zombieSweeperInterval) } //nolint:lll @@ -1446,8 +1459,8 @@ func newServer(cfg *Config, listenAddrs []net.Addr, // channel bandwidth. return uint16(input.MaxHTLCNumber / 2) }, - ZombieSweeperInterval: 1 * time.Minute, - ReservationTimeout: 10 * time.Minute, + ZombieSweeperInterval: zombieSweeperInterval, + ReservationTimeout: reservationTimeout, MinChanSize: btcutil.Amount(cfg.MinChanSize), MaxChanSize: btcutil.Amount(cfg.MaxChanSize), MaxPendingChannels: cfg.MaxPendingChannels,