github.com/intfoundation/intchain@v0.0.0-20220727031208-4316ad31ca73/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/intfoundation/intchain/common/hexutil" 26 "github.com/intfoundation/intchain/crypto" 27 "github.com/intfoundation/intchain/metrics" 28 "github.com/intfoundation/intchain/p2p" 29 "github.com/intfoundation/intchain/p2p/discover" 30 "github.com/intfoundation/intchain/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, api.node.config.HTTPTimeouts); 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 // StopRPC 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 default: 342 root[name] = "Unknown metric type" 343 } 344 } else { 345 switch metric := metric.(type) { 346 case metrics.Counter: 347 root[name] = map[string]interface{}{ 348 "Overall": float64(metric.Count()), 349 } 350 351 case metrics.Meter: 352 root[name] = map[string]interface{}{ 353 "Avg01Min": format(metric.Rate1()*60, metric.Rate1()), 354 "Avg05Min": format(metric.Rate5()*300, metric.Rate5()), 355 "Avg15Min": format(metric.Rate15()*900, metric.Rate15()), 356 "Overall": format(float64(metric.Count()), metric.RateMean()), 357 } 358 359 case metrics.Timer: 360 root[name] = map[string]interface{}{ 361 "Avg01Min": format(metric.Rate1()*60, metric.Rate1()), 362 "Avg05Min": format(metric.Rate5()*300, metric.Rate5()), 363 "Avg15Min": format(metric.Rate15()*900, metric.Rate15()), 364 "Overall": format(float64(metric.Count()), metric.RateMean()), 365 "Maximum": time.Duration(metric.Max()).String(), 366 "Minimum": time.Duration(metric.Min()).String(), 367 "Percentiles": map[string]interface{}{ 368 "5": time.Duration(metric.Percentile(0.05)).String(), 369 "20": time.Duration(metric.Percentile(0.2)).String(), 370 "50": time.Duration(metric.Percentile(0.5)).String(), 371 "80": time.Duration(metric.Percentile(0.8)).String(), 372 "95": time.Duration(metric.Percentile(0.95)).String(), 373 }, 374 } 375 376 default: 377 root[name] = "Unknown metric type" 378 } 379 } 380 }) 381 return counters, nil 382 } 383 384 // PublicWeb3API offers helper utils 385 type PublicWeb3API struct { 386 stack *Node 387 } 388 389 // NewPublicWeb3API creates a new Web3Service instance 390 func NewPublicWeb3API(stack *Node) *PublicWeb3API { 391 return &PublicWeb3API{stack} 392 } 393 394 // ClientVersion returns the node name 395 func (s *PublicWeb3API) ClientVersion() string { 396 return s.stack.Server().Name 397 } 398 399 // Sha3 applies the ethereum sha3 implementation on the input. 400 // It assumes the input is hex encoded. 401 func (s *PublicWeb3API) Sha3(input hexutil.Bytes) hexutil.Bytes { 402 return crypto.Keccak256(input) 403 }