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

lightningnetwork / lnd / 12312390362

13 Dec 2024 08:44AM UTC coverage: 57.458% (+8.5%) from 48.92%
12312390362

Pull #9343

github

ellemouton
fn: rework the ContextGuard and add tests

In this commit, the ContextGuard struct is re-worked such that the
context that its new main WithCtx method provides is cancelled in sync
with a parent context being cancelled or with it's quit channel being
cancelled. Tests are added to assert the behaviour. In order for the
close of the quit channel to be consistent with the cancelling of the
derived context, the quit channel _must_ be contained internal to the
ContextGuard so that callers are only able to close the channel via the
exposed Quit method which will then take care to first cancel any
derived context that depend on the quit channel before returning.
Pull Request #9343: fn: expand the ContextGuard and add tests

101853 of 177264 relevant lines covered (57.46%)

24972.93 hits per line

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

0.0
/subrpcserver_config.go
1
package lnd
2

3
import (
4
        "fmt"
5
        "net"
6
        "reflect"
7

8
        "github.com/btcsuite/btcd/chaincfg"
9
        "github.com/btcsuite/btclog/v2"
10
        "github.com/lightningnetwork/lnd/aliasmgr"
11
        "github.com/lightningnetwork/lnd/autopilot"
12
        "github.com/lightningnetwork/lnd/chainreg"
13
        "github.com/lightningnetwork/lnd/channeldb"
14
        "github.com/lightningnetwork/lnd/fn/v2"
15
        graphdb "github.com/lightningnetwork/lnd/graph/db"
16
        "github.com/lightningnetwork/lnd/htlcswitch"
17
        "github.com/lightningnetwork/lnd/invoices"
18
        "github.com/lightningnetwork/lnd/lncfg"
19
        "github.com/lightningnetwork/lnd/lnrpc/autopilotrpc"
20
        "github.com/lightningnetwork/lnd/lnrpc/chainrpc"
21
        "github.com/lightningnetwork/lnd/lnrpc/devrpc"
22
        "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
23
        "github.com/lightningnetwork/lnd/lnrpc/neutrinorpc"
24
        "github.com/lightningnetwork/lnd/lnrpc/peersrpc"
25
        "github.com/lightningnetwork/lnd/lnrpc/routerrpc"
26
        "github.com/lightningnetwork/lnd/lnrpc/signrpc"
27
        "github.com/lightningnetwork/lnd/lnrpc/walletrpc"
28
        "github.com/lightningnetwork/lnd/lnrpc/watchtowerrpc"
29
        "github.com/lightningnetwork/lnd/lnrpc/wtclientrpc"
30
        "github.com/lightningnetwork/lnd/lnwire"
31
        "github.com/lightningnetwork/lnd/macaroons"
32
        "github.com/lightningnetwork/lnd/netann"
33
        "github.com/lightningnetwork/lnd/routing"
34
        "github.com/lightningnetwork/lnd/sweep"
35
        "github.com/lightningnetwork/lnd/watchtower"
36
        "github.com/lightningnetwork/lnd/watchtower/wtclient"
37
        "google.golang.org/protobuf/proto"
38
)
39

40
// subRPCServerConfigs is special sub-config in the main configuration that
41
// houses the configuration for the optional sub-servers. These sub-RPC servers
42
// are meant to house experimental new features that may eventually make it
43
// into the main RPC server that lnd exposes. Special methods are present on
44
// this struct to allow the main RPC server to create and manipulate the
45
// sub-RPC servers in a generalized manner.
46
type subRPCServerConfigs struct {
47
        // SignRPC is a sub-RPC server that exposes signing of arbitrary inputs
48
        // as a gRPC service.
49
        SignRPC *signrpc.Config `group:"signrpc" namespace:"signrpc"`
50

51
        // WalletKitRPC is a sub-RPC server that exposes functionality allowing
52
        // a client to send transactions through a wallet, publish them, and
53
        // also requests keys and addresses under control of the backing
54
        // wallet.
55
        WalletKitRPC *walletrpc.Config `group:"walletrpc" namespace:"walletrpc"`
56

57
        // AutopilotRPC is a sub-RPC server that exposes methods on the running
58
        // autopilot as a gRPC service.
59
        AutopilotRPC *autopilotrpc.Config `group:"autopilotrpc" namespace:"autopilotrpc"`
60

61
        // ChainRPC is a sub-RPC server that exposes functionality allowing a
62
        // client to be notified of certain on-chain events (new blocks,
63
        // confirmations, spends).
64
        ChainRPC *chainrpc.Config `group:"chainrpc" namespace:"chainrpc"`
65

66
        // InvoicesRPC is a sub-RPC server that exposes invoice related methods
67
        // as a gRPC service.
68
        InvoicesRPC *invoicesrpc.Config `group:"invoicesrpc" namespace:"invoicesrpc"`
69

70
        // PeersRPC is a sub-RPC server that exposes peer related methods
71
        // as a gRPC service.
72
        PeersRPC *peersrpc.Config `group:"peersrpc" namespace:"peersrpc"`
73

74
        // NeutrinoKitRPC is a sub-RPC server that exposes functionality allowing
75
        // a client to interact with a running neutrino node.
76
        NeutrinoKitRPC *neutrinorpc.Config `group:"neutrinorpc" namespace:"neutrinorpc"`
77

78
        // RouterRPC is a sub-RPC server the exposes functionality that allows
79
        // clients to send payments on the network, and perform Lightning
80
        // payment related queries such as requests for estimates of off-chain
81
        // fees.
82
        RouterRPC *routerrpc.Config `group:"routerrpc" namespace:"routerrpc"`
83

84
        // WatchtowerRPC is a sub-RPC server that exposes functionality allowing
85
        // clients to monitor and control their embedded watchtower.
86
        WatchtowerRPC *watchtowerrpc.Config `group:"watchtowerrpc" namespace:"watchtowerrpc"`
87

88
        // WatchtowerClientRPC is a sub-RPC server that exposes functionality
89
        // that allows clients to interact with the active watchtower client
90
        // instance within lnd in order to add, remove, list registered client
91
        // towers, etc.
92
        WatchtowerClientRPC *wtclientrpc.Config `group:"wtclientrpc" namespace:"wtclientrpc"`
93

94
        // DevRPC is a sub-RPC server that exposes functionality that allows
95
        // developers manipulate LND state that is normally not possible.
96
        // Should only be used for development purposes.
97
        DevRPC *devrpc.Config `group:"devrpc" namespace:"devrpc"`
98
}
99

100
// PopulateDependencies attempts to iterate through all the sub-server configs
101
// within this struct, and populate the items it requires based on the main
102
// configuration file, and the chain control.
103
//
104
// NOTE: This MUST be called before any callers are permitted to execute the
105
// FetchConfig method.
106
func (s *subRPCServerConfigs) PopulateDependencies(cfg *Config,
107
        cc *chainreg.ChainControl,
108
        networkDir string, macService *macaroons.Service,
109
        atpl *autopilot.Manager,
110
        invoiceRegistry *invoices.InvoiceRegistry,
111
        htlcSwitch *htlcswitch.Switch,
112
        activeNetParams *chaincfg.Params,
113
        chanRouter *routing.ChannelRouter,
114
        routerBackend *routerrpc.RouterBackend,
115
        nodeSigner *netann.NodeSigner,
116
        graphDB *graphdb.ChannelGraph,
117
        chanStateDB *channeldb.ChannelStateDB,
118
        sweeper *sweep.UtxoSweeper,
119
        tower *watchtower.Standalone,
120
        towerClientMgr *wtclient.Manager,
121
        tcpResolver lncfg.TCPResolver,
122
        genInvoiceFeatures func() *lnwire.FeatureVector,
123
        genAmpInvoiceFeatures func() *lnwire.FeatureVector,
124
        getNodeAnnouncement func() lnwire.NodeAnnouncement,
125
        updateNodeAnnouncement func(features *lnwire.RawFeatureVector,
126
                modifiers ...netann.NodeAnnModifier) error,
127
        parseAddr func(addr string) (net.Addr, error),
128
        rpcLogger btclog.Logger, aliasMgr *aliasmgr.Manager,
129
        auxDataParser fn.Option[AuxDataParser],
130
        invoiceHtlcModifier *invoices.HtlcModificationInterceptor) error {
×
131

×
132
        // First, we'll use reflect to obtain a version of the config struct
×
133
        // that allows us to programmatically inspect its fields.
×
134
        selfVal := extractReflectValue(s)
×
135
        selfType := selfVal.Type()
×
136

×
137
        numFields := selfVal.NumField()
×
138
        for i := 0; i < numFields; i++ {
×
139
                field := selfVal.Field(i)
×
140
                fieldElem := field.Elem()
×
141
                fieldName := selfType.Field(i).Name
×
142

×
143
                ltndLog.Debugf("Populating dependencies for sub RPC "+
×
144
                        "server: %v", fieldName)
×
145

×
146
                // If this sub-config doesn't actually have any fields, then we
×
147
                // can skip it, as the build tag for it is likely off.
×
148
                if fieldElem.NumField() == 0 {
×
149
                        continue
×
150
                }
151
                if !fieldElem.CanSet() {
×
152
                        continue
×
153
                }
154

155
                switch subCfg := field.Interface().(type) {
×
156
                case *signrpc.Config:
×
157
                        subCfgValue := extractReflectValue(subCfg)
×
158

×
159
                        subCfgValue.FieldByName("MacService").Set(
×
160
                                reflect.ValueOf(macService),
×
161
                        )
×
162
                        subCfgValue.FieldByName("NetworkDir").Set(
×
163
                                reflect.ValueOf(networkDir),
×
164
                        )
×
165
                        subCfgValue.FieldByName("Signer").Set(
×
166
                                reflect.ValueOf(cc.Signer),
×
167
                        )
×
168
                        subCfgValue.FieldByName("KeyRing").Set(
×
169
                                reflect.ValueOf(cc.KeyRing),
×
170
                        )
×
171

172
                case *walletrpc.Config:
×
173
                        subCfgValue := extractReflectValue(subCfg)
×
174

×
175
                        subCfgValue.FieldByName("NetworkDir").Set(
×
176
                                reflect.ValueOf(networkDir),
×
177
                        )
×
178
                        subCfgValue.FieldByName("MacService").Set(
×
179
                                reflect.ValueOf(macService),
×
180
                        )
×
181
                        subCfgValue.FieldByName("FeeEstimator").Set(
×
182
                                reflect.ValueOf(cc.FeeEstimator),
×
183
                        )
×
184
                        subCfgValue.FieldByName("Wallet").Set(
×
185
                                reflect.ValueOf(cc.Wallet),
×
186
                        )
×
187
                        subCfgValue.FieldByName("CoinSelectionLocker").Set(
×
188
                                reflect.ValueOf(cc.Wallet),
×
189
                        )
×
190
                        subCfgValue.FieldByName("KeyRing").Set(
×
191
                                reflect.ValueOf(cc.KeyRing),
×
192
                        )
×
193
                        subCfgValue.FieldByName("Sweeper").Set(
×
194
                                reflect.ValueOf(sweeper),
×
195
                        )
×
196
                        subCfgValue.FieldByName("Chain").Set(
×
197
                                reflect.ValueOf(cc.ChainIO),
×
198
                        )
×
199
                        subCfgValue.FieldByName("ChainParams").Set(
×
200
                                reflect.ValueOf(activeNetParams),
×
201
                        )
×
202
                        subCfgValue.FieldByName("CurrentNumAnchorChans").Set(
×
203
                                reflect.ValueOf(cc.Wallet.CurrentNumAnchorChans),
×
204
                        )
×
205
                        subCfgValue.FieldByName("CoinSelectionStrategy").Set(
×
206
                                reflect.ValueOf(
×
207
                                        cc.Wallet.Cfg.CoinSelectionStrategy,
×
208
                                ),
×
209
                        )
×
210
                        subCfgValue.FieldByName("ChanStateDB").Set(
×
211
                                reflect.ValueOf(chanStateDB),
×
212
                        )
×
213

214
                case *autopilotrpc.Config:
×
215
                        subCfgValue := extractReflectValue(subCfg)
×
216

×
217
                        subCfgValue.FieldByName("Manager").Set(
×
218
                                reflect.ValueOf(atpl),
×
219
                        )
×
220

221
                case *chainrpc.Config:
×
222
                        subCfgValue := extractReflectValue(subCfg)
×
223

×
224
                        subCfgValue.FieldByName("NetworkDir").Set(
×
225
                                reflect.ValueOf(networkDir),
×
226
                        )
×
227
                        subCfgValue.FieldByName("MacService").Set(
×
228
                                reflect.ValueOf(macService),
×
229
                        )
×
230
                        subCfgValue.FieldByName("ChainNotifier").Set(
×
231
                                reflect.ValueOf(cc.ChainNotifier),
×
232
                        )
×
233
                        subCfgValue.FieldByName("Chain").Set(
×
234
                                reflect.ValueOf(cc.ChainIO),
×
235
                        )
×
236

237
                case *invoicesrpc.Config:
×
238
                        subCfgValue := extractReflectValue(subCfg)
×
239

×
240
                        subCfgValue.FieldByName("NetworkDir").Set(
×
241
                                reflect.ValueOf(networkDir),
×
242
                        )
×
243
                        subCfgValue.FieldByName("MacService").Set(
×
244
                                reflect.ValueOf(macService),
×
245
                        )
×
246
                        subCfgValue.FieldByName("InvoiceRegistry").Set(
×
247
                                reflect.ValueOf(invoiceRegistry),
×
248
                        )
×
249
                        subCfgValue.FieldByName("HtlcModifier").Set(
×
250
                                reflect.ValueOf(invoiceHtlcModifier),
×
251
                        )
×
252
                        subCfgValue.FieldByName("IsChannelActive").Set(
×
253
                                reflect.ValueOf(htlcSwitch.HasActiveLink),
×
254
                        )
×
255
                        subCfgValue.FieldByName("ChainParams").Set(
×
256
                                reflect.ValueOf(activeNetParams),
×
257
                        )
×
258
                        subCfgValue.FieldByName("NodeSigner").Set(
×
259
                                reflect.ValueOf(nodeSigner),
×
260
                        )
×
261
                        defaultDelta := cfg.Bitcoin.TimeLockDelta
×
262
                        subCfgValue.FieldByName("DefaultCLTVExpiry").Set(
×
263
                                reflect.ValueOf(defaultDelta),
×
264
                        )
×
265
                        subCfgValue.FieldByName("GraphDB").Set(
×
266
                                reflect.ValueOf(graphDB),
×
267
                        )
×
268
                        subCfgValue.FieldByName("ChanStateDB").Set(
×
269
                                reflect.ValueOf(chanStateDB),
×
270
                        )
×
271
                        subCfgValue.FieldByName("GenInvoiceFeatures").Set(
×
272
                                reflect.ValueOf(genInvoiceFeatures),
×
273
                        )
×
274
                        subCfgValue.FieldByName("GenAmpInvoiceFeatures").Set(
×
275
                                reflect.ValueOf(genAmpInvoiceFeatures),
×
276
                        )
×
277
                        subCfgValue.FieldByName("GetAlias").Set(
×
278
                                reflect.ValueOf(aliasMgr.GetPeerAlias),
×
279
                        )
×
280

×
281
                        parseAuxData := func(m proto.Message) error {
×
282
                                return fn.MapOptionZ(
×
283
                                        auxDataParser,
×
284
                                        func(p AuxDataParser) error {
×
285
                                                return p.InlineParseCustomData(
×
286
                                                        m,
×
287
                                                )
×
288
                                        },
×
289
                                )
290
                        }
291
                        subCfgValue.FieldByName("ParseAuxData").Set(
×
292
                                reflect.ValueOf(parseAuxData),
×
293
                        )
×
294

295
                case *neutrinorpc.Config:
×
296
                        subCfgValue := extractReflectValue(subCfg)
×
297

×
298
                        subCfgValue.FieldByName("NeutrinoCS").Set(
×
299
                                reflect.ValueOf(cc.Cfg.NeutrinoCS),
×
300
                        )
×
301

302
                // RouterRPC isn't conditionally compiled and doesn't need to be
303
                // populated using reflection.
304
                case *routerrpc.Config:
×
305
                        subCfgValue := extractReflectValue(subCfg)
×
306

×
307
                        subCfgValue.FieldByName("AliasMgr").Set(
×
308
                                reflect.ValueOf(aliasMgr),
×
309
                        )
×
310

311
                case *watchtowerrpc.Config:
×
312
                        subCfgValue := extractReflectValue(subCfg)
×
313

×
314
                        subCfgValue.FieldByName("Active").Set(
×
315
                                reflect.ValueOf(tower != nil),
×
316
                        )
×
317
                        subCfgValue.FieldByName("Tower").Set(
×
318
                                reflect.ValueOf(tower),
×
319
                        )
×
320

321
                case *wtclientrpc.Config:
×
322
                        subCfgValue := extractReflectValue(subCfg)
×
323

×
324
                        if towerClientMgr != nil {
×
325
                                subCfgValue.FieldByName("Active").Set(
×
326
                                        reflect.ValueOf(towerClientMgr != nil),
×
327
                                )
×
328
                                subCfgValue.FieldByName("ClientMgr").Set(
×
329
                                        reflect.ValueOf(towerClientMgr),
×
330
                                )
×
331
                        }
×
332
                        subCfgValue.FieldByName("Resolver").Set(
×
333
                                reflect.ValueOf(tcpResolver),
×
334
                        )
×
335
                        subCfgValue.FieldByName("Log").Set(
×
336
                                reflect.ValueOf(rpcLogger),
×
337
                        )
×
338

339
                case *devrpc.Config:
×
340
                        subCfgValue := extractReflectValue(subCfg)
×
341

×
342
                        subCfgValue.FieldByName("ActiveNetParams").Set(
×
343
                                reflect.ValueOf(activeNetParams),
×
344
                        )
×
345

×
346
                        subCfgValue.FieldByName("GraphDB").Set(
×
347
                                reflect.ValueOf(graphDB),
×
348
                        )
×
349

×
350
                        subCfgValue.FieldByName("Switch").Set(
×
351
                                reflect.ValueOf(htlcSwitch),
×
352
                        )
×
353

354
                case *peersrpc.Config:
×
355
                        subCfgValue := extractReflectValue(subCfg)
×
356

×
357
                        subCfgValue.FieldByName("GetNodeAnnouncement").Set(
×
358
                                reflect.ValueOf(getNodeAnnouncement),
×
359
                        )
×
360

×
361
                        subCfgValue.FieldByName("ParseAddr").Set(
×
362
                                reflect.ValueOf(parseAddr),
×
363
                        )
×
364

×
365
                        subCfgValue.FieldByName("UpdateNodeAnnouncement").Set(
×
366
                                reflect.ValueOf(updateNodeAnnouncement),
×
367
                        )
×
368

369
                default:
×
370
                        return fmt.Errorf("unknown field: %v, %T", fieldName,
×
371
                                cfg)
×
372
                }
373
        }
374

375
        // Populate routerrpc dependencies.
376
        s.RouterRPC.NetworkDir = networkDir
×
377
        s.RouterRPC.MacService = macService
×
378
        s.RouterRPC.Router = chanRouter
×
379
        s.RouterRPC.RouterBackend = routerBackend
×
380

×
381
        return nil
×
382
}
383

384
// FetchConfig attempts to locate an existing configuration file mapped to the
385
// target sub-server. If we're unable to find a config file matching the
386
// subServerName name, then false will be returned for the second parameter.
387
//
388
// NOTE: Part of the lnrpc.SubServerConfigDispatcher interface.
389
func (s *subRPCServerConfigs) FetchConfig(subServerName string) (interface{}, bool) {
×
390
        // First, we'll use reflect to obtain a version of the config struct
×
391
        // that allows us to programmatically inspect its fields.
×
392
        selfVal := extractReflectValue(s)
×
393

×
394
        // Now that we have the value of the struct, we can check to see if it
×
395
        // has an attribute with the same name as the subServerName.
×
396
        configVal := selfVal.FieldByName(subServerName)
×
397

×
398
        // We'll now ensure that this field actually exists in this value. If
×
399
        // not, then we'll return false for the ok value to indicate to the
×
400
        // caller that this field doesn't actually exist.
×
401
        if !configVal.IsValid() {
×
402
                return nil, false
×
403
        }
×
404

405
        configValElem := configVal.Elem()
×
406

×
407
        // If a config of this type is found, it doesn't have any fields, then
×
408
        // it's the same as if it wasn't present. This can happen if the build
×
409
        // tag for the sub-server is inactive.
×
410
        if configValElem.NumField() == 0 {
×
411
                return nil, false
×
412
        }
×
413

414
        // At this pint, we know that the field is actually present in the
415
        // config struct, so we can return it directly.
416
        return configVal.Interface(), true
×
417
}
418

419
// extractReflectValue attempts to extract the value from an interface using
420
// the reflect package. The resulting reflect.Value allows the caller to
421
// programmatically examine and manipulate the underlying value.
422
func extractReflectValue(instance interface{}) reflect.Value {
×
423
        var val reflect.Value
×
424

×
425
        // If the type of the instance is a pointer, then we need to deference
×
426
        // the pointer one level to get its value. Otherwise, we can access the
×
427
        // value directly.
×
428
        if reflect.TypeOf(instance).Kind() == reflect.Ptr {
×
429
                val = reflect.ValueOf(instance).Elem()
×
430
        } else {
×
431
                val = reflect.ValueOf(instance)
×
432
        }
×
433

434
        return val
×
435
}
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