mirror of
https://github.com/lightninglabs/lightning-terminal.git
synced 2026-08-13 12:33:36 +02:00
multi: new disable mode for the taproot assets subserver
Enable to start litd with taproot asset subserver disabled. The default mode for the new sub-server is "Disabled" Add coverage in itests for flows with some subservers disabled (based on Elle's #537)
This commit is contained in:
parent
e78d1cbd38
commit
219a9ccb85
5 changed files with 350 additions and 101 deletions
15
config.go
15
config.go
|
|
@ -42,12 +42,13 @@ const (
|
|||
|
||||
ModeIntegrated = "integrated"
|
||||
ModeRemote = "remote"
|
||||
ModeDisable = "disable"
|
||||
|
||||
DefaultLndMode = ModeRemote
|
||||
defaultFaradayMode = ModeIntegrated
|
||||
defaultLoopMode = ModeIntegrated
|
||||
defaultPoolMode = ModeIntegrated
|
||||
defaultTapMode = ModeIntegrated
|
||||
defaultTapMode = ModeDisable
|
||||
|
||||
defaultConfigFilename = "lit.conf"
|
||||
|
||||
|
|
@ -195,7 +196,7 @@ type Config struct {
|
|||
PoolMode string `long:"pool-mode" description:"The mode to run pool in, either 'integrated' (default) or 'remote'. 'integrated' means poold is started alongside the UI and everything is stored in pool's main data directory, configure everything by using the --pool.* flags. 'remote' means the UI connects to an existing poold node and acts as a proxy for gRPC calls to it." choice:"integrated" choice:"remote"`
|
||||
Pool *pool.Config `group:"Integrated pool options (use when pool-mode=integrated)" namespace:"pool"`
|
||||
|
||||
TaprootAssetsMode string `long:"taproot-assets-mode" description:"The mode to run taproot assets in, either 'integrated' (default) or 'remote'. 'integrated' means tapd is started alongside the UI and everything is stored in tap's main data directory, configure everything by using the --taproot-assets.* flags. 'remote' means the UI connects to an existing tapd node and acts as a proxy for gRPC calls to it." choice:"integrated" choice:"remote"`
|
||||
TaprootAssetsMode string `long:"taproot-assets-mode" description:"The mode to run taproot assets in, either 'integrated' (default), 'remote' or 'disable'. 'integrated' means tapd is started alongside the UI and everything is stored in tap's main data directory, configure everything by using the --taproot-assets.* flags. 'remote' means the UI connects to an existing tapd node and acts as a proxy for gRPC calls to it. 'disable' means that LiT is started without a connection to tapd" choice:"integrated" choice:"disable"`
|
||||
TaprootAssets *tapcfg.Config `group:"Integrated taproot assets options (use when taproot-assets=integrated)" namespace:"taproot-assets"`
|
||||
|
||||
RPCMiddleware *mid.Config `group:"RPC middleware options" namespace:"rpcmiddleware"`
|
||||
|
|
@ -462,9 +463,13 @@ func loadAndValidateConfig(interceptor signal.Interceptor) (*Config, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
cfg.TaprootAssets, err = tapcfg.ValidateConfig(*cfg.TaprootAssets, log)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if cfg.TaprootAssetsMode != ModeDisable {
|
||||
cfg.TaprootAssets, err = tapcfg.ValidateConfig(
|
||||
*cfg.TaprootAssets, log,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// We've set the network before and have now validated the loop config
|
||||
|
|
|
|||
|
|
@ -195,6 +195,7 @@ var (
|
|||
grpcWebURI string
|
||||
restWebURI string
|
||||
restPOST bool
|
||||
canDisable bool
|
||||
}{{
|
||||
name: "lnrpc",
|
||||
macaroonFn: lndMacaroonFn,
|
||||
|
|
@ -252,6 +253,7 @@ var (
|
|||
allowedThroughLNC: true,
|
||||
grpcWebURI: "/taprpc.TaprootAssets/ListAssets",
|
||||
restWebURI: "/v1/taproot-assets/assets",
|
||||
canDisable: true,
|
||||
}, {
|
||||
name: "litrpc-sessions",
|
||||
macaroonFn: litMacaroonFn,
|
||||
|
|
@ -297,9 +299,37 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
// testSuite defines the signature of a test suite. The boolean parameter
|
||||
// indicates if the UI password is set or disabled.
|
||||
type testSuite func(context.Context, *NetworkHarness, *testing.T, bool, int)
|
||||
// testSuite defines the signature of a test suite. The first boolean parameter
|
||||
// indicates if the UI password is set or disabled and the second one indicates
|
||||
// if the LiT sub-servers are disabled.
|
||||
type testSuite func(context.Context, *NetworkHarness, *testing.T, bool, bool,
|
||||
int)
|
||||
|
||||
// testDisablingSubServers will restart LiT with some sub-servers disabled and
|
||||
// will then run the test suite against it.
|
||||
func testDisablingSubServers(ctx context.Context, net *NetworkHarness,
|
||||
t *testing.T, test testSuite, node *HarnessNode) {
|
||||
|
||||
// Restart the Lit node with some the sub-servers disabled.
|
||||
err := net.RestartNode(
|
||||
node, nil, []LitArgOption{
|
||||
WithLitArg("taproot-assets-mode", "disable"),
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
if !node.Cfg.RemoteMode {
|
||||
// Reconnect Alice and Bob so that tests can continue to assert
|
||||
// that the nodes each have one peer at start up. This is only
|
||||
// required if the node is running in integrated mode since in
|
||||
// remote mode, the peers would never have disconnected.
|
||||
net.ConnectNodes(t, net.Alice, net.Bob)
|
||||
}
|
||||
|
||||
t.Run("disable sub-servers", func(t *testing.T) {
|
||||
test(ctx, net, t, false, true, 3)
|
||||
})
|
||||
}
|
||||
|
||||
// testWithAndWithoutUIPassword runs the given test suite against the given node
|
||||
// both with the UI password set and without the UI password with the UI
|
||||
|
|
@ -308,7 +338,7 @@ func testWithAndWithoutUIPassword(ctx context.Context, net *NetworkHarness,
|
|||
t *testing.T, test testSuite, node *HarnessNode) {
|
||||
|
||||
t.Run("with UI password", func(t *testing.T) {
|
||||
test(ctx, net, t, false, 1)
|
||||
test(ctx, net, t, false, false, 1)
|
||||
})
|
||||
|
||||
// Restart the node without the ui password and disable the UI.
|
||||
|
|
@ -329,7 +359,7 @@ func testWithAndWithoutUIPassword(ctx context.Context, net *NetworkHarness,
|
|||
}
|
||||
|
||||
t.Run("without UI password", func(t *testing.T) {
|
||||
test(ctx, net, t, true, 2)
|
||||
test(ctx, net, t, true, false, 2)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -342,12 +372,16 @@ func testModeIntegrated(ctx context.Context, net *NetworkHarness,
|
|||
testWithAndWithoutUIPassword(
|
||||
ctx, net, t.t, integratedTestSuite, net.Alice,
|
||||
)
|
||||
|
||||
testDisablingSubServers(
|
||||
ctx, net, t.t, integratedTestSuite, net.Alice,
|
||||
)
|
||||
}
|
||||
|
||||
// integratedTestSuite makes sure that in integrated mode all daemons work
|
||||
// correctly.
|
||||
func integratedTestSuite(ctx context.Context, net *NetworkHarness, t *testing.T,
|
||||
withoutUIPassword bool, runNum int) {
|
||||
withoutUIPassword, subServersDisabled bool, runNum int) {
|
||||
|
||||
// Some very basic functionality tests to make sure lnd is working fine
|
||||
// in integrated mode.
|
||||
|
|
@ -367,12 +401,17 @@ func integratedTestSuite(ctx context.Context, net *NetworkHarness, t *testing.T,
|
|||
|
||||
for _, endpoint := range endpoints {
|
||||
endpoint := endpoint
|
||||
endpointDisabled := subServersDisabled &&
|
||||
endpoint.canDisable
|
||||
|
||||
tt.Run(endpoint.name+" lnd port", func(ttt *testing.T) {
|
||||
runGRPCAuthTest(
|
||||
ttt, cfg.RPCAddr(), cfg.TLSCertPath,
|
||||
endpoint.macaroonFn(cfg),
|
||||
endpoint.requestFn,
|
||||
endpoint.successPattern,
|
||||
endpointDisabled,
|
||||
"Unimplemented desc = unknown service",
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -382,6 +421,9 @@ func integratedTestSuite(ctx context.Context, net *NetworkHarness, t *testing.T,
|
|||
endpoint.macaroonFn(cfg),
|
||||
endpoint.requestFn,
|
||||
endpoint.successPattern,
|
||||
endpointDisabled,
|
||||
"unknown permissions required for "+
|
||||
"method",
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
@ -392,11 +434,16 @@ func integratedTestSuite(ctx context.Context, net *NetworkHarness, t *testing.T,
|
|||
|
||||
for _, endpoint := range endpoints {
|
||||
endpoint := endpoint
|
||||
endpointDisabled := subServersDisabled &&
|
||||
endpoint.canDisable
|
||||
|
||||
tt.Run(endpoint.name+" lnd port", func(ttt *testing.T) {
|
||||
runUIPasswordCheck(
|
||||
ttt, cfg.RPCAddr(), cfg.TLSCertPath,
|
||||
cfg.UIPassword, endpoint.requestFn,
|
||||
true, endpoint.successPattern,
|
||||
endpointDisabled,
|
||||
"Unimplemented desc = unknown service",
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -411,6 +458,9 @@ func integratedTestSuite(ctx context.Context, net *NetworkHarness, t *testing.T,
|
|||
cfg.UIPassword, endpoint.requestFn,
|
||||
shouldFailWithoutMacaroon,
|
||||
endpoint.successPattern,
|
||||
endpointDisabled,
|
||||
"unknown permissions required for "+
|
||||
"method",
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
@ -427,11 +477,16 @@ func integratedTestSuite(ctx context.Context, net *NetworkHarness, t *testing.T,
|
|||
|
||||
for _, endpoint := range endpoints {
|
||||
endpoint := endpoint
|
||||
endpointDisabled := subServersDisabled &&
|
||||
endpoint.canDisable
|
||||
|
||||
tt.Run(endpoint.name+" lit port", func(ttt *testing.T) {
|
||||
runGRPCWebAuthTest(
|
||||
ttt, cfg.LitAddr(), cfg.UIPassword,
|
||||
endpoint.grpcWebURI,
|
||||
withoutUIPassword,
|
||||
withoutUIPassword, endpointDisabled,
|
||||
"unknown permissions required for "+
|
||||
"method",
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
@ -449,12 +504,17 @@ func integratedTestSuite(ctx context.Context, net *NetworkHarness, t *testing.T,
|
|||
|
||||
for _, endpoint := range endpoints {
|
||||
endpoint := endpoint
|
||||
endpointDisabled := subServersDisabled &&
|
||||
endpoint.canDisable
|
||||
|
||||
tt.Run(endpoint.name+" lnd port", func(ttt *testing.T) {
|
||||
runGRPCAuthTest(
|
||||
ttt, cfg.RPCAddr(), cfg.TLSCertPath,
|
||||
superMacFile,
|
||||
endpoint.requestFn,
|
||||
endpoint.successPattern,
|
||||
endpointDisabled,
|
||||
"Unimplemented desc = unknown service",
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -464,6 +524,9 @@ func integratedTestSuite(ctx context.Context, net *NetworkHarness, t *testing.T,
|
|||
superMacFile,
|
||||
endpoint.requestFn,
|
||||
endpoint.successPattern,
|
||||
endpointDisabled,
|
||||
"unknown permissions required for "+
|
||||
"method",
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
@ -474,6 +537,8 @@ func integratedTestSuite(ctx context.Context, net *NetworkHarness, t *testing.T,
|
|||
|
||||
for _, endpoint := range endpoints {
|
||||
endpoint := endpoint
|
||||
endpointDisabled := subServersDisabled &&
|
||||
endpoint.canDisable
|
||||
|
||||
tt.Run(endpoint.name+" lit port", func(ttt *testing.T) {
|
||||
runRESTAuthTest(
|
||||
|
|
@ -482,7 +547,7 @@ func integratedTestSuite(ctx context.Context, net *NetworkHarness, t *testing.T,
|
|||
endpoint.restWebURI,
|
||||
endpoint.successPattern,
|
||||
endpoint.restPOST,
|
||||
withoutUIPassword,
|
||||
withoutUIPassword, endpointDisabled,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
@ -504,12 +569,16 @@ func integratedTestSuite(ctx context.Context, net *NetworkHarness, t *testing.T,
|
|||
|
||||
for _, endpoint := range endpoints {
|
||||
endpoint := endpoint
|
||||
endpointDisabled := subServersDisabled &&
|
||||
endpoint.canDisable
|
||||
|
||||
tt.Run(endpoint.name+" lit port", func(ttt *testing.T) {
|
||||
runLNCAuthTest(
|
||||
ttt, rawLNCConn, endpoint.requestFn,
|
||||
endpoint.successPattern,
|
||||
endpoint.allowedThroughLNC,
|
||||
"unknown service",
|
||||
endpointDisabled,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
@ -566,12 +635,16 @@ func integratedTestSuite(ctx context.Context, net *NetworkHarness, t *testing.T,
|
|||
|
||||
for _, endpoint := range endpoints {
|
||||
endpoint := endpoint
|
||||
endpointDisabled := subServersDisabled &&
|
||||
endpoint.canDisable
|
||||
|
||||
tt.Run(endpoint.name+" lit port", func(ttt *testing.T) {
|
||||
allowed := customURIs[endpoint.grpcWebURI]
|
||||
runLNCAuthTest(
|
||||
ttt, rawLNCConn, endpoint.requestFn,
|
||||
endpoint.successPattern,
|
||||
allowed, "permission denied",
|
||||
endpointDisabled,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
@ -636,7 +709,8 @@ func runCertificateCheck(t *testing.T, node *HarnessNode) {
|
|||
|
||||
// runGRPCAuthTest tests authentication of the given gRPC interface.
|
||||
func runGRPCAuthTest(t *testing.T, hostPort, tlsCertPath, macPath string,
|
||||
makeRequest requestFn, successContent string) {
|
||||
makeRequest requestFn, successContent string, disabled bool,
|
||||
disabledErr string) {
|
||||
|
||||
ctxb := context.Background()
|
||||
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
|
||||
|
|
@ -648,35 +722,48 @@ func runGRPCAuthTest(t *testing.T, hostPort, tlsCertPath, macPath string,
|
|||
|
||||
// We have a connection without any macaroon. A call should fail.
|
||||
_, err = makeRequest(ctxt, rawConn)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "expected 1 macaroon, got 0")
|
||||
if disabled {
|
||||
require.ErrorContains(t, err, disabledErr)
|
||||
} else {
|
||||
require.ErrorContains(t, err, "expected 1 macaroon, got 0")
|
||||
}
|
||||
|
||||
// Add dummy data as the macaroon, that should fail as well.
|
||||
ctxm := macaroonContext(ctxt, []byte("dummy"))
|
||||
_, err = makeRequest(ctxm, rawConn)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "packet too short")
|
||||
if disabled {
|
||||
require.ErrorContains(t, err, disabledErr)
|
||||
} else {
|
||||
require.ErrorContains(t, err, "packet too short")
|
||||
}
|
||||
|
||||
// Add a macaroon that can be parsed but that's not issued by lnd, which
|
||||
// should also fail.
|
||||
ctxm = macaroonContext(ctxt, dummyMacBytes)
|
||||
_, err = makeRequest(ctxm, rawConn)
|
||||
require.Error(t, err)
|
||||
|
||||
errStr := err.Error()
|
||||
err1 := strings.Contains(errStr, "cannot get macaroon: root")
|
||||
err2 := strings.Contains(errStr, "cannot get macaroon: sql: no")
|
||||
require.Truef(
|
||||
t, err1 || err2, "no macaroon, got unexpected error: %v", err,
|
||||
)
|
||||
if disabled {
|
||||
require.ErrorContains(t, err, disabledErr)
|
||||
} else {
|
||||
errStr := err.Error()
|
||||
err1 := strings.Contains(errStr, "cannot get macaroon: root")
|
||||
err2 := strings.Contains(errStr, "cannot get macaroon: sql: no")
|
||||
require.Truef(
|
||||
t, err1 || err2, "no macaroon, got unexpected error: "+
|
||||
"%v", err,
|
||||
)
|
||||
}
|
||||
|
||||
// Then finally we try with the correct macaroon which should now
|
||||
// succeed.
|
||||
// succeed, as long as it is not for a disabled sub-server.
|
||||
macBytes, err := os.ReadFile(macPath)
|
||||
require.NoError(t, err)
|
||||
ctxm = macaroonContext(ctxt, macBytes)
|
||||
resp, err := makeRequest(ctxm, rawConn)
|
||||
require.NoError(t, err)
|
||||
if disabled {
|
||||
require.ErrorContains(t, err, disabledErr)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
json, err := marshalOptions.Marshal(resp)
|
||||
require.NoError(t, err)
|
||||
|
|
@ -686,7 +773,7 @@ func runGRPCAuthTest(t *testing.T, hostPort, tlsCertPath, macPath string,
|
|||
// runUIPasswordCheck tests UI password authentication.
|
||||
func runUIPasswordCheck(t *testing.T, hostPort, tlsCertPath, uiPassword string,
|
||||
makeRequest requestFn, shouldFailWithoutMacaroon bool,
|
||||
successContent string) {
|
||||
successContent string, disabled bool, disabledErr string) {
|
||||
|
||||
ctxb := context.Background()
|
||||
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
|
||||
|
|
@ -698,36 +785,57 @@ func runUIPasswordCheck(t *testing.T, hostPort, tlsCertPath, uiPassword string,
|
|||
|
||||
// Make sure that a call without any metadata results in an error.
|
||||
_, err = makeRequest(ctxt, rawConn)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "expected 1 macaroon, got 0")
|
||||
if disabled {
|
||||
require.ErrorContains(t, err, disabledErr)
|
||||
} else {
|
||||
require.ErrorContains(t, err, "expected 1 macaroon, got 0")
|
||||
}
|
||||
|
||||
// We can do the same calls by providing a UI password. Make sure that
|
||||
// sending an incorrect one is ignored.
|
||||
ctxm := uiPasswordContext(ctxt, "foobar", false)
|
||||
_, err = makeRequest(ctxm, rawConn)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "expected 1 macaroon, got 0")
|
||||
if disabled {
|
||||
require.ErrorContains(t, err, disabledErr)
|
||||
} else {
|
||||
require.ErrorContains(t, err, "expected 1 macaroon, got 0")
|
||||
}
|
||||
|
||||
// Sending a dummy macaroon along with the incorrect UI password also
|
||||
// shouldn't be allowed and result in an error.
|
||||
ctxm = uiPasswordContext(ctxt, "foobar", true)
|
||||
_, err = makeRequest(ctxm, rawConn)
|
||||
require.Error(t, err)
|
||||
errStr := err.Error()
|
||||
err1 := strings.Contains(errStr, "invalid auth: invalid basic auth")
|
||||
err2 := strings.Contains(errStr, "cannot get macaroon: root key with")
|
||||
err3 := strings.Contains(errStr, "cannot get macaroon: sql: no rows")
|
||||
require.Truef(t, err1 || err2 || err3, "wrong UI password and dummy "+
|
||||
"mac, got unexpected error: %v", err)
|
||||
if disabled {
|
||||
require.ErrorContains(t, err, disabledErr)
|
||||
} else {
|
||||
errStr := err.Error()
|
||||
err1 := strings.Contains(
|
||||
errStr, "invalid auth: invalid basic auth",
|
||||
)
|
||||
err2 := strings.Contains(
|
||||
errStr, "cannot get macaroon: root key with",
|
||||
)
|
||||
err3 := strings.Contains(
|
||||
errStr, "cannot get macaroon: sql: no rows",
|
||||
)
|
||||
|
||||
// Using the correct UI password should work for all requests.
|
||||
require.Truef(t, err1 || err2 || err3, "wrong UI password and "+
|
||||
"dummy mac, got unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// Using the correct UI password should work for all requests unless the
|
||||
// request is for a disabled sub-server.
|
||||
ctxm = uiPasswordContext(ctxt, uiPassword, false)
|
||||
resp, err := makeRequest(ctxm, rawConn)
|
||||
|
||||
// On lnd's gRPC interface we don't support using the UI password.
|
||||
if shouldFailWithoutMacaroon {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "expected 1 macaroon, got 0")
|
||||
if disabled {
|
||||
require.ErrorContains(t, err, disabledErr)
|
||||
} else {
|
||||
require.ErrorContains(t, err, "expected 1 macaroon, "+
|
||||
"got 0")
|
||||
}
|
||||
|
||||
// Sending a dummy macaroon will allow us to not get an error in
|
||||
// case of the litrpc calls, where we don't support macaroons
|
||||
|
|
@ -737,16 +845,31 @@ func runUIPasswordCheck(t *testing.T, hostPort, tlsCertPath, uiPassword string,
|
|||
ctxm = uiPasswordContext(ctxt, uiPassword, true)
|
||||
_, err = makeRequest(ctxm, rawConn)
|
||||
|
||||
require.Error(t, err)
|
||||
errStr := err.Error()
|
||||
err1 := strings.Contains(errStr, "cannot get macaroon: root")
|
||||
err2 := strings.Contains(errStr, "cannot get macaroon: sql: no")
|
||||
require.Truef(t, err1 || err2, "no macaroon, got unexpected "+
|
||||
"error: %v", err)
|
||||
if disabled {
|
||||
require.ErrorContains(t, err, disabledErr)
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
errStr := err.Error()
|
||||
err1 := strings.Contains(
|
||||
errStr, "cannot get macaroon: root",
|
||||
)
|
||||
err2 := strings.Contains(
|
||||
errStr, "cannot get macaroon: sql: no",
|
||||
)
|
||||
|
||||
require.Truef(t, err1 || err2, "no macaroon, got "+
|
||||
"unexpected error: %v", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// We expect the call to succeed.
|
||||
if disabled {
|
||||
require.ErrorContains(t, err, disabledErr)
|
||||
return
|
||||
}
|
||||
|
||||
// We expect the call to succeed unless it is for a disabled sub-server.
|
||||
require.NoError(t, err)
|
||||
|
||||
json, err := marshalOptions.Marshal(resp)
|
||||
|
|
@ -775,7 +898,7 @@ func runIndexPageCheck(t *testing.T, hostPort string, uiDisabled bool) {
|
|||
|
||||
// runGRPCWebAuthTest tests authentication of the given gRPC interface.
|
||||
func runGRPCWebAuthTest(t *testing.T, hostPort, uiPassword, grpcWebURI string,
|
||||
shouldFailWithUIPassword bool) {
|
||||
shouldFailWithUIPassword, disabled bool, disableErr string) {
|
||||
|
||||
basicAuth := base64.StdEncoding.EncodeToString(
|
||||
[]byte(fmt.Sprintf("%s:%s", uiPassword, uiPassword)),
|
||||
|
|
@ -792,10 +915,17 @@ func runGRPCWebAuthTest(t *testing.T, hostPort, uiPassword, grpcWebURI string,
|
|||
_, responseHeader, err := postURL(url, emptyGrpcWebRequest, header)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(
|
||||
t, "expected 1 macaroon, got 0",
|
||||
responseHeader.Get("grpc-message"),
|
||||
)
|
||||
if disabled {
|
||||
require.Contains(
|
||||
t, responseHeader.Get("grpc-message"), disableErr,
|
||||
)
|
||||
} else {
|
||||
require.Equal(
|
||||
t, "expected 1 macaroon, got 0",
|
||||
responseHeader.Get("grpc-message"),
|
||||
)
|
||||
}
|
||||
|
||||
require.Equal(
|
||||
t, fmt.Sprintf("%d", codes.Unknown),
|
||||
responseHeader.Get("grpc-status"),
|
||||
|
|
@ -818,16 +948,27 @@ func runGRPCWebAuthTest(t *testing.T, hostPort, uiPassword, grpcWebURI string,
|
|||
return
|
||||
}
|
||||
|
||||
require.Empty(t, responseHeader.Get("grpc-message"))
|
||||
require.Empty(t, responseHeader.Get("grpc-status"))
|
||||
if disabled {
|
||||
require.Contains(
|
||||
t, responseHeader.Get("grpc-message"), disableErr,
|
||||
)
|
||||
require.Equal(
|
||||
t, fmt.Sprintf("%d", codes.Unknown),
|
||||
responseHeader.Get("grpc-status"),
|
||||
)
|
||||
} else {
|
||||
require.Empty(t, responseHeader.Get("grpc-message"))
|
||||
require.Empty(t, responseHeader.Get("grpc-status"))
|
||||
|
||||
// We get the status encoded as trailer in the response.
|
||||
require.Contains(t, body, "grpc-status: 0")
|
||||
// We get the status encoded as trailer in the response.
|
||||
require.Contains(t, body, "grpc-status: 0")
|
||||
}
|
||||
}
|
||||
|
||||
// runRESTAuthTest tests authentication of the given REST interface.
|
||||
func runRESTAuthTest(t *testing.T, hostPort, uiPassword, macaroonPath, restURI,
|
||||
successPattern string, usePOST, shouldFailWithUIPassword bool) {
|
||||
successPattern string, usePOST, shouldFailWithUIPassword,
|
||||
disabled bool) {
|
||||
|
||||
basicAuth := base64.StdEncoding.EncodeToString(
|
||||
[]byte(fmt.Sprintf("%s:%s", uiPassword, uiPassword)),
|
||||
|
|
@ -846,19 +987,24 @@ func runRESTAuthTest(t *testing.T, hostPort, uiPassword, macaroonPath, restURI,
|
|||
body, responseHeader, err := callURL(url, method, nil, nil, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equalf(
|
||||
t, "application/grpc",
|
||||
responseHeader.Get("grpc-metadata-content-type"),
|
||||
"response headers: %v, body: %v", responseHeader, body,
|
||||
)
|
||||
require.Equal(
|
||||
t, "application/json",
|
||||
responseHeader.Get("content-type"),
|
||||
)
|
||||
require.Contains(
|
||||
t, body,
|
||||
"expected 1 macaroon, got 0",
|
||||
)
|
||||
|
||||
if disabled {
|
||||
require.Empty(
|
||||
t, responseHeader.Get("grpc-metadata-content-type"),
|
||||
)
|
||||
require.Contains(t, body, "Not Found")
|
||||
} else {
|
||||
require.Equalf(
|
||||
t, "application/grpc",
|
||||
responseHeader.Get("grpc-metadata-content-type"),
|
||||
"response headers: %v, body: %v", responseHeader, body,
|
||||
)
|
||||
require.Contains(t, body, "expected 1 macaroon, got 0")
|
||||
}
|
||||
|
||||
// Now add the UI password which should make the request succeed.
|
||||
body, responseHeader, err = callURL(
|
||||
|
|
@ -866,13 +1012,16 @@ func runRESTAuthTest(t *testing.T, hostPort, uiPassword, macaroonPath, restURI,
|
|||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
if shouldFailWithUIPassword {
|
||||
require.Contains(
|
||||
t, body,
|
||||
"expected 1 macaroon, got 0",
|
||||
)
|
||||
} else {
|
||||
switch {
|
||||
case shouldFailWithUIPassword:
|
||||
require.Contains(t, body, "expected 1 macaroon, got 0")
|
||||
|
||||
case disabled:
|
||||
require.Contains(t, body, "Not Found")
|
||||
|
||||
default:
|
||||
require.Contains(t, body, successPattern)
|
||||
|
||||
}
|
||||
|
||||
// And finally, try with the given macaroon.
|
||||
|
|
@ -888,14 +1037,19 @@ func runRESTAuthTest(t *testing.T, hostPort, uiPassword, macaroonPath, restURI,
|
|||
url, method, nil, macaroonHeader, false,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, successPattern)
|
||||
|
||||
if disabled {
|
||||
require.Contains(t, body, "Not Found")
|
||||
} else {
|
||||
require.Contains(t, body, successPattern)
|
||||
}
|
||||
}
|
||||
|
||||
// runLNCAuthTest tests authentication of the given interface when connecting
|
||||
// through Lightning Node Connect.
|
||||
func runLNCAuthTest(t *testing.T, rawLNCConn grpc.ClientConnInterface,
|
||||
makeRequest requestFn, successContent string, callAllowed bool,
|
||||
expectErrContains string) {
|
||||
expectErrContains string, disabled bool) {
|
||||
|
||||
ctxt, cancel := context.WithTimeout(
|
||||
context.Background(), defaultTimeout,
|
||||
|
|
@ -910,14 +1064,26 @@ func runLNCAuthTest(t *testing.T, rawLNCConn grpc.ClientConnInterface,
|
|||
|
||||
// Is this a disallowed call?
|
||||
if !callAllowed {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), expectErrContains)
|
||||
if disabled {
|
||||
require.ErrorContains(t, err, "unknown permissions "+
|
||||
"required for method")
|
||||
} else {
|
||||
require.ErrorContains(t, err, expectErrContains)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// The call should be allowed, so we expect no error.
|
||||
require.NoError(t, err)
|
||||
// The call should be allowed, so we expect no error unless this is
|
||||
// for a disabled sub-server.
|
||||
if disabled {
|
||||
require.ErrorContains(t, err, "unknown permissions "+
|
||||
"required for method")
|
||||
|
||||
return
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
json, err := marshalOptions.Marshal(resp)
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
|
|
@ -19,11 +19,13 @@ func testModeRemote(ctx context.Context, net *NetworkHarness,
|
|||
t *harnessTest) {
|
||||
|
||||
testWithAndWithoutUIPassword(ctx, net, t.t, remoteTestSuite, net.Bob)
|
||||
|
||||
testDisablingSubServers(ctx, net, t.t, remoteTestSuite, net.Bob)
|
||||
}
|
||||
|
||||
// remoteTestSuite makes sure that in remote mode all daemons work correctly.
|
||||
func remoteTestSuite(ctx context.Context, net *NetworkHarness, t *testing.T,
|
||||
withoutUIPassword bool, runNum int) {
|
||||
withoutUIPassword, subServersDisabled bool, runNum int) {
|
||||
|
||||
// Some very basic functionality tests to make sure lnd is working fine
|
||||
// in remote mode.
|
||||
|
|
@ -54,12 +56,18 @@ func remoteTestSuite(ctx context.Context, net *NetworkHarness, t *testing.T,
|
|||
|
||||
for _, endpoint := range endpoints {
|
||||
endpoint := endpoint
|
||||
endpointEnabled := subServersDisabled &&
|
||||
endpoint.canDisable
|
||||
|
||||
tt.Run(endpoint.name+" lit port", func(ttt *testing.T) {
|
||||
runGRPCAuthTest(
|
||||
ttt, cfg.LitAddr(), cfg.LitTLSCertPath,
|
||||
endpoint.macaroonFn(cfg),
|
||||
endpoint.requestFn,
|
||||
endpoint.successPattern,
|
||||
endpointEnabled,
|
||||
"unknown permissions required for "+
|
||||
"method",
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
@ -70,6 +78,8 @@ func remoteTestSuite(ctx context.Context, net *NetworkHarness, t *testing.T,
|
|||
|
||||
for _, endpoint := range endpoints {
|
||||
endpoint := endpoint
|
||||
endpointEnabled := subServersDisabled &&
|
||||
endpoint.canDisable
|
||||
|
||||
shouldFailWithoutMacaroon := false
|
||||
if withoutUIPassword {
|
||||
|
|
@ -82,6 +92,9 @@ func remoteTestSuite(ctx context.Context, net *NetworkHarness, t *testing.T,
|
|||
cfg.UIPassword, endpoint.requestFn,
|
||||
shouldFailWithoutMacaroon,
|
||||
endpoint.successPattern,
|
||||
endpointEnabled,
|
||||
"unknown permissions required for "+
|
||||
"method",
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
@ -96,10 +109,16 @@ func remoteTestSuite(ctx context.Context, net *NetworkHarness, t *testing.T,
|
|||
|
||||
for _, endpoint := range endpoints {
|
||||
endpoint := endpoint
|
||||
endpointEnabled := subServersDisabled &&
|
||||
endpoint.canDisable
|
||||
|
||||
tt.Run(endpoint.name+" lit port", func(ttt *testing.T) {
|
||||
runGRPCWebAuthTest(
|
||||
ttt, cfg.LitAddr(), cfg.UIPassword,
|
||||
endpoint.grpcWebURI, withoutUIPassword,
|
||||
endpointEnabled,
|
||||
"unknown permissions required for "+
|
||||
"method",
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
@ -117,12 +136,18 @@ func remoteTestSuite(ctx context.Context, net *NetworkHarness, t *testing.T,
|
|||
|
||||
for _, endpoint := range endpoints {
|
||||
endpoint := endpoint
|
||||
endpointEnabled := subServersDisabled &&
|
||||
endpoint.canDisable
|
||||
|
||||
tt.Run(endpoint.name+" lit port", func(ttt *testing.T) {
|
||||
runGRPCAuthTest(
|
||||
ttt, cfg.LitAddr(), cfg.LitTLSCertPath,
|
||||
superMacFile,
|
||||
endpoint.requestFn,
|
||||
endpoint.successPattern,
|
||||
endpointEnabled,
|
||||
"unknown permissions required for "+
|
||||
"method",
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
@ -133,6 +158,8 @@ func remoteTestSuite(ctx context.Context, net *NetworkHarness, t *testing.T,
|
|||
|
||||
for _, endpoint := range endpoints {
|
||||
endpoint := endpoint
|
||||
endpointDisabled := subServersDisabled &&
|
||||
endpoint.canDisable
|
||||
|
||||
tt.Run(endpoint.name+" lit port", func(ttt *testing.T) {
|
||||
runRESTAuthTest(
|
||||
|
|
@ -141,6 +168,7 @@ func remoteTestSuite(ctx context.Context, net *NetworkHarness, t *testing.T,
|
|||
endpoint.restWebURI,
|
||||
endpoint.successPattern,
|
||||
endpoint.restPOST, withoutUIPassword,
|
||||
endpointDisabled,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
@ -162,12 +190,16 @@ func remoteTestSuite(ctx context.Context, net *NetworkHarness, t *testing.T,
|
|||
|
||||
for _, endpoint := range endpoints {
|
||||
endpoint := endpoint
|
||||
endpointDisabled := subServersDisabled &&
|
||||
endpoint.canDisable
|
||||
|
||||
tt.Run(endpoint.name+" lit port", func(ttt *testing.T) {
|
||||
runLNCAuthTest(
|
||||
ttt, rawLNCConn, endpoint.requestFn,
|
||||
endpoint.successPattern,
|
||||
endpoint.allowedThroughLNC,
|
||||
"unknown service",
|
||||
endpointDisabled,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
@ -203,12 +235,16 @@ func remoteTestSuite(ctx context.Context, net *NetworkHarness, t *testing.T,
|
|||
|
||||
for _, endpoint := range endpoints {
|
||||
endpoint := endpoint
|
||||
endpointDisabled := subServersDisabled &&
|
||||
endpoint.canDisable
|
||||
|
||||
tt.Run(endpoint.name+" lit port", func(ttt *testing.T) {
|
||||
allowed := customURIs[endpoint.grpcWebURI]
|
||||
runLNCAuthTest(
|
||||
ttt, rawLNCConn, endpoint.requestFn,
|
||||
endpoint.successPattern,
|
||||
allowed, "permission denied",
|
||||
endpointDisabled,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightninglabs/faraday/frdrpc"
|
||||
terminal "github.com/lightninglabs/lightning-terminal"
|
||||
"github.com/lightninglabs/lightning-terminal/litrpc"
|
||||
"github.com/lightninglabs/loop/looprpc"
|
||||
"github.com/lightninglabs/pool/poolrpc"
|
||||
|
|
@ -63,7 +64,8 @@ var (
|
|||
type LitNodeConfig struct {
|
||||
*node.BaseNodeConfig
|
||||
|
||||
LitArgs []string
|
||||
LitArgs []string
|
||||
ActiveArgs *litArgs
|
||||
|
||||
RemoteMode bool
|
||||
|
||||
|
|
@ -132,6 +134,19 @@ func (l *litArgs) addArg(name, value string) {
|
|||
l.args[name] = value
|
||||
}
|
||||
|
||||
// getArg gets the arg with the given name from the set and returns the value.
|
||||
// The boolean returned will be true if the argument is in the set. If the
|
||||
// boolean is true but the string is empty, it means it is a boolean config
|
||||
// flag.
|
||||
func (l *litArgs) getArg(name string) (string, bool) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
value, ok := l.args[name]
|
||||
|
||||
return value, ok
|
||||
}
|
||||
|
||||
// toArgList converts the litArgs map to an arguments string slice.
|
||||
func (l *litArgs) toArgList() []string {
|
||||
l.mu.Lock()
|
||||
|
|
@ -178,6 +193,8 @@ func (cfg *LitNodeConfig) GenArgs(opts ...LitArgOption) []string {
|
|||
opt(args)
|
||||
}
|
||||
|
||||
cfg.ActiveArgs = args
|
||||
|
||||
return args.toArgList()
|
||||
}
|
||||
|
||||
|
|
@ -192,6 +209,7 @@ func (cfg *LitNodeConfig) defaultLitdArgs() *litArgs {
|
|||
"loop.loopdir": cfg.LoopDir,
|
||||
"pool.basedir": cfg.PoolDir,
|
||||
"taproot-assets.tapddir": cfg.TapdDir,
|
||||
"taproot-assets-mode": "integrated",
|
||||
"uipassword": cfg.UIPassword,
|
||||
"enablerest": "",
|
||||
"restcors": "*",
|
||||
|
|
@ -547,7 +565,7 @@ func renameFile(fromFileName, toFileName string) {
|
|||
// This may not clean up properly if an error is returned, so the caller should
|
||||
// call shutdown() regardless of the return value.
|
||||
func (hn *HarnessNode) Start(litdBinary string, litdError chan<- error,
|
||||
wait bool, litArgOpts ...LitArgOption) error {
|
||||
waitForStart bool, litArgOpts ...LitArgOption) error {
|
||||
|
||||
hn.quit = make(chan struct{})
|
||||
|
||||
|
|
@ -648,7 +666,7 @@ func (hn *HarnessNode) Start(litdBinary string, litdError chan<- error,
|
|||
|
||||
// We may want to skip waiting for the node to come up (eg. the node
|
||||
// is waiting to become the leader).
|
||||
if !wait {
|
||||
if !waitForStart {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -689,7 +707,16 @@ func (hn *HarnessNode) Start(litdBinary string, litdError chan<- error,
|
|||
}
|
||||
hn.litConn = litConn
|
||||
|
||||
return nil
|
||||
ctxt, cancel := context.WithTimeout(
|
||||
context.Background(), lntest.DefaultTimeout,
|
||||
)
|
||||
defer cancel()
|
||||
return wait.NoError(func() error {
|
||||
litConn := litrpc.NewProxyClient(hn.litConn)
|
||||
|
||||
_, err = litConn.GetInfo(ctxt, &litrpc.GetInfoRequest{})
|
||||
return err
|
||||
}, lntest.DefaultTimeout)
|
||||
}
|
||||
|
||||
// WaitUntilStarted waits until the wallet state flips from "WAITING_TO_START".
|
||||
|
|
@ -738,16 +765,19 @@ func (hn *HarnessNode) WaitUntilStarted(conn grpc.ClientConnInterface,
|
|||
return err
|
||||
}
|
||||
|
||||
tapClient, err := hn.tapClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tapMode, _ := hn.Cfg.ActiveArgs.getArg("taproot-assets-mode")
|
||||
if tapMode != terminal.ModeDisable {
|
||||
tapClient, err := hn.tapClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = tapClient.ListAssets(
|
||||
ctxt, &taprpc.ListAssetRequest{},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
_, err = tapClient.ListAssets(
|
||||
ctxt, &taprpc.ListAssetRequest{},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -822,9 +852,19 @@ func (hn *HarnessNode) waitForState(conn grpc.ClientConnInterface,
|
|||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
stateStream, err := stateClient.SubscribeState(
|
||||
ctx, &lnrpc.SubscribeStateRequest{},
|
||||
var (
|
||||
stateStream lnrpc.State_SubscribeStateClient
|
||||
err error
|
||||
)
|
||||
|
||||
subscribeFunc := func() error {
|
||||
stateStream, err = stateClient.SubscribeState(
|
||||
ctx, &lnrpc.SubscribeStateRequest{},
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
err = wait.NoError(subscribeFunc, lntest.DefaultTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
10
terminal.go
10
terminal.go
|
|
@ -1422,10 +1422,12 @@ func (g *LightningTerminal) initSubServers() {
|
|||
g.cfg.Pool, g.cfg.Remote.Pool, g.cfg.poolRemote,
|
||||
))
|
||||
|
||||
g.subServerMgr.AddServer(subservers.NewTaprootAssetsSubServer(
|
||||
g.cfg.TaprootAssets, g.cfg.Remote.TaprootAssets,
|
||||
g.cfg.tapRemote,
|
||||
))
|
||||
if g.cfg.TaprootAssetsMode != ModeDisable {
|
||||
g.subServerMgr.AddServer(subservers.NewTaprootAssetsSubServer(
|
||||
g.cfg.TaprootAssets, g.cfg.Remote.TaprootAssets,
|
||||
g.cfg.tapRemote,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// BakeSuperMacaroon uses the lnd client to bake a macaroon that can include
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue