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 }