Merge pull request #3648 from cfromknecht/safe-disconnect

config+itest: allow unsafe disconnect by default
This commit is contained in:
Conner Fromknecht 2019-11-22 21:07:17 -08:00 committed by GitHub
commit a83958408d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 107 additions and 73 deletions

View File

@ -271,7 +271,7 @@ type config struct {
Profile string `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65535"` Profile string `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65535"`
UnsafeDisconnect bool `long:"unsafe-disconnect" description:"Allows the rpcserver to intentionally disconnect from peers with open channels. USED FOR TESTING ONLY."` UnsafeDisconnect bool `long:"unsafe-disconnect" description:"DEPRECATED: Allows the rpcserver to intentionally disconnect from peers with open channels. THIS FLAG WILL BE REMOVED IN 0.10.0"`
UnsafeReplay bool `long:"unsafe-replay" description:"Causes a link to replay the adds on its commitment txn after starting up, this enables testing of the sphinx replay logic."` UnsafeReplay bool `long:"unsafe-replay" description:"Causes a link to replay the adds on its commitment txn after starting up, this enables testing of the sphinx replay logic."`
MaxPendingChannels int `long:"maxpendingchannels" description:"The maximum number of incoming pending channels permitted per peer."` MaxPendingChannels int `long:"maxpendingchannels" description:"The maximum number of incoming pending channels permitted per peer."`
BackupFilePath string `long:"backupfilepath" description:"The target location of the channel backup file"` BackupFilePath string `long:"backupfilepath" description:"The target location of the channel backup file"`
@ -389,6 +389,7 @@ func loadConfig() (*config, error) {
Dir: defaultLitecoindDir, Dir: defaultLitecoindDir,
RPCHost: defaultRPCHost, RPCHost: defaultRPCHost,
}, },
UnsafeDisconnect: true,
MaxPendingChannels: DefaultMaxPendingChannels, MaxPendingChannels: DefaultMaxPendingChannels,
NoSeedBackup: defaultNoSeedBackup, NoSeedBackup: defaultNoSeedBackup,
MinBackoff: defaultMinBackoff, MinBackoff: defaultMinBackoff,

View File

@ -1195,28 +1195,25 @@ func (n *NetworkHarness) AssertChannelExists(ctx context.Context,
req := &lnrpc.ListChannelsRequest{} req := &lnrpc.ListChannelsRequest{}
var predErr error return wait.NoError(func() error {
pred := func() bool {
resp, err := node.ListChannels(ctx, req) resp, err := node.ListChannels(ctx, req)
if err != nil { if err != nil {
predErr = fmt.Errorf("unable fetch node's channels: %v", err) return fmt.Errorf("unable fetch node's channels: %v", err)
return false
} }
for _, channel := range resp.Channels { for _, channel := range resp.Channels {
if channel.ChannelPoint == chanPoint.String() { if channel.ChannelPoint == chanPoint.String() {
return channel.Active if channel.Active {
return nil
}
return fmt.Errorf("channel %s inactive",
chanPoint)
} }
} }
return false
}
if err := wait.Predicate(pred, time.Second*15); err != nil { return fmt.Errorf("channel %s not found", chanPoint)
return fmt.Errorf("channel not found: %v", predErr) }, 15*time.Second)
}
return nil
} }
// DumpLogs reads the current logs generated by the passed node, and returns // DumpLogs reads the current logs generated by the passed node, and returns

View File

@ -2172,13 +2172,46 @@ func testOpenChannelAfterReorg(net *lntest.NetworkHarness, t *harnessTest) {
closeReorgedChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false) closeReorgedChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false)
} }
// testDisconnectingTargetPeer performs a test which // testDisconnectingTargetPeer performs a test which disconnects Alice-peer from
// disconnects Alice-peer from Bob-peer and then re-connects them again // Bob-peer and then re-connects them again. We expect Alice to be able to
// disconnect at any point.
func testDisconnectingTargetPeer(net *lntest.NetworkHarness, t *harnessTest) { func testDisconnectingTargetPeer(net *lntest.NetworkHarness, t *harnessTest) {
ctxb := context.Background() ctxb := context.Background()
// We'll start both nodes with a high backoff so that they don't
// reconnect automatically during our test.
args := []string{
"--minbackoff=1m",
"--maxbackoff=1m",
}
alice, err := net.NewNode("Alice", args)
if err != nil {
t.Fatalf("unable to create new node: %v", err)
}
defer shutdownAndAssert(net, t, alice)
bob, err := net.NewNode("Bob", args)
if err != nil {
t.Fatalf("unable to create new node: %v", err)
}
defer shutdownAndAssert(net, t, bob)
// Start by connecting Alice and Bob with no channels.
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
if err := net.ConnectNodes(ctxt, alice, bob); err != nil {
t.Fatalf("unable to connect Alice's peer to Bob's: err %v", err)
}
// Check existing connection. // Check existing connection.
assertNumConnections(t, net.Alice, net.Bob, 1) assertNumConnections(t, alice, bob, 1)
// Give Alice some coins so she can fund a channel.
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
err = net.SendCoins(ctxt, btcutil.SatoshiPerBitcoin, alice)
if err != nil {
t.Fatalf("unable to send coins to carol: %v", err)
}
chanAmt := lnd.MaxBtcFundingAmount chanAmt := lnd.MaxBtcFundingAmount
pushAmt := btcutil.Amount(0) pushAmt := btcutil.Amount(0)
@ -2186,30 +2219,31 @@ func testDisconnectingTargetPeer(net *lntest.NetworkHarness, t *harnessTest) {
// Create a new channel that requires 1 confs before it's considered // Create a new channel that requires 1 confs before it's considered
// open, then broadcast the funding transaction // open, then broadcast the funding transaction
const numConfs = 1 const numConfs = 1
ctxt, _ := context.WithTimeout(ctxb, channelOpenTimeout) ctxt, _ = context.WithTimeout(ctxb, channelOpenTimeout)
pendingUpdate, err := net.OpenPendingChannel(ctxt, net.Alice, net.Bob, pendingUpdate, err := net.OpenPendingChannel(
chanAmt, pushAmt) ctxt, alice, bob, chanAmt, pushAmt,
)
if err != nil { if err != nil {
t.Fatalf("unable to open channel: %v", err) t.Fatalf("unable to open channel: %v", err)
} }
// At this point, the channel's funding transaction will have // At this point, the channel's funding transaction will have been
// been broadcast, but not confirmed. Alice and Bob's nodes // broadcast, but not confirmed. Alice and Bob's nodes should reflect
// should reflect this when queried via RPC. // this when queried via RPC.
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
assertNumOpenChannelsPending(ctxt, t, net.Alice, net.Bob, 1) assertNumOpenChannelsPending(ctxt, t, alice, bob, 1)
// Disconnect Alice-peer from Bob-peer and get error // Disconnect Alice-peer from Bob-peer and get error causes by one
// causes by one pending channel with detach node is existing. // pending channel with detach node is existing.
if err := net.DisconnectNodes(ctxt, net.Alice, net.Bob); err == nil { if err := net.DisconnectNodes(ctxt, alice, bob); err != nil {
t.Fatalf("Bob's peer was disconnected from Alice's"+ t.Fatalf("Bob's peer was disconnected from Alice's"+
" while one pending channel is existing: err %v", err) " while one pending channel is existing: err %v", err)
} }
time.Sleep(time.Millisecond * 300) time.Sleep(time.Millisecond * 300)
// Check existing connection. // Assert that the connection was torn down.
assertNumConnections(t, net.Alice, net.Bob, 1) assertNumConnections(t, alice, bob, 0)
fundingTxID, err := chainhash.NewHash(pendingUpdate.Txid) fundingTxID, err := chainhash.NewHash(pendingUpdate.Txid)
if err != nil { if err != nil {
@ -2223,15 +2257,21 @@ func testDisconnectingTargetPeer(net *lntest.NetworkHarness, t *harnessTest) {
block := mineBlocks(t, net, numConfs, 1)[0] block := mineBlocks(t, net, numConfs, 1)[0]
assertTxInBlock(t, block, fundingTxID) assertTxInBlock(t, block, fundingTxID)
// At this point, the channel should be fully opened and there should // At this point, the channel should be fully opened and there should be
// be no pending channels remaining for either node. // no pending channels remaining for either node.
time.Sleep(time.Millisecond * 300) time.Sleep(time.Millisecond * 300)
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
assertNumOpenChannelsPending(ctxt, t, net.Alice, net.Bob, 0) assertNumOpenChannelsPending(ctxt, t, alice, bob, 0)
// The channel should be listed in the peer information returned by // Reconnect the nodes so that the channel can become active.
// both peers. ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
if err := net.ConnectNodes(ctxt, alice, bob); err != nil {
t.Fatalf("unable to connect Alice's peer to Bob's: err %v", err)
}
// The channel should be listed in the peer information returned by both
// peers.
outPoint := wire.OutPoint{ outPoint := wire.OutPoint{
Hash: *fundingTxID, Hash: *fundingTxID,
Index: pendingUpdate.OutputIndex, Index: pendingUpdate.OutputIndex,
@ -2239,17 +2279,33 @@ func testDisconnectingTargetPeer(net *lntest.NetworkHarness, t *harnessTest) {
// Check both nodes to ensure that the channel is ready for operation. // Check both nodes to ensure that the channel is ready for operation.
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
if err := net.AssertChannelExists(ctxt, net.Alice, &outPoint); err != nil { if err := net.AssertChannelExists(ctxt, alice, &outPoint); err != nil {
t.Fatalf("unable to assert channel existence: %v", err) t.Fatalf("unable to assert channel existence: %v", err)
} }
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
if err := net.AssertChannelExists(ctxt, net.Bob, &outPoint); err != nil { if err := net.AssertChannelExists(ctxt, bob, &outPoint); err != nil {
t.Fatalf("unable to assert channel existence: %v", err) t.Fatalf("unable to assert channel existence: %v", err)
} }
// Finally, immediately close the channel. This function will also // Disconnect Alice-peer from Bob-peer and get error causes by one
// block until the channel is closed and will additionally assert the // active channel with detach node is existing.
// relevant channel closing post conditions. if err := net.DisconnectNodes(ctxt, alice, bob); err != nil {
t.Fatalf("Bob's peer was disconnected from Alice's"+
" while one active channel is existing: err %v", err)
}
// Check existing connection.
assertNumConnections(t, alice, bob, 0)
// Reconnect both nodes before force closing the channel.
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
if err := net.ConnectNodes(ctxt, alice, bob); err != nil {
t.Fatalf("unable to connect Alice's peer to Bob's: err %v", err)
}
// Finally, immediately close the channel. This function will also block
// until the channel is closed and will additionally assert the relevant
// channel closing post conditions.
chanPoint := &lnrpc.ChannelPoint{ chanPoint := &lnrpc.ChannelPoint{
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{ FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
FundingTxidBytes: pendingUpdate.Txid, FundingTxidBytes: pendingUpdate.Txid,
@ -2257,48 +2313,30 @@ func testDisconnectingTargetPeer(net *lntest.NetworkHarness, t *harnessTest) {
OutputIndex: pendingUpdate.OutputIndex, OutputIndex: pendingUpdate.OutputIndex,
} }
// Disconnect Alice-peer from Bob-peer and get error
// causes by one active channel with detach node is existing.
if err := net.DisconnectNodes(ctxt, net.Alice, net.Bob); err == nil {
t.Fatalf("Bob's peer was disconnected from Alice's"+
" while one active channel is existing: err %v", err)
}
// Check existing connection.
assertNumConnections(t, net.Alice, net.Bob, 1)
ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout) ctxt, _ = context.WithTimeout(ctxb, channelCloseTimeout)
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, true) closeChannelAndAssert(ctxt, t, net, alice, chanPoint, true)
// Disconnect Alice-peer from Bob-peer without getting error // Disconnect Alice-peer from Bob-peer without getting error about
// about existing channels. // existing channels.
var predErr error if err := net.DisconnectNodes(ctxt, alice, bob); err != nil {
err = wait.Predicate(func() bool {
if err := net.DisconnectNodes(ctxt, net.Alice, net.Bob); err != nil {
predErr = err
return false
}
return true
}, time.Second*15)
if err != nil {
t.Fatalf("unable to disconnect Bob's peer from Alice's: err %v", t.Fatalf("unable to disconnect Bob's peer from Alice's: err %v",
predErr) err)
} }
// Check zero peer connections. // Check zero peer connections.
assertNumConnections(t, net.Alice, net.Bob, 0) assertNumConnections(t, alice, bob, 0)
// Finally, re-connect both nodes. // Finally, re-connect both nodes.
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
if err := net.ConnectNodes(ctxt, net.Alice, net.Bob); err != nil { if err := net.ConnectNodes(ctxt, alice, bob); err != nil {
t.Fatalf("unable to connect Alice's peer to Bob's: err %v", err) t.Fatalf("unable to connect Alice's peer to Bob's: err %v", err)
} }
// Check existing connection. // Check existing connection.
assertNumConnections(t, net.Alice, net.Bob, 1) assertNumConnections(t, alice, net.Bob, 1)
// Cleanup by mining the force close and sweep transaction. // Cleanup by mining the force close and sweep transaction.
cleanupForceClose(t, net, net.Alice, chanPoint) cleanupForceClose(t, net, alice, chanPoint)
} }
// testFundingPersistence is intended to ensure that the Funding Manager // testFundingPersistence is intended to ensure that the Funding Manager
@ -3569,9 +3607,8 @@ func testSphinxReplayPersistence(net *lntest.NetworkHarness, t *harnessTest) {
defer shutdownAndAssert(net, t, dave) defer shutdownAndAssert(net, t, dave)
// Next, we'll create Carol and establish a channel to from her to // Next, we'll create Carol and establish a channel to from her to
// Dave. Carol is started in both unsafe-replay and unsafe-disconnect, // Dave. Carol is started in both unsafe-replay which will cause her to
// which will cause her to replay any pending Adds held in memory upon // replay any pending Adds held in memory upon reconnection.
// reconnection.
carol, err := net.NewNode("Carol", []string{"--unsafe-replay"}) carol, err := net.NewNode("Carol", []string{"--unsafe-replay"})
if err != nil { if err != nil {
t.Fatalf("unable to create new nodes: %v", err) t.Fatalf("unable to create new nodes: %v", err)
@ -11562,7 +11599,7 @@ func testSwitchOfflineDelivery(net *lntest.NetworkHarness, t *harnessTest) {
// Carol -> Dave -> Alice -> Bob // Carol -> Dave -> Alice -> Bob
// //
// First, we'll create Dave and establish a channel to Alice. // First, we'll create Dave and establish a channel to Alice.
dave, err := net.NewNode("Dave", []string{"--unsafe-disconnect"}) dave, err := net.NewNode("Dave", nil)
if err != nil { if err != nil {
t.Fatalf("unable to create new nodes: %v", err) t.Fatalf("unable to create new nodes: %v", err)
} }
@ -11892,7 +11929,7 @@ func testSwitchOfflineDeliveryPersistence(net *lntest.NetworkHarness, t *harness
// Carol -> Dave -> Alice -> Bob // Carol -> Dave -> Alice -> Bob
// //
// First, we'll create Dave and establish a channel to Alice. // First, we'll create Dave and establish a channel to Alice.
dave, err := net.NewNode("Dave", []string{"--unsafe-disconnect"}) dave, err := net.NewNode("Dave", nil)
if err != nil { if err != nil {
t.Fatalf("unable to create new nodes: %v", err) t.Fatalf("unable to create new nodes: %v", err)
} }
@ -12229,7 +12266,7 @@ func testSwitchOfflineDeliveryOutgoingOffline(
// Carol -> Dave -> Alice -> Bob // Carol -> Dave -> Alice -> Bob
// //
// First, we'll create Dave and establish a channel to Alice. // First, we'll create Dave and establish a channel to Alice.
dave, err := net.NewNode("Dave", []string{"--unsafe-disconnect"}) dave, err := net.NewNode("Dave", nil)
if err != nil { if err != nil {
t.Fatalf("unable to create new nodes: %v", err) t.Fatalf("unable to create new nodes: %v", err)
} }
@ -12969,7 +13006,6 @@ func testSendUpdateDisableChannel(net *lntest.NetworkHarness, t *harnessTest) {
carol, err := net.NewNode("Carol", []string{ carol, err := net.NewNode("Carol", []string{
"--minbackoff=10s", "--minbackoff=10s",
"--unsafe-disconnect",
"--chan-enable-timeout=1.5s", "--chan-enable-timeout=1.5s",
"--chan-disable-timeout=3s", "--chan-disable-timeout=3s",
"--chan-status-sample-interval=.5s", "--chan-status-sample-interval=.5s",