Skip to content

Commit

Permalink
multi: Add itest for funding timeout
Browse files Browse the repository at this point in the history
This commit adds an integration test that
verifies the funding timeout behavior in the
funding manager, in dev/integration test.
Signed-off-by: Nishant Bansal <[email protected]>
  • Loading branch information
NishantBansal2003 committed Feb 27, 2025
1 parent 5fe900d commit 98e91b7
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 8 deletions.
21 changes: 19 additions & 2 deletions funding/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/labels"
"github.com/lightningnetwork/lnd/lncfg"
"github.com/lightningnetwork/lnd/lnpeer"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnutils"
Expand Down Expand Up @@ -339,6 +340,11 @@ type DevConfig struct {
// remote node's channel ready message once the channel as been marked
// as `channelReadySent`.
ProcessChannelReadyWait time.Duration

// MaxWaitNumBlocksFundingConf is the maximum number of blocks
// to wait for the funding transaction to be confirmed before forgetting
// channels that aren't initiated by us.
MaxWaitNumBlocksFundingConf int
}

// Config defines the configuration for the FundingManager. All elements
Expand Down Expand Up @@ -3164,9 +3170,20 @@ func (f *Manager) waitForTimeout(completeChan *channeldb.OpenChannel,

defer epochClient.Cancel()

// For the waitBlocksForFundingConf different values are set
// in case we are in a dev environment so enhance test
// capabilities.
waitBlocksForFundingConf := MaxWaitNumBlocksFundingConf

// Get the waitBlocksForFundingConf. If we are not in
// development mode, this would be nil.
if lncfg.IsDevBuild() {
waitBlocksForFundingConf = f.cfg.Dev.MaxWaitNumBlocksFundingConf
}

// On block maxHeight we will cancel the funding confirmation wait.
broadcastHeight := completeChan.BroadcastHeight()
maxHeight := broadcastHeight + MaxWaitNumBlocksFundingConf
maxHeight := broadcastHeight + uint32(waitBlocksForFundingConf)
for {
select {
case epoch, ok := <-epochClient.Epochs:
Expand All @@ -3182,7 +3199,7 @@ func (f *Manager) waitForTimeout(completeChan *channeldb.OpenChannel,
log.Warnf("Waited for %v blocks without "+
"seeing funding transaction confirmed,"+
" cancelling.",
MaxWaitNumBlocksFundingConf)
waitBlocksForFundingConf)

// Notify the caller of the timeout.
close(timeoutChan)
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 @@ -670,6 +670,10 @@ var allTestCases = []*lntest.TestCase{
Name: "fee replacement",
TestFunc: testFeeReplacement,
},
{
Name: "funding manager funding timeout",
TestFunc: testFundingManagerFundingTimeout,
},
}

// appendPrefixed is used to add a prefix to each test name in the subtests
Expand Down
54 changes: 54 additions & 0 deletions itest/lnd_open_channel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -967,3 +967,57 @@ func testOpenChannelLockedBalance(ht *lntest.HarnessTest) {
// Finally, we check to make sure the balance is unlocked again.
ht.AssertWalletLockedBalance(alice, 0)
}

// testFundingManagerFundingTimeout tests that after an OpenChannel, and before
// the funding transaction is confirmed, if a user is not the channel initiator
// ,the channel is forgotten after waitBlocksForFundingConf.
func testFundingManagerFundingTimeout(ht *lntest.HarnessTest) {
// Set the maximum wait blocks for funding confirmation.
waitBlocksForFundingConf := 10

// Create nodes for testing, ensuring Alice has sufficient initial
// funds.
alice := ht.NewNodeWithCoins("Alice", nil)
bob := ht.NewNode("Bob", nil)

// Restart Bob with the custom configuration for funding confirmation
// timeout.
ht.RestartNodeWithExtraArgs(bob, []string{
"--dev.maxwaitnumblocksfundingconf=10",
})

// Ensure Alice and Bob are connected.
ht.EnsureConnected(alice, bob)

// Open the channel between Alice and Bob.
// This runs through the process up until the funding transaction is
// broadcasted.
ht.OpenChannelAssertPending(alice, bob,
lntest.OpenChannelParams{
Amt: 500000,
PushAmt: 0,
})

// At this point, both nodes have a pending channel waiting for the
// funding transaction to be confirmed.
ht.AssertNumPendingOpenChannels(alice, 1)
ht.AssertNumPendingOpenChannels(bob, 1)

// We expect Bob to forget the channel after waitBlocksForFundingConf
// blocks, so mine waitBlocksForFundingConf-1, and check that it is
// still pending.
ht.MineEmptyBlocks(waitBlocksForFundingConf - 1)
ht.AssertNumPendingOpenChannels(bob, 1)

// Now mine one additional block to reach waitBlocksForFundingConf.
ht.MineEmptyBlocks(1)

// Bob should now have forgotten the channel.
ht.AssertNumPendingOpenChannels(bob, 0)

// Since Alice was the initiator, her pending channel should remain.
ht.AssertNumPendingOpenChannels(alice, 1)

// Cleanup the mempool by mining blocks.
ht.MineBlocksAndAssertNumTxes(6, 1)
}
5 changes: 5 additions & 0 deletions lncfg/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ const (
// DefaultZombieSweeperInterval is the default time interval at which
// unfinished (zombiestate) open channel flows are purged from memory.
DefaultZombieSweeperInterval = 1 * time.Minute

// DefaultMaxWaitNumBlocksFundingConf is the maximum number of blocks
// to wait for the funding transaction to confirm before forgetting
// channels that aren't initiated by us. 2016 blocks is ~2 weeks.
DefaultMaxWaitNumBlocksFundingConf = 2016
)

// CleanAndExpandPath expands environment variables and leading ~ in the
Expand Down
6 changes: 6 additions & 0 deletions lncfg/dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,9 @@ func (d *DevConfig) GetReservationTimeout() time.Duration {
func (d *DevConfig) GetZombieSweeperInterval() time.Duration {
return DefaultZombieSweeperInterval
}

// GetMaxWaitNumBlocksFundingConf returns the config value for
// `MaxWaitNumBlocksFundingConf`.
func (d *DevConfig) GetMaxWaitNumBlocksFundingConf() int {
return DefaultMaxWaitNumBlocksFundingConf
}
19 changes: 15 additions & 4 deletions lncfg/dev_integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ func IsDevBuild() bool {
//
//nolint:ll
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."`
UnsafeDisconnect bool `long:"unsafedisconnect" description:"Allows the rpcserver to intentionally disconnect from peers with open channels."`
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."`
UnsafeDisconnect bool `long:"unsafedisconnect" description:"Allows the rpcserver to intentionally disconnect from peers with open channels."`
MaxWaitNumBlocksFundingConf int `long:"maxwaitnumblocksfundingconf" description:"Maximum blocks to wait for funding confirmation before discarding non-initiated channels."`
}

// ChannelReadyWait returns the config value `ProcessChannelReadyWait`.
Expand Down Expand Up @@ -54,3 +55,13 @@ func (d *DevConfig) GetZombieSweeperInterval() time.Duration {
func (d *DevConfig) GetUnsafeDisconnect() bool {
return d.UnsafeDisconnect
}

// GetMaxWaitNumBlocksFundingConf returns the config value for
// `MaxWaitNumBlocksFundingConf`.
func (d *DevConfig) GetMaxWaitNumBlocksFundingConf() int {
if d.MaxWaitNumBlocksFundingConf == 0 {
return DefaultMaxWaitNumBlocksFundingConf
}

return d.MaxWaitNumBlocksFundingConf
}
16 changes: 14 additions & 2 deletions rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -3850,9 +3850,21 @@ func (r *rpcServer) fetchPendingOpenChannels() (pendingOpenChannels, error) {
commitBaseWeight := blockchain.GetTransactionWeight(utx)
commitWeight := commitBaseWeight + witnessWeight

// For the waitBlocksForFundingConf different values are set
// in case we are in a dev environment so enhance test
// capabilities.
waitBlocksForFundingConf := funding.MaxWaitNumBlocksFundingConf

// Get the waitBlocksForFundingConf. If we are not in
// development mode, this would be nil.
if lncfg.IsDevBuild() {
waitBlocksForFundingConf = r.cfg.Dev.
GetMaxWaitNumBlocksFundingConf()
}

// FundingExpiryBlocks is the distance from the current block
// height to the broadcast height + MaxWaitNumBlocksFundingConf.
maxFundingHeight := funding.MaxWaitNumBlocksFundingConf +
// height to the broadcast height + waitBlocksForFundingConf.
maxFundingHeight := uint32(waitBlocksForFundingConf) +
pendingChan.BroadcastHeight()
fundingExpiryBlocks := int32(maxFundingHeight) - currentHeight

Expand Down
2 changes: 2 additions & 0 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1449,6 +1449,8 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
if lncfg.IsDevBuild() {
devCfg = &funding.DevConfig{
ProcessChannelReadyWait: cfg.Dev.ChannelReadyWait(),
MaxWaitNumBlocksFundingConf: cfg.Dev.
GetMaxWaitNumBlocksFundingConf(),
}

reservationTimeout = cfg.Dev.GetReservationTimeout()
Expand Down

0 comments on commit 98e91b7

Please sign in to comment.