diff --git a/lncfg/dev.go b/lncfg/dev.go index 0bc305cff..7eb295824 100644 --- a/lncfg/dev.go +++ b/lncfg/dev.go @@ -24,6 +24,17 @@ func (d *DevConfig) ChannelReadyWait() time.Duration { return 0 } +// GetUnsafeDisconnect returns the config value, which is always true for +// production build. +// +// TODO(yy): this is a temporary solution to allow users to reconnect peers to +// trigger a reestablishiment for the active channels. Once a new dedicated RPC +// is added to realize that functionality, this function should return false to +// forbidden disconnecting peers while there are active channels. +func (d *DevConfig) GetUnsafeDisconnect() bool { + return true +} + // GetReservationTimeout returns the config value for `ReservationTimeout`. func (d *DevConfig) GetReservationTimeout() time.Duration { return DefaultReservationTimeout diff --git a/lncfg/dev_integration.go b/lncfg/dev_integration.go index 526c86b6a..86a4af275 100644 --- a/lncfg/dev_integration.go +++ b/lncfg/dev_integration.go @@ -22,6 +22,7 @@ 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."` } // ChannelReadyWait returns the config value `ProcessChannelReadyWait`. @@ -46,3 +47,8 @@ func (d *DevConfig) GetZombieSweeperInterval() time.Duration { return d.ZombieSweeperInterval } + +// ChannelReadyWait returns the config value `UnsafeDisconnect`. +func (d *DevConfig) GetUnsafeDisconnect() bool { + return d.UnsafeDisconnect +} diff --git a/lntest/node/config.go b/lntest/node/config.go index 855325ebc..449ad2fad 100644 --- a/lntest/node/config.go +++ b/lntest/node/config.go @@ -206,7 +206,6 @@ func (cfg *BaseNodeConfig) GenArgs() []string { args = append(args, backendArgs...) nodeArgs := []string{ - "--bitcoin.active", "--nobootstrap", "--debuglevel=debug,DISC=trace", "--bitcoin.defaultchanconfs=1", @@ -241,6 +240,9 @@ func (cfg *BaseNodeConfig) GenArgs() []string { // Speed up the tests for bitcoind backend. "--bitcoind.blockpollinginterval=100ms", "--bitcoind.txpollinginterval=100ms", + + // Allow unsafe disconnect in itest. + "--dev.unsafedisconnect", } args = append(args, nodeArgs...) diff --git a/rpcserver.go b/rpcserver.go index 95bdd39bf..370dfd992 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -1747,15 +1747,27 @@ func (r *rpcServer) DisconnectPeer(ctx context.Context, // In order to avoid erroneously disconnecting from a peer that we have // an active channel with, if we have any channels active with this // peer, then we'll disallow disconnecting from them. - if len(nodeChannels) > 0 { - return nil, fmt.Errorf("cannot disconnect from peer(%x), "+ - "all active channels with the peer need to be closed "+ - "first", pubKeyBytes) + if len(nodeChannels) != 0 { + // If we are not in a dev environment or the configed dev value + // `unsafedisconnect` is false, we return an error since there + // are active channels. + if !r.cfg.Dev.GetUnsafeDisconnect() { + return nil, fmt.Errorf("cannot disconnect from "+ + "peer(%x), still has %d active channels", + pubKeyBytes, len(nodeChannels)) + } + + // We are in a dev environment, print a warning log and + // disconnect. + rpcsLog.Warnf("UnsafeDisconnect mode, disconnecting from "+ + "peer(%x) while there are %d active channels", + pubKeyBytes, len(nodeChannels)) } // With all initial validation complete, we'll now request that the // server disconnects from the peer. - if err := r.server.DisconnectPeer(peerPubKey); err != nil { + err = r.server.DisconnectPeer(peerPubKey) + if err != nil { return nil, fmt.Errorf("unable to disconnect peer: %v", err) }