mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-04 01:36:24 +01:00
multi: Add itest for funding timeout
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 <nishant.bansal.282003@gmail.com>
This commit is contained in:
parent
5fe900d18d
commit
49d3fcf5b2
9 changed files with 131 additions and 22 deletions
|
@ -29,6 +29,7 @@ import (
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/keychain"
|
"github.com/lightningnetwork/lnd/keychain"
|
||||||
"github.com/lightningnetwork/lnd/labels"
|
"github.com/lightningnetwork/lnd/labels"
|
||||||
|
"github.com/lightningnetwork/lnd/lncfg"
|
||||||
"github.com/lightningnetwork/lnd/lnpeer"
|
"github.com/lightningnetwork/lnd/lnpeer"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnutils"
|
"github.com/lightningnetwork/lnd/lnutils"
|
||||||
|
@ -101,11 +102,6 @@ const (
|
||||||
|
|
||||||
msgBufferSize = 50
|
msgBufferSize = 50
|
||||||
|
|
||||||
// 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. 2016 blocks is ~2 weeks.
|
|
||||||
MaxWaitNumBlocksFundingConf = 2016
|
|
||||||
|
|
||||||
// pendingChansLimit is the maximum number of pending channels that we
|
// pendingChansLimit is the maximum number of pending channels that we
|
||||||
// can have. After this point, pending channel opens will start to be
|
// can have. After this point, pending channel opens will start to be
|
||||||
// rejected.
|
// rejected.
|
||||||
|
@ -339,6 +335,11 @@ type DevConfig struct {
|
||||||
// remote node's channel ready message once the channel as been marked
|
// remote node's channel ready message once the channel as been marked
|
||||||
// as `channelReadySent`.
|
// as `channelReadySent`.
|
||||||
ProcessChannelReadyWait time.Duration
|
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 uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config defines the configuration for the FundingManager. All elements
|
// Config defines the configuration for the FundingManager. All elements
|
||||||
|
@ -3164,9 +3165,20 @@ func (f *Manager) waitForTimeout(completeChan *channeldb.OpenChannel,
|
||||||
|
|
||||||
defer epochClient.Cancel()
|
defer epochClient.Cancel()
|
||||||
|
|
||||||
|
// For the waitBlocksForFundingConf different values are set in case we
|
||||||
|
// are in a dev environment so enhance test capabilities.
|
||||||
|
var waitBlocksForFundingConf uint32 = lncfg.
|
||||||
|
DefaultMaxWaitNumBlocksFundingConf
|
||||||
|
|
||||||
|
// Get the waitBlocksForFundingConf. If we are not in development mode,
|
||||||
|
// this would be DefaultMaxWaitNumBlocksFundingConf.
|
||||||
|
if lncfg.IsDevBuild() {
|
||||||
|
waitBlocksForFundingConf = f.cfg.Dev.MaxWaitNumBlocksFundingConf
|
||||||
|
}
|
||||||
|
|
||||||
// On block maxHeight we will cancel the funding confirmation wait.
|
// On block maxHeight we will cancel the funding confirmation wait.
|
||||||
broadcastHeight := completeChan.BroadcastHeight()
|
broadcastHeight := completeChan.BroadcastHeight()
|
||||||
maxHeight := broadcastHeight + MaxWaitNumBlocksFundingConf
|
maxHeight := broadcastHeight + waitBlocksForFundingConf
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case epoch, ok := <-epochClient.Epochs:
|
case epoch, ok := <-epochClient.Epochs:
|
||||||
|
@ -3182,7 +3194,7 @@ func (f *Manager) waitForTimeout(completeChan *channeldb.OpenChannel,
|
||||||
log.Warnf("Waited for %v blocks without "+
|
log.Warnf("Waited for %v blocks without "+
|
||||||
"seeing funding transaction confirmed,"+
|
"seeing funding transaction confirmed,"+
|
||||||
" cancelling.",
|
" cancelling.",
|
||||||
MaxWaitNumBlocksFundingConf)
|
waitBlocksForFundingConf)
|
||||||
|
|
||||||
// Notify the caller of the timeout.
|
// Notify the caller of the timeout.
|
||||||
close(timeoutChan)
|
close(timeoutChan)
|
||||||
|
|
|
@ -2334,14 +2334,15 @@ func TestFundingManagerFundingTimeout(t *testing.T) {
|
||||||
// mine 2016-1, and check that it is still pending.
|
// mine 2016-1, and check that it is still pending.
|
||||||
bob.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
bob.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
||||||
Height: fundingBroadcastHeight +
|
Height: fundingBroadcastHeight +
|
||||||
MaxWaitNumBlocksFundingConf - 1,
|
lncfg.DefaultMaxWaitNumBlocksFundingConf - 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bob should still be waiting for the channel to open.
|
// Bob should still be waiting for the channel to open.
|
||||||
assertNumPendingChannelsRemains(t, bob, 1)
|
assertNumPendingChannelsRemains(t, bob, 1)
|
||||||
|
|
||||||
bob.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
bob.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
||||||
Height: fundingBroadcastHeight + MaxWaitNumBlocksFundingConf,
|
Height: fundingBroadcastHeight +
|
||||||
|
lncfg.DefaultMaxWaitNumBlocksFundingConf,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bob should have sent an Error message to Alice.
|
// Bob should have sent an Error message to Alice.
|
||||||
|
@ -2387,16 +2388,16 @@ func TestFundingManagerFundingNotTimeoutInitiator(t *testing.T) {
|
||||||
t.Fatalf("alice did not publish funding tx")
|
t.Fatalf("alice did not publish funding tx")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increase the height to 1 minus the MaxWaitNumBlocksFundingConf
|
// Increase the height to 1 minus the DefaultMaxWaitNumBlocksFundingConf
|
||||||
// height.
|
// height.
|
||||||
alice.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
alice.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
||||||
Height: fundingBroadcastHeight +
|
Height: fundingBroadcastHeight +
|
||||||
MaxWaitNumBlocksFundingConf - 1,
|
lncfg.DefaultMaxWaitNumBlocksFundingConf - 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
bob.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
bob.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
||||||
Height: fundingBroadcastHeight +
|
Height: fundingBroadcastHeight +
|
||||||
MaxWaitNumBlocksFundingConf - 1,
|
lncfg.DefaultMaxWaitNumBlocksFundingConf - 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assert both and Alice and Bob still have 1 pending channels.
|
// Assert both and Alice and Bob still have 1 pending channels.
|
||||||
|
@ -2404,13 +2405,16 @@ func TestFundingManagerFundingNotTimeoutInitiator(t *testing.T) {
|
||||||
|
|
||||||
assertNumPendingChannelsRemains(t, bob, 1)
|
assertNumPendingChannelsRemains(t, bob, 1)
|
||||||
|
|
||||||
// Increase both Alice and Bob to MaxWaitNumBlocksFundingConf height.
|
// Increase both Alice and Bob to DefaultMaxWaitNumBlocksFundingConf
|
||||||
|
// height.
|
||||||
alice.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
alice.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
||||||
Height: fundingBroadcastHeight + MaxWaitNumBlocksFundingConf,
|
Height: fundingBroadcastHeight +
|
||||||
|
lncfg.DefaultMaxWaitNumBlocksFundingConf,
|
||||||
}
|
}
|
||||||
|
|
||||||
bob.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
bob.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
|
||||||
Height: fundingBroadcastHeight + MaxWaitNumBlocksFundingConf,
|
Height: fundingBroadcastHeight +
|
||||||
|
lncfg.DefaultMaxWaitNumBlocksFundingConf,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since Alice was the initiator, the channel should not have timed out.
|
// Since Alice was the initiator, the channel should not have timed out.
|
||||||
|
|
|
@ -670,6 +670,10 @@ var allTestCases = []*lntest.TestCase{
|
||||||
Name: "fee replacement",
|
Name: "fee replacement",
|
||||||
TestFunc: testFeeReplacement,
|
TestFunc: testFeeReplacement,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "funding manager funding timeout",
|
||||||
|
TestFunc: testFundingManagerFundingTimeout,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendPrefixed is used to add a prefix to each test name in the subtests
|
// appendPrefixed is used to add a prefix to each test name in the subtests
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/lightningnetwork/lnd/chainreg"
|
"github.com/lightningnetwork/lnd/chainreg"
|
||||||
"github.com/lightningnetwork/lnd/funding"
|
"github.com/lightningnetwork/lnd/funding"
|
||||||
|
"github.com/lightningnetwork/lnd/lncfg"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||||
"github.com/lightningnetwork/lnd/lntest"
|
"github.com/lightningnetwork/lnd/lntest"
|
||||||
|
@ -844,7 +845,7 @@ func testFundingExpiryBlocksOnPending(ht *lntest.HarnessTest) {
|
||||||
// blocks and verify the value of FundingExpiryBlock at each step.
|
// blocks and verify the value of FundingExpiryBlock at each step.
|
||||||
const numEmptyBlocks = 3
|
const numEmptyBlocks = 3
|
||||||
for i := int32(0); i < numEmptyBlocks; i++ {
|
for i := int32(0); i < numEmptyBlocks; i++ {
|
||||||
expectedVal := funding.MaxWaitNumBlocksFundingConf - i
|
expectedVal := lncfg.DefaultMaxWaitNumBlocksFundingConf - i
|
||||||
pending := ht.AssertNumPendingOpenChannels(alice, 1)
|
pending := ht.AssertNumPendingOpenChannels(alice, 1)
|
||||||
require.Equal(ht, expectedVal, pending[0].FundingExpiryBlocks)
|
require.Equal(ht, expectedVal, pending[0].FundingExpiryBlocks)
|
||||||
pending = ht.AssertNumPendingOpenChannels(bob, 1)
|
pending = ht.AssertNumPendingOpenChannels(bob, 1)
|
||||||
|
@ -967,3 +968,55 @@ func testOpenChannelLockedBalance(ht *lntest.HarnessTest) {
|
||||||
// Finally, we check to make sure the balance is unlocked again.
|
// Finally, we check to make sure the balance is unlocked again.
|
||||||
ht.AssertWalletLockedBalance(alice, 0)
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -72,6 +72,11 @@ const (
|
||||||
// DefaultZombieSweeperInterval is the default time interval at which
|
// DefaultZombieSweeperInterval is the default time interval at which
|
||||||
// unfinished (zombiestate) open channel flows are purged from memory.
|
// unfinished (zombiestate) open channel flows are purged from memory.
|
||||||
DefaultZombieSweeperInterval = 1 * time.Minute
|
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
|
// CleanAndExpandPath expands environment variables and leading ~ in the
|
||||||
|
|
|
@ -46,3 +46,9 @@ func (d *DevConfig) GetReservationTimeout() time.Duration {
|
||||||
func (d *DevConfig) GetZombieSweeperInterval() time.Duration {
|
func (d *DevConfig) GetZombieSweeperInterval() time.Duration {
|
||||||
return DefaultZombieSweeperInterval
|
return DefaultZombieSweeperInterval
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMaxWaitNumBlocksFundingConf returns the config value for
|
||||||
|
// `MaxWaitNumBlocksFundingConf`.
|
||||||
|
func (d *DevConfig) GetMaxWaitNumBlocksFundingConf() uint32 {
|
||||||
|
return DefaultMaxWaitNumBlocksFundingConf
|
||||||
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ type DevConfig struct {
|
||||||
ReservationTimeout time.Duration `long:"reservationtimeout" description:"The maximum time we keep a pending channel open flow in memory."`
|
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."`
|
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."`
|
UnsafeDisconnect bool `long:"unsafedisconnect" description:"Allows the rpcserver to intentionally disconnect from peers with open channels."`
|
||||||
|
MaxWaitNumBlocksFundingConf uint32 `long:"maxwaitnumblocksfundingconf" description:"Maximum blocks to wait for funding confirmation before discarding non-initiated channels."`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChannelReadyWait returns the config value `ProcessChannelReadyWait`.
|
// ChannelReadyWait returns the config value `ProcessChannelReadyWait`.
|
||||||
|
@ -54,3 +55,13 @@ func (d *DevConfig) GetZombieSweeperInterval() time.Duration {
|
||||||
func (d *DevConfig) GetUnsafeDisconnect() bool {
|
func (d *DevConfig) GetUnsafeDisconnect() bool {
|
||||||
return d.UnsafeDisconnect
|
return d.UnsafeDisconnect
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMaxWaitNumBlocksFundingConf returns the config value for
|
||||||
|
// `MaxWaitNumBlocksFundingConf`.
|
||||||
|
func (d *DevConfig) GetMaxWaitNumBlocksFundingConf() uint32 {
|
||||||
|
if d.MaxWaitNumBlocksFundingConf == 0 {
|
||||||
|
return DefaultMaxWaitNumBlocksFundingConf
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.MaxWaitNumBlocksFundingConf
|
||||||
|
}
|
||||||
|
|
16
rpcserver.go
16
rpcserver.go
|
@ -3850,9 +3850,21 @@ func (r *rpcServer) fetchPendingOpenChannels() (pendingOpenChannels, error) {
|
||||||
commitBaseWeight := blockchain.GetTransactionWeight(utx)
|
commitBaseWeight := blockchain.GetTransactionWeight(utx)
|
||||||
commitWeight := commitBaseWeight + witnessWeight
|
commitWeight := commitBaseWeight + witnessWeight
|
||||||
|
|
||||||
|
// For the waitBlocksForFundingConf different values are set in
|
||||||
|
// case we are in dev environment so enhance test capabilities.
|
||||||
|
var waitBlocksForFundingConf uint32 = lncfg.
|
||||||
|
DefaultMaxWaitNumBlocksFundingConf
|
||||||
|
|
||||||
|
// 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
|
// FundingExpiryBlocks is the distance from the current block
|
||||||
// height to the broadcast height + MaxWaitNumBlocksFundingConf.
|
// height to the broadcast height + waitBlocksForFundingConf.
|
||||||
maxFundingHeight := funding.MaxWaitNumBlocksFundingConf +
|
maxFundingHeight := waitBlocksForFundingConf +
|
||||||
pendingChan.BroadcastHeight()
|
pendingChan.BroadcastHeight()
|
||||||
fundingExpiryBlocks := int32(maxFundingHeight) - currentHeight
|
fundingExpiryBlocks := int32(maxFundingHeight) - currentHeight
|
||||||
|
|
||||||
|
|
|
@ -1449,6 +1449,8 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
|
||||||
if lncfg.IsDevBuild() {
|
if lncfg.IsDevBuild() {
|
||||||
devCfg = &funding.DevConfig{
|
devCfg = &funding.DevConfig{
|
||||||
ProcessChannelReadyWait: cfg.Dev.ChannelReadyWait(),
|
ProcessChannelReadyWait: cfg.Dev.ChannelReadyWait(),
|
||||||
|
MaxWaitNumBlocksFundingConf: cfg.Dev.
|
||||||
|
GetMaxWaitNumBlocksFundingConf(),
|
||||||
}
|
}
|
||||||
|
|
||||||
reservationTimeout = cfg.Dev.GetReservationTimeout()
|
reservationTimeout = cfg.Dev.GetReservationTimeout()
|
||||||
|
|
Loading…
Add table
Reference in a new issue