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  }