github.com/klaytn/klaytn@v1.12.1/node/api.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2015 The go-ethereum Authors
     3  // This file is part of go-ethereum.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from node/api.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package node
    22  
    23  import (
    24  	"context"
    25  	"encoding/hex"
    26  	"fmt"
    27  	"strings"
    28  	"time"
    29  
    30  	"github.com/klaytn/klaytn/common/hexutil"
    31  	"github.com/klaytn/klaytn/crypto"
    32  	"github.com/klaytn/klaytn/crypto/bls"
    33  	"github.com/klaytn/klaytn/networks/p2p"
    34  	"github.com/klaytn/klaytn/networks/p2p/discover"
    35  	"github.com/klaytn/klaytn/networks/rpc"
    36  	"github.com/rcrowley/go-metrics"
    37  )
    38  
    39  // PrivateAdminAPI is the collection of administrative API methods exposed only
    40  // over a secure RPC channel.
    41  type PrivateAdminAPI struct {
    42  	node *Node // Node interfaced by this API
    43  }
    44  
    45  // NewPrivateAdminAPI creates a new API definition for the private admin methods
    46  // of the node itself.
    47  func NewPrivateAdminAPI(node *Node) *PrivateAdminAPI {
    48  	return &PrivateAdminAPI{node: node}
    49  }
    50  
    51  // addPeerInternal does common part for AddPeer.
    52  func addPeerInternal(server p2p.Server, url string, onParentChain bool) (*discover.Node, error) {
    53  	// Try to add the url as a static peer and return
    54  	node, err := discover.ParseNode(url)
    55  	if err != nil {
    56  		return nil, fmt.Errorf("invalid kni: %v", err)
    57  	}
    58  	server.AddPeer(node)
    59  	return node, nil
    60  }
    61  
    62  // AddPeer requests connecting to a remote node, and also maintaining the new
    63  // connection at all times, even reconnecting if it is lost.
    64  func (api *PrivateAdminAPI) AddPeer(url string) (bool, error) {
    65  	// Make sure the server is running, fail otherwise
    66  	server := api.node.Server()
    67  	if server == nil {
    68  		return false, ErrNodeStopped
    69  	}
    70  	// TODO-Klaytn Refactoring this to check whether the url is valid or not by dialing and return it.
    71  	if _, err := addPeerInternal(server, url, false); err != nil {
    72  		return false, err
    73  	} else {
    74  		return true, nil
    75  	}
    76  }
    77  
    78  // RemovePeer disconnects from a a remote node if the connection exists
    79  func (api *PrivateAdminAPI) RemovePeer(url string) (bool, error) {
    80  	// Make sure the server is running, fail otherwise
    81  	server := api.node.Server()
    82  	if server == nil {
    83  		return false, ErrNodeStopped
    84  	}
    85  	// Try to remove the url as a static peer and return
    86  	node, err := discover.ParseNode(url)
    87  	if err != nil {
    88  		return false, fmt.Errorf("invalid kni: %v", err)
    89  	}
    90  	server.RemovePeer(node)
    91  	return true, nil
    92  }
    93  
    94  // PeerEvents creates an RPC subscription which receives peer events from the
    95  // node's p2p.Server
    96  func (api *PrivateAdminAPI) PeerEvents(ctx context.Context) (*rpc.Subscription, error) {
    97  	// Make sure the server is running, fail otherwise
    98  	server := api.node.Server()
    99  	if server == nil {
   100  		return nil, ErrNodeStopped
   101  	}
   102  
   103  	// Create the subscription
   104  	notifier, supported := rpc.NotifierFromContext(ctx)
   105  	if !supported {
   106  		return nil, rpc.ErrNotificationsUnsupported
   107  	}
   108  	rpcSub := notifier.CreateSubscription()
   109  
   110  	go func() {
   111  		events := make(chan *p2p.PeerEvent)
   112  		sub := server.SubscribeEvents(events)
   113  		defer sub.Unsubscribe()
   114  
   115  		for {
   116  			select {
   117  			case event := <-events:
   118  				notifier.Notify(rpcSub.ID, event)
   119  			case <-sub.Err():
   120  				return
   121  			case <-rpcSub.Err():
   122  				return
   123  			case <-notifier.Closed():
   124  				return
   125  			}
   126  		}
   127  	}()
   128  
   129  	return rpcSub, nil
   130  }
   131  
   132  // StartHTTP starts the HTTP RPC API server.
   133  func (api *PrivateAdminAPI) StartHTTP(host *string, port *int, cors *string, apis *string, vhosts *string) (bool, error) {
   134  	api.node.lock.Lock()
   135  	defer api.node.lock.Unlock()
   136  
   137  	if api.node.httpHandler != nil {
   138  		return false, fmt.Errorf("HTTP RPC already running on %s", api.node.httpEndpoint)
   139  	}
   140  
   141  	if host == nil {
   142  		h := DefaultHTTPHost
   143  		if api.node.config.HTTPHost != "" {
   144  			h = api.node.config.HTTPHost
   145  		}
   146  		host = &h
   147  	}
   148  	if port == nil {
   149  		port = &api.node.config.HTTPPort
   150  	}
   151  
   152  	allowedOrigins := api.node.config.HTTPCors
   153  	if cors != nil {
   154  		allowedOrigins = nil
   155  		for _, origin := range strings.Split(*cors, ",") {
   156  			allowedOrigins = append(allowedOrigins, strings.TrimSpace(origin))
   157  		}
   158  	}
   159  
   160  	allowedVHosts := api.node.config.HTTPVirtualHosts
   161  	if vhosts != nil {
   162  		allowedVHosts = nil
   163  		for _, vhost := range strings.Split(*host, ",") {
   164  			allowedVHosts = append(allowedVHosts, strings.TrimSpace(vhost))
   165  		}
   166  	}
   167  
   168  	modules := api.node.httpWhitelist
   169  	if apis != nil {
   170  		modules = nil
   171  		for _, m := range strings.Split(*apis, ",") {
   172  			modules = append(modules, strings.TrimSpace(m))
   173  		}
   174  	}
   175  
   176  	if err := api.node.startHTTP(
   177  		fmt.Sprintf("%s:%d", *host, *port),
   178  		api.node.rpcAPIs, modules, allowedOrigins, allowedVHosts, api.node.config.HTTPTimeouts); err != nil {
   179  		return false, err
   180  	}
   181  
   182  	return true, nil
   183  }
   184  
   185  // StartRPC starts the HTTP RPC API server.
   186  // This method is deprecated. Use StartHTTP instead.
   187  func (api *PrivateAdminAPI) StartRPC(host *string, port *int, cors *string, apis *string, vhosts *string) (bool, error) {
   188  	logger.Warn("Deprecation warning", "method", "admin.StartRPC", "use-instead", "admin.StartHTTP")
   189  	return api.StartHTTP(host, port, cors, apis, vhosts)
   190  }
   191  
   192  // StopHTTP terminates an already running HTTP RPC API endpoint.
   193  func (api *PrivateAdminAPI) StopHTTP() (bool, error) {
   194  	api.node.lock.Lock()
   195  	defer api.node.lock.Unlock()
   196  
   197  	if api.node.httpHandler == nil {
   198  		return false, fmt.Errorf("HTTP RPC not running")
   199  	}
   200  	api.node.stopHTTP()
   201  	return true, nil
   202  }
   203  
   204  // StopRPC terminates an already running HTTP RPC API endpoint.
   205  // This method is deprecated. Use StopHTTP instead.
   206  func (api *PrivateAdminAPI) StopRPC() (bool, error) {
   207  	logger.Warn("Deprecation warning", "method", "admin.StopRPC", "use-instead", "admin.StopHTTP")
   208  	return api.StopHTTP()
   209  }
   210  
   211  // StartWS starts the websocket RPC API server.
   212  func (api *PrivateAdminAPI) StartWS(host *string, port *int, allowedOrigins *string, apis *string) (bool, error) {
   213  	api.node.lock.Lock()
   214  	defer api.node.lock.Unlock()
   215  
   216  	if api.node.wsHandler != nil {
   217  		return false, fmt.Errorf("WebSocket RPC already running on %s", api.node.wsEndpoint)
   218  	}
   219  
   220  	if host == nil {
   221  		h := DefaultWSHost
   222  		if api.node.config.WSHost != "" {
   223  			h = api.node.config.WSHost
   224  		}
   225  		host = &h
   226  	}
   227  	if port == nil {
   228  		port = &api.node.config.WSPort
   229  	}
   230  
   231  	origins := api.node.config.WSOrigins
   232  	if allowedOrigins != nil {
   233  		origins = nil
   234  		for _, origin := range strings.Split(*allowedOrigins, ",") {
   235  			origins = append(origins, strings.TrimSpace(origin))
   236  		}
   237  	}
   238  
   239  	modules := api.node.config.WSModules
   240  	if apis != nil {
   241  		modules = nil
   242  		for _, m := range strings.Split(*apis, ",") {
   243  			modules = append(modules, strings.TrimSpace(m))
   244  		}
   245  	}
   246  
   247  	if err := api.node.startWS(
   248  		fmt.Sprintf("%s:%d", *host, *port),
   249  		api.node.rpcAPIs, modules, origins, api.node.config.WSExposeAll); err != nil {
   250  		return false, err
   251  	}
   252  
   253  	return true, nil
   254  }
   255  
   256  // StopRPC terminates an already running websocket RPC API endpoint.
   257  func (api *PrivateAdminAPI) StopWS() (bool, error) {
   258  	api.node.lock.Lock()
   259  	defer api.node.lock.Unlock()
   260  
   261  	if api.node.wsHandler == nil {
   262  		return false, fmt.Errorf("WebSocket RPC not running")
   263  	}
   264  	api.node.stopWS()
   265  	return true, nil
   266  }
   267  
   268  func (api *PrivateAdminAPI) SetMaxSubscriptionPerWSConn(num int32) {
   269  	logger.Info("Change the max subscription number for a websocket connection",
   270  		"old", rpc.MaxSubscriptionPerWSConn, "new", num)
   271  	rpc.MaxSubscriptionPerWSConn = num
   272  }
   273  
   274  // PublicAdminAPI is the collection of administrative API methods exposed over
   275  // both secure and unsecure RPC channels.
   276  type PublicAdminAPI struct {
   277  	node *Node // Node interfaced by this API
   278  }
   279  
   280  // NewPublicAdminAPI creates a new API definition for the public admin methods
   281  // of the node itself.
   282  func NewPublicAdminAPI(node *Node) *PublicAdminAPI {
   283  	return &PublicAdminAPI{node: node}
   284  }
   285  
   286  // Peers retrieves all the information we know about each individual peer at the
   287  // protocol granularity.
   288  func (api *PublicAdminAPI) Peers() ([]*p2p.PeerInfo, error) {
   289  	server := api.node.Server()
   290  	if server == nil {
   291  		return nil, ErrNodeStopped
   292  	}
   293  	return server.PeersInfo(), nil
   294  }
   295  
   296  // BlsPublicKeyInfoOutput has string fields unlike system.BlsPublicKeyInfo.
   297  type BlsPublicKeyInfoOutput struct {
   298  	PublicKey string `json:"publicKey"`
   299  	Pop       string `json:"pop"`
   300  }
   301  
   302  // NodeInfoOutput extends p2p.NodeInfo with additional fields.
   303  type NodeInfoOutput struct {
   304  	p2p.NodeInfo
   305  
   306  	// Node address derived from node key
   307  	NodeAddress string `json:"nodeAddress"`
   308  
   309  	// BLS public key information for consensus
   310  	BlsPublicKeyInfo BlsPublicKeyInfoOutput `json:"blsPublicKeyInfo"`
   311  }
   312  
   313  // NodeInfo retrieves all the information we know about the host node at the
   314  // protocol granularity.
   315  func (api *PublicAdminAPI) NodeInfo() (*NodeInfoOutput, error) {
   316  	server := api.node.Server()
   317  	if server == nil {
   318  		return nil, ErrNodeStopped
   319  	}
   320  
   321  	var (
   322  		nodeKey  = api.node.config.NodeKey()
   323  		nodeAddr = crypto.PubkeyToAddress(nodeKey.PublicKey)
   324  
   325  		blsPriv = api.node.config.BlsNodeKey()
   326  		blsPub  = blsPriv.PublicKey().Marshal()
   327  		blsPop  = bls.PopProve(blsPriv).Marshal()
   328  	)
   329  
   330  	info := &NodeInfoOutput{
   331  		NodeInfo:    *server.NodeInfo(),
   332  		NodeAddress: nodeAddr.Hex(),
   333  		BlsPublicKeyInfo: BlsPublicKeyInfoOutput{
   334  			PublicKey: hex.EncodeToString(blsPub),
   335  			Pop:       hex.EncodeToString(blsPop),
   336  		},
   337  	}
   338  	return info, nil
   339  }
   340  
   341  // Datadir retrieves the current data directory the node is using.
   342  func (api *PublicAdminAPI) Datadir() string {
   343  	return api.node.DataDir()
   344  }
   345  
   346  // PublicDebugAPI is the collection of debugging related API methods exposed over
   347  // both secure and unsecure RPC channels.
   348  type PublicDebugAPI struct {
   349  	node *Node // Node interfaced by this API
   350  }
   351  
   352  // NewPublicDebugAPI creates a new API definition for the public debug methods
   353  // of the node itself.
   354  func NewPublicDebugAPI(node *Node) *PublicDebugAPI {
   355  	return &PublicDebugAPI{node: node}
   356  }
   357  
   358  // Metrics retrieves all the known system metric collected by the node.
   359  func (api *PublicDebugAPI) Metrics(raw bool) (map[string]interface{}, error) {
   360  	// Create a rate formatter
   361  	units := []string{"", "K", "M", "G", "T", "E", "P"}
   362  	round := func(value float64, prec int) string {
   363  		unit := 0
   364  		for value >= 1000 {
   365  			unit, value, prec = unit+1, value/1000, 2
   366  		}
   367  		return fmt.Sprintf(fmt.Sprintf("%%.%df%s", prec, units[unit]), value)
   368  	}
   369  	format := func(total float64, rate float64) string {
   370  		return fmt.Sprintf("%s (%s/s)", round(total, 0), round(rate, 2))
   371  	}
   372  	// Iterate over all the metrics, and just dump for now
   373  	counters := make(map[string]interface{})
   374  	metrics.DefaultRegistry.Each(func(name string, metric interface{}) {
   375  		// Create or retrieve the counter hierarchy for this metric
   376  		root, parts := counters, strings.Split(name, "/")
   377  		for _, part := range parts[:len(parts)-1] {
   378  			if _, ok := root[part]; !ok {
   379  				root[part] = make(map[string]interface{})
   380  			}
   381  			root = root[part].(map[string]interface{})
   382  		}
   383  		name = parts[len(parts)-1]
   384  
   385  		// Fill the counter with the metric details, formatting if requested
   386  		if raw {
   387  			switch metric := metric.(type) {
   388  			case metrics.Counter:
   389  				root[name] = map[string]interface{}{
   390  					"Overall": float64(metric.Count()),
   391  				}
   392  
   393  			case metrics.Meter:
   394  				root[name] = map[string]interface{}{
   395  					"AvgRate01Min": metric.Rate1(),
   396  					"AvgRate05Min": metric.Rate5(),
   397  					"AvgRate15Min": metric.Rate15(),
   398  					"MeanRate":     metric.RateMean(),
   399  					"Overall":      float64(metric.Count()),
   400  				}
   401  
   402  			case metrics.Timer:
   403  				root[name] = map[string]interface{}{
   404  					"AvgRate01Min": metric.Rate1(),
   405  					"AvgRate05Min": metric.Rate5(),
   406  					"AvgRate15Min": metric.Rate15(),
   407  					"MeanRate":     metric.RateMean(),
   408  					"Mean":         metric.Mean(),
   409  					"StdDev":       metric.StdDev(),
   410  					"Overall":      float64(metric.Count()),
   411  					"Percentiles": map[string]interface{}{
   412  						"5":  metric.Percentile(0.05),
   413  						"20": metric.Percentile(0.2),
   414  						"50": metric.Percentile(0.5),
   415  						"80": metric.Percentile(0.8),
   416  						"95": metric.Percentile(0.95),
   417  					},
   418  				}
   419  
   420  			default:
   421  				root[name] = "Unknown metric type"
   422  			}
   423  		} else {
   424  			switch metric := metric.(type) {
   425  			case metrics.Counter:
   426  				root[name] = map[string]interface{}{
   427  					"Overall": float64(metric.Count()),
   428  				}
   429  
   430  			case metrics.Meter:
   431  				root[name] = map[string]interface{}{
   432  					"Avg01Min": format(metric.Rate1()*60, metric.Rate1()),
   433  					"Avg05Min": format(metric.Rate5()*300, metric.Rate5()),
   434  					"Avg15Min": format(metric.Rate15()*900, metric.Rate15()),
   435  					"Overall":  format(float64(metric.Count()), metric.RateMean()),
   436  				}
   437  
   438  			case metrics.Timer:
   439  				root[name] = map[string]interface{}{
   440  					"Avg01Min": format(metric.Rate1()*60, metric.Rate1()),
   441  					"Avg05Min": format(metric.Rate5()*300, metric.Rate5()),
   442  					"Avg15Min": format(metric.Rate15()*900, metric.Rate15()),
   443  					"Overall":  format(float64(metric.Count()), metric.RateMean()),
   444  					"Maximum":  time.Duration(metric.Max()).String(),
   445  					"Minimum":  time.Duration(metric.Min()).String(),
   446  					"Percentiles": map[string]interface{}{
   447  						"5":  time.Duration(metric.Percentile(0.05)).String(),
   448  						"20": time.Duration(metric.Percentile(0.2)).String(),
   449  						"50": time.Duration(metric.Percentile(0.5)).String(),
   450  						"80": time.Duration(metric.Percentile(0.8)).String(),
   451  						"95": time.Duration(metric.Percentile(0.95)).String(),
   452  					},
   453  				}
   454  
   455  			default:
   456  				root[name] = "Unknown metric type"
   457  			}
   458  		}
   459  	})
   460  	return counters, nil
   461  }
   462  
   463  // PublicKlayAPI offers helper utils
   464  type PublicKlayAPI struct {
   465  	stack *Node
   466  }
   467  
   468  // NewPublicKlayAPI creates a new Web3Service instance
   469  func NewPublicKlayAPI(stack *Node) *PublicKlayAPI {
   470  	return &PublicKlayAPI{stack}
   471  }
   472  
   473  // ClientVersion returns the node name
   474  func (s *PublicKlayAPI) ClientVersion() string {
   475  	return s.stack.Server().Name()
   476  }
   477  
   478  // Sha3 applies the Klaytn sha3 implementation on the input.
   479  // It assumes the input is hex encoded.
   480  func (s *PublicKlayAPI) Sha3(input hexutil.Bytes) hexutil.Bytes {
   481  	return crypto.Keccak256(input)
   482  }