github.com/decred/dcrlnd@v0.7.6/lnrpc/autopilotrpc/autopilot_server.go (about) 1 //go:build !no_autopilotrpc 2 // +build !no_autopilotrpc 3 4 package autopilotrpc 5 6 import ( 7 "context" 8 "encoding/hex" 9 "sync/atomic" 10 11 "github.com/decred/dcrd/dcrec/secp256k1/v4" 12 "github.com/decred/dcrlnd/autopilot" 13 "github.com/decred/dcrlnd/lnrpc" 14 "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" 15 "google.golang.org/grpc" 16 "gopkg.in/macaroon-bakery.v2/bakery" 17 ) 18 19 const ( 20 // subServerName is the name of the sub rpc server. We'll use this name 21 // to register ourselves, and we also require that the main 22 // SubServerConfigDispatcher instance recognize tt as the name of our 23 // RPC service. 24 subServerName = "AutopilotRPC" 25 ) 26 27 var ( 28 // macPermissions maps RPC calls to the permissions they require. 29 macPermissions = map[string][]bakery.Op{ 30 "/autopilotrpc.Autopilot/Status": {{ 31 Entity: "info", 32 Action: "read", 33 }}, 34 "/autopilotrpc.Autopilot/ModifyStatus": {{ 35 Entity: "onchain", 36 Action: "write", 37 }, { 38 Entity: "offchain", 39 Action: "write", 40 }}, 41 "/autopilotrpc.Autopilot/QueryScores": {{ 42 Entity: "info", 43 Action: "read", 44 }}, 45 "/autopilotrpc.Autopilot/SetScores": {{ 46 Entity: "onchain", 47 Action: "write", 48 }, { 49 Entity: "offchain", 50 Action: "write", 51 }}, 52 } 53 ) 54 55 // ServerShell is a shell struct holding a reference to the actual sub-server. 56 // It is used to register the gRPC sub-server with the root server before we 57 // have the necessary dependencies to populate the actual sub-server. 58 type ServerShell struct { 59 AutopilotServer 60 } 61 62 // Server is a sub-server of the main RPC server: the autopilot RPC. This sub 63 // RPC server allows external callers to access the status of the autopilot 64 // currently active within lnd, as well as configuring it at runtime. 65 type Server struct { 66 started int32 // To be used atomically. 67 shutdown int32 // To be used atomically. 68 69 cfg *Config 70 71 manager *autopilot.Manager 72 } 73 74 // A compile time check to ensure that Server fully implements the 75 // AutopilotServer gRPC service. 76 var _ AutopilotServer = (*Server)(nil) 77 78 // New returns a new instance of the autopilotrpc Autopilot sub-server. We also 79 // return the set of permissions for the macaroons that we may create within 80 // this method. If the macaroons we need aren't found in the filepath, then 81 // we'll create them on start up. If we're unable to locate, or create the 82 // macaroons we need, then we'll return with an error. 83 func New(cfg *Config) (*Server, lnrpc.MacaroonPerms, error) { 84 // We don't create any new macaroons for this subserver, instead reuse 85 // existing onchain/offchain permissions. 86 server := &Server{ 87 cfg: cfg, 88 manager: cfg.Manager, 89 } 90 91 return server, macPermissions, nil 92 } 93 94 // Start launches any helper goroutines required for the Server to function. 95 // 96 // NOTE: This is part of the lnrpc.SubServer interface. 97 func (s *Server) Start() error { 98 if atomic.AddInt32(&s.started, 1) != 1 { 99 return nil 100 } 101 102 return s.manager.Start() 103 } 104 105 // Stop signals any active goroutines for a graceful closure. 106 // 107 // NOTE: This is part of the lnrpc.SubServer interface. 108 func (s *Server) Stop() error { 109 if atomic.AddInt32(&s.shutdown, 1) != 1 { 110 return nil 111 } 112 113 return s.manager.Stop() 114 } 115 116 // Name returns a unique string representation of the sub-server. This can be 117 // used to identify the sub-server and also de-duplicate them. 118 // 119 // NOTE: This is part of the lnrpc.SubServer interface. 120 func (s *Server) Name() string { 121 return subServerName 122 } 123 124 // RegisterWithRootServer will be called by the root gRPC server to direct a 125 // sub RPC server to register itself with the main gRPC root server. Until this 126 // is called, each sub-server won't be able to have 127 // requests routed towards it. 128 // 129 // NOTE: This is part of the lnrpc.GrpcHandler interface. 130 func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error { 131 // We make sure that we register it with the main gRPC server to ensure 132 // all our methods are routed properly. 133 RegisterAutopilotServer(grpcServer, r) 134 135 log.Debugf("Autopilot RPC server successfully register with root " + 136 "gRPC server") 137 138 return nil 139 } 140 141 // RegisterWithRestServer will be called by the root REST mux to direct a sub 142 // RPC server to register itself with the main REST mux server. Until this is 143 // called, each sub-server won't be able to have requests routed towards it. 144 // 145 // NOTE: This is part of the lnrpc.GrpcHandler interface. 146 func (r *ServerShell) RegisterWithRestServer(ctx context.Context, 147 mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error { 148 149 // We make sure that we register it with the main REST server to ensure 150 // all our methods are routed properly. 151 err := RegisterAutopilotHandlerFromEndpoint(ctx, mux, dest, opts) 152 if err != nil { 153 log.Errorf("Could not register Autopilot REST server "+ 154 "with root REST server: %v", err) 155 return err 156 } 157 158 log.Debugf("Autopilot REST server successfully registered with " + 159 "root REST server") 160 return nil 161 } 162 163 // CreateSubServer populates the subserver's dependencies using the passed 164 // SubServerConfigDispatcher. This method should fully initialize the 165 // sub-server instance, making it ready for action. It returns the macaroon 166 // permissions that the sub-server wishes to pass on to the root server for all 167 // methods routed towards it. 168 // 169 // NOTE: This is part of the lnrpc.GrpcHandler interface. 170 func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispatcher) ( 171 lnrpc.SubServer, lnrpc.MacaroonPerms, error) { 172 173 subServer, macPermissions, err := createNewSubServer(configRegistry) 174 if err != nil { 175 return nil, nil, err 176 } 177 178 r.AutopilotServer = subServer 179 return subServer, macPermissions, nil 180 } 181 182 // Status returns the current status of the autopilot agent. 183 // 184 // NOTE: Part of the AutopilotServer interface. 185 func (s *Server) Status(ctx context.Context, 186 in *StatusRequest) (*StatusResponse, error) { 187 188 return &StatusResponse{ 189 Active: s.manager.IsActive(), 190 }, nil 191 } 192 193 // ModifyStatus activates the current autopilot agent, if active. 194 // 195 // NOTE: Part of the AutopilotServer interface. 196 func (s *Server) ModifyStatus(ctx context.Context, 197 in *ModifyStatusRequest) (*ModifyStatusResponse, error) { 198 199 log.Debugf("Setting agent enabled=%v", in.Enable) 200 201 var err error 202 if in.Enable { 203 err = s.manager.StartAgent() 204 } else { 205 err = s.manager.StopAgent() 206 } 207 return &ModifyStatusResponse{}, err 208 } 209 210 // QueryScores queries all available autopilot heuristics, in addition to any 211 // active combination of these heruristics, for the scores they would give to 212 // the given nodes. 213 // 214 // NOTE: Part of the AutopilotServer interface. 215 func (s *Server) QueryScores(ctx context.Context, in *QueryScoresRequest) ( 216 *QueryScoresResponse, error) { 217 218 var nodes []autopilot.NodeID 219 for _, pubStr := range in.Pubkeys { 220 pubHex, err := hex.DecodeString(pubStr) 221 if err != nil { 222 return nil, err 223 } 224 pubKey, err := secp256k1.ParsePubKey(pubHex) 225 if err != nil { 226 return nil, err 227 } 228 nID := autopilot.NewNodeID(pubKey) 229 nodes = append(nodes, nID) 230 } 231 232 // Query the heuristics. 233 heuristicScores, err := s.manager.QueryHeuristics( 234 nodes, !in.IgnoreLocalState, 235 ) 236 if err != nil { 237 return nil, err 238 } 239 240 resp := &QueryScoresResponse{} 241 for heuristic, scores := range heuristicScores { 242 result := &QueryScoresResponse_HeuristicResult{ 243 Heuristic: heuristic, 244 Scores: make(map[string]float64), 245 } 246 247 for pub, score := range scores { 248 pubkeyHex := hex.EncodeToString(pub[:]) 249 result.Scores[pubkeyHex] = score 250 } 251 252 // Since a node not being part of the internally returned 253 // scores imply a zero score, we add these before we return the 254 // RPC results. 255 for _, node := range nodes { 256 if _, ok := scores[node]; ok { 257 continue 258 } 259 pubkeyHex := hex.EncodeToString(node[:]) 260 result.Scores[pubkeyHex] = 0.0 261 } 262 263 resp.Results = append(resp.Results, result) 264 } 265 266 return resp, nil 267 } 268 269 // SetScores sets the scores of the external score heuristic, if active. 270 // 271 // NOTE: Part of the AutopilotServer interface. 272 func (s *Server) SetScores(ctx context.Context, 273 in *SetScoresRequest) (*SetScoresResponse, error) { 274 275 scores := make(map[autopilot.NodeID]float64) 276 for pubStr, score := range in.Scores { 277 pubHex, err := hex.DecodeString(pubStr) 278 if err != nil { 279 return nil, err 280 } 281 pubKey, err := secp256k1.ParsePubKey(pubHex) 282 if err != nil { 283 return nil, err 284 } 285 nID := autopilot.NewNodeID(pubKey) 286 scores[nID] = score 287 } 288 289 if err := s.manager.SetNodeScores(in.Heuristic, scores); err != nil { 290 return nil, err 291 } 292 293 return &SetScoresResponse{}, nil 294 }