From 989fe50da83d30bbeb2c7082b16305899f4deaa7 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Fri, 14 Dec 2018 09:24:00 +0100 Subject: [PATCH 1/4] lntest: define BackendCfg and btcd impl BackendCfg is an interface that can be backed by different Bitcoin node implementations. We currently use the btcdHarness as our chain backend. --- lnd_test.go | 7 +++- lntest/btcd.go | 87 +++++++++++++++++++++++++++++++++++++++++++++++ lntest/harness.go | 20 ++++++----- lntest/node.go | 30 ++++++++++------ 4 files changed, 123 insertions(+), 21 deletions(-) create mode 100644 lntest/btcd.go diff --git a/lnd_test.go b/lnd_test.go index cb78e648..ab4e2c83 100644 --- a/lnd_test.go +++ b/lnd_test.go @@ -12970,9 +12970,14 @@ func TestLightningNetworkDaemon(t *testing.T) { } }() + chainBackend := lntest.BtcdBackendConfig{ + RPCConfig: btcdHarness.RPCConfig(), + P2PAddress: btcdHarness.P2PAddress(), + } + // First create the network harness to gain access to its // 'OnTxAccepted' call back. - lndHarness, err = lntest.NewNetworkHarness(btcdHarness) + lndHarness, err = lntest.NewNetworkHarness(btcdHarness, chainBackend) if err != nil { ht.Fatalf("unable to create lightning network harness: %v", err) } diff --git a/lntest/btcd.go b/lntest/btcd.go new file mode 100644 index 00000000..7cbe9f28 --- /dev/null +++ b/lntest/btcd.go @@ -0,0 +1,87 @@ +package lntest + +import ( + "encoding/hex" + "fmt" + "os" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/integration/rpctest" + "github.com/btcsuite/btcd/rpcclient" +) + +// logDir is the name of the temporary log directory. +const logDir = "./.backendlogs" + +// BtcdBackendConfig is an implementation of the BackendConfig interface +// backed by a btcd node. +type BtcdBackendConfig struct { + // RPCConfig houses the connection config to the backing btcd instance. + RPCConfig rpcclient.ConnConfig + + // P2PAddress is the p2p address of the btcd instance. + P2PAddress string +} + +// GenArgs returns the arguments needed to be passed to LND at startup for +// using this node as a chain backend. +func (b BtcdBackendConfig) GenArgs() []string { + var args []string + encodedCert := hex.EncodeToString(b.RPCConfig.Certificates) + args = append(args, "--bitcoin.node=btcd") + args = append(args, fmt.Sprintf("--btcd.rpchost=%v", b.RPCConfig.Host)) + args = append(args, fmt.Sprintf("--btcd.rpcuser=%v", b.RPCConfig.User)) + args = append(args, fmt.Sprintf("--btcd.rpcpass=%v", b.RPCConfig.Pass)) + args = append(args, fmt.Sprintf("--btcd.rawrpccert=%v", encodedCert)) + + return args +} + +// P2PAddr returns the address of this node to be used when connection over the +// Bitcoin P2P network. +func (b BtcdBackendConfig) P2PAddr() string { + return b.P2PAddress +} + +// NewBtcdBackend starts a new rpctest.Harness and returns a BtcdBackendConfig +// for that node. +func NewBtcdBackend() (*BtcdBackendConfig, func(), error) { + args := []string{ + "--rejectnonstd", + "--txindex", + "--trickleinterval=100ms", + "--debuglevel=debug", + "--logdir=" + logDir, + } + netParams := &chaincfg.SimNetParams + chainBackend, err := rpctest.New(netParams, nil, args) + if err != nil { + return nil, nil, fmt.Errorf("unable to create btcd node: %v", err) + } + + if err := chainBackend.SetUp(false, 0); err != nil { + return nil, nil, fmt.Errorf("unable to set up btcd backend: %v", err) + } + + bd := &BtcdBackendConfig{ + RPCConfig: chainBackend.RPCConfig(), + P2PAddress: chainBackend.P2PAddress(), + } + + cleanUp := func() { + chainBackend.TearDown() + + // After shutting down the chain backend, we'll make a copy of + // the log file before deleting the temporary log dir. + logFile := logDir + "/" + netParams.Name + "/btcd.log" + err := CopyFile("./output_btcd_chainbackend.log", logFile) + if err != nil { + fmt.Printf("unable to copy file: %v\n", err) + } + if err = os.RemoveAll(logDir); err != nil { + fmt.Printf("Cannot remove dir %s: %v\n", logDir, err) + } + } + + return bd, cleanUp, nil +} diff --git a/lntest/harness.go b/lntest/harness.go index 0a3a9d2e..70ab825c 100644 --- a/lntest/harness.go +++ b/lntest/harness.go @@ -16,7 +16,6 @@ import ( "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/integration/rpctest" - "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" @@ -50,13 +49,16 @@ const ( // The harness by default is created with two active nodes on the network: // Alice and Bob. type NetworkHarness struct { - rpcConfig rpcclient.ConnConfig netParams *chaincfg.Params // Miner is a reference to a running full node that can be used to create // new blocks on the network. Miner *rpctest.Harness + // BackendCfg houses the information necessary to use a node as LND + // chain backend, such as rpc configuration, P2P information etc. + BackendCfg BackendConfig + activeNodes map[int]*HarnessNode nodesByPub map[string]*HarnessNode @@ -82,7 +84,7 @@ type NetworkHarness struct { // TODO(roasbeef): add option to use golang's build library to a binary of the // current repo. This will save developers from having to manually `go install` // within the repo each time before changes -func NewNetworkHarness(r *rpctest.Harness) (*NetworkHarness, error) { +func NewNetworkHarness(r *rpctest.Harness, b BackendConfig) (*NetworkHarness, error) { n := NetworkHarness{ activeNodes: make(map[int]*HarnessNode), nodesByPub: make(map[string]*HarnessNode), @@ -91,7 +93,7 @@ func NewNetworkHarness(r *rpctest.Harness) (*NetworkHarness, error) { lndErrorChan: make(chan error), netParams: r.ActiveNet, Miner: r, - rpcConfig: r.RPCConfig(), + BackendCfg: b, quit: make(chan struct{}), } go n.networkWatcher() @@ -355,11 +357,11 @@ func (n *NetworkHarness) RestoreNodeWithSeed(name string, extraArgs []string, func (n *NetworkHarness) newNode(name string, extraArgs []string, hasSeed bool) (*HarnessNode, error) { node, err := newNode(nodeConfig{ - Name: name, - HasSeed: hasSeed, - RPCConfig: &n.rpcConfig, - NetParams: n.netParams, - ExtraArgs: extraArgs, + Name: name, + HasSeed: hasSeed, + BackendCfg: n.BackendCfg, + NetParams: n.netParams, + ExtraArgs: extraArgs, }) if err != nil { return nil, err diff --git a/lntest/node.go b/lntest/node.go index d518dd95..55581367 100644 --- a/lntest/node.go +++ b/lntest/node.go @@ -22,7 +22,6 @@ import ( "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/go-errors/errors" @@ -91,12 +90,24 @@ func generateListeningPorts() (int, int, int) { return p2p, rpc, rest } +// BackendConfig is an interface that abstracts away the specific chain backend +// node implementation. +type BackendConfig interface { + // GenArgs returns the arguments needed to be passed to LND at startup + // for using this node as a chain backend. + GenArgs() []string + + // P2PAddr returns the address of this node to be used when connection + // over the Bitcoin P2P network. + P2PAddr() string +} + type nodeConfig struct { - Name string - RPCConfig *rpcclient.ConnConfig - NetParams *chaincfg.Params - BaseDir string - ExtraArgs []string + Name string + BackendCfg BackendConfig + NetParams *chaincfg.Params + BaseDir string + ExtraArgs []string DataDir string LogDir string @@ -144,16 +155,13 @@ func (cfg nodeConfig) genArgs() []string { args = append(args, "--bitcoin.regtest") } - encodedCert := hex.EncodeToString(cfg.RPCConfig.Certificates) + backendArgs := cfg.BackendCfg.GenArgs() + args = append(args, backendArgs...) args = append(args, "--bitcoin.active") args = append(args, "--nobootstrap") args = append(args, "--debuglevel=debug") args = append(args, "--bitcoin.defaultchanconfs=1") args = append(args, fmt.Sprintf("--bitcoin.defaultremotedelay=%v", DefaultCSV)) - args = append(args, fmt.Sprintf("--btcd.rpchost=%v", cfg.RPCConfig.Host)) - args = append(args, fmt.Sprintf("--btcd.rpcuser=%v", cfg.RPCConfig.User)) - args = append(args, fmt.Sprintf("--btcd.rpcpass=%v", cfg.RPCConfig.Pass)) - args = append(args, fmt.Sprintf("--btcd.rawrpccert=%v", encodedCert)) args = append(args, fmt.Sprintf("--rpclisten=%v", cfg.RPCAddr())) args = append(args, fmt.Sprintf("--restlisten=%v", cfg.RESTAddr())) args = append(args, fmt.Sprintf("--listen=%v", cfg.P2PAddr())) From 92b984a2338dc25af7b0ade97354965e6fa2162a Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Fri, 14 Dec 2018 09:24:00 +0100 Subject: [PATCH 2/4] lnd_test+lntest: seperate miner and chain backend Since we are no longer passing in the miner as the chain backend, we don't have to export the fields. --- lnd_test.go | 74 +++++++++++++++++++++++++++++--------------------- lntest/btcd.go | 22 +++++++-------- 2 files changed, 54 insertions(+), 42 deletions(-) diff --git a/lnd_test.go b/lnd_test.go index ab4e2c83..246ce9bf 100644 --- a/lnd_test.go +++ b/lnd_test.go @@ -12930,54 +12930,73 @@ var testsCases = []*testCase{ func TestLightningNetworkDaemon(t *testing.T) { ht := newHarnessTest(t) + // Start a btcd chain backend. + chainBackend, cleanUp, err := lntest.NewBtcdBackend() + if err != nil { + ht.Fatalf("unable to start btcd: %v", err) + } + defer cleanUp() + + // Declare the network harness here to gain access to its + // 'OnTxAccepted' call back. var lndHarness *lntest.NetworkHarness - // First create an instance of the btcd's rpctest.Harness. This will be - // used to fund the wallets of the nodes within the test network and to - // drive blockchain related events within the network. Revert the default - // setting of accepting non-standard transactions on simnet to reject them. - // Transactions on the lightning network should always be standard to get - // better guarantees of getting included in to blocks. - logDir := "./.backendlogs" + // Create an instance of the btcd's rpctest.Harness that will act as + // the miner for all tests. This will be used to fund the wallets of + // the nodes within the test network and to drive blockchain related + // events within the network. Revert the default setting of accepting + // non-standard transactions on simnet to reject them. Transactions on + // the lightning network should always be standard to get better + // guarantees of getting included in to blocks. + // + // We will also connect it to our chain backend. + minerLogDir := "./.minerlogs" args := []string{ "--rejectnonstd", "--txindex", "--debuglevel=debug", - "--logdir=" + logDir, + "--logdir=" + minerLogDir, + "--trickleinterval=100ms", + "--connect=" + chainBackend.P2PAddr(), } handlers := &rpcclient.NotificationHandlers{ OnTxAccepted: func(hash *chainhash.Hash, amt btcutil.Amount) { lndHarness.OnTxAccepted(hash) }, } - btcdHarness, err := rpctest.New(harnessNetParams, handlers, args) + + miner, err := rpctest.New(harnessNetParams, handlers, args) if err != nil { ht.Fatalf("unable to create mining node: %v", err) } defer func() { - btcdHarness.TearDown() + miner.TearDown() - // After shutting down the chain backend, we'll make a copy of - // the log file before deleting the temporary log dir. - logFile := logDir + "/" + harnessNetParams.Name + "/btcd.log" - err := lntest.CopyFile("./output_btcd_chainbackend.log", - logFile) + // After shutting down the miner, we'll make a copy of the log + // file before deleting the temporary log dir. + logFile := fmt.Sprintf( + "%s/%s/btcd.log", minerLogDir, harnessNetParams.Name, + ) + err := lntest.CopyFile("./output_btcd_miner.log", logFile) if err != nil { fmt.Printf("unable to copy file: %v\n", err) } - if err = os.RemoveAll(logDir); err != nil { - fmt.Printf("Cannot remove dir %s: %v\n", logDir, err) + if err = os.RemoveAll(minerLogDir); err != nil { + fmt.Printf("Cannot remove dir %s: %v\n", + minerLogDir, err) } }() - chainBackend := lntest.BtcdBackendConfig{ - RPCConfig: btcdHarness.RPCConfig(), - P2PAddress: btcdHarness.P2PAddress(), + if err := miner.SetUp(true, 50); err != nil { + ht.Fatalf("unable to set up mining node: %v", err) + } + if err := miner.Node.NotifyNewTransactions(false); err != nil { + ht.Fatalf("unable to request transaction notifications: %v", err) } - // First create the network harness to gain access to its - // 'OnTxAccepted' call back. - lndHarness, err = lntest.NewNetworkHarness(btcdHarness, chainBackend) + // Now we can set up our test harness (LND instance), with the chain + // backend we just created. + lndHarness, err = lntest.NewNetworkHarness(miner, chainBackend) if err != nil { ht.Fatalf("unable to create lightning network harness: %v", err) } @@ -12999,17 +13018,10 @@ func TestLightningNetworkDaemon(t *testing.T) { } }() - if err := btcdHarness.SetUp(true, 50); err != nil { - ht.Fatalf("unable to set up mining node: %v", err) - } - if err := btcdHarness.Node.NotifyNewTransactions(false); err != nil { - ht.Fatalf("unable to request transaction notifications: %v", err) - } - // Next mine enough blocks in order for segwit and the CSV package // soft-fork to activate on SimNet. numBlocks := chaincfg.SimNetParams.MinerConfirmationWindow * 2 - if _, err := btcdHarness.Node.Generate(numBlocks); err != nil { + if _, err := miner.Node.Generate(numBlocks); err != nil { ht.Fatalf("unable to generate blocks: %v", err) } diff --git a/lntest/btcd.go b/lntest/btcd.go index 7cbe9f28..48e4d71f 100644 --- a/lntest/btcd.go +++ b/lntest/btcd.go @@ -16,22 +16,22 @@ const logDir = "./.backendlogs" // BtcdBackendConfig is an implementation of the BackendConfig interface // backed by a btcd node. type BtcdBackendConfig struct { - // RPCConfig houses the connection config to the backing btcd instance. - RPCConfig rpcclient.ConnConfig + // rpcConfig houses the connection config to the backing btcd instance. + rpcConfig rpcclient.ConnConfig - // P2PAddress is the p2p address of the btcd instance. - P2PAddress string + // p2pAddress is the p2p address of the btcd instance. + p2pAddress string } // GenArgs returns the arguments needed to be passed to LND at startup for // using this node as a chain backend. func (b BtcdBackendConfig) GenArgs() []string { var args []string - encodedCert := hex.EncodeToString(b.RPCConfig.Certificates) + encodedCert := hex.EncodeToString(b.rpcConfig.Certificates) args = append(args, "--bitcoin.node=btcd") - args = append(args, fmt.Sprintf("--btcd.rpchost=%v", b.RPCConfig.Host)) - args = append(args, fmt.Sprintf("--btcd.rpcuser=%v", b.RPCConfig.User)) - args = append(args, fmt.Sprintf("--btcd.rpcpass=%v", b.RPCConfig.Pass)) + args = append(args, fmt.Sprintf("--btcd.rpchost=%v", b.rpcConfig.Host)) + args = append(args, fmt.Sprintf("--btcd.rpcuser=%v", b.rpcConfig.User)) + args = append(args, fmt.Sprintf("--btcd.rpcpass=%v", b.rpcConfig.Pass)) args = append(args, fmt.Sprintf("--btcd.rawrpccert=%v", encodedCert)) return args @@ -40,7 +40,7 @@ func (b BtcdBackendConfig) GenArgs() []string { // P2PAddr returns the address of this node to be used when connection over the // Bitcoin P2P network. func (b BtcdBackendConfig) P2PAddr() string { - return b.P2PAddress + return b.p2pAddress } // NewBtcdBackend starts a new rpctest.Harness and returns a BtcdBackendConfig @@ -64,8 +64,8 @@ func NewBtcdBackend() (*BtcdBackendConfig, func(), error) { } bd := &BtcdBackendConfig{ - RPCConfig: chainBackend.RPCConfig(), - P2PAddress: chainBackend.P2PAddress(), + rpcConfig: chainBackend.RPCConfig(), + p2pAddress: chainBackend.P2PAddress(), } cleanUp := func() { From 2b16fb72718663bb74a460df14b504e8a49b395f Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Fri, 14 Dec 2018 09:24:00 +0100 Subject: [PATCH 3/4] lnd_test: fix reorg test by using new Node RPC Since the chain backend and miner is now seperate nodes, the reorg test must be slightly rewritten. We use the Btcd Node RPC to connect and disconnect the three bitcoin nodes in question (chain backend, miner, temp miner) to control the reorg scenario. --- lnd_test.go | 228 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 161 insertions(+), 67 deletions(-) diff --git a/lnd_test.go b/lnd_test.go index 246ce9bf..be94669d 100644 --- a/lnd_test.go +++ b/lnd_test.go @@ -21,6 +21,7 @@ import ( "crypto/rand" "crypto/sha256" + "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/integration/rpctest" @@ -1542,57 +1543,110 @@ func testUpdateChannelPolicy(net *lntest.NetworkHarness, t *harnessTest) { closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint3, false) } +// waitForNodeBlockHeight queries the node for its current block height until +// it reaches the passed height. +func waitForNodeBlockHeight(ctx context.Context, node *lntest.HarnessNode, + height int32) error { + var predErr error + err := lntest.WaitPredicate(func() bool { + ctxt, _ := context.WithTimeout(ctx, 10*time.Second) + info, err := node.GetInfo(ctxt, &lnrpc.GetInfoRequest{}) + if err != nil { + predErr = err + return false + } + + if int32(info.BlockHeight) != height { + predErr = fmt.Errorf("expected block height to "+ + "be %v, was %v", height, info.BlockHeight) + return false + } + return true + }, 15*time.Second) + if err != nil { + return predErr + } + return nil +} + +// assertMinerBlockHeightDelta ensures that tempMiner is 'delta' blocks ahead +// of miner. +func assertMinerBlockHeightDelta(t *harnessTest, + miner, tempMiner *rpctest.Harness, delta int32) { + + // Ensure the chain lengths are what we expect. + var predErr error + err := lntest.WaitPredicate(func() bool { + _, tempMinerHeight, err := tempMiner.Node.GetBestBlock() + if err != nil { + predErr = fmt.Errorf("unable to get current "+ + "blockheight %v", err) + return false + } + + _, minerHeight, err := miner.Node.GetBestBlock() + if err != nil { + predErr = fmt.Errorf("unable to get current "+ + "blockheight %v", err) + return false + } + + if tempMinerHeight != minerHeight+delta { + predErr = fmt.Errorf("expected new miner(%d) to be %d "+ + "blocks ahead of original miner(%d)", + tempMinerHeight, delta, minerHeight) + return false + } + return true + }, time.Second*15) + if err != nil { + t.Fatalf(predErr.Error()) + } +} + // testOpenChannelAfterReorg tests that in the case where we have an open // channel where the funding tx gets reorged out, the channel will no // longer be present in the node's routing table. func testOpenChannelAfterReorg(net *lntest.NetworkHarness, t *harnessTest) { - ctxb := context.Background() + var ( + ctxb = context.Background() + temp = "temp" + perm = "perm" + ) // Set up a new miner that we can use to cause a reorg. args := []string{"--rejectnonstd", "--txindex"} - miner, err := rpctest.New(harnessNetParams, + tempMiner, err := rpctest.New(harnessNetParams, &rpcclient.NotificationHandlers{}, args) if err != nil { t.Fatalf("unable to create mining node: %v", err) } - if err := miner.SetUp(true, 50); err != nil { + if err := tempMiner.SetUp(false, 0); err != nil { t.Fatalf("unable to set up mining node: %v", err) } - defer miner.TearDown() - - if err := miner.Node.NotifyNewTransactions(false); err != nil { - t.Fatalf("unable to request transaction notifications: %v", err) - } + defer tempMiner.TearDown() // We start by connecting the new miner to our original miner, // such that it will sync to our original chain. - if err := rpctest.ConnectNode(net.Miner, miner); err != nil { - t.Fatalf("unable to connect harnesses: %v", err) + err = net.Miner.Node.Node( + btcjson.NConnect, tempMiner.P2PAddress(), &temp, + ) + if err != nil { + t.Fatalf("unable to remove node: %v", err) } - nodeSlice := []*rpctest.Harness{net.Miner, miner} + nodeSlice := []*rpctest.Harness{net.Miner, tempMiner} if err := rpctest.JoinNodes(nodeSlice, rpctest.Blocks); err != nil { t.Fatalf("unable to join node on blocks: %v", err) } - // The two should be on the same blockheight. - _, newNodeHeight, err := miner.Node.GetBestBlock() - if err != nil { - t.Fatalf("unable to get current blockheight %v", err) - } + // The two miners should be on the same blockheight. + assertMinerBlockHeightDelta(t, net.Miner, tempMiner, 0) - _, orgNodeHeight, err := net.Miner.Node.GetBestBlock() - if err != nil { - t.Fatalf("unable to get current blockheight %v", err) - } - - if newNodeHeight != orgNodeHeight { - t.Fatalf("expected new miner(%d) and original miner(%d) to "+ - "be on the same height", newNodeHeight, orgNodeHeight) - } - - // We disconnect the two nodes, such that we can start mining on them - // individually without the other one learning about the new blocks. - err = net.Miner.Node.AddNode(miner.P2PAddress(), rpcclient.ANRemove) + // We disconnect the two miners, such that we can mine two different + // chains and can cause a reorg later. + err = net.Miner.Node.Node( + btcjson.NDisconnect, tempMiner.P2PAddress(), &temp, + ) if err != nil { t.Fatalf("unable to remove node: %v", err) } @@ -1608,7 +1662,8 @@ func testOpenChannelAfterReorg(net *lntest.NetworkHarness, t *harnessTest) { t.Fatalf("unable to open channel: %v", err) } - // Wait for miner to have seen the funding tx. + // Wait for miner to have seen the funding tx. The temporary miner is + // disconnected, and won't see the transaction. _, err = waitForTxInMempool(net.Miner.Node, minerMempoolTimeout) if err != nil { t.Fatalf("failed to find funding tx in mempool: %v", err) @@ -1627,25 +1682,27 @@ func testOpenChannelAfterReorg(net *lntest.NetworkHarness, t *harnessTest) { // We now cause a fork, by letting our original miner mine 10 blocks, // and our new miner mine 15. This will also confirm our pending - // channel, which should be considered open. + // channel on the original miner's chain, which should be considered + // open. block := mineBlocks(t, net, 10, 1)[0] assertTxInBlock(t, block, fundingTxID) - miner.Node.Generate(15) + if _, err := tempMiner.Node.Generate(15); err != nil { + t.Fatalf("unable to generate blocks: %v", err) + } - // Ensure the chain lengths are what we expect. - _, newNodeHeight, err = miner.Node.GetBestBlock() + // Ensure the chain lengths are what we expect, with the temp miner + // being 5 blocks ahead. + assertMinerBlockHeightDelta(t, net.Miner, tempMiner, 5) + + // Wait for Alice to sync to the original miner's chain. + _, minerHeight, err := net.Miner.Node.GetBestBlock() if err != nil { t.Fatalf("unable to get current blockheight %v", err) } - - _, orgNodeHeight, err = net.Miner.Node.GetBestBlock() + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + err = waitForNodeBlockHeight(ctxt, net.Alice, minerHeight) if err != nil { - t.Fatalf("unable to get current blockheight %v", err) - } - - if newNodeHeight != orgNodeHeight+5 { - t.Fatalf("expected new miner(%d) to be 5 blocks ahead of "+ - "original miner(%d)", newNodeHeight, orgNodeHeight) + t.Fatalf("unable to sync to chain: %v", err) } chanPoint := &lnrpc.ChannelPoint{ @@ -1689,49 +1746,86 @@ func testOpenChannelAfterReorg(net *lntest.NetworkHarness, t *harnessTest) { numEdges) } - // Connecting the two miners should now cause our original one to sync - // to the new, and longer chain. - if err := rpctest.ConnectNode(net.Miner, miner); err != nil { - t.Fatalf("unable to connect harnesses: %v", err) + // Now we disconnect Alice's chain backend from the original miner, and + // connect the two miners together. Since the temporary miner knows + // about a longer chain, both miners should sync to that chain. + err = net.Miner.Node.Node( + btcjson.NRemove, net.BackendCfg.P2PAddr(), &perm, + ) + if err != nil { + t.Fatalf("unable to remove node: %v", err) } - if err := rpctest.JoinNodes(nodeSlice, rpctest.Blocks); err != nil { + // Connecting to the temporary miner should now cause our original + // chain to be re-orged out. + err = net.Miner.Node.Node( + btcjson.NConnect, tempMiner.P2PAddress(), &temp, + ) + if err != nil { + t.Fatalf("unable to remove node: %v", err) + } + + nodes := []*rpctest.Harness{tempMiner, net.Miner} + if err := rpctest.JoinNodes(nodes, rpctest.Blocks); err != nil { t.Fatalf("unable to join node on blocks: %v", err) } // Once again they should be on the same chain. - _, newNodeHeight, err = miner.Node.GetBestBlock() + assertMinerBlockHeightDelta(t, net.Miner, tempMiner, 0) + + // Now we disconnect the two miners, and connect our original miner to + // our chain backend once again. + err = net.Miner.Node.Node( + btcjson.NDisconnect, tempMiner.P2PAddress(), &temp, + ) + if err != nil { + t.Fatalf("unable to remove node: %v", err) + } + + err = net.Miner.Node.Node( + btcjson.NConnect, net.BackendCfg.P2PAddr(), &perm, + ) + if err != nil { + t.Fatalf("unable to remove node: %v", err) + } + + // This should have caused a reorg, and Alice should sync to the longer + // chain, where the funding transaction is not confirmed. + _, tempMinerHeight, err := tempMiner.Node.GetBestBlock() if err != nil { t.Fatalf("unable to get current blockheight %v", err) } - - _, orgNodeHeight, err = net.Miner.Node.GetBestBlock() + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + err = waitForNodeBlockHeight(ctxt, net.Alice, tempMinerHeight) if err != nil { - t.Fatalf("unable to get current blockheight %v", err) + t.Fatalf("unable to sync to chain: %v", err) } - if newNodeHeight != orgNodeHeight { - t.Fatalf("expected new miner(%d) and original miner(%d) to "+ - "be on the same height", newNodeHeight, orgNodeHeight) - } - - time.Sleep(time.Second * 2) - // Since the fundingtx was reorged out, Alice should now have no edges // in her graph. req = &lnrpc.ChannelGraphRequest{ IncludeUnannounced: true, } - ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - chanGraph, err = net.Alice.DescribeGraph(ctxt, req) - if err != nil { - t.Fatalf("unable to query for alice's routing table: %v", err) - } - numEdges = len(chanGraph.Edges) - if numEdges != 0 { - t.Fatalf("expected to find no edge in the graph, found %d", - numEdges) + var predErr error + err = lntest.WaitPredicate(func() bool { + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + chanGraph, err = net.Alice.DescribeGraph(ctxt, req) + if err != nil { + predErr = fmt.Errorf("unable to query for alice's routing table: %v", err) + return false + } + + numEdges = len(chanGraph.Edges) + if numEdges != 0 { + predErr = fmt.Errorf("expected to find no edge in the graph, found %d", + numEdges) + return false + } + return true + }, time.Second*15) + if err != nil { + t.Fatalf(predErr.Error()) } // Cleanup by mining the funding tx again, then closing the channel. From 5dc87ad4d7ee39f37d0d84e2619986a5837c49b5 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Wed, 16 Jan 2019 10:11:47 +0100 Subject: [PATCH 4/4] gitignore: ignore .minerlogs folder --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 290cf568..796fd68c 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ _testmain.go # Integration test log files output*.log /.backendlogs +/.minerlogs cmd/cmd *.key