github.com/aidoskuneen/adk-node@v0.0.0-20220315131952-2e32567cb7f4/node/api.go (about)

     1  // Copyright 2021 The adkgo Authors
     2  // This file is part of the adkgo library (adapted for adkgo from go--ethereum v1.10.8).
     3  //
     4  // the adkgo library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // the adkgo library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the adkgo library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package node
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"strings"
    23  
    24  	"github.com/aidoskuneen/adk-node/common/hexutil"
    25  	"github.com/aidoskuneen/adk-node/crypto"
    26  	"github.com/aidoskuneen/adk-node/internal/debug"
    27  	"github.com/aidoskuneen/adk-node/log"
    28  	"github.com/aidoskuneen/adk-node/p2p"
    29  	"github.com/aidoskuneen/adk-node/p2p/enode"
    30  	"github.com/aidoskuneen/adk-node/rpc"
    31  )
    32  
    33  // apis returns the collection of built-in RPC APIs.
    34  func (n *Node) apis() []rpc.API {
    35  	return []rpc.API{
    36  		{
    37  			Namespace: "admin",
    38  			Version:   "1.0",
    39  			Service:   &privateAdminAPI{n},
    40  		}, {
    41  			Namespace: "admin",
    42  			Version:   "1.0",
    43  			Service:   &publicAdminAPI{n},
    44  			Public:    true,
    45  		}, {
    46  			Namespace: "debug",
    47  			Version:   "1.0",
    48  			Service:   debug.Handler,
    49  		}, {
    50  			Namespace: "web3",
    51  			Version:   "1.0",
    52  			Service:   &publicWeb3API{n},
    53  			Public:    true,
    54  		},
    55  	}
    56  }
    57  
    58  // privateAdminAPI is the collection of administrative API methods exposed only
    59  // over a secure RPC channel.
    60  type privateAdminAPI struct {
    61  	node *Node // Node interfaced by this API
    62  }
    63  
    64  // AddPeer requests connecting to a remote node, and also maintaining the new
    65  // connection at all times, even reconnecting if it is lost.
    66  func (api *privateAdminAPI) AddPeer(url string) (bool, error) {
    67  	// Make sure the server is running, fail otherwise
    68  	server := api.node.Server()
    69  	if server == nil {
    70  		return false, ErrNodeStopped
    71  	}
    72  	// Try to add the url as a static peer and return
    73  	node, err := enode.Parse(enode.ValidSchemes, url)
    74  	if err != nil {
    75  		return false, fmt.Errorf("invalid enode: %v", err)
    76  	}
    77  	server.AddPeer(node)
    78  	return true, nil
    79  }
    80  
    81  // RemovePeer disconnects from a remote node if the connection exists
    82  func (api *privateAdminAPI) RemovePeer(url string) (bool, error) {
    83  	// Make sure the server is running, fail otherwise
    84  	server := api.node.Server()
    85  	if server == nil {
    86  		return false, ErrNodeStopped
    87  	}
    88  	// Try to remove the url as a static peer and return
    89  	node, err := enode.Parse(enode.ValidSchemes, url)
    90  	if err != nil {
    91  		return false, fmt.Errorf("invalid enode: %v", err)
    92  	}
    93  	server.RemovePeer(node)
    94  	return true, nil
    95  }
    96  
    97  // AddTrustedPeer allows a remote node to always connect, even if slots are full
    98  func (api *privateAdminAPI) AddTrustedPeer(url string) (bool, error) {
    99  	// Make sure the server is running, fail otherwise
   100  	server := api.node.Server()
   101  	if server == nil {
   102  		return false, ErrNodeStopped
   103  	}
   104  	node, err := enode.Parse(enode.ValidSchemes, url)
   105  	if err != nil {
   106  		return false, fmt.Errorf("invalid enode: %v", err)
   107  	}
   108  	server.AddTrustedPeer(node)
   109  	return true, nil
   110  }
   111  
   112  // RemoveTrustedPeer removes a remote node from the trusted peer set, but it
   113  // does not disconnect it automatically.
   114  func (api *privateAdminAPI) RemoveTrustedPeer(url string) (bool, error) {
   115  	// Make sure the server is running, fail otherwise
   116  	server := api.node.Server()
   117  	if server == nil {
   118  		return false, ErrNodeStopped
   119  	}
   120  	node, err := enode.Parse(enode.ValidSchemes, url)
   121  	if err != nil {
   122  		return false, fmt.Errorf("invalid enode: %v", err)
   123  	}
   124  	server.RemoveTrustedPeer(node)
   125  	return true, nil
   126  }
   127  
   128  // PeerEvents creates an RPC subscription which receives peer events from the
   129  // node's p2p.Server
   130  func (api *privateAdminAPI) PeerEvents(ctx context.Context) (*rpc.Subscription, error) {
   131  	// Make sure the server is running, fail otherwise
   132  	server := api.node.Server()
   133  	if server == nil {
   134  		return nil, ErrNodeStopped
   135  	}
   136  
   137  	// Create the subscription
   138  	notifier, supported := rpc.NotifierFromContext(ctx)
   139  	if !supported {
   140  		return nil, rpc.ErrNotificationsUnsupported
   141  	}
   142  	rpcSub := notifier.CreateSubscription()
   143  
   144  	go func() {
   145  		events := make(chan *p2p.PeerEvent)
   146  		sub := server.SubscribeEvents(events)
   147  		defer sub.Unsubscribe()
   148  
   149  		for {
   150  			select {
   151  			case event := <-events:
   152  				notifier.Notify(rpcSub.ID, event)
   153  			case <-sub.Err():
   154  				return
   155  			case <-rpcSub.Err():
   156  				return
   157  			case <-notifier.Closed():
   158  				return
   159  			}
   160  		}
   161  	}()
   162  
   163  	return rpcSub, nil
   164  }
   165  
   166  // StartHTTP starts the HTTP RPC API server.
   167  func (api *privateAdminAPI) StartHTTP(host *string, port *int, cors *string, apis *string, vhosts *string) (bool, error) {
   168  	api.node.lock.Lock()
   169  	defer api.node.lock.Unlock()
   170  
   171  	// Determine host and port.
   172  	if host == nil {
   173  		h := DefaultHTTPHost
   174  		if api.node.config.HTTPHost != "" {
   175  			h = api.node.config.HTTPHost
   176  		}
   177  		host = &h
   178  	}
   179  	if port == nil {
   180  		port = &api.node.config.HTTPPort
   181  	}
   182  
   183  	// Determine config.
   184  	config := httpConfig{
   185  		CorsAllowedOrigins: api.node.config.HTTPCors,
   186  		Vhosts:             api.node.config.HTTPVirtualHosts,
   187  		Modules:            api.node.config.HTTPModules,
   188  	}
   189  	if cors != nil {
   190  		config.CorsAllowedOrigins = nil
   191  		for _, origin := range strings.Split(*cors, ",") {
   192  			config.CorsAllowedOrigins = append(config.CorsAllowedOrigins, strings.TrimSpace(origin))
   193  		}
   194  	}
   195  	if vhosts != nil {
   196  		config.Vhosts = nil
   197  		for _, vhost := range strings.Split(*host, ",") {
   198  			config.Vhosts = append(config.Vhosts, strings.TrimSpace(vhost))
   199  		}
   200  	}
   201  	if apis != nil {
   202  		config.Modules = nil
   203  		for _, m := range strings.Split(*apis, ",") {
   204  			config.Modules = append(config.Modules, strings.TrimSpace(m))
   205  		}
   206  	}
   207  
   208  	if err := api.node.http.setListenAddr(*host, *port); err != nil {
   209  		return false, err
   210  	}
   211  	if err := api.node.http.enableRPC(api.node.rpcAPIs, config); err != nil {
   212  		return false, err
   213  	}
   214  	if err := api.node.http.start(); err != nil {
   215  		return false, err
   216  	}
   217  	return true, nil
   218  }
   219  
   220  // StartRPC starts the HTTP RPC API server.
   221  // This method is deprecated. Use StartHTTP instead.
   222  func (api *privateAdminAPI) StartRPC(host *string, port *int, cors *string, apis *string, vhosts *string) (bool, error) {
   223  	log.Warn("Deprecation warning", "method", "admin.StartRPC", "use-instead", "admin.StartHTTP")
   224  	return api.StartHTTP(host, port, cors, apis, vhosts)
   225  }
   226  
   227  // StopHTTP shuts down the HTTP server.
   228  func (api *privateAdminAPI) StopHTTP() (bool, error) {
   229  	api.node.http.stop()
   230  	return true, nil
   231  }
   232  
   233  // StopRPC shuts down the HTTP server.
   234  // This method is deprecated. Use StopHTTP instead.
   235  func (api *privateAdminAPI) StopRPC() (bool, error) {
   236  	log.Warn("Deprecation warning", "method", "admin.StopRPC", "use-instead", "admin.StopHTTP")
   237  	return api.StopHTTP()
   238  }
   239  
   240  // StartWS starts the websocket RPC API server.
   241  func (api *privateAdminAPI) StartWS(host *string, port *int, allowedOrigins *string, apis *string) (bool, error) {
   242  	api.node.lock.Lock()
   243  	defer api.node.lock.Unlock()
   244  
   245  	// Determine host and port.
   246  	if host == nil {
   247  		h := DefaultWSHost
   248  		if api.node.config.WSHost != "" {
   249  			h = api.node.config.WSHost
   250  		}
   251  		host = &h
   252  	}
   253  	if port == nil {
   254  		port = &api.node.config.WSPort
   255  	}
   256  
   257  	// Determine config.
   258  	config := wsConfig{
   259  		Modules: api.node.config.WSModules,
   260  		Origins: api.node.config.WSOrigins,
   261  		// ExposeAll: api.node.config.WSExposeAll,
   262  	}
   263  	if apis != nil {
   264  		config.Modules = nil
   265  		for _, m := range strings.Split(*apis, ",") {
   266  			config.Modules = append(config.Modules, strings.TrimSpace(m))
   267  		}
   268  	}
   269  	if allowedOrigins != nil {
   270  		config.Origins = nil
   271  		for _, origin := range strings.Split(*allowedOrigins, ",") {
   272  			config.Origins = append(config.Origins, strings.TrimSpace(origin))
   273  		}
   274  	}
   275  
   276  	// Enable WebSocket on the server.
   277  	server := api.node.wsServerForPort(*port)
   278  	if err := server.setListenAddr(*host, *port); err != nil {
   279  		return false, err
   280  	}
   281  	if err := server.enableWS(api.node.rpcAPIs, config); err != nil {
   282  		return false, err
   283  	}
   284  	if err := server.start(); err != nil {
   285  		return false, err
   286  	}
   287  	api.node.http.log.Info("WebSocket endpoint opened", "url", api.node.WSEndpoint())
   288  	return true, nil
   289  }
   290  
   291  // StopWS terminates all WebSocket servers.
   292  func (api *privateAdminAPI) StopWS() (bool, error) {
   293  	api.node.http.stopWS()
   294  	api.node.ws.stop()
   295  	return true, nil
   296  }
   297  
   298  // publicAdminAPI is the collection of administrative API methods exposed over
   299  // both secure and unsecure RPC channels.
   300  type publicAdminAPI struct {
   301  	node *Node // Node interfaced by this API
   302  }
   303  
   304  // Peers retrieves all the information we know about each individual peer at the
   305  // protocol granularity.
   306  func (api *publicAdminAPI) Peers() ([]*p2p.PeerInfo, error) {
   307  	server := api.node.Server()
   308  	if server == nil {
   309  		return nil, ErrNodeStopped
   310  	}
   311  	return server.PeersInfo(), nil
   312  }
   313  
   314  // NodeInfo retrieves all the information we know about the host node at the
   315  // protocol granularity.
   316  func (api *publicAdminAPI) NodeInfo() (*p2p.NodeInfo, error) {
   317  	server := api.node.Server()
   318  	if server == nil {
   319  		return nil, ErrNodeStopped
   320  	}
   321  	return server.NodeInfo(), nil
   322  }
   323  
   324  // Datadir retrieves the current data directory the node is using.
   325  func (api *publicAdminAPI) Datadir() string {
   326  	return api.node.DataDir()
   327  }
   328  
   329  // publicWeb3API offers helper utils
   330  type publicWeb3API struct {
   331  	stack *Node
   332  }
   333  
   334  // ClientVersion returns the node name
   335  func (s *publicWeb3API) ClientVersion() string {
   336  	return s.stack.Server().Name
   337  }
   338  
   339  // Sha3 applies the ethereum sha3 implementation on the input.
   340  // It assumes the input is hex encoded.
   341  func (s *publicWeb3API) Sha3(input hexutil.Bytes) hexutil.Bytes {
   342  	return crypto.Keccak256(input)
   343  }