diff --git a/itest/lnd_misc_test.go b/itest/lnd_misc_test.go index ae0cbcd54..ccd879ab6 100644 --- a/itest/lnd_misc_test.go +++ b/itest/lnd_misc_test.go @@ -1421,9 +1421,10 @@ func testSendSelectedCoinsChannelReserve(ht *lntest.HarnessTest) { // Create a two-hop network: Alice -> Bob. // // NOTE: Alice will have one UTXO after the funding. - _, nodes := createSimpleNetwork( - ht, []string{"--protocol.anchors"}, 2, - lntest.OpenChannelParams{ + cfg := []string{"--protocol.anchors"} + cfgs := [][]string{cfg, cfg} + _, nodes := ht.CreateSimpleNetwork( + cfgs, lntest.OpenChannelParams{ Amt: chanAmt, }, ) diff --git a/itest/lnd_payment_test.go b/itest/lnd_payment_test.go index 501221206..cb6409373 100644 --- a/itest/lnd_payment_test.go +++ b/itest/lnd_payment_test.go @@ -41,9 +41,10 @@ func testPaymentSucceededHTLCRemoteSwept(ht *lntest.HarnessTest) { openChannelParams := lntest.OpenChannelParams{ Amt: chanAmt, } + cfgs := [][]string{nil, nil} // Create a two hop network: Alice -> Bob. - chanPoints, nodes := createSimpleNetwork(ht, nil, 2, openChannelParams) + chanPoints, nodes := ht.CreateSimpleNetwork(cfgs, openChannelParams) chanPoint := chanPoints[0] alice, bob := nodes[0], nodes[1] @@ -197,9 +198,10 @@ func runTestPaymentHTLCTimeout(ht *lntest.HarnessTest, restartAlice bool) { openChannelParams := lntest.OpenChannelParams{ Amt: chanAmt, } + cfgs := [][]string{nil, nil} // Create a two hop network: Alice -> Bob. - chanPoints, nodes := createSimpleNetwork(ht, nil, 2, openChannelParams) + chanPoints, nodes := ht.CreateSimpleNetwork(cfgs, openChannelParams) chanPoint := chanPoints[0] alice, bob := nodes[0], nodes[1] @@ -1247,9 +1249,10 @@ func runSendToRouteFailHTLCTimeout(ht *lntest.HarnessTest, restartAlice bool) { openChannelParams := lntest.OpenChannelParams{ Amt: chanAmt, } + cfgs := [][]string{nil, nil} // Create a two hop network: Alice -> Bob. - chanPoints, nodes := createSimpleNetwork(ht, nil, 2, openChannelParams) + chanPoints, nodes := ht.CreateSimpleNetwork(cfgs, openChannelParams) chanPoint := chanPoints[0] alice, bob := nodes[0], nodes[1] diff --git a/itest/lnd_routing_test.go b/itest/lnd_routing_test.go index d92de5aba..c2fc6886b 100644 --- a/itest/lnd_routing_test.go +++ b/itest/lnd_routing_test.go @@ -1525,8 +1525,9 @@ func testRouteFeeCutoff(ht *lntest.HarnessTest) { func testFeeLimitAfterQueryRoutes(ht *lntest.HarnessTest) { // Create a three hop network: Alice -> Bob -> Carol. chanAmt := btcutil.Amount(100000) - chanPoints, nodes := createSimpleNetwork( - ht, []string{}, 3, lntest.OpenChannelParams{Amt: chanAmt}, + cfgs := [][]string{nil, nil, nil} + chanPoints, nodes := ht.CreateSimpleNetwork( + cfgs, lntest.OpenChannelParams{Amt: chanAmt}, ) alice, bob, carol := nodes[0], nodes[1], nodes[2] chanPointAliceBob, chanPointBobCarol := chanPoints[0], chanPoints[1] diff --git a/itest/lnd_sweep_test.go b/itest/lnd_sweep_test.go index fbe3e6e4c..17e0910b6 100644 --- a/itest/lnd_sweep_test.go +++ b/itest/lnd_sweep_test.go @@ -98,12 +98,14 @@ func testSweepCPFPAnchorOutgoingTimeout(ht *lntest.HarnessTest) { // swept so we can focus on testing HTLCs. fmt.Sprintf("--bitcoin.defaultremotedelay=%v", cltvDelta*10), } + cfgs := [][]string{cfg, cfg, cfg} + openChannelParams := lntest.OpenChannelParams{ Amt: invoiceAmt * 10, } // Create a three hop network: Alice -> Bob -> Carol. - chanPoints, nodes := createSimpleNetwork(ht, cfg, 3, openChannelParams) + chanPoints, nodes := ht.CreateSimpleNetwork(cfgs, openChannelParams) // Unwrap the results. abChanPoint, bcChanPoint := chanPoints[0], chanPoints[1] @@ -426,12 +428,14 @@ func testSweepCPFPAnchorIncomingTimeout(ht *lntest.HarnessTest) { // swept so we can focus on testing HTLCs. fmt.Sprintf("--bitcoin.defaultremotedelay=%v", cltvDelta*10), } + cfgs := [][]string{cfg, cfg, cfg} + openChannelParams := lntest.OpenChannelParams{ Amt: invoiceAmt * 10, } // Create a three hop network: Alice -> Bob -> Carol. - chanPoints, nodes := createSimpleNetwork(ht, cfg, 3, openChannelParams) + chanPoints, nodes := ht.CreateSimpleNetwork(cfgs, openChannelParams) // Unwrap the results. abChanPoint, bcChanPoint := chanPoints[0], chanPoints[1] @@ -771,12 +775,14 @@ func testSweepHTLCs(ht *lntest.HarnessTest) { // swept so we can focus on testing HTLCs. fmt.Sprintf("--bitcoin.defaultremotedelay=%v", cltvDelta*10), } + cfgs := [][]string{cfg, cfg, cfg} + openChannelParams := lntest.OpenChannelParams{ Amt: invoiceAmt * 10, } // Create a three hop network: Alice -> Bob -> Carol. - chanPoints, nodes := createSimpleNetwork(ht, cfg, 3, openChannelParams) + chanPoints, nodes := ht.CreateSimpleNetwork(cfgs, openChannelParams) // Unwrap the results. abChanPoint, bcChanPoint := chanPoints[0], chanPoints[1] @@ -1298,13 +1304,15 @@ func testSweepCommitOutputAndAnchor(ht *lntest.HarnessTest) { fmt.Sprintf("--sweeper.nodeadlineconftarget=%v", deadline), fmt.Sprintf("--bitcoin.defaultremotedelay=%v", toLocalCSV), } + cfgs := [][]string{cfg, cfg} + openChannelParams := lntest.OpenChannelParams{ Amt: fundAmt, PushAmt: bobBalance, } // Create a two hop network: Alice -> Bob. - chanPoints, nodes := createSimpleNetwork(ht, cfg, 2, openChannelParams) + chanPoints, nodes := ht.CreateSimpleNetwork(cfgs, openChannelParams) // Unwrap the results. chanPoint := chanPoints[0] @@ -1780,67 +1788,6 @@ func testSweepCommitOutputAndAnchor(ht *lntest.HarnessTest) { ht.MineBlocksAndAssertNumTxes(1, 2) } -// createSimpleNetwork creates the specified number of nodes and makes a -// topology of `node1 -> node2 -> node3...`. Each node is created using the -// specified config, the neighbors are connected, and the channels are opened. -// Each node will be funded with a single UTXO of 1 BTC except the last one. -func createSimpleNetwork(ht *lntest.HarnessTest, nodeCfg []string, - numNodes int, p lntest.OpenChannelParams) ([]*lnrpc.ChannelPoint, - []*node.HarnessNode) { - - // Make a slice of nodes. - nodes := make([]*node.HarnessNode, numNodes) - - // Create new nodes. - for i := range nodes { - nodeName := fmt.Sprintf("Node%q", string(rune('A'+i))) - n := ht.NewNode(nodeName, nodeCfg) - nodes[i] = n - } - - // Connect the nodes in a chain. - for i := 1; i < len(nodes); i++ { - nodeA := nodes[i-1] - nodeB := nodes[i] - ht.EnsureConnected(nodeA, nodeB) - } - - // Fund all the nodes expect the last one. - for i := 0; i < len(nodes)-1; i++ { - node := nodes[i] - ht.FundCoinsUnconfirmed(btcutil.SatoshiPerBitcoin, node) - } - - // Mine 1 block to get the above coins confirmed. - ht.MineBlocksAndAssertNumTxes(1, numNodes-1) - - // Open channels in batch to save blocks mined. - reqs := make([]*lntest.OpenChannelRequest, 0, len(nodes)-1) - for i := 0; i < len(nodes)-1; i++ { - nodeA := nodes[i] - nodeB := nodes[i+1] - - req := &lntest.OpenChannelRequest{ - Local: nodeA, - Remote: nodeB, - Param: p, - } - reqs = append(reqs, req) - } - resp := ht.OpenMultiChannelsAsync(reqs) - - // Make sure the nodes know each other's channels if they are public. - if !p.Private { - for _, node := range nodes { - for _, chanPoint := range resp { - ht.AssertTopologyChannelOpen(node, chanPoint) - } - } - } - - return resp, nodes -} - // testBumpFee checks that when a new input is requested, it's first bumped via // CPFP, then RBF. Along the way, we check the `BumpFee` can properly update // the fee function used by supplying new params. @@ -2185,9 +2132,10 @@ func testBumpForceCloseFee(ht *lntest.HarnessTest) { cfg := []string{ "--protocol.anchors", } + cfgs := [][]string{cfg, cfg} // Create a two hop network: Alice -> Bob. - chanPoints, nodes := createSimpleNetwork(ht, cfg, 2, openChannelParams) + chanPoints, nodes := ht.CreateSimpleNetwork(cfgs, openChannelParams) // Unwrap the results. chanPoint := chanPoints[0] diff --git a/lntest/harness.go b/lntest/harness.go index fd35b84d4..274f22413 100644 --- a/lntest/harness.go +++ b/lntest/harness.go @@ -2209,3 +2209,155 @@ func (h *HarnessTest) SendCoins(a, b *node.HarnessNode, return tx } + +// CreateSimpleNetwork creates the number of nodes specified by the number of +// configs and makes a topology of `node1 -> node2 -> node3...`. Each node is +// created using the specified config, the neighbors are connected, and the +// channels are opened. Each node will be funded with a single UTXO of 1 BTC +// except the last one. +// +// For instance, to create a network with 2 nodes that share the same node +// config, +// +// cfg := []string{"--protocol.anchors"} +// cfgs := [][]string{cfg, cfg} +// params := OpenChannelParams{...} +// chanPoints, nodes := ht.CreateSimpleNetwork(cfgs, params) +// +// This will create two nodes and open an anchor channel between them. +func (h *HarnessTest) CreateSimpleNetwork(nodeCfgs [][]string, + p OpenChannelParams) ([]*lnrpc.ChannelPoint, []*node.HarnessNode) { + + // Create new nodes. + nodes := h.createNodes(nodeCfgs) + + var resp []*lnrpc.ChannelPoint + + // Open zero-conf channels if specified. + if p.ZeroConf { + resp = h.openZeroConfChannelsForNodes(nodes, p) + } else { + // Open channels between the nodes. + resp = h.openChannelsForNodes(nodes, p) + } + + return resp, nodes +} + +// acceptChannel is used to accept a single channel that comes across. This +// should be run in a goroutine and is used to test nodes with the zero-conf +// feature bit. +func acceptChannel(t *testing.T, zeroConf bool, stream rpc.AcceptorClient) { + req, err := stream.Recv() + require.NoError(t, err) + + resp := &lnrpc.ChannelAcceptResponse{ + Accept: true, + PendingChanId: req.PendingChanId, + ZeroConf: zeroConf, + } + err = stream.Send(resp) + require.NoError(t, err) +} + +// createNodes creates the number of nodes specified by the number of configs. +// Each node is created using the specified config, the neighbors are +// connected. +func (h *HarnessTest) createNodes(nodeCfgs [][]string) []*node.HarnessNode { + // Get the number of nodes. + numNodes := len(nodeCfgs) + + // Make a slice of nodes. + nodes := make([]*node.HarnessNode, numNodes) + + // Create new nodes. + for i, nodeCfg := range nodeCfgs { + nodeName := fmt.Sprintf("Node%q", string(rune('A'+i))) + n := h.NewNode(nodeName, nodeCfg) + nodes[i] = n + } + + // Connect the nodes in a chain. + for i := 1; i < len(nodes); i++ { + nodeA := nodes[i-1] + nodeB := nodes[i] + h.EnsureConnected(nodeA, nodeB) + } + + // Fund all the nodes expect the last one. + for i := 0; i < len(nodes)-1; i++ { + node := nodes[i] + h.FundCoinsUnconfirmed(btcutil.SatoshiPerBitcoin, node) + } + + // Mine 1 block to get the above coins confirmed. + h.MineBlocksAndAssertNumTxes(1, numNodes-1) + + return nodes +} + +// openChannelsForNodes takes a list of nodes and makes a topology of `node1 -> +// node2 -> node3...`. +func (h *HarnessTest) openChannelsForNodes(nodes []*node.HarnessNode, + p OpenChannelParams) []*lnrpc.ChannelPoint { + + // Sanity check the params. + require.False(h, p.ZeroConf, "zero-conf channels must be disabled") + require.Greater(h, len(nodes), 1, "need at least 2 nodes") + + // Open channels in batch to save blocks mined. + reqs := make([]*OpenChannelRequest, 0, len(nodes)-1) + for i := 0; i < len(nodes)-1; i++ { + nodeA := nodes[i] + nodeB := nodes[i+1] + + req := &OpenChannelRequest{ + Local: nodeA, + Remote: nodeB, + Param: p, + } + reqs = append(reqs, req) + } + resp := h.OpenMultiChannelsAsync(reqs) + + // Make sure the nodes know each other's channels if they are public. + if !p.Private { + for _, node := range nodes { + for _, chanPoint := range resp { + h.AssertTopologyChannelOpen(node, chanPoint) + } + } + } + + return resp +} + +// openZeroConfChannelsForNodes takes a list of nodes and makes a topology of +// `node1 -> node2 -> node3...` with zero-conf channels. +func (h *HarnessTest) openZeroConfChannelsForNodes(nodes []*node.HarnessNode, + p OpenChannelParams) []*lnrpc.ChannelPoint { + + // Sanity check the params. + require.True(h, p.ZeroConf, "zero-conf channels must be enabled") + require.Greater(h, len(nodes), 1, "need at least 2 nodes") + + // We are opening numNodes-1 channels. + cancels := make([]context.CancelFunc, 0, len(nodes)-1) + + // Create the channel acceptors. + for _, node := range nodes[1:] { + acceptor, cancel := node.RPC.ChannelAcceptor() + go acceptChannel(h.T, true, acceptor) + + cancels = append(cancels, cancel) + } + + // Open channels between the nodes. + resp := h.openChannelsForNodes(nodes, p) + + for _, cancel := range cancels { + cancel() + } + + return resp +}