• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 13974489001

20 Mar 2025 04:32PM UTC coverage: 56.292% (-2.9%) from 59.168%
13974489001

Pull #8754

github

web-flow
Merge aed149e6b into ea050d06f
Pull Request #8754: Add `Outbound` Remote Signer implementation

594 of 1713 new or added lines in 26 files covered. (34.68%)

23052 existing lines in 272 files now uncovered.

105921 of 188165 relevant lines covered (56.29%)

23796.34 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/lnd.go
1
// Copyright (c) 2013-2017 The btcsuite developers
2
// Copyright (c) 2015-2016 The Decred developers
3
// Copyright (C) 2015-2022 The Lightning Network Developers
4

5
package lnd
6

7
import (
8
        "context"
9
        "errors"
10
        "fmt"
11
        "log/slog"
12
        "net"
13
        "net/http"
14
        "net/http/pprof"
15
        "os"
16
        "runtime"
17
        runtimePprof "runtime/pprof"
18
        "strings"
19
        "sync"
20
        "time"
21

22
        "github.com/btcsuite/btcd/btcutil"
23
        proxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
24
        "github.com/lightningnetwork/lnd/autopilot"
25
        "github.com/lightningnetwork/lnd/build"
26
        "github.com/lightningnetwork/lnd/chanacceptor"
27
        "github.com/lightningnetwork/lnd/channeldb"
28
        "github.com/lightningnetwork/lnd/cluster"
29
        "github.com/lightningnetwork/lnd/keychain"
30
        "github.com/lightningnetwork/lnd/lncfg"
31
        "github.com/lightningnetwork/lnd/lnrpc"
32
        "github.com/lightningnetwork/lnd/lnwallet"
33
        "github.com/lightningnetwork/lnd/lnwallet/rpcwallet"
34
        "github.com/lightningnetwork/lnd/macaroons"
35
        "github.com/lightningnetwork/lnd/monitoring"
36
        "github.com/lightningnetwork/lnd/rpcperms"
37
        "github.com/lightningnetwork/lnd/signal"
38
        "github.com/lightningnetwork/lnd/tor"
39
        "github.com/lightningnetwork/lnd/walletunlocker"
40
        "github.com/lightningnetwork/lnd/watchtower"
41
        "google.golang.org/grpc"
42
        "google.golang.org/grpc/credentials"
43
        "google.golang.org/grpc/keepalive"
44
        "gopkg.in/macaroon-bakery.v2/bakery"
45
        "gopkg.in/macaroon.v2"
46
)
47

48
const (
49
        // adminMacaroonFilePermissions is the file permission that is used for
50
        // creating the admin macaroon file.
51
        //
52
        // Why 640 is safe:
53
        // Assuming a reasonably secure Linux system, it will have a
54
        // separate group for each user. E.g. a new user lnd gets assigned group
55
        // lnd which nothing else belongs to. A system that does not do this is
56
        // inherently broken already.
57
        //
58
        // Since there is no other user in the group, no other user can read
59
        // admin macaroon unless the administrator explicitly allowed it. Thus
60
        // there's no harm allowing group read.
61
        adminMacaroonFilePermissions = 0640
62

63
        // leaderResignTimeout is the timeout used when resigning from the
64
        // leader role. This is kept short so LND can shut down quickly in case
65
        // of a system failure or network partition making the cluster
66
        // unresponsive. The cluster itself should ensure that the leader is not
67
        // elected again until the previous leader has resigned or the leader
68
        // election timeout has passed.
69
        leaderResignTimeout = 5 * time.Second
70
)
71

72
// AdminAuthOptions returns a list of DialOptions that can be used to
73
// authenticate with the RPC server with admin capabilities.
74
// skipMacaroons=true should be set if we don't want to include macaroons with
75
// the auth options. This is needed for instance for the WalletUnlocker
76
// service, which must be usable also before macaroons are created.
77
//
78
// NOTE: This should only be called after the RPCListener has signaled it is
79
// ready.
80
func AdminAuthOptions(cfg *Config, skipMacaroons bool) ([]grpc.DialOption,
81
        error) {
×
82

×
83
        creds, err := credentials.NewClientTLSFromFile(cfg.TLSCertPath, "")
×
84
        if err != nil {
×
85
                return nil, fmt.Errorf("unable to read TLS cert: %w", err)
×
86
        }
×
87

88
        // Create a dial options array.
89
        opts := []grpc.DialOption{
×
90
                grpc.WithTransportCredentials(creds),
×
91
        }
×
92

×
93
        // Get the admin macaroon if macaroons are active.
×
94
        if !skipMacaroons && !cfg.NoMacaroons {
×
95
                // Load the admin macaroon file.
×
96
                macBytes, err := os.ReadFile(cfg.AdminMacPath)
×
97
                if err != nil {
×
98
                        return nil, fmt.Errorf("unable to read macaroon "+
×
99
                                "path (check the network setting!): %v", err)
×
100
                }
×
101

102
                mac := &macaroon.Macaroon{}
×
103
                if err = mac.UnmarshalBinary(macBytes); err != nil {
×
104
                        return nil, fmt.Errorf("unable to decode macaroon: %w",
×
105
                                err)
×
106
                }
×
107

108
                // Now we append the macaroon credentials to the dial options.
109
                cred, err := macaroons.NewMacaroonCredential(mac)
×
110
                if err != nil {
×
111
                        return nil, fmt.Errorf("error cloning mac: %w", err)
×
112
                }
×
113
                opts = append(opts, grpc.WithPerRPCCredentials(cred))
×
114
        }
115

116
        return opts, nil
×
117
}
118

119
// ListenerWithSignal is a net.Listener that has an additional Ready channel
120
// that will be closed when a server starts listening.
121
type ListenerWithSignal struct {
122
        net.Listener
123

124
        // Ready will be closed by the server listening on Listener.
125
        Ready chan struct{}
126

127
        // MacChan is an optional way to pass the admin macaroon to the program
128
        // that started lnd. The channel should be buffered to avoid lnd being
129
        // blocked on sending to the channel.
130
        MacChan chan []byte
131
}
132

133
// ListenerCfg is a wrapper around custom listeners that can be passed to lnd
134
// when calling its main method.
135
type ListenerCfg struct {
136
        // RPCListeners can be set to the listeners to use for the RPC server.
137
        // If empty a regular network listener will be created.
138
        RPCListeners []*ListenerWithSignal
139
}
140

141
var errStreamIsolationWithProxySkip = errors.New(
142
        "while stream isolation is enabled, the TOR proxy may not be skipped",
143
)
144

145
// Main is the true entry point for lnd. It accepts a fully populated and
146
// validated main configuration struct and an optional listener config struct.
147
// This function starts all main system components then blocks until a signal
148
// is received on the shutdownChan at which point everything is shut down again.
149
func Main(cfg *Config, lisCfg ListenerCfg, implCfg *ImplementationCfg,
UNCOV
150
        interceptor signal.Interceptor) error {
×
UNCOV
151

×
UNCOV
152
        defer func() {
×
UNCOV
153
                ltndLog.Info("Shutdown complete")
×
UNCOV
154
                err := cfg.LogRotator.Close()
×
UNCOV
155
                if err != nil {
×
156
                        ltndLog.Errorf("Could not close log rotator: %v", err)
×
157
                }
×
158
        }()
159

UNCOV
160
        ctx, cancel := context.WithCancel(context.Background())
×
UNCOV
161
        defer cancel()
×
UNCOV
162

×
UNCOV
163
        ctx, err := build.WithBuildInfo(ctx, cfg.LogConfig)
×
UNCOV
164
        if err != nil {
×
165
                return fmt.Errorf("unable to add build info to context: %w",
×
166
                        err)
×
167
        }
×
168

UNCOV
169
        mkErr := func(msg string, err error, attrs ...any) error {
×
170
                ltndLog.ErrorS(ctx, "Shutting down due to error in main "+
×
171
                        "method", err, attrs...)
×
172

×
173
                var (
×
174
                        params = []any{err}
×
175
                        fmtStr = msg + ": %w"
×
176
                )
×
177
                for _, attr := range attrs {
×
178
                        fmtStr += " %s"
×
179

×
180
                        params = append(params, attr)
×
181
                }
×
182

183
                return fmt.Errorf(fmtStr, params...)
×
184
        }
185

186
        // Show version at startup.
UNCOV
187
        ltndLog.InfoS(ctx, "Version Info",
×
UNCOV
188
                slog.String("version", build.Version()),
×
UNCOV
189
                slog.String("commit", build.Commit),
×
UNCOV
190
                slog.Any("debuglevel", build.Deployment),
×
UNCOV
191
                slog.String("logging", cfg.DebugLevel))
×
UNCOV
192

×
UNCOV
193
        var network string
×
UNCOV
194
        switch {
×
195
        case cfg.Bitcoin.TestNet3:
×
196
                network = "testnet"
×
197

198
        case cfg.Bitcoin.MainNet:
×
199
                network = "mainnet"
×
200

201
        case cfg.Bitcoin.SimNet:
×
202
                network = "simnet"
×
203

UNCOV
204
        case cfg.Bitcoin.RegTest:
×
UNCOV
205
                network = "regtest"
×
206

207
        case cfg.Bitcoin.SigNet:
×
208
                network = "signet"
×
209
        }
210

UNCOV
211
        ltndLog.InfoS(ctx, "Network Info",
×
UNCOV
212
                "active_chain", strings.Title(BitcoinChainName),
×
UNCOV
213
                "network", network)
×
UNCOV
214

×
UNCOV
215
        // Enable http profiling server if requested.
×
UNCOV
216
        if cfg.Pprof.Profile != "" {
×
217
                // Create the http handler.
×
218
                pprofMux := http.NewServeMux()
×
219
                pprofMux.HandleFunc("/debug/pprof/", pprof.Index)
×
220
                pprofMux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
×
221
                pprofMux.HandleFunc("/debug/pprof/profile", pprof.Profile)
×
222
                pprofMux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
×
223
                pprofMux.HandleFunc("/debug/pprof/trace", pprof.Trace)
×
224

×
225
                if cfg.Pprof.BlockingProfile != 0 {
×
226
                        runtime.SetBlockProfileRate(cfg.Pprof.BlockingProfile)
×
227
                }
×
228
                if cfg.Pprof.MutexProfile != 0 {
×
229
                        runtime.SetMutexProfileFraction(cfg.Pprof.MutexProfile)
×
230
                }
×
231

232
                // Redirect all requests to the pprof handler, thus visiting
233
                // `127.0.0.1:6060` will be redirected to
234
                // `127.0.0.1:6060/debug/pprof`.
235
                pprofMux.Handle("/", http.RedirectHandler(
×
236
                        "/debug/pprof/", http.StatusSeeOther,
×
237
                ))
×
238

×
239
                ltndLog.InfoS(ctx, "Pprof listening", "addr", cfg.Pprof.Profile)
×
240

×
241
                // Create the pprof server.
×
242
                pprofServer := &http.Server{
×
243
                        Addr:              cfg.Pprof.Profile,
×
244
                        Handler:           pprofMux,
×
245
                        ReadHeaderTimeout: cfg.HTTPHeaderTimeout,
×
246
                }
×
247

×
248
                // Shut the server down when lnd is shutting down.
×
249
                defer func() {
×
250
                        ltndLog.InfoS(ctx, "Stopping pprof server...")
×
251
                        err := pprofServer.Shutdown(ctx)
×
252
                        if err != nil {
×
253
                                ltndLog.ErrorS(ctx, "Stop pprof server", err)
×
254
                        }
×
255
                }()
256

257
                // Start the pprof server.
258
                go func() {
×
259
                        err := pprofServer.ListenAndServe()
×
260
                        if err != nil && !errors.Is(err, http.ErrServerClosed) {
×
261
                                ltndLog.ErrorS(ctx, "Could not serve pprof "+
×
262
                                        "server", err)
×
263
                        }
×
264
                }()
265
        }
266

267
        // Write cpu profile if requested.
UNCOV
268
        if cfg.Pprof.CPUProfile != "" {
×
269
                f, err := os.Create(cfg.Pprof.CPUProfile)
×
270
                if err != nil {
×
271
                        return mkErr("unable to create CPU profile", err)
×
272
                }
×
273
                _ = runtimePprof.StartCPUProfile(f)
×
274
                defer func() {
×
275
                        _ = f.Close()
×
276
                }()
×
277
                defer runtimePprof.StopCPUProfile()
×
278
        }
279

280
        // Run configuration dependent DB pre-initialization. Note that this
281
        // needs to be done early and once during the startup process, before
282
        // any DB access.
UNCOV
283
        if err := cfg.DB.Init(ctx, cfg.graphDatabaseDir()); err != nil {
×
284
                return mkErr("error initializing DBs", err)
×
285
        }
×
286

UNCOV
287
        tlsManagerCfg := &TLSManagerCfg{
×
UNCOV
288
                TLSCertPath:        cfg.TLSCertPath,
×
UNCOV
289
                TLSKeyPath:         cfg.TLSKeyPath,
×
UNCOV
290
                TLSEncryptKey:      cfg.TLSEncryptKey,
×
UNCOV
291
                TLSExtraIPs:        cfg.TLSExtraIPs,
×
UNCOV
292
                TLSExtraDomains:    cfg.TLSExtraDomains,
×
UNCOV
293
                TLSAutoRefresh:     cfg.TLSAutoRefresh,
×
UNCOV
294
                TLSDisableAutofill: cfg.TLSDisableAutofill,
×
UNCOV
295
                TLSCertDuration:    cfg.TLSCertDuration,
×
UNCOV
296

×
UNCOV
297
                LetsEncryptDir:    cfg.LetsEncryptDir,
×
UNCOV
298
                LetsEncryptDomain: cfg.LetsEncryptDomain,
×
UNCOV
299
                LetsEncryptListen: cfg.LetsEncryptListen,
×
UNCOV
300

×
UNCOV
301
                DisableRestTLS: cfg.DisableRestTLS,
×
UNCOV
302

×
UNCOV
303
                HTTPHeaderTimeout: cfg.HTTPHeaderTimeout,
×
UNCOV
304
        }
×
UNCOV
305
        tlsManager := NewTLSManager(tlsManagerCfg)
×
UNCOV
306
        serverOpts, restDialOpts, restListen, cleanUp,
×
UNCOV
307
                err := tlsManager.SetCertificateBeforeUnlock()
×
UNCOV
308
        if err != nil {
×
309
                return mkErr("error setting cert before unlock", err)
×
310
        }
×
UNCOV
311
        if cleanUp != nil {
×
UNCOV
312
                defer cleanUp()
×
UNCOV
313
        }
×
314

315
        // If we have chosen to start with a dedicated listener for the
316
        // rpc server, we set it directly.
UNCOV
317
        grpcListeners := append([]*ListenerWithSignal{}, lisCfg.RPCListeners...)
×
UNCOV
318
        if len(grpcListeners) == 0 {
×
UNCOV
319
                // Otherwise we create listeners from the RPCListeners defined
×
UNCOV
320
                // in the config.
×
UNCOV
321
                for _, grpcEndpoint := range cfg.RPCListeners {
×
UNCOV
322
                        // Start a gRPC server listening for HTTP/2
×
UNCOV
323
                        // connections.
×
UNCOV
324
                        lis, err := lncfg.ListenOnAddress(grpcEndpoint)
×
UNCOV
325
                        if err != nil {
×
326
                                return mkErr("unable to listen on grpc "+
×
327
                                        "endpoint", err,
×
328
                                        slog.String(
×
329
                                                "endpoint",
×
330
                                                grpcEndpoint.String(),
×
331
                                        ))
×
332
                        }
×
UNCOV
333
                        defer lis.Close()
×
UNCOV
334

×
UNCOV
335
                        grpcListeners = append(
×
UNCOV
336
                                grpcListeners, &ListenerWithSignal{
×
UNCOV
337
                                        Listener: lis,
×
UNCOV
338
                                        Ready:    make(chan struct{}),
×
UNCOV
339
                                },
×
UNCOV
340
                        )
×
341
                }
342
        }
343

344
        // Create a new RPC interceptor that we'll add to the GRPC server. This
345
        // will be used to log the API calls invoked on the GRPC server.
UNCOV
346
        interceptorChain := rpcperms.NewInterceptorChain(
×
UNCOV
347
                rpcsLog, cfg.NoMacaroons, cfg.RPCMiddleware.Mandatory,
×
UNCOV
348
        )
×
UNCOV
349
        if err := interceptorChain.Start(); err != nil {
×
350
                return mkErr("error starting interceptor chain", err)
×
351
        }
×
UNCOV
352
        defer func() {
×
UNCOV
353
                err := interceptorChain.Stop()
×
UNCOV
354
                if err != nil {
×
355
                        ltndLog.Warnf("error stopping RPC interceptor "+
×
356
                                "chain: %v", err)
×
357
                }
×
358
        }()
359

360
        // Allow the user to overwrite some defaults of the gRPC library related
361
        // to connection keepalive (server side and client side pings).
UNCOV
362
        serverKeepalive := keepalive.ServerParameters{
×
UNCOV
363
                Time:    cfg.GRPC.ServerPingTime,
×
UNCOV
364
                Timeout: cfg.GRPC.ServerPingTimeout,
×
UNCOV
365
        }
×
UNCOV
366
        clientKeepalive := keepalive.EnforcementPolicy{
×
UNCOV
367
                MinTime:             cfg.GRPC.ClientPingMinWait,
×
UNCOV
368
                PermitWithoutStream: cfg.GRPC.ClientAllowPingWithoutStream,
×
UNCOV
369
        }
×
UNCOV
370

×
UNCOV
371
        rpcServerOpts := interceptorChain.CreateServerOpts()
×
UNCOV
372
        serverOpts = append(serverOpts, rpcServerOpts...)
×
UNCOV
373
        serverOpts = append(
×
UNCOV
374
                serverOpts, grpc.MaxRecvMsgSize(lnrpc.MaxGrpcMsgSize),
×
UNCOV
375
                grpc.KeepaliveParams(serverKeepalive),
×
UNCOV
376
                grpc.KeepaliveEnforcementPolicy(clientKeepalive),
×
UNCOV
377
        )
×
UNCOV
378

×
UNCOV
379
        grpcServer := grpc.NewServer(serverOpts...)
×
UNCOV
380
        defer grpcServer.Stop()
×
UNCOV
381

×
UNCOV
382
        // We'll also register the RPC interceptor chain as the StateServer, as
×
UNCOV
383
        // it can be used to query for the current state of the wallet.
×
UNCOV
384
        lnrpc.RegisterStateServer(grpcServer, interceptorChain)
×
UNCOV
385

×
UNCOV
386
        // Initialize, and register our implementation of the gRPC interface
×
UNCOV
387
        // exported by the rpcServer.
×
UNCOV
388
        rpcServer := newRPCServer(cfg, interceptorChain, implCfg, interceptor)
×
UNCOV
389
        err = rpcServer.RegisterWithGrpcServer(grpcServer)
×
UNCOV
390
        if err != nil {
×
391
                return mkErr("error registering gRPC server", err)
×
392
        }
×
393

394
        // Now that both the WalletUnlocker and LightningService have been
395
        // registered with the GRPC server, we can start listening.
UNCOV
396
        err = startGrpcListen(cfg, grpcServer, grpcListeners)
×
UNCOV
397
        if err != nil {
×
398
                return mkErr("error starting gRPC listener", err)
×
399
        }
×
400

401
        // Now start the REST proxy for our gRPC server above. We'll ensure
402
        // we direct LND to connect to its loopback address rather than a
403
        // wildcard to prevent certificate issues when accessing the proxy
404
        // externally.
UNCOV
405
        stopProxy, err := startRestProxy(
×
UNCOV
406
                ctx, cfg, rpcServer, restDialOpts, restListen,
×
UNCOV
407
        )
×
UNCOV
408
        if err != nil {
×
409
                return mkErr("error starting REST proxy", err)
×
410
        }
×
UNCOV
411
        defer stopProxy()
×
UNCOV
412

×
UNCOV
413
        // Start leader election if we're running on etcd. Continuation will be
×
UNCOV
414
        // blocked until this instance is elected as the current leader or
×
UNCOV
415
        // shutting down.
×
UNCOV
416
        elected := false
×
UNCOV
417
        var leaderElector cluster.LeaderElector
×
UNCOV
418
        if cfg.Cluster.EnableLeaderElection {
×
419
                electionCtx, cancelElection := context.WithCancel(ctx)
×
420

×
421
                go func() {
×
422
                        <-interceptor.ShutdownChannel()
×
423
                        cancelElection()
×
424
                }()
×
425

426
                ltndLog.InfoS(ctx, "Using leader elector",
×
427
                        "elector", cfg.Cluster.LeaderElector)
×
428

×
429
                leaderElector, err = cfg.Cluster.MakeLeaderElector(
×
430
                        electionCtx, cfg.DB,
×
431
                )
×
432
                if err != nil {
×
433
                        return err
×
434
                }
×
435

436
                defer func() {
×
437
                        if !elected {
×
438
                                return
×
439
                        }
×
440

441
                        ltndLog.InfoS(ctx, "Attempting to resign from "+
×
442
                                "leader role", "cluster_id", cfg.Cluster.ID)
×
443

×
444
                        // Ensure that we don't block the shutdown process if
×
445
                        // the leader resigning process takes too long. The
×
446
                        // cluster will ensure that the leader is not elected
×
447
                        // again until the previous leader has resigned or the
×
448
                        // leader election timeout has passed.
×
449
                        timeoutCtx, cancel := context.WithTimeout(
×
450
                                ctx, leaderResignTimeout,
×
451
                        )
×
452
                        defer cancel()
×
453

×
454
                        if err := leaderElector.Resign(timeoutCtx); err != nil {
×
455
                                ltndLog.Errorf("Leader elector failed to "+
×
456
                                        "resign: %v", err)
×
457
                        }
×
458
                }()
459

460
                ltndLog.InfoS(ctx, "Starting leadership campaign",
×
461
                        "cluster_id", cfg.Cluster.ID)
×
462

×
463
                if err := leaderElector.Campaign(electionCtx); err != nil {
×
464
                        return mkErr("leadership campaign failed", err)
×
465
                }
×
466

467
                elected = true
×
468
                ltndLog.InfoS(ctx, "Elected as leader",
×
469
                        "cluster_id", cfg.Cluster.ID)
×
470
        }
471

UNCOV
472
        dbs, cleanUp, err := implCfg.DatabaseBuilder.BuildDatabase(ctx)
×
UNCOV
473
        switch {
×
474
        case errors.Is(err, channeldb.ErrDryRunMigrationOK):
×
475
                ltndLog.InfoS(ctx, "Exiting due to BuildDatabase error",
×
476
                        slog.Any("err", err))
×
477
                return nil
×
478
        case err != nil:
×
479
                return mkErr("unable to open databases", err)
×
480
        }
481

UNCOV
482
        defer cleanUp()
×
UNCOV
483

×
UNCOV
484
        partialChainControl, walletConfig, cleanUp, err := implCfg.BuildWalletConfig(
×
UNCOV
485
                ctx, dbs, &implCfg.AuxComponents, interceptorChain,
×
UNCOV
486
                grpcListeners,
×
UNCOV
487
        )
×
UNCOV
488
        if err != nil {
×
489
                return mkErr("error creating wallet config", err)
×
490
        }
×
491

UNCOV
492
        defer cleanUp()
×
UNCOV
493

×
UNCOV
494
        activeChainControl, cleanUp, err := implCfg.BuildChainControl(
×
UNCOV
495
                partialChainControl, walletConfig,
×
UNCOV
496
        )
×
UNCOV
497
        if err != nil {
×
498
                return mkErr("error loading chain control", err)
×
499
        }
×
500

UNCOV
501
        defer cleanUp()
×
UNCOV
502

×
NEW
503
        // Prepare the sub-servers, and insert the permissions required to
×
NEW
504
        // access them into the interceptor chain. Note that we do not yet have
×
NEW
505
        // all dependencies required to use all sub-servers, but we need be able
×
NEW
506
        // to allow a remote signer to connect to lnd before we can derive the
×
NEW
507
        // keys create the required dependencies.
×
NEW
508
        err = rpcServer.prepareSubServers(
×
NEW
509
                interceptorChain.MacaroonService(), cfg.SubRPCServers,
×
NEW
510
                activeChainControl,
×
NEW
511
        )
×
NEW
512
        if err != nil {
×
NEW
513
                return mkErr("error adding sub server permissions", err)
×
NEW
514
        }
×
515

NEW
516
        defer func() {
×
NEW
517
                err := rpcServer.Stop()
×
NEW
518
                if err != nil {
×
NEW
519
                        ltndLog.Errorf("Error stopping the RPC server", err)
×
NEW
520
                }
×
521
        }()
522

523
        // To ensure that a potential remote signer can connect to lnd before we
524
        // can handle other requests, we set the interceptor chain to be ready
525
        // accept remote signer connections, if enabled by the cfg.
NEW
526
        if cfg.RemoteSigner.AllowInboundConnection {
×
NEW
527
                interceptorChain.SetAllowRemoteSigner()
×
NEW
528
        }
×
529

530
        // We'll wait until the wallet is fully ready to be used before we
531
        // proceed to derive keys from it.
NEW
532
        select {
×
NEW
533
        case err = <-activeChainControl.Wallet.WalletController.ReadySignal():
×
NEW
534
                if err != nil {
×
NEW
535
                        return mkErr("error when waiting for wallet to be "+
×
NEW
536
                                "ready", err)
×
NEW
537
                }
×
538

NEW
539
        case <-interceptor.ShutdownChannel():
×
NEW
540
                // If we receive a shutdown signal while waiting for the wallet
×
NEW
541
                // to be ready, we must stop blocking so that all the deferred
×
NEW
542
                // clean up functions can be executed. That will also shut down
×
NEW
543
                // the wallet.
×
NEW
544
                // We can't continue to execute the code below as we can't
×
NEW
545
                // do any operations which requires private keys.
×
NEW
546
                return mkErr("Shutting down", errors.New("shutdown signal "+
×
NEW
547
                        "received while waiting for wallet to be ready"))
×
548
        }
549

550
        // TODO(roasbeef): add rotation
UNCOV
551
        idKeyDesc, err := activeChainControl.KeyRing.DeriveKey(
×
UNCOV
552
                keychain.KeyLocator{
×
UNCOV
553
                        Family: keychain.KeyFamilyNodeKey,
×
UNCOV
554
                        Index:  0,
×
UNCOV
555
                },
×
UNCOV
556
        )
×
UNCOV
557
        if err != nil {
×
558
                return mkErr("error deriving node key", err)
×
559
        }
×
560

UNCOV
561
        if cfg.Tor.StreamIsolation && cfg.Tor.SkipProxyForClearNetTargets {
×
562
                return errStreamIsolationWithProxySkip
×
563
        }
×
564

UNCOV
565
        if cfg.Tor.Active {
×
566
                if cfg.Tor.SkipProxyForClearNetTargets {
×
567
                        srvrLog.InfoS(ctx, "Onion services are accessible "+
×
568
                                "via Tor! NOTE: Traffic to clearnet services "+
×
569
                                "is not routed via Tor.")
×
570
                } else {
×
571
                        srvrLog.InfoS(ctx, "Proxying all network traffic "+
×
572
                                "via Tor! NOTE: Ensure the backend node is "+
×
573
                                "proxying over Tor as well",
×
574
                                "stream_isolation", cfg.Tor.StreamIsolation)
×
575
                }
×
576
        }
577

578
        // If tor is active and either v2 or v3 onion services have been
579
        // specified, make a tor controller and pass it into both the watchtower
580
        // server and the regular lnd server.
UNCOV
581
        var torController *tor.Controller
×
UNCOV
582
        if cfg.Tor.Active && (cfg.Tor.V2 || cfg.Tor.V3) {
×
583
                torController = tor.NewController(
×
584
                        cfg.Tor.Control, cfg.Tor.TargetIPAddress,
×
585
                        cfg.Tor.Password,
×
586
                )
×
587

×
588
                // Start the tor controller before giving it to any other
×
589
                // subsystems.
×
590
                if err := torController.Start(); err != nil {
×
591
                        return mkErr("unable to initialize tor controller",
×
592
                                err)
×
593
                }
×
594
                defer func() {
×
595
                        if err := torController.Stop(); err != nil {
×
596
                                ltndLog.ErrorS(ctx, "Error stopping tor "+
×
597
                                        "controller", err)
×
598
                        }
×
599
                }()
600
        }
601

UNCOV
602
        var tower *watchtower.Standalone
×
UNCOV
603
        if cfg.Watchtower.Active {
×
UNCOV
604
                towerKeyDesc, err := activeChainControl.KeyRing.DeriveKey(
×
UNCOV
605
                        keychain.KeyLocator{
×
UNCOV
606
                                Family: keychain.KeyFamilyTowerID,
×
UNCOV
607
                                Index:  0,
×
UNCOV
608
                        },
×
UNCOV
609
                )
×
UNCOV
610
                if err != nil {
×
611
                        return mkErr("error deriving tower key", err)
×
612
                }
×
613

UNCOV
614
                wtCfg := &watchtower.Config{
×
UNCOV
615
                        BlockFetcher:   activeChainControl.ChainIO,
×
UNCOV
616
                        DB:             dbs.TowerServerDB,
×
UNCOV
617
                        EpochRegistrar: activeChainControl.ChainNotifier,
×
UNCOV
618
                        Net:            cfg.net,
×
UNCOV
619
                        NewAddress: func() (btcutil.Address, error) {
×
620
                                return activeChainControl.Wallet.NewAddress(
×
621
                                        lnwallet.TaprootPubkey, false,
×
622
                                        lnwallet.DefaultAccountName,
×
623
                                )
×
624
                        },
×
625
                        NodeKeyECDH: keychain.NewPubKeyECDH(
626
                                towerKeyDesc, activeChainControl.KeyRing,
627
                        ),
628
                        PublishTx: activeChainControl.Wallet.PublishTransaction,
629
                        ChainHash: *cfg.ActiveNetParams.GenesisHash,
630
                }
631

632
                // If there is a tor controller (user wants auto hidden
633
                // services), then store a pointer in the watchtower config.
UNCOV
634
                if torController != nil {
×
635
                        wtCfg.TorController = torController
×
636
                        wtCfg.WatchtowerKeyPath = cfg.Tor.WatchtowerKeyPath
×
637
                        wtCfg.EncryptKey = cfg.Tor.EncryptKey
×
638
                        wtCfg.KeyRing = activeChainControl.KeyRing
×
639

×
640
                        switch {
×
641
                        case cfg.Tor.V2:
×
642
                                wtCfg.Type = tor.V2
×
643
                        case cfg.Tor.V3:
×
644
                                wtCfg.Type = tor.V3
×
645
                        }
646
                }
647

UNCOV
648
                wtConfig, err := cfg.Watchtower.Apply(
×
UNCOV
649
                        wtCfg, lncfg.NormalizeAddresses,
×
UNCOV
650
                )
×
UNCOV
651
                if err != nil {
×
652
                        return mkErr("unable to configure watchtower", err)
×
653
                }
×
654

UNCOV
655
                tower, err = watchtower.New(wtConfig)
×
UNCOV
656
                if err != nil {
×
657
                        return mkErr("unable to create watchtower", err)
×
658
                }
×
659
        }
660

661
        // Initialize the MultiplexAcceptor. If lnd was started with the
662
        // zero-conf feature bit, then this will be a ZeroConfAcceptor.
663
        // Otherwise, this will be a ChainedAcceptor.
UNCOV
664
        var multiAcceptor chanacceptor.MultiplexAcceptor
×
UNCOV
665
        if cfg.ProtocolOptions.ZeroConf() {
×
UNCOV
666
                multiAcceptor = chanacceptor.NewZeroConfAcceptor()
×
UNCOV
667
        } else {
×
UNCOV
668
                multiAcceptor = chanacceptor.NewChainedAcceptor()
×
UNCOV
669
        }
×
670

671
        // Set up the remote signer client. If cfg.WatchOnlyNode.Enable isn't
672
        // set to true, this remote signer client won't run when the server
673
        // starts.
NEW
674
        rscBuilder := rpcwallet.NewRemoteSignerClientBuilder(cfg.WatchOnlyNode)
×
NEW
675

×
NEW
676
        rsClient, err := rscBuilder.Build(rpcServer.subServers)
×
NEW
677
        if err != nil {
×
NEW
678
                return mkErr("unable to create remote signer client", err)
×
NEW
679
        }
×
680

681
        // Set up the core server which will listen for incoming peer
682
        // connections.
UNCOV
683
        server, err := newServer(
×
UNCOV
684
                cfg, cfg.Listeners, dbs, activeChainControl, &idKeyDesc,
×
UNCOV
685
                activeChainControl.Cfg.WalletUnlockParams.ChansToRestore,
×
UNCOV
686
                multiAcceptor, torController, tlsManager, leaderElector,
×
NEW
687
                implCfg, rsClient,
×
UNCOV
688
        )
×
UNCOV
689
        if err != nil {
×
690
                return mkErr("unable to create server", err)
×
691
        }
×
692

693
        // Set up an autopilot manager from the current config. This will be
694
        // used to manage the underlying autopilot agent, starting and stopping
695
        // it at will.
UNCOV
696
        atplCfg, err := initAutoPilot(
×
UNCOV
697
                server, cfg.Autopilot, activeChainControl.MinHtlcIn,
×
UNCOV
698
                cfg.ActiveNetParams,
×
UNCOV
699
        )
×
UNCOV
700
        if err != nil {
×
701
                return mkErr("unable to initialize autopilot", err)
×
702
        }
×
703

UNCOV
704
        atplManager, err := autopilot.NewManager(atplCfg)
×
UNCOV
705
        if err != nil {
×
706
                return mkErr("unable to create autopilot manager", err)
×
707
        }
×
UNCOV
708
        if err := atplManager.Start(); err != nil {
×
709
                return mkErr("unable to start autopilot manager", err)
×
710
        }
×
UNCOV
711
        defer atplManager.Stop()
×
UNCOV
712

×
UNCOV
713
        err = tlsManager.LoadPermanentCertificate(activeChainControl.KeyRing)
×
UNCOV
714
        if err != nil {
×
715
                return mkErr("unable to load permanent TLS certificate", err)
×
716
        }
×
717

718
        // Now we have created all dependencies necessary to be able to use all
719
        // sub-servers, so we add the dependencies to the sub-servers.
UNCOV
720
        err = rpcServer.addDeps(
×
UNCOV
721
                server, interceptorChain.MacaroonService(), cfg.SubRPCServers,
×
UNCOV
722
                atplManager, server.invoices, tower, multiAcceptor,
×
UNCOV
723
                server.invoiceHtlcModifier,
×
UNCOV
724
        )
×
UNCOV
725
        if err != nil {
×
726
                return mkErr("unable to add deps to RPC server", err)
×
727
        }
×
728

729
        // We transition the RPC state to Active, as the sub-servers are now
730
        // ready to be used.
UNCOV
731
        interceptorChain.SetRPCActive()
×
UNCOV
732

×
UNCOV
733
        if err := interceptor.Notifier.NotifyReady(true); err != nil {
×
734
                return mkErr("error notifying ready", err)
×
735
        }
×
736

737
        // We'll wait until we're fully synced to continue the start up of the
738
        // remainder of the daemon. This ensures that we don't accept any
739
        // possibly invalid state transitions, or accept channels with spent
740
        // funds.
UNCOV
741
        _, bestHeight, err := activeChainControl.ChainIO.GetBestBlock()
×
UNCOV
742
        if err != nil {
×
743
                return mkErr("unable to determine chain tip", err)
×
744
        }
×
745

UNCOV
746
        ltndLog.InfoS(ctx, "Waiting for chain backend to finish sync",
×
UNCOV
747
                slog.Int64("start_height", int64(bestHeight)))
×
UNCOV
748

×
UNCOV
749
        type syncResult struct {
×
UNCOV
750
                synced        bool
×
UNCOV
751
                bestBlockTime int64
×
UNCOV
752
                err           error
×
UNCOV
753
        }
×
UNCOV
754

×
UNCOV
755
        var syncedResChan = make(chan syncResult, 1)
×
UNCOV
756

×
UNCOV
757
        for {
×
UNCOV
758
                // We check if the wallet is synced in a separate goroutine as
×
UNCOV
759
                // the call is blocking, and we want to be able to interrupt it
×
UNCOV
760
                // if the daemon is shutting down.
×
UNCOV
761
                go func() {
×
UNCOV
762
                        synced, bestBlockTime, err := activeChainControl.Wallet.
×
UNCOV
763
                                IsSynced()
×
UNCOV
764
                        syncedResChan <- syncResult{synced, bestBlockTime, err}
×
UNCOV
765
                }()
×
766

UNCOV
767
                select {
×
768
                case <-interceptor.ShutdownChannel():
×
769
                        return nil
×
770

UNCOV
771
                case res := <-syncedResChan:
×
UNCOV
772
                        if res.err != nil {
×
773
                                return mkErr("unable to determine if wallet "+
×
774
                                        "is synced", res.err)
×
775
                        }
×
776

UNCOV
777
                        ltndLog.DebugS(ctx, "Syncing to block chain",
×
UNCOV
778
                                "best_block_time", time.Unix(res.bestBlockTime, 0),
×
UNCOV
779
                                "is_synced", res.synced)
×
UNCOV
780

×
UNCOV
781
                        if res.synced {
×
UNCOV
782
                                break
×
783
                        }
784

785
                        // If we're not yet synced, we'll wait for a second
786
                        // before checking again.
UNCOV
787
                        select {
×
788
                        case <-interceptor.ShutdownChannel():
×
789
                                return nil
×
790

UNCOV
791
                        case <-time.After(time.Second):
×
UNCOV
792
                                continue
×
793
                        }
794
                }
795

UNCOV
796
                break
×
797
        }
798

UNCOV
799
        _, bestHeight, err = activeChainControl.ChainIO.GetBestBlock()
×
UNCOV
800
        if err != nil {
×
801
                return mkErr("unable to determine chain tip", err)
×
802
        }
×
803

UNCOV
804
        ltndLog.InfoS(ctx, "Chain backend is fully synced!",
×
UNCOV
805
                "end_height", bestHeight)
×
UNCOV
806

×
UNCOV
807
        // With all the relevant chains initialized, we can finally start the
×
UNCOV
808
        // server itself. We start the server in an asynchronous goroutine so
×
UNCOV
809
        // that we are able to interrupt and shutdown the daemon gracefully in
×
UNCOV
810
        // case the startup of the subservers do not behave as expected.
×
UNCOV
811
        errChan := make(chan error)
×
UNCOV
812
        go func() {
×
UNCOV
813
                errChan <- server.Start()
×
UNCOV
814
        }()
×
815

UNCOV
816
        defer func() {
×
UNCOV
817
                err := server.Stop()
×
UNCOV
818
                if err != nil {
×
819
                        ltndLog.WarnS(ctx, "Stopping the server including all "+
×
820
                                "its subsystems failed with", err)
×
821
                }
×
822
        }()
823

UNCOV
824
        select {
×
UNCOV
825
        case err := <-errChan:
×
UNCOV
826
                if err == nil {
×
UNCOV
827
                        break
×
828
                }
829

830
                return mkErr("unable to start server", err)
×
831

832
        case <-interceptor.ShutdownChannel():
×
833
                return nil
×
834
        }
835

836
        // We transition the server state to Active, as the server is up.
UNCOV
837
        interceptorChain.SetServerActive()
×
UNCOV
838

×
UNCOV
839
        // Now that the server has started, if the autopilot mode is currently
×
UNCOV
840
        // active, then we'll start the autopilot agent immediately. It will be
×
UNCOV
841
        // stopped together with the autopilot service.
×
UNCOV
842
        if cfg.Autopilot.Active {
×
843
                if err := atplManager.StartAgent(); err != nil {
×
844
                        return mkErr("unable to start autopilot agent", err)
×
845
                }
×
846
        }
847

UNCOV
848
        if cfg.Watchtower.Active {
×
UNCOV
849
                if err := tower.Start(); err != nil {
×
850
                        return mkErr("unable to start watchtower", err)
×
851
                }
×
UNCOV
852
                defer tower.Stop()
×
853
        }
854

855
        // Wait for shutdown signal from either a graceful server stop or from
856
        // the interrupt handler.
UNCOV
857
        <-interceptor.ShutdownChannel()
×
UNCOV
858
        return nil
×
859
}
860

861
// bakeMacaroon creates a new macaroon with newest version and the given
862
// permissions then returns it binary serialized.
863
func bakeMacaroon(ctx context.Context, svc *macaroons.Service,
UNCOV
864
        permissions []bakery.Op) ([]byte, error) {
×
UNCOV
865

×
UNCOV
866
        mac, err := svc.NewMacaroon(
×
UNCOV
867
                ctx, macaroons.DefaultRootKeyID, permissions...,
×
UNCOV
868
        )
×
UNCOV
869
        if err != nil {
×
870
                return nil, err
×
871
        }
×
872

UNCOV
873
        return mac.M().MarshalBinary()
×
874
}
875

876
// saveMacaroon bakes a macaroon with the specified macaroon permissions and
877
// writes it to a file with the given filename and file permissions.
878
func saveMacaroon(ctx context.Context, svc *macaroons.Service, filename string,
UNCOV
879
        macaroonPermissions []bakery.Op, filePermissions os.FileMode) error {
×
UNCOV
880

×
UNCOV
881
        macaroonBytes, err := bakeMacaroon(ctx, svc, macaroonPermissions)
×
UNCOV
882
        if err != nil {
×
883
                return err
×
884
        }
×
UNCOV
885
        err = os.WriteFile(filename, macaroonBytes, filePermissions)
×
UNCOV
886
        if err != nil {
×
887
                _ = os.Remove(filename)
×
888
                return err
×
889
        }
×
890

UNCOV
891
        return nil
×
892
}
893

894
// genDefaultMacaroons checks for three default macaroon files and generates
895
// them if they do not exist; one admin-level, one for invoice access and one
896
// read-only. Each macaroon is checked and created independently to ensure all
897
// three exist. The admin macaroon can also be used to generate more granular
898
// macaroons.
899
func genDefaultMacaroons(ctx context.Context, svc *macaroons.Service,
UNCOV
900
        admFile, roFile, invoiceFile string) error {
×
UNCOV
901

×
UNCOV
902
        // First, we'll generate a macaroon that only allows the caller to
×
UNCOV
903
        // access invoice related calls. This is useful for merchants and other
×
UNCOV
904
        // services to allow an isolated instance that can only query and
×
UNCOV
905
        // modify invoices.
×
UNCOV
906
        if !lnrpc.FileExists(invoiceFile) {
×
UNCOV
907
                err := saveMacaroon(
×
UNCOV
908
                        ctx, svc, invoiceFile, invoicePermissions, 0644,
×
UNCOV
909
                )
×
UNCOV
910
                if err != nil {
×
911
                        return err
×
912
                }
×
913
        }
914

915
        // Generate the read-only macaroon and write it to a file.
UNCOV
916
        if !lnrpc.FileExists(roFile) {
×
UNCOV
917
                err := saveMacaroon(
×
UNCOV
918
                        ctx, svc, roFile, readPermissions, 0644,
×
UNCOV
919
                )
×
UNCOV
920
                if err != nil {
×
921
                        return err
×
922
                }
×
923
        }
924

925
        // Generate the admin macaroon and write it to a file.
UNCOV
926
        if !lnrpc.FileExists(admFile) {
×
UNCOV
927
                err := saveMacaroon(
×
UNCOV
928
                        ctx, svc, admFile, adminPermissions(),
×
UNCOV
929
                        adminMacaroonFilePermissions,
×
UNCOV
930
                )
×
UNCOV
931
                if err != nil {
×
932
                        return err
×
933
                }
×
934
        }
935

UNCOV
936
        return nil
×
937
}
938

939
// adminPermissions returns a list of all permissions in a safe way that doesn't
940
// modify any of the source lists.
UNCOV
941
func adminPermissions() []bakery.Op {
×
UNCOV
942
        admin := make([]bakery.Op, len(readPermissions)+len(writePermissions))
×
UNCOV
943
        copy(admin[:len(readPermissions)], readPermissions)
×
UNCOV
944
        copy(admin[len(readPermissions):], writePermissions)
×
UNCOV
945
        return admin
×
UNCOV
946
}
×
947

948
// createWalletUnlockerService creates a WalletUnlockerService from the passed
949
// config.
UNCOV
950
func createWalletUnlockerService(cfg *Config) *walletunlocker.UnlockerService {
×
UNCOV
951
        // The macaroonFiles are passed to the wallet unlocker so they can be
×
UNCOV
952
        // deleted and recreated in case the root macaroon key is also changed
×
UNCOV
953
        // during the change password operation.
×
UNCOV
954
        macaroonFiles := []string{
×
UNCOV
955
                cfg.AdminMacPath, cfg.ReadMacPath, cfg.InvoiceMacPath,
×
UNCOV
956
        }
×
UNCOV
957

×
UNCOV
958
        return walletunlocker.New(
×
UNCOV
959
                cfg.ActiveNetParams.Params, macaroonFiles,
×
UNCOV
960
                cfg.ResetWalletTransactions, nil,
×
UNCOV
961
        )
×
UNCOV
962
}
×
963

964
// startGrpcListen starts the GRPC server on the passed listeners.
965
func startGrpcListen(cfg *Config, grpcServer *grpc.Server,
UNCOV
966
        listeners []*ListenerWithSignal) error {
×
UNCOV
967

×
UNCOV
968
        // Use a WaitGroup so we can be sure the instructions on how to input the
×
UNCOV
969
        // password is the last thing to be printed to the console.
×
UNCOV
970
        var wg sync.WaitGroup
×
UNCOV
971

×
UNCOV
972
        for _, lis := range listeners {
×
UNCOV
973
                wg.Add(1)
×
UNCOV
974
                go func(lis *ListenerWithSignal) {
×
UNCOV
975
                        rpcsLog.Infof("RPC server listening on %s", lis.Addr())
×
UNCOV
976

×
UNCOV
977
                        // Close the ready chan to indicate we are listening.
×
UNCOV
978
                        close(lis.Ready)
×
UNCOV
979

×
UNCOV
980
                        wg.Done()
×
UNCOV
981
                        _ = grpcServer.Serve(lis)
×
UNCOV
982
                }(lis)
×
983
        }
984

985
        // If Prometheus monitoring is enabled, start the Prometheus exporter.
UNCOV
986
        if cfg.Prometheus.Enabled() {
×
987
                err := monitoring.ExportPrometheusMetrics(
×
988
                        grpcServer, cfg.Prometheus,
×
989
                )
×
990
                if err != nil {
×
991
                        return err
×
992
                }
×
993
        }
994

995
        // Wait for gRPC servers to be up running.
UNCOV
996
        wg.Wait()
×
UNCOV
997

×
UNCOV
998
        return nil
×
999
}
1000

1001
// startRestProxy starts the given REST proxy on the listeners found in the
1002
// config.
1003
func startRestProxy(ctx context.Context, cfg *Config, rpcServer *rpcServer,
1004
        restDialOpts []grpc.DialOption,
UNCOV
1005
        restListen func(net.Addr) (net.Listener, error)) (func(), error) {
×
UNCOV
1006

×
UNCOV
1007
        // We use the first RPC listener as the destination for our REST proxy.
×
UNCOV
1008
        // If the listener is set to listen on all interfaces, we replace it
×
UNCOV
1009
        // with localhost, as we cannot dial it directly.
×
UNCOV
1010
        restProxyDest := cfg.RPCListeners[0].String()
×
UNCOV
1011
        switch {
×
1012
        case strings.Contains(restProxyDest, "0.0.0.0"):
×
1013
                restProxyDest = strings.Replace(
×
1014
                        restProxyDest, "0.0.0.0", "127.0.0.1", 1,
×
1015
                )
×
1016

1017
        case strings.Contains(restProxyDest, "[::]"):
×
1018
                restProxyDest = strings.Replace(
×
1019
                        restProxyDest, "[::]", "[::1]", 1,
×
1020
                )
×
1021
        }
1022

UNCOV
1023
        var shutdownFuncs []func()
×
UNCOV
1024
        shutdown := func() {
×
UNCOV
1025
                for _, shutdownFn := range shutdownFuncs {
×
UNCOV
1026
                        shutdownFn()
×
UNCOV
1027
                }
×
1028
        }
1029

1030
        // Start a REST proxy for our gRPC server.
UNCOV
1031
        ctx, cancel := context.WithCancel(ctx)
×
UNCOV
1032
        shutdownFuncs = append(shutdownFuncs, cancel)
×
UNCOV
1033

×
UNCOV
1034
        // We'll set up a proxy that will forward REST calls to the GRPC
×
UNCOV
1035
        // server.
×
UNCOV
1036
        //
×
UNCOV
1037
        // The default JSON marshaler of the REST proxy only sets OrigName to
×
UNCOV
1038
        // true, which instructs it to use the same field names as specified in
×
UNCOV
1039
        // the proto file and not switch to camel case. What we also want is
×
UNCOV
1040
        // that the marshaler prints all values, even if they are falsey.
×
UNCOV
1041
        customMarshalerOption := proxy.WithMarshalerOption(
×
UNCOV
1042
                proxy.MIMEWildcard, &proxy.JSONPb{
×
UNCOV
1043
                        MarshalOptions:   *lnrpc.RESTJsonMarshalOpts,
×
UNCOV
1044
                        UnmarshalOptions: *lnrpc.RESTJsonUnmarshalOpts,
×
UNCOV
1045
                },
×
UNCOV
1046
        )
×
UNCOV
1047
        mux := proxy.NewServeMux(
×
UNCOV
1048
                customMarshalerOption,
×
UNCOV
1049

×
UNCOV
1050
                // Don't allow falling back to other HTTP methods, we want exact
×
UNCOV
1051
                // matches only. The actual method to be used can be overwritten
×
UNCOV
1052
                // by setting X-HTTP-Method-Override so there should be no
×
UNCOV
1053
                // reason for not specifying the correct method in the first
×
UNCOV
1054
                // place.
×
UNCOV
1055
                proxy.WithDisablePathLengthFallback(),
×
UNCOV
1056
        )
×
UNCOV
1057

×
UNCOV
1058
        // Register our services with the REST proxy.
×
UNCOV
1059
        err := rpcServer.RegisterWithRestProxy(
×
UNCOV
1060
                ctx, mux, restDialOpts, restProxyDest,
×
UNCOV
1061
        )
×
UNCOV
1062
        if err != nil {
×
1063
                return nil, err
×
1064
        }
×
1065

1066
        // Wrap the default grpc-gateway handler with the WebSocket handler.
UNCOV
1067
        restHandler := lnrpc.NewWebSocketProxy(
×
UNCOV
1068
                mux, rpcsLog, cfg.WSPingInterval, cfg.WSPongWait,
×
UNCOV
1069
                lnrpc.LndClientStreamingURIs,
×
UNCOV
1070
        )
×
UNCOV
1071

×
UNCOV
1072
        // Use a WaitGroup so we can be sure the instructions on how to input the
×
UNCOV
1073
        // password is the last thing to be printed to the console.
×
UNCOV
1074
        var wg sync.WaitGroup
×
UNCOV
1075

×
UNCOV
1076
        // Now spin up a network listener for each requested port and start a
×
UNCOV
1077
        // goroutine that serves REST with the created mux there.
×
UNCOV
1078
        for _, restEndpoint := range cfg.RESTListeners {
×
UNCOV
1079
                lis, err := restListen(restEndpoint)
×
UNCOV
1080
                if err != nil {
×
1081
                        ltndLog.Errorf("gRPC proxy unable to listen on %s",
×
1082
                                restEndpoint)
×
1083
                        return nil, err
×
1084
                }
×
1085

UNCOV
1086
                shutdownFuncs = append(shutdownFuncs, func() {
×
UNCOV
1087
                        err := lis.Close()
×
UNCOV
1088
                        if err != nil {
×
1089
                                rpcsLog.Errorf("Error closing listener: %v",
×
1090
                                        err)
×
1091
                        }
×
1092
                })
1093

UNCOV
1094
                wg.Add(1)
×
UNCOV
1095
                go func() {
×
UNCOV
1096
                        rpcsLog.Infof("gRPC proxy started at %s", lis.Addr())
×
UNCOV
1097

×
UNCOV
1098
                        // Create our proxy chain now. A request will pass
×
UNCOV
1099
                        // through the following chain:
×
UNCOV
1100
                        // req ---> CORS handler --> WS proxy --->
×
UNCOV
1101
                        //   REST proxy --> gRPC endpoint
×
UNCOV
1102
                        corsHandler := allowCORS(restHandler, cfg.RestCORS)
×
UNCOV
1103

×
UNCOV
1104
                        wg.Done()
×
UNCOV
1105
                        err := http.Serve(lis, corsHandler)
×
UNCOV
1106
                        if err != nil && !lnrpc.IsClosedConnError(err) {
×
1107
                                rpcsLog.Error(err)
×
1108
                        }
×
1109
                }()
1110
        }
1111

1112
        // Wait for REST servers to be up running.
UNCOV
1113
        wg.Wait()
×
UNCOV
1114

×
UNCOV
1115
        return shutdown, nil
×
1116
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc