2016-08-30 08:07:54 +03:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"runtime/debug"
|
|
|
|
"testing"
|
|
|
|
|
2016-08-30 21:12:31 +03:00
|
|
|
"golang.org/x/net/context"
|
|
|
|
|
2016-08-30 08:07:54 +03:00
|
|
|
"github.com/roasbeef/btcd/rpctest"
|
|
|
|
"github.com/roasbeef/btcd/wire"
|
2016-08-31 05:36:33 +03:00
|
|
|
"github.com/roasbeef/btcrpcclient"
|
2016-08-30 08:07:54 +03:00
|
|
|
"github.com/roasbeef/btcutil"
|
|
|
|
)
|
|
|
|
|
|
|
|
type lndTestCase func(net *networkHarness, t *testing.T)
|
|
|
|
|
|
|
|
func assertTxInBlock(block *btcutil.Block, txid *wire.ShaHash, t *testing.T) {
|
|
|
|
for _, tx := range block.Transactions() {
|
|
|
|
if bytes.Equal(txid[:], tx.Sha()[:]) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Fatalf("funding tx was not included in block")
|
|
|
|
}
|
|
|
|
|
|
|
|
// testBasicChannelFunding performs a test excercising expected behavior from a
|
|
|
|
// basic funding workflow. The test creates a new channel between Alice and
|
|
|
|
// Bob, then immediately closes the channel after asserting some expected post
|
|
|
|
// conditions. Finally, the chain itelf is checked to ensure the closing
|
|
|
|
// transaction was mined.
|
|
|
|
func testBasicChannelFunding(net *networkHarness, t *testing.T) {
|
2016-08-31 05:36:33 +03:00
|
|
|
ctxb := context.Background()
|
2016-08-30 08:07:54 +03:00
|
|
|
|
2016-08-31 21:59:08 +03:00
|
|
|
// First establish a channel ween with a capacity of 0.5 BTC between
|
|
|
|
// Alice and Bob.
|
|
|
|
chanAmt := btcutil.Amount(btcutil.SatoshiPerBitcoin / 2)
|
|
|
|
chanOpenUpdate, err := net.OpenChannel(ctxb, net.AliceClient, net.BobClient, chanAmt, 1)
|
2016-08-31 05:36:33 +03:00
|
|
|
if err != nil {
|
2016-08-31 21:59:08 +03:00
|
|
|
t.Fatalf("unable to open channel: %v", err)
|
2016-08-31 05:36:33 +03:00
|
|
|
}
|
|
|
|
|
2016-08-31 21:59:08 +03:00
|
|
|
// Mine a block, then wait for Alice's node to notify us that the
|
|
|
|
// channel has been opened. The funding transaction should be found
|
|
|
|
// within the newly mined block.
|
2016-08-30 08:07:54 +03:00
|
|
|
blockHash, err := net.Miner.Node.Generate(1)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to generate block: %v", err)
|
|
|
|
}
|
|
|
|
block, err := net.Miner.Node.GetBlock(blockHash[0])
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to get block: %v", err)
|
|
|
|
}
|
2016-08-31 21:59:08 +03:00
|
|
|
fundingChanPoint, err := net.WaitForChannelOpen(chanOpenUpdate)
|
2016-08-30 08:07:54 +03:00
|
|
|
if err != nil {
|
2016-08-31 21:59:08 +03:00
|
|
|
t.Fatalf("error while waiting for channeel open: %v", err)
|
2016-08-30 08:07:54 +03:00
|
|
|
}
|
2016-08-31 21:59:08 +03:00
|
|
|
fundingTxID, err := wire.NewShaHash(fundingChanPoint.FundingTxid)
|
2016-08-30 08:07:54 +03:00
|
|
|
if err != nil {
|
2016-08-31 21:59:08 +03:00
|
|
|
t.Fatalf("unable to create sha hash: %v", err)
|
2016-08-30 08:07:54 +03:00
|
|
|
}
|
2016-08-31 21:59:08 +03:00
|
|
|
assertTxInBlock(block, fundingTxID, t)
|
2016-08-30 08:07:54 +03:00
|
|
|
|
|
|
|
// The channel should be listed in the peer information returned by
|
|
|
|
// both peers.
|
2016-08-31 21:59:08 +03:00
|
|
|
err = net.AssertChannelExists(ctxb, net.AliceClient, net.BobClient,
|
|
|
|
fundingChanPoint)
|
2016-08-30 08:07:54 +03:00
|
|
|
if err != nil {
|
2016-08-31 21:59:08 +03:00
|
|
|
t.Fatalf("unable to assert channel existence: %v", err)
|
2016-08-30 08:07:54 +03:00
|
|
|
}
|
2016-08-31 05:36:33 +03:00
|
|
|
|
2016-08-31 21:59:08 +03:00
|
|
|
// Initiate a close from Alice's side.
|
|
|
|
closeUpdates, err := net.CloseChannel(ctxb, net.AliceClient, fundingChanPoint, false)
|
2016-08-31 05:36:33 +03:00
|
|
|
if err != nil {
|
2016-08-31 21:59:08 +03:00
|
|
|
t.Fatalf("unable to clsoe channel: %v", err)
|
2016-08-31 05:36:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Finally, generate a single block, wait for the final close status
|
|
|
|
// update, then ensure that the closing transaction was included in the
|
|
|
|
// block.
|
2016-08-30 08:07:54 +03:00
|
|
|
blockHash, err = net.Miner.Node.Generate(1)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to generate block: %v", err)
|
|
|
|
}
|
|
|
|
block, err = net.Miner.Node.GetBlock(blockHash[0])
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to get block: %v", err)
|
|
|
|
}
|
2016-08-31 21:59:08 +03:00
|
|
|
|
|
|
|
closingTxid, err := net.WaitForChannelClose(closeUpdates)
|
2016-08-30 08:07:54 +03:00
|
|
|
if err != nil {
|
2016-08-31 21:59:08 +03:00
|
|
|
t.Fatalf("error while waiting for channel close: %v", err)
|
2016-08-31 05:36:33 +03:00
|
|
|
}
|
2016-08-31 21:59:08 +03:00
|
|
|
assertTxInBlock(block, closingTxid, t)
|
2016-08-30 08:07:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
var lndTestCases = map[string]lndTestCase{
|
|
|
|
"basic funding flow": testBasicChannelFunding,
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestLightningNetworkDaemon performs a series of integration tests amongst a
|
|
|
|
// programatically driven network of lnd nodes.
|
|
|
|
func TestLightningNetworkDaemon(t *testing.T) {
|
|
|
|
var btcdHarness *rpctest.Harness
|
|
|
|
var lightningNetwork *networkHarness
|
|
|
|
var currentTest string
|
|
|
|
var err error
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
// If one of the integration tests caused a panic within the main
|
|
|
|
// goroutine, then tear down all the harnesses in order to avoid
|
|
|
|
// any leaked processes.
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
fmt.Println("recovering from test panic: ", r)
|
|
|
|
if err := btcdHarness.TearDown(); err != nil {
|
|
|
|
fmt.Println("unable to tear btcd harnesses: ", err)
|
|
|
|
}
|
|
|
|
if err := lightningNetwork.TearDownAll(); err != nil {
|
|
|
|
fmt.Println("unable to tear lnd harnesses: ", err)
|
|
|
|
}
|
|
|
|
t.Fatalf("test %v panicked: %s", currentTest, debug.Stack())
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2016-08-31 05:34:13 +03:00
|
|
|
// First create the network harness to gain access to its
|
|
|
|
// 'OnTxAccepted' call back.
|
|
|
|
lightningNetwork, err = newNetworkHarness(nil)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create lightning network harness: %v", err)
|
|
|
|
}
|
|
|
|
defer lightningNetwork.TearDownAll()
|
|
|
|
|
|
|
|
handlers := &btcrpcclient.NotificationHandlers{
|
|
|
|
OnTxAccepted: lightningNetwork.OnTxAccepted,
|
|
|
|
}
|
|
|
|
|
2016-08-30 08:07:54 +03:00
|
|
|
// First create an intance 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.
|
2016-08-31 05:34:13 +03:00
|
|
|
btcdHarness, err = rpctest.New(harnessNetParams, handlers, nil)
|
2016-08-30 08:07:54 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to create mining node: %v", err)
|
|
|
|
}
|
|
|
|
defer btcdHarness.TearDown()
|
|
|
|
if err = btcdHarness.SetUp(true, 50); err != nil {
|
|
|
|
t.Fatalf("unable to set up mining node: %v", err)
|
|
|
|
}
|
2016-08-31 05:34:13 +03:00
|
|
|
if err := btcdHarness.Node.NotifyNewTransactions(false); err != nil {
|
|
|
|
t.Fatalf("unable to request transaction notifications: %v", err)
|
|
|
|
}
|
2016-08-30 08:07:54 +03:00
|
|
|
|
2016-08-31 05:34:13 +03:00
|
|
|
// With the btcd harness created, we can now complete the
|
|
|
|
// initialization of the network.
|
|
|
|
if err := lightningNetwork.InitializeSeedNodes(btcdHarness); err != nil {
|
|
|
|
t.Fatalf("unable to initialize seed nodes: %v", err)
|
2016-08-30 08:07:54 +03:00
|
|
|
}
|
|
|
|
if err = lightningNetwork.SetUp(); err != nil {
|
|
|
|
t.Fatalf("unable to set up test lightning network: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Logf("Running %v integration tests", len(lndTestCases))
|
|
|
|
for testName, lnTest := range lndTestCases {
|
|
|
|
t.Logf("Executing test %v", testName)
|
|
|
|
|
|
|
|
currentTest = testName
|
|
|
|
lnTest(lightningNetwork, t)
|
|
|
|
}
|
|
|
|
}
|