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

lightningnetwork / lnd / 11954082915

21 Nov 2024 01:20PM UTC coverage: 59.327% (+0.6%) from 58.776%
11954082915

Pull #8754

github

ViktorTigerstrom
itest: wrap deriveCustomScopeAccounts at 80 chars

This commit fixes that word wrapping for the deriveCustomScopeAccounts
function docs, and ensures that it wraps at 80 characters or less.
Pull Request #8754: Add `Outbound` Remote Signer implementation

1940 of 2984 new or added lines in 44 files covered. (65.01%)

226 existing lines in 37 files now uncovered.

135234 of 227947 relevant lines covered (59.33%)

19316.75 hits per line

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

43.71
/lnrpc/autopilotrpc/autopilot_server.go
1
//go:build autopilotrpc
2
// +build autopilotrpc
3

4
package autopilotrpc
5

6
import (
7
        "context"
8
        "encoding/hex"
9
        "errors"
10
        "sync"
11
        "sync/atomic"
12

13
        "github.com/btcsuite/btcd/btcec/v2"
14
        "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
15
        "github.com/lightningnetwork/lnd/autopilot"
16
        "github.com/lightningnetwork/lnd/lnrpc"
17
        "google.golang.org/grpc"
18
        "gopkg.in/macaroon-bakery.v2/bakery"
19
)
20

21
const (
22
        // subServerName is the name of the sub rpc server. We'll use this name
23
        // to register ourselves, and we also require that the main
24
        // SubServerConfigDispatcher instance recognize tt as the name of our
25
        // RPC service.
26
        subServerName = "AutopilotRPC"
27
)
28

29
var (
30
        // macPermissions maps RPC calls to the permissions they require.
31
        macPermissions = map[string][]bakery.Op{
32
                "/autopilotrpc.Autopilot/Status": {{
33
                        Entity: "info",
34
                        Action: "read",
35
                }},
36
                "/autopilotrpc.Autopilot/ModifyStatus": {{
37
                        Entity: "onchain",
38
                        Action: "write",
39
                }, {
40
                        Entity: "offchain",
41
                        Action: "write",
42
                }},
43
                "/autopilotrpc.Autopilot/QueryScores": {{
44
                        Entity: "info",
45
                        Action: "read",
46
                }},
47
                "/autopilotrpc.Autopilot/SetScores": {{
48
                        Entity: "onchain",
49
                        Action: "write",
50
                }, {
51
                        Entity: "offchain",
52
                        Action: "write",
53
                }},
54
        }
55
)
56

57
// ServerShell is a shell struct holding a reference to the actual sub-server.
58
// It is used to register the gRPC sub-server with the root server before we
59
// have the necessary dependencies to populate the actual sub-server.
60
type ServerShell struct {
61
        AutopilotServer
62
}
63

64
// Server is a sub-server of the main RPC server: the autopilot RPC. This sub
65
// RPC server allows external callers to access the status of the autopilot
66
// currently active within lnd, as well as configuring it at runtime.
67
type Server struct {
68
        injected int32 // To be used atomically.
69
        shutdown int32 // To be used atomically.
70

71
        // Required by the grpc-gateway/v2 library for forward compatibility.
72
        // Must be after the atomically used variables to not break struct
73
        // alignment.
74
        UnimplementedAutopilotServer
75

76
        cfg *Config
77

78
        manager *autopilot.Manager
79

80
        // This mutex should be held when accessing any fields of this struct,
81
        // that can be accessed before the dependencies have been injected.
82
        mu sync.Mutex
83
}
84

85
// A compile time check to ensure that Server fully implements the
86
// AutopilotServer gRPC service.
87
var _ AutopilotServer = (*Server)(nil)
88

89
// New returns a new instance of the autopilotrpc Autopilot sub-server. We also
90
// return the set of permissions for the macaroons that we may create within
91
// this method. If the macaroons we need aren't found in the filepath, then
92
// we'll create them on start up. If we're unable to locate, or create the
93
// macaroons we need, then we'll return with an error.
94
func New() (*Server, lnrpc.MacaroonPerms, error) {
4✔
95
        // We don't create any new macaroons for this subserver, instead reuse
4✔
96
        // existing onchain/offchain permissions.
4✔
97
        return &Server{cfg: &Config{}}, macPermissions, nil
4✔
98
}
4✔
99

100
// Stop signals any active goroutines for a graceful closure.
101
//
102
// NOTE: This is part of the lnrpc.SubServer interface.
103
func (s *Server) Stop() error {
4✔
104
        // As the Stop function could in theory in the future be called before
4✔
105
        // the InjectDependencies function has been executed, we need to hold
4✔
106
        // the lock here.
4✔
107
        s.mu.Lock()
4✔
108
        defer s.mu.Unlock()
4✔
109

4✔
110
        if atomic.AddInt32(&s.shutdown, 1) != 1 {
4✔
111
                return nil
×
112
        }
×
113

114
        if s.manager == nil {
4✔
NEW
115
                return nil
×
NEW
116
        }
×
117

118
        return s.manager.Stop()
4✔
119
}
120

121
// Name returns a unique string representation of the sub-server. This can be
122
// used to identify the sub-server and also de-duplicate them.
123
//
124
// NOTE: This is part of the lnrpc.SubServer interface.
125
func (s *Server) Name() string {
4✔
126
        return subServerName
4✔
127
}
4✔
128

129
// InjectDependencies populates the sub-server's dependencies. If the
130
// finalizeDependencies boolean is true, then the sub-server will finalize its
131
// dependencies and return an error if any required dependencies are missing.
132
//
133
// NOTE: This is part of the lnrpc.SubServer interface.
134
func (s *Server) InjectDependencies(
135
        configRegistry lnrpc.SubServerConfigDispatcher,
136
        finalizeDependencies bool) error {
4✔
137

4✔
138
        if finalizeDependencies && atomic.AddInt32(&s.injected, 1) != 1 {
4✔
NEW
139
                return lnrpc.ErrDependenciesFinalized
×
NEW
140
        }
×
141

142
        s.mu.Lock()
4✔
143
        defer s.mu.Unlock()
4✔
144

4✔
145
        if s.shutdown != 0 {
4✔
NEW
146
                return errors.New("server shutting down")
×
NEW
147
        }
×
148

149
        cfg, err := getConfig(configRegistry, finalizeDependencies)
4✔
150
        if err != nil {
4✔
NEW
151
                return err
×
NEW
152
        }
×
153

154
        s.cfg = cfg
4✔
155

4✔
156
        // If we're not finalizing the dependencies, we the manager doesn't
4✔
157
        // need to be set, and would then error if we tried to start it.
4✔
158
        if !finalizeDependencies && cfg.Manager == nil {
4✔
NEW
159
                return nil
×
NEW
160
        }
×
161

162
        s.manager = cfg.Manager
4✔
163

4✔
164
        return s.manager.Start()
4✔
165
}
166

167
// RegisterWithRootServer will be called by the root gRPC server to direct a
168
// sub RPC server to register itself with the main gRPC root server. Until this
169
// is called, each sub-server won't be able to have
170
// requests routed towards it.
171
//
172
// NOTE: This is part of the lnrpc.GrpcHandler interface.
173
func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
4✔
174
        // We make sure that we register it with the main gRPC server to ensure
4✔
175
        // all our methods are routed properly.
4✔
176
        RegisterAutopilotServer(grpcServer, r)
4✔
177

4✔
178
        log.Debugf("Autopilot RPC server successfully register with root " +
4✔
179
                "gRPC server")
4✔
180

4✔
181
        return nil
4✔
182
}
4✔
183

184
// RegisterWithRestServer will be called by the root REST mux to direct a sub
185
// RPC server to register itself with the main REST mux server. Until this is
186
// called, each sub-server won't be able to have requests routed towards it.
187
//
188
// NOTE: This is part of the lnrpc.GrpcHandler interface.
189
func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
190
        mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
4✔
191

4✔
192
        // We make sure that we register it with the main REST server to ensure
4✔
193
        // all our methods are routed properly.
4✔
194
        err := RegisterAutopilotHandlerFromEndpoint(ctx, mux, dest, opts)
4✔
195
        if err != nil {
4✔
196
                log.Errorf("Could not register Autopilot REST server "+
×
197
                        "with root REST server: %v", err)
×
198
                return err
×
199
        }
×
200

201
        log.Debugf("Autopilot REST server successfully registered with " +
4✔
202
                "root REST server")
4✔
203
        return nil
4✔
204
}
205

206
// CreateSubServer creates an instance of the sub-server, and returns the
207
// macaroon permissions that the sub-server wishes to pass on to the root server
208
// for all methods routed towards it.
209
//
210
// NOTE: This is part of the lnrpc.GrpcHandler interface.
211
func (r *ServerShell) CreateSubServer() (
212
        lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
4✔
213

4✔
214
        subServer, macPermissions, err := New()
4✔
215
        if err != nil {
4✔
216
                return nil, nil, err
×
217
        }
×
218

219
        r.AutopilotServer = subServer
4✔
220
        return subServer, macPermissions, nil
4✔
221
}
222

223
// Status returns the current status of the autopilot agent.
224
//
225
// NOTE: Part of the AutopilotServer interface.
226
func (s *Server) Status(ctx context.Context,
227
        in *StatusRequest) (*StatusResponse, error) {
4✔
228

4✔
229
        return &StatusResponse{
4✔
230
                Active: s.manager.IsActive(),
4✔
231
        }, nil
4✔
232
}
4✔
233

234
// ModifyStatus activates the current autopilot agent, if active.
235
//
236
// NOTE: Part of the AutopilotServer interface.
237
func (s *Server) ModifyStatus(ctx context.Context,
238
        in *ModifyStatusRequest) (*ModifyStatusResponse, error) {
×
239

×
240
        log.Debugf("Setting agent enabled=%v", in.Enable)
×
241

×
242
        var err error
×
243
        if in.Enable {
×
244
                err = s.manager.StartAgent()
×
245
        } else {
×
246
                err = s.manager.StopAgent()
×
247
        }
×
248
        return &ModifyStatusResponse{}, err
×
249
}
250

251
// QueryScores queries all available autopilot heuristics, in addition to any
252
// active combination of these heruristics, for the scores they would give to
253
// the given nodes.
254
//
255
// NOTE: Part of the AutopilotServer interface.
256
func (s *Server) QueryScores(ctx context.Context, in *QueryScoresRequest) (
257
        *QueryScoresResponse, error) {
×
258

×
259
        var nodes []autopilot.NodeID
×
260
        for _, pubStr := range in.Pubkeys {
×
261
                pubHex, err := hex.DecodeString(pubStr)
×
262
                if err != nil {
×
263
                        return nil, err
×
264
                }
×
265
                pubKey, err := btcec.ParsePubKey(pubHex)
×
266
                if err != nil {
×
267
                        return nil, err
×
268
                }
×
269
                nID := autopilot.NewNodeID(pubKey)
×
270
                nodes = append(nodes, nID)
×
271
        }
272

273
        // Query the heuristics.
274
        heuristicScores, err := s.manager.QueryHeuristics(
×
275
                nodes, !in.IgnoreLocalState,
×
276
        )
×
277
        if err != nil {
×
278
                return nil, err
×
279
        }
×
280

281
        resp := &QueryScoresResponse{}
×
282
        for heuristic, scores := range heuristicScores {
×
283
                result := &QueryScoresResponse_HeuristicResult{
×
284
                        Heuristic: heuristic,
×
285
                        Scores:    make(map[string]float64),
×
286
                }
×
287

×
288
                for pub, score := range scores {
×
289
                        pubkeyHex := hex.EncodeToString(pub[:])
×
290
                        result.Scores[pubkeyHex] = score
×
291
                }
×
292

293
                // Since a node not being part of the internally returned
294
                // scores imply a zero score, we add these before we return the
295
                // RPC results.
296
                for _, node := range nodes {
×
297
                        if _, ok := scores[node]; ok {
×
298
                                continue
×
299
                        }
300
                        pubkeyHex := hex.EncodeToString(node[:])
×
301
                        result.Scores[pubkeyHex] = 0.0
×
302
                }
303

304
                resp.Results = append(resp.Results, result)
×
305
        }
306

307
        return resp, nil
×
308
}
309

310
// SetScores sets the scores of the external score heuristic, if active.
311
//
312
// NOTE: Part of the AutopilotServer interface.
313
func (s *Server) SetScores(ctx context.Context,
314
        in *SetScoresRequest) (*SetScoresResponse, error) {
×
315

×
316
        scores := make(map[autopilot.NodeID]float64)
×
317
        for pubStr, score := range in.Scores {
×
318
                pubHex, err := hex.DecodeString(pubStr)
×
319
                if err != nil {
×
320
                        return nil, err
×
321
                }
×
322
                pubKey, err := btcec.ParsePubKey(pubHex)
×
323
                if err != nil {
×
324
                        return nil, err
×
325
                }
×
326
                nID := autopilot.NewNodeID(pubKey)
×
327
                scores[nID] = score
×
328
        }
329

330
        if err := s.manager.SetNodeScores(in.Heuristic, scores); err != nil {
×
331
                return nil, err
×
332
        }
×
333

334
        return &SetScoresResponse{}, nil
×
335
}
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