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