github.com/chapsuk/go-ethereum@v1.8.12-0.20180615081455-574378edb50c/node/api.go (about)

     1  // Copyright 2015 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package node
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"strings"
    23  	"time"
    24  
    25  	"github.com/ethereum/go-ethereum/common/hexutil"
    26  	"github.com/ethereum/go-ethereum/crypto"
    27  	"github.com/ethereum/go-ethereum/metrics"
    28  	"github.com/ethereum/go-ethereum/p2p"
    29  	"github.com/ethereum/go-ethereum/p2p/discover"
    30  	"github.com/ethereum/go-ethereum/rpc"
    31  )
    32  
    33  // PrivateAdminAPI is the collection of administrative API methods exposed only
    34  // over a secure RPC channel.
    35  type PrivateAdminAPI struct {
    36  	node *Node // Node interfaced by this API
    37  }
    38  
    39  // NewPrivateAdminAPI creates a new API definition for the private admin methods
    40  // of the node itself.
    41  func NewPrivateAdminAPI(node *Node) *PrivateAdminAPI {
    42  	return &PrivateAdminAPI{node: node}
    43  }
    44  
    45  // AddPeer requests connecting to a remote node, and also maintaining the new
    46  // connection at all times, even reconnecting if it is lost.
    47  func (api *PrivateAdminAPI) AddPeer(url string) (bool, error) {
    48  	// Make sure the server is running, fail otherwise
    49  	server := api.node.Server()
    50  	if server == nil {
    51  		return false, ErrNodeStopped
    52  	}
    53  	// Try to add the url as a static peer and return
    54  	node, err := discover.ParseNode(url)
    55  	if err != nil {
    56  		return false, fmt.Errorf("invalid enode: %v", err)
    57  	}
    58  	server.AddPeer(node)
    59  	return true, nil
    60  }
    61  
    62  // RemovePeer disconnects from a a remote node if the connection exists
    63  func (api *PrivateAdminAPI) RemovePeer(url string) (bool, error) {
    64  	// Make sure the server is running, fail otherwise
    65  	server := api.node.Server()
    66  	if server == nil {
    67  		return false, ErrNodeStopped
    68  	}
    69  	// Try to remove the url as a static peer and return
    70  	node, err := discover.ParseNode(url)
    71  	if err != nil {
    72  		return false, fmt.Errorf("invalid enode: %v", err)
    73  	}
    74  	server.RemovePeer(node)
    75  	return true, nil
    76  }
    77  
    78  // PeerEvents creates an RPC subscription which receives peer events from the
    79  // node's p2p.Server
    80  func (api *PrivateAdminAPI) PeerEvents(ctx context.Context) (*rpc.Subscription, error) {
    81  	// Make sure the server is running, fail otherwise
    82  	server := api.node.Server()
    83  	if server == nil {
    84  		return nil, ErrNodeStopped
    85  	}
    86  
    87  	// Create the subscription
    88  	notifier, supported := rpc.NotifierFromContext(ctx)
    89  	if !supported {
    90  		return nil, rpc.ErrNotificationsUnsupported
    91  	}
    92  	rpcSub := notifier.CreateSubscription()
    93  
    94  	go func() {
    95  		events := make(chan *p2p.PeerEvent)
    96  		sub := server.SubscribeEvents(events)
    97  		defer sub.Unsubscribe()
    98  
    99  		for {
   100  			select {
   101  			case event := <-events:
   102  				notifier.Notify(rpcSub.ID, event)
   103  			case <-sub.Err():
   104  				return
   105  			case <-rpcSub.Err():
   106  				return
   107  			case <-notifier.Closed():
   108  				return
   109  			}
   110  		}
   111  	}()
   112  
   113  	return rpcSub, nil
   114  }
   115  
   116  // StartRPC starts the HTTP RPC API server.
   117  func (api *PrivateAdminAPI) StartRPC(host *string, port *int, cors *string, apis *string, vhosts *string) (bool, error) {
   118  	api.node.lock.Lock()
   119  	defer api.node.lock.Unlock()
   120  
   121  	if api.node.httpHandler != nil {
   122  		return false, fmt.Errorf("HTTP RPC already running on %s", api.node.httpEndpoint)
   123  	}
   124  
   125  	if host == nil {
   126  		h := DefaultHTTPHost
   127  		if api.node.config.HTTPHost != "" {
   128  			h = api.node.config.HTTPHost
   129  		}
   130  		host = &h
   131  	}
   132  	if port == nil {
   133  		port = &api.node.config.HTTPPort
   134  	}
   135  
   136  	allowedOrigins := api.node.config.HTTPCors
   137  	if cors != nil {
   138  		allowedOrigins = nil
   139  		for _, origin := range strings.Split(*cors, ",") {
   140  			allowedOrigins = append(allowedOrigins, strings.TrimSpace(origin))
   141  		}
   142  	}
   143  
   144  	allowedVHosts := api.node.config.HTTPVirtualHosts
   145  	if vhosts != nil {
   146  		allowedVHosts = nil
   147  		for _, vhost := range strings.Split(*host, ",") {
   148  			allowedVHosts = append(allowedVHosts, strings.TrimSpace(vhost))
   149  		}
   150  	}
   151  
   152  	modules := api.node.httpWhitelist
   153  	if apis != nil {
   154  		modules = nil
   155  		for _, m := range strings.Split(*apis, ",") {
   156  			modules = append(modules, strings.TrimSpace(m))
   157  		}
   158  	}
   159  
   160  	if err := api.node.startHTTP(fmt.Sprintf("%s:%d", *host, *port), api.node.rpcAPIs, modules, allowedOrigins, allowedVHosts); err != nil {
   161  		return false, err
   162  	}
   163  	return true, nil
   164  }
   165  
   166  // StopRPC terminates an already running HTTP RPC API endpoint.
   167  func (api *PrivateAdminAPI) StopRPC() (bool, error) {
   168  	api.node.lock.Lock()
   169  	defer api.node.lock.Unlock()
   170  
   171  	if api.node.httpHandler == nil {
   172  		return false, fmt.Errorf("HTTP RPC not running")
   173  	}
   174  	api.node.stopHTTP()
   175  	return true, nil
   176  }
   177  
   178  // StartWS starts the websocket RPC API server.
   179  func (api *PrivateAdminAPI) StartWS(host *string, port *int, allowedOrigins *string, apis *string) (bool, error) {
   180  	api.node.lock.Lock()
   181  	defer api.node.lock.Unlock()
   182  
   183  	if api.node.wsHandler != nil {
   184  		return false, fmt.Errorf("WebSocket RPC already running on %s", api.node.wsEndpoint)
   185  	}
   186  
   187  	if host == nil {
   188  		h := DefaultWSHost
   189  		if api.node.config.WSHost != "" {
   190  			h = api.node.config.WSHost
   191  		}
   192  		host = &h
   193  	}
   194  	if port == nil {
   195  		port = &api.node.config.WSPort
   196  	}
   197  
   198  	origins := api.node.config.WSOrigins
   199  	if allowedOrigins != nil {
   200  		origins = nil
   201  		for _, origin := range strings.Split(*allowedOrigins, ",") {
   202  			origins = append(origins, strings.TrimSpace(origin))
   203  		}
   204  	}
   205  
   206  	modules := api.node.config.WSModules
   207  	if apis != nil {
   208  		modules = nil
   209  		for _, m := range strings.Split(*apis, ",") {
   210  			modules = append(modules, strings.TrimSpace(m))
   211  		}
   212  	}
   213  
   214  	if err := api.node.startWS(fmt.Sprintf("%s:%d", *host, *port), api.node.rpcAPIs, modules, origins, api.node.config.WSExposeAll); err != nil {
   215  		return false, err
   216  	}
   217  	return true, nil
   218  }
   219  
   220  // StopWS terminates an already running websocket RPC API endpoint.
   221  func (api *PrivateAdminAPI) StopWS() (bool, error) {
   222  	api.node.lock.Lock()
   223  	defer api.node.lock.Unlock()
   224  
   225  	if api.node.wsHandler == nil {
   226  		return false, fmt.Errorf("WebSocket RPC not running")
   227  	}
   228  	api.node.stopWS()
   229  	return true, nil
   230  }
   231  
   232  // PublicAdminAPI is the collection of administrative API methods exposed over
   233  // both secure and unsecure RPC channels.
   234  type PublicAdminAPI struct {
   235  	node *Node // Node interfaced by this API
   236  }
   237  
   238  // NewPublicAdminAPI creates a new API definition for the public admin methods
   239  // of the node itself.
   240  func NewPublicAdminAPI(node *Node) *PublicAdminAPI {
   241  	return &PublicAdminAPI{node: node}
   242  }
   243  
   244  // Peers retrieves all the information we know about each individual peer at the
   245  // protocol granularity.
   246  func (api *PublicAdminAPI) Peers() ([]*p2p.PeerInfo, error) {
   247  	server := api.node.Server()
   248  	if server == nil {
   249  		return nil, ErrNodeStopped
   250  	}
   251  	return server.PeersInfo(), nil
   252  }
   253  
   254  // NodeInfo retrieves all the information we know about the host node at the
   255  // protocol granularity.
   256  func (api *PublicAdminAPI) NodeInfo() (*p2p.NodeInfo, error) {
   257  	server := api.node.Server()
   258  	if server == nil {
   259  		return nil, ErrNodeStopped
   260  	}
   261  	return server.NodeInfo(), nil
   262  }
   263  
   264  // Datadir retrieves the current data directory the node is using.
   265  func (api *PublicAdminAPI) Datadir() string {
   266  	return api.node.DataDir()
   267  }
   268  
   269  // PublicDebugAPI is the collection of debugging related API methods exposed over
   270  // both secure and unsecure RPC channels.
   271  type PublicDebugAPI struct {
   272  	node *Node // Node interfaced by this API
   273  }
   274  
   275  // NewPublicDebugAPI creates a new API definition for the public debug methods
   276  // of the node itself.
   277  func NewPublicDebugAPI(node *Node) *PublicDebugAPI {
   278  	return &PublicDebugAPI{node: node}
   279  }
   280  
   281  // Metrics retrieves all the known system metric collected by the node.
   282  func (api *PublicDebugAPI) Metrics(raw bool) (map[string]interface{}, error) {
   283  	// Create a rate formatter
   284  	units := []string{"", "K", "M", "G", "T", "E", "P"}
   285  	round := func(value float64, prec int) string {
   286  		unit := 0
   287  		for value >= 1000 {
   288  			unit, value, prec = unit+1, value/1000, 2
   289  		}
   290  		return fmt.Sprintf(fmt.Sprintf("%%.%df%s", prec, units[unit]), value)
   291  	}
   292  	format := func(total float64, rate float64) string {
   293  		return fmt.Sprintf("%s (%s/s)", round(total, 0), round(rate, 2))
   294  	}
   295  	// Iterate over all the metrics, and just dump for now
   296  	counters := make(map[string]interface{})
   297  	metrics.DefaultRegistry.Each(func(name string, metric interface{}) {
   298  		// Create or retrieve the counter hierarchy for this metric
   299  		root, parts := counters, strings.Split(name, "/")
   300  		for _, part := range parts[:len(parts)-1] {
   301  			if _, ok := root[part]; !ok {
   302  				root[part] = make(map[string]interface{})
   303  			}
   304  			root = root[part].(map[string]interface{})
   305  		}
   306  		name = parts[len(parts)-1]
   307  
   308  		// Fill the counter with the metric details, formatting if requested
   309  		if raw {
   310  			switch metric := metric.(type) {
   311  			case metrics.Counter:
   312  				root[name] = map[string]interface{}{
   313  					"Overall": float64(metric.Count()),
   314  				}
   315  
   316  			case metrics.Meter:
   317  				root[name] = map[string]interface{}{
   318  					"AvgRate01Min": metric.Rate1(),
   319  					"AvgRate05Min": metric.Rate5(),
   320  					"AvgRate15Min": metric.Rate15(),
   321  					"MeanRate":     metric.RateMean(),
   322  					"Overall":      float64(metric.Count()),
   323  				}
   324  
   325  			case metrics.Timer:
   326  				root[name] = map[string]interface{}{
   327  					"AvgRate01Min": metric.Rate1(),
   328  					"AvgRate05Min": metric.Rate5(),
   329  					"AvgRate15Min": metric.Rate15(),
   330  					"MeanRate":     metric.RateMean(),
   331  					"Overall":      float64(metric.Count()),
   332  					"Percentiles": map[string]interface{}{
   333  						"5":  metric.Percentile(0.05),
   334  						"20": metric.Percentile(0.2),
   335  						"50": metric.Percentile(0.5),
   336  						"80": metric.Percentile(0.8),
   337  						"95": metric.Percentile(0.95),
   338  					},
   339  				}
   340  
   341  			case metrics.ResettingTimer:
   342  				t := metric.Snapshot()
   343  				ps := t.Percentiles([]float64{5, 20, 50, 80, 95})
   344  				root[name] = map[string]interface{}{
   345  					"Measurements": len(t.Values()),
   346  					"Mean":         time.Duration(t.Mean()).String(),
   347  					"Percentiles": map[string]interface{}{
   348  						"5":  time.Duration(ps[0]).String(),
   349  						"20": time.Duration(ps[1]).String(),
   350  						"50": time.Duration(ps[2]).String(),
   351  						"80": time.Duration(ps[3]).String(),
   352  						"95": time.Duration(ps[4]).String(),
   353  					},
   354  				}
   355  
   356  			default:
   357  				root[name] = "Unknown metric type"
   358  			}
   359  		} else {
   360  			switch metric := metric.(type) {
   361  			case metrics.Counter:
   362  				root[name] = map[string]interface{}{
   363  					"Overall": float64(metric.Count()),
   364  				}
   365  
   366  			case metrics.Meter:
   367  				root[name] = map[string]interface{}{
   368  					"Avg01Min": format(metric.Rate1()*60, metric.Rate1()),
   369  					"Avg05Min": format(metric.Rate5()*300, metric.Rate5()),
   370  					"Avg15Min": format(metric.Rate15()*900, metric.Rate15()),
   371  					"Overall":  format(float64(metric.Count()), metric.RateMean()),
   372  				}
   373  
   374  			case metrics.Timer:
   375  				root[name] = map[string]interface{}{
   376  					"Avg01Min": format(metric.Rate1()*60, metric.Rate1()),
   377  					"Avg05Min": format(metric.Rate5()*300, metric.Rate5()),
   378  					"Avg15Min": format(metric.Rate15()*900, metric.Rate15()),
   379  					"Overall":  format(float64(metric.Count()), metric.RateMean()),
   380  					"Maximum":  time.Duration(metric.Max()).String(),
   381  					"Minimum":  time.Duration(metric.Min()).String(),
   382  					"Percentiles": map[string]interface{}{
   383  						"5":  time.Duration(metric.Percentile(0.05)).String(),
   384  						"20": time.Duration(metric.Percentile(0.2)).String(),
   385  						"50": time.Duration(metric.Percentile(0.5)).String(),
   386  						"80": time.Duration(metric.Percentile(0.8)).String(),
   387  						"95": time.Duration(metric.Percentile(0.95)).String(),
   388  					},
   389  				}
   390  
   391  			case metrics.ResettingTimer:
   392  				t := metric.Snapshot()
   393  				ps := t.Percentiles([]float64{5, 20, 50, 80, 95})
   394  				root[name] = map[string]interface{}{
   395  					"Measurements": len(t.Values()),
   396  					"Mean":         time.Duration(t.Mean()).String(),
   397  					"Percentiles": map[string]interface{}{
   398  						"5":  time.Duration(ps[0]).String(),
   399  						"20": time.Duration(ps[1]).String(),
   400  						"50": time.Duration(ps[2]).String(),
   401  						"80": time.Duration(ps[3]).String(),
   402  						"95": time.Duration(ps[4]).String(),
   403  					},
   404  				}
   405  
   406  			default:
   407  				root[name] = "Unknown metric type"
   408  			}
   409  		}
   410  	})
   411  	return counters, nil
   412  }
   413  
   414  // PublicWeb3API offers helper utils
   415  type PublicWeb3API struct {
   416  	stack *Node
   417  }
   418  
   419  // NewPublicWeb3API creates a new Web3Service instance
   420  func NewPublicWeb3API(stack *Node) *PublicWeb3API {
   421  	return &PublicWeb3API{stack}
   422  }
   423  
   424  // ClientVersion returns the node name
   425  func (s *PublicWeb3API) ClientVersion() string {
   426  	return s.stack.Server().Name
   427  }
   428  
   429  // Sha3 applies the ethereum sha3 implementation on the input.
   430  // It assumes the input is hex encoded.
   431  func (s *PublicWeb3API) Sha3(input hexutil.Bytes) hexutil.Bytes {
   432  	return crypto.Keccak256(input)
   433  }