itest+make: add remote mode integration test

This commit is contained in:
Oliver Gugger 2021-12-14 18:02:23 +01:00
parent defe05bac3
commit 82556f5775
No known key found for this signature in database
GPG key ID: 8E4256593F177720
8 changed files with 188 additions and 15 deletions

1
.gitignore vendored
View file

@ -10,6 +10,7 @@ litcli-debug
itest/btcd-itest
itest/litd-itest
itest/lnd-itest
itest/itest.test
itest/.logs
itest/*.log

View file

@ -167,6 +167,7 @@ travis-itest: lint
build-itest: app-build
@$(call print, "Building itest btcd and litd.")
CGO_ENABLED=0 $(GOBUILD) -tags="$(ITEST_TAGS)" -o itest/btcd-itest -ldflags "$(ITEST_LDFLAGS)" $(BTCD_PKG)
CGO_ENABLED=0 $(GOBUILD) -tags="$(ITEST_TAGS)" -o itest/lnd-itest -ldflags "$(ITEST_LDFLAGS)" $(LND_PKG)/cmd/lnd
itest-only:
@$(call print, "Building itest binary.")

View file

@ -0,0 +1,93 @@
package itest
import (
"context"
"testing"
"github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/stretchr/testify/require"
)
// testModeRemote makes sure that in remote mode all daemons work correctly.
func testModeRemote(net *NetworkHarness, t *harnessTest) {
ctx := context.Background()
// Some very basic functionality tests to make sure lnd is working fine
// in remote mode.
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, net.Bob)
// We expect a non-empty alias (truncated node ID) to be returned.
resp, err := net.Bob.GetInfo(ctx, &lnrpc.GetInfoRequest{})
require.NoError(t.t, err)
require.NotEmpty(t.t, resp.Alias)
require.Contains(t.t, resp.Alias, "0")
t.t.Run("certificate check", func(tt *testing.T) {
cfg := net.Bob.Cfg
// In remote mode we expect the LiT HTTPS port (8443 by default)
// and to have its own certificate
litCerts, err := getServerCertificates(cfg.LitAddr())
require.NoError(tt, err)
require.Len(tt, litCerts, 1)
require.Equal(
tt, "litd autogenerated cert",
litCerts[0].Issuer.Organization[0],
)
})
t.t.Run("gRPC macaroon auth check", func(tt *testing.T) {
cfg := net.Bob.Cfg
for _, endpoint := range endpoints {
endpoint := endpoint
tt.Run(endpoint.name+" lit port", func(ttt *testing.T) {
if !endpoint.supportsMacAuthOnLitPort {
return
}
runGRPCAuthTest(
ttt, cfg.LitAddr(), cfg.LitTLSCertPath,
endpoint.macaroonFn(cfg),
endpoint.requestFn,
endpoint.successPattern,
)
})
}
})
t.t.Run("UI password auth check", func(tt *testing.T) {
cfg := net.Bob.Cfg
for _, endpoint := range endpoints {
endpoint := endpoint
tt.Run(endpoint.name+" lit port", func(ttt *testing.T) {
runUIPasswordCheck(
ttt, cfg.LitAddr(), cfg.LitTLSCertPath,
cfg.UIPassword,
endpoint.requestFn, false,
!endpoint.supportsUIPasswordOnLitPort,
endpoint.successPattern,
)
})
}
})
t.t.Run("UI index page fallback", func(tt *testing.T) {
runIndexPageCheck(tt, net.Bob.Cfg.LitAddr())
})
t.t.Run("grpc-web auth", func(tt *testing.T) {
cfg := net.Bob.Cfg
for _, endpoint := range endpoints {
endpoint := endpoint
tt.Run(endpoint.name+" lit port", func(ttt *testing.T) {
runGRPCWebAuthTest(
ttt, cfg.LitAddr(), cfg.UIPassword,
endpoint.grpcWebURI,
)
})
}
})
}

View file

@ -52,6 +52,8 @@ var (
// numActiveNodes is the number of active nodes within the test network.
numActiveNodes = 0
numActiveNodesMtx sync.Mutex
defaultLndPassphrase = []byte("default-wallet-password")
)
type LitNodeConfig struct {
@ -59,9 +61,12 @@ type LitNodeConfig struct {
LitArgs []string
RemoteMode bool
FaradayMacPath string
LoopMacPath string
PoolMacPath string
LitTLSCertPath string
UIPassword string
LitDir string
@ -105,9 +110,9 @@ func (cfg *LitNodeConfig) GenArgs() []string {
fmt.Sprintf("--pool.basedir=%s", cfg.PoolDir),
fmt.Sprintf("--uipassword=%s", cfg.UIPassword),
"--restcors=*",
"--lnd-mode=integrated",
}
)
litArgs = append(litArgs, cfg.LitArgs...)
switch cfg.NetParams {
case &chaincfg.TestNet3Params:
@ -118,9 +123,27 @@ func (cfg *LitNodeConfig) GenArgs() []string {
litArgs = append(litArgs, "--network=regtest")
}
// In remote mode, we don't need any lnd specific arguments other than
// those we need to connect.
if cfg.RemoteMode {
litArgs = append(litArgs, "--lnd-mode=remote")
litArgs = append(litArgs, fmt.Sprintf(
"--remote.lnd.rpcserver=%s", cfg.RPCAddr()),
)
litArgs = append(litArgs, fmt.Sprintf(
"--remote.lnd.tlscertpath=%s", cfg.TLSCertPath),
)
litArgs = append(litArgs, fmt.Sprintf(
"--remote.lnd.macaroonpath=%s", cfg.AdminMacPath),
)
return litArgs
}
// All arguments so far were for lnd. Let's namespace them now so we can
// add args for the other daemons and LiT itself afterwards.
litArgs = append(litArgs, cfg.LitArgs...)
litArgs = append(litArgs, "--lnd-mode=integrated")
lndArgs := cfg.BaseNodeConfig.GenArgs()
for idx := range lndArgs {
litArgs = append(
@ -156,6 +179,9 @@ type HarnessNode struct {
// NodeID is a unique identifier for the node within a NetworkHarness.
NodeID int
RemoteLnd *lntest.HarnessNode
RemoteLndHarness *lntest.NetworkHarness
// PubKey is the serialized compressed identity public key of the node.
// This field will only be populated once the node itself has been
// started via the start() method.
@ -220,7 +246,7 @@ var _ lnrpc.WalletUnlockerClient = (*HarnessNode)(nil)
var _ invoicesrpc.InvoicesClient = (*HarnessNode)(nil)
// newNode creates a new test lightning node instance from the passed config.
func newNode(cfg *LitNodeConfig) (*HarnessNode, error) {
func newNode(cfg *LitNodeConfig, harness *NetworkHarness) (*HarnessNode, error) {
if cfg.BaseDir == "" {
var err error
cfg.BaseDir, err = ioutil.TempDir("", "litdtest-node")
@ -252,6 +278,7 @@ func newNode(cfg *LitNodeConfig) (*HarnessNode, error) {
cfg.PoolMacPath = filepath.Join(
cfg.PoolDir, cfg.NetParams.Name, "pool.macaroon",
)
cfg.LitTLSCertPath = filepath.Join(cfg.LitDir, "tls.cert")
cfg.GenerateListeningPorts()
// Generate a random UI password by reading 16 random bytes and base64
@ -270,9 +297,41 @@ func newNode(cfg *LitNodeConfig) (*HarnessNode, error) {
numActiveNodes++
numActiveNodesMtx.Unlock()
var (
remoteNode *lntest.HarnessNode
remoteNodeHarness *lntest.NetworkHarness
err error
)
if cfg.RemoteMode {
lndBinary := strings.ReplaceAll(
getLitdBinary(), itestLitdBinary, itestLndBinary,
)
remoteNodeHarness, err = lntest.NewNetworkHarness(
harness.Miner, harness.BackendCfg, lndBinary,
lntest.BackendBbolt,
)
if err != nil {
return nil, err
}
remoteNode, _, _, err = remoteNodeHarness.NewNodeWithSeed(
cfg.Name, cfg.ExtraArgs, defaultLndPassphrase, false,
)
if err != nil {
return nil, err
}
cfg.RPCPort = remoteNode.Cfg.RPCPort
cfg.P2PPort = remoteNode.Cfg.P2PPort
cfg.TLSCertPath = remoteNode.Cfg.TLSCertPath
cfg.AdminMacPath = remoteNode.Cfg.AdminMacPath
}
return &HarnessNode{
Cfg: cfg,
NodeID: nodeNum,
RemoteLnd: remoteNode,
RemoteLndHarness: remoteNodeHarness,
chanWatchRequests: make(chan *chanWatchRequest),
openChans: make(map[wire.OutPoint]int),
openChanWatchers: make(map[wire.OutPoint][]chan struct{}),
@ -929,14 +988,21 @@ func (hn *HarnessNode) ReadMacaroon(macPath string, timeout time.Duration) (
func (hn *HarnessNode) ConnectRPCWithMacaroon(mac *macaroon.Macaroon) (
*grpc.ClientConn, error) {
var (
certPath = hn.Cfg.TLSCertPath
connectAddr = hn.Cfg.RPCAddr()
)
if hn.Cfg.RemoteMode {
certPath = hn.Cfg.LitTLSCertPath
connectAddr = hn.Cfg.LitAddr()
}
// Wait until TLS certificate is created and has valid content before
// using it, up to 30 sec.
var tlsCreds credentials.TransportCredentials
err := wait.NoError(func() error {
var err error
tlsCreds, err = credentials.NewClientTLSFromFile(
hn.Cfg.TLSCertPath, "",
)
tlsCreds, err = credentials.NewClientTLSFromFile(certPath, "")
return err
}, lntest.DefaultTimeout)
if err != nil {
@ -948,11 +1014,13 @@ func (hn *HarnessNode) ConnectRPCWithMacaroon(mac *macaroon.Macaroon) (
grpc.WithTransportCredentials(tlsCreds),
}
ctx, cancel := context.WithTimeout(context.Background(), lntest.DefaultTimeout)
ctx, cancel := context.WithTimeout(
context.Background(), lntest.DefaultTimeout,
)
defer cancel()
if mac == nil {
return grpc.DialContext(ctx, hn.Cfg.RPCAddr(), opts...)
return grpc.DialContext(ctx, connectAddr, opts...)
}
macCred, err := macaroons.NewMacaroonCredential(mac)
if err != nil {
@ -960,7 +1028,7 @@ func (hn *HarnessNode) ConnectRPCWithMacaroon(mac *macaroon.Macaroon) (
}
opts = append(opts, grpc.WithPerRPCCredentials(macCred))
return grpc.DialContext(ctx, hn.Cfg.RPCAddr(), opts...)
return grpc.DialContext(ctx, connectAddr, opts...)
}
// ConnectRPC uses the TLS certificate and admin macaroon files written by the
@ -1061,6 +1129,10 @@ func (hn *HarnessNode) stop() error {
}
}
if hn.Cfg.RemoteMode {
return hn.RemoteLndHarness.ShutdownNode(hn.RemoteLnd)
}
return nil
}

View file

@ -73,7 +73,7 @@ func TestLightningTerminal(t *testing.T) {
// Now we can set up our test harness (LND instance), with the chain
// backend we just created.
ht := newHarnessTest(t, nil)
binary := ht.getLitdBinary()
binary := getLitdBinary()
litdHarness, err = NewNetworkHarness(miner, chainBackend, binary)
if err != nil {
ht.Fatalf("unable to create lightning network harness: %v", err)

View file

@ -8,4 +8,8 @@ var allTestCases = []*testCase{
name: "test mode integrated",
test: testModeIntegrated,
},
{
name: "test mode remote",
test: testModeRemote,
},
}

View file

@ -144,14 +144,14 @@ func (n *NetworkHarness) SetUp(t *testing.T,
eg.Go(func() error {
var err error
n.Alice, err = n.newNode(
"Alice", lndArgs, litArgs, false, nil, true,
"Alice", lndArgs, litArgs, false, false, nil, true,
)
return err
})
eg.Go(func() error {
var err error
n.Bob, err = n.newNode(
"Bob", lndArgs, litArgs, false, nil, true,
"Bob", lndArgs, litArgs, false, true, nil, true,
)
return err
})
@ -265,8 +265,8 @@ func (n *NetworkHarness) Stop() {
// can be used immediately. Otherwise, the node will require an additional
// initialization phase where the wallet is either created or restored.
func (n *NetworkHarness) newNode(name string, extraArgs, litArgs []string,
hasSeed bool, password []byte, wait bool, opts ...lntest.NodeOption) (
*HarnessNode, error) {
hasSeed, remoteMode bool, password []byte, wait bool,
opts ...lntest.NodeOption) (*HarnessNode, error) {
baseCfg := &lntest.BaseNodeConfig{
Name: name,
@ -283,9 +283,10 @@ func (n *NetworkHarness) newNode(name string, extraArgs, litArgs []string,
cfg := &LitNodeConfig{
BaseNodeConfig: baseCfg,
LitArgs: litArgs,
RemoteMode: remoteMode,
}
node, err := newNode(cfg)
node, err := newNode(cfg, n)
if err != nil {
return nil, err
}

View file

@ -31,6 +31,7 @@ const (
defaultTimeout = lntest.DefaultTimeout
minerMempoolTimeout = lntest.MinerMempoolTimeout
itestLitdBinary = "litd-itest"
itestLndBinary = "lnd-itest"
)
// harnessTest wraps a regular testing.T providing enhanced error detection
@ -103,7 +104,7 @@ func (h *harnessTest) Log(args ...interface{}) {
h.t.Log(args...)
}
func (h *harnessTest) getLitdBinary() string {
func getLitdBinary() string {
binary := itestLitdBinary
litdExec := ""
if litdExecutable != nil && *litdExecutable != "" {