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  }