github.com/ethereum-optimism/optimism@v1.7.2/op-node/p2p/rpc_server.go (about) 1 package p2p 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "net" 8 "time" 9 10 "github.com/ethereum-optimism/optimism/op-node/p2p/gating" 11 12 decredSecp "github.com/decred/dcrd/dcrec/secp256k1/v4" 13 "github.com/ethereum-optimism/optimism/op-node/p2p/store" 14 pubsub "github.com/libp2p/go-libp2p-pubsub" 15 "github.com/libp2p/go-libp2p-testing/netutil" 16 "github.com/libp2p/go-libp2p/core/connmgr" 17 "github.com/libp2p/go-libp2p/core/crypto" 18 "github.com/libp2p/go-libp2p/core/host" 19 "github.com/libp2p/go-libp2p/core/network" 20 "github.com/libp2p/go-libp2p/core/peer" 21 "github.com/libp2p/go-libp2p/core/peerstore" 22 23 gcrypto "github.com/ethereum/go-ethereum/crypto" 24 "github.com/ethereum/go-ethereum/log" 25 "github.com/ethereum/go-ethereum/p2p/discover" 26 "github.com/ethereum/go-ethereum/p2p/enode" 27 28 "github.com/ethereum-optimism/optimism/op-node/metrics" 29 ) 30 31 // TODO: dynamic peering 32 // - req-resp protocol to ensure peers from a different chain learn they shouldn't be connected 33 // - banning peers based on score 34 35 var ( 36 ErrDisabledDiscovery = errors.New("discovery disabled") 37 ErrNoConnectionManager = errors.New("no connection manager") 38 ErrNoConnectionGater = errors.New("no connection gater") 39 ErrInvalidRequest = errors.New("invalid request") 40 ) 41 42 type Node interface { 43 // Host returns the libp2p host 44 Host() host.Host 45 // Dv5Local returns the control over the Discv5 data of the local node, nil if disabled 46 Dv5Local() *enode.LocalNode 47 // Dv5Udp returns the control over the Discv5 network, nil if disabled 48 Dv5Udp() *discover.UDPv5 49 // GossipSub returns the gossip router 50 GossipSub() *pubsub.PubSub 51 // GossipOut returns the gossip output/info control 52 GossipOut() GossipOut 53 // ConnectionGater returns the connection gater, to ban/unban peers with, may be nil 54 ConnectionGater() gating.BlockingConnectionGater 55 // ConnectionManager returns the connection manager, to protect peers with, may be nil 56 ConnectionManager() connmgr.ConnManager 57 } 58 59 type APIBackend struct { 60 node Node 61 log log.Logger 62 m metrics.Metricer 63 } 64 65 var _ API = (*APIBackend)(nil) 66 67 func NewP2PAPIBackend(node Node, log log.Logger, m metrics.Metricer) *APIBackend { 68 if m == nil { 69 m = metrics.NoopMetrics 70 } 71 72 return &APIBackend{ 73 node: node, 74 log: log, 75 m: m, 76 } 77 } 78 79 func (s *APIBackend) Self(ctx context.Context) (*PeerInfo, error) { 80 recordDur := s.m.RecordRPCServerRequest("opp2p_self") 81 defer recordDur() 82 h := s.node.Host() 83 nw := h.Network() 84 pstore := h.Peerstore() 85 info, err := dumpPeer(h.ID(), nw, pstore, s.node.ConnectionManager()) 86 if err != nil { 87 return nil, err 88 } 89 info.GossipBlocks = true 90 info.Latency = 0 91 if local := s.node.Dv5Local(); local != nil { 92 info.ENR = local.Node().String() 93 } 94 return info, nil 95 } 96 97 func dumpPeer(id peer.ID, nw network.Network, pstore peerstore.Peerstore, connMgr connmgr.ConnManager) (*PeerInfo, error) { 98 info := &PeerInfo{ 99 PeerID: id, 100 } 101 102 // we might not have the pubkey if it's from a multi-addr and if we never discovered/connected them 103 pub := pstore.PubKey(id) 104 if pub != nil { 105 if testPub, ok := pub.(netutil.TestBogusPublicKey); ok { 106 info.NodeID = enode.ID(gcrypto.Keccak256Hash(testPub)) 107 } else { 108 typedPub, ok := pub.(*crypto.Secp256k1PublicKey) 109 if !ok { 110 return nil, fmt.Errorf("unexpected pubkey type: %T", pub) 111 } 112 info.NodeID = enode.PubkeyToIDV4((*decredSecp.PublicKey)(typedPub).ToECDSA()) 113 } 114 } 115 if eps, ok := pstore.(store.ExtendedPeerstore); ok { 116 if dat, err := eps.GetPeerScores(id); err == nil { 117 info.PeerScores = dat 118 } 119 if md, err := eps.GetPeerMetadata(id); err == nil { 120 info.ENR = md.ENR 121 info.ChainID = md.OPStackID 122 } 123 } 124 if dat, err := pstore.Get(id, "ProtocolVersion"); err == nil { 125 protocolVersion, ok := dat.(string) 126 if ok { 127 info.ProtocolVersion = protocolVersion 128 } 129 } 130 if dat, err := pstore.Get(id, "AgentVersion"); err == nil { 131 agentVersion, ok := dat.(string) 132 if ok { 133 info.UserAgent = agentVersion 134 } 135 } 136 // include the /p2p/ address component in all of the addresses for convenience of the API user. 137 p2pAddrs, err := peer.AddrInfoToP2pAddrs(&peer.AddrInfo{ID: id, Addrs: pstore.Addrs(id)}) 138 if err == nil { 139 for _, addr := range p2pAddrs { 140 info.Addresses = append(info.Addresses, addr.String()) 141 } 142 } 143 info.Connectedness = nw.Connectedness(id) 144 if protocols, err := pstore.GetProtocols(id); err == nil { 145 for _, id := range protocols { 146 info.Protocols = append(info.Protocols, string(id)) 147 } 148 } 149 // get the first connection direction, if any (will default to unknown when there are no connections) 150 for _, c := range nw.ConnsToPeer(id) { 151 info.Direction = c.Stat().Direction 152 break 153 } 154 info.Latency = pstore.LatencyEWMA(id) 155 if connMgr != nil { 156 info.Protected = connMgr.IsProtected(id, "") 157 } 158 159 return info, nil 160 } 161 162 // Peers lists information of peers. Optionally filter to only retrieve connected peers. 163 func (s *APIBackend) Peers(ctx context.Context, connected bool) (*PeerDump, error) { 164 recordDur := s.m.RecordRPCServerRequest("opp2p_peers") 165 defer recordDur() 166 h := s.node.Host() 167 nw := h.Network() 168 pstore := h.Peerstore() 169 var peers []peer.ID 170 if connected { 171 peers = nw.Peers() 172 } else { 173 peers = pstore.Peers() 174 } 175 176 dump := &PeerDump{Peers: make(map[string]*PeerInfo)} 177 for _, id := range peers { 178 peerInfo, err := dumpPeer(id, nw, pstore, s.node.ConnectionManager()) 179 if err != nil { 180 s.log.Debug("failed to dump peer info in RPC request", "peer", id, "err", err) 181 continue 182 } 183 // We don't use the peer.ID type as key, 184 // since JSON decoding can't use the provided json unmarshaler (on *string type). 185 dump.Peers[id.String()] = peerInfo 186 if peerInfo.Connectedness == network.Connected { 187 dump.TotalConnected += 1 188 } 189 } 190 for _, id := range s.node.GossipOut().AllBlockTopicsPeers() { 191 if p, ok := dump.Peers[id.String()]; ok { 192 p.GossipBlocks = true 193 } 194 } 195 if gater := s.node.ConnectionGater(); gater != nil { 196 dump.BannedPeers = gater.ListBlockedPeers() 197 dump.BannedSubnets = gater.ListBlockedSubnets() 198 dump.BannedIPS = gater.ListBlockedAddrs() 199 } 200 return dump, nil 201 } 202 203 type PeerStats struct { 204 Connected uint `json:"connected"` 205 Table uint `json:"table"` 206 BlocksTopic uint `json:"blocksTopic"` 207 BlocksTopicV2 uint `json:"blocksTopicV2"` 208 BlocksTopicV3 uint `json:"blocksTopicV3"` 209 Banned uint `json:"banned"` 210 Known uint `json:"known"` 211 } 212 213 func (s *APIBackend) PeerStats(_ context.Context) (*PeerStats, error) { 214 recordDur := s.m.RecordRPCServerRequest("opp2p_peerStats") 215 defer recordDur() 216 h := s.node.Host() 217 nw := h.Network() 218 pstore := h.Peerstore() 219 220 stats := &PeerStats{ 221 Connected: uint(len(nw.Peers())), 222 Table: 0, 223 BlocksTopic: uint(len(s.node.GossipOut().BlocksTopicV1Peers())), 224 BlocksTopicV2: uint(len(s.node.GossipOut().BlocksTopicV2Peers())), 225 BlocksTopicV3: uint(len(s.node.GossipOut().BlocksTopicV3Peers())), 226 Banned: 0, 227 Known: uint(len(pstore.Peers())), 228 } 229 if gater := s.node.ConnectionGater(); gater != nil { 230 stats.Banned = uint(len(gater.ListBlockedPeers())) 231 } 232 if dv5 := s.node.Dv5Udp(); dv5 != nil { 233 stats.Table = uint(len(dv5.AllNodes())) 234 } 235 return stats, nil 236 } 237 238 func (s *APIBackend) DiscoveryTable(_ context.Context) ([]*enode.Node, error) { 239 recordDur := s.m.RecordRPCServerRequest("opp2p_discoveryTable") 240 defer recordDur() 241 if dv5 := s.node.Dv5Udp(); dv5 != nil { 242 return dv5.AllNodes(), nil 243 } else { 244 return nil, ErrDisabledDiscovery 245 } 246 } 247 248 func (s *APIBackend) BlockPeer(_ context.Context, id peer.ID) error { 249 recordDur := s.m.RecordRPCServerRequest("opp2p_blockPeer") 250 if err := id.Validate(); err != nil { 251 log.Warn("invalid peer ID", "method", "BlockPeer", "peer", id, "err", err) 252 return ErrInvalidRequest 253 } 254 defer recordDur() 255 if gater := s.node.ConnectionGater(); gater == nil { 256 return ErrNoConnectionGater 257 } else { 258 return gater.BlockPeer(id) 259 } 260 } 261 262 func (s *APIBackend) UnblockPeer(_ context.Context, id peer.ID) error { 263 recordDur := s.m.RecordRPCServerRequest("opp2p_unblockPeer") 264 if err := id.Validate(); err != nil { 265 log.Warn("invalid peer ID", "method", "UnblockPeer", "peer", id, "err", err) 266 return ErrInvalidRequest 267 } 268 defer recordDur() 269 if gater := s.node.ConnectionGater(); gater == nil { 270 return ErrNoConnectionGater 271 } else { 272 return gater.UnblockPeer(id) 273 } 274 } 275 276 func (s *APIBackend) ListBlockedPeers(_ context.Context) ([]peer.ID, error) { 277 recordDur := s.m.RecordRPCServerRequest("opp2p_listBlockedPeers") 278 defer recordDur() 279 if gater := s.node.ConnectionGater(); gater == nil { 280 return nil, ErrNoConnectionGater 281 } else { 282 return gater.ListBlockedPeers(), nil 283 } 284 } 285 286 // BlockAddr adds an IP address to the set of blocked addresses. 287 // Note: active connections to the IP address are not automatically closed. 288 func (s *APIBackend) BlockAddr(_ context.Context, ip net.IP) error { 289 recordDur := s.m.RecordRPCServerRequest("opp2p_blockAddr") 290 if ip == nil { 291 log.Warn("invalid IP", "method", "BlockAddr") 292 return ErrInvalidRequest 293 } 294 defer recordDur() 295 if gater := s.node.ConnectionGater(); gater == nil { 296 return ErrNoConnectionGater 297 } else { 298 return gater.BlockAddr(ip) 299 } 300 } 301 302 func (s *APIBackend) UnblockAddr(_ context.Context, ip net.IP) error { 303 recordDur := s.m.RecordRPCServerRequest("opp2p_unblockAddr") 304 if ip == nil { 305 log.Warn("invalid IP", "method", "UnblockAddr") 306 return ErrInvalidRequest 307 } 308 defer recordDur() 309 if gater := s.node.ConnectionGater(); gater == nil { 310 return ErrNoConnectionGater 311 } else { 312 return gater.UnblockAddr(ip) 313 } 314 } 315 316 func (s *APIBackend) ListBlockedAddrs(_ context.Context) ([]net.IP, error) { 317 recordDur := s.m.RecordRPCServerRequest("opp2p_listBlockedAddrs") 318 defer recordDur() 319 if gater := s.node.ConnectionGater(); gater == nil { 320 return nil, ErrNoConnectionGater 321 } else { 322 return gater.ListBlockedAddrs(), nil 323 } 324 } 325 326 // BlockSubnet adds an IP subnet to the set of blocked addresses. 327 // Note: active connections to the IP subnet are not automatically closed. 328 func (s *APIBackend) BlockSubnet(_ context.Context, ipnet *net.IPNet) error { 329 recordDur := s.m.RecordRPCServerRequest("opp2p_blockSubnet") 330 if ipnet == nil { 331 log.Warn("invalid IPNet", "method", "BlockSubnet") 332 return ErrInvalidRequest 333 } 334 defer recordDur() 335 if gater := s.node.ConnectionGater(); gater == nil { 336 return ErrNoConnectionGater 337 } else { 338 return gater.BlockSubnet(ipnet) 339 } 340 } 341 342 func (s *APIBackend) UnblockSubnet(_ context.Context, ipnet *net.IPNet) error { 343 recordDur := s.m.RecordRPCServerRequest("opp2p_unblockSubnet") 344 if ipnet == nil { 345 log.Warn("invalid IPNet", "method", "UnblockSubnet") 346 return ErrInvalidRequest 347 } 348 defer recordDur() 349 if gater := s.node.ConnectionGater(); gater == nil { 350 return ErrNoConnectionGater 351 } else { 352 return gater.UnblockSubnet(ipnet) 353 } 354 } 355 356 func (s *APIBackend) ListBlockedSubnets(_ context.Context) ([]*net.IPNet, error) { 357 recordDur := s.m.RecordRPCServerRequest("opp2p_listBlockedSubnets") 358 defer recordDur() 359 if gater := s.node.ConnectionGater(); gater == nil { 360 return nil, ErrNoConnectionGater 361 } else { 362 return gater.ListBlockedSubnets(), nil 363 } 364 } 365 366 func (s *APIBackend) ProtectPeer(_ context.Context, id peer.ID) error { 367 recordDur := s.m.RecordRPCServerRequest("opp2p_protectPeer") 368 if err := id.Validate(); err != nil { 369 log.Warn("invalid peer ID", "method", "ProtectPeer", "peer", id, "err", err) 370 return ErrInvalidRequest 371 } 372 defer recordDur() 373 if manager := s.node.ConnectionManager(); manager == nil { 374 return ErrNoConnectionManager 375 } else { 376 manager.Protect(id, "api-protected") 377 return nil 378 } 379 } 380 381 func (s *APIBackend) UnprotectPeer(_ context.Context, id peer.ID) error { 382 recordDur := s.m.RecordRPCServerRequest("opp2p_unprotectPeer") 383 if err := id.Validate(); err != nil { 384 log.Warn("invalid peer ID", "method", "UnprotectPeer", "peer", id, "err", err) 385 return ErrInvalidRequest 386 } 387 defer recordDur() 388 if manager := s.node.ConnectionManager(); manager == nil { 389 return ErrNoConnectionManager 390 } else { 391 manager.Unprotect(id, "api-protected") 392 return nil 393 } 394 } 395 396 // ConnectPeer connects to a given peer address, and wait for protocol negotiation & identification of the peer 397 func (s *APIBackend) ConnectPeer(ctx context.Context, addr string) error { 398 recordDur := s.m.RecordRPCServerRequest("opp2p_connectPeer") 399 defer recordDur() 400 h := s.node.Host() 401 addrInfo, err := peer.AddrInfoFromString(addr) 402 if err != nil { 403 return fmt.Errorf("bad peer address: %w", err) 404 } 405 // Put a sanity limit on the connection time 406 ctx, cancel := context.WithTimeout(ctx, time.Second*30) 407 defer cancel() 408 return h.Connect(ctx, *addrInfo) 409 } 410 411 func (s *APIBackend) DisconnectPeer(_ context.Context, id peer.ID) error { 412 recordDur := s.m.RecordRPCServerRequest("opp2p_disconnectPeer") 413 if err := id.Validate(); err != nil { 414 log.Warn("invalid peer ID", "method", "DisconnectPeer", "peer", id, "err", err) 415 return ErrInvalidRequest 416 } 417 defer recordDur() 418 err := s.node.Host().Network().ClosePeer(id) 419 if err != nil { 420 return err 421 } 422 ps := s.node.Host().Peerstore() 423 ps.RemovePeer(id) 424 ps.ClearAddrs(id) 425 err = s.node.ConnectionGater().UnblockPeer(id) 426 if err != nil { 427 return fmt.Errorf("closed peer but failed to unblock: %w", err) 428 } 429 return nil 430 }