github.com/ethereum-optimism/optimism/l2geth@v0.0.0-20230612200230-50b04ade19e3/p2p/simulations/adapters/inproc.go (about)

     1  // Copyright 2017 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 adapters
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"math"
    23  	"net"
    24  	"sync"
    25  
    26  	"github.com/ethereum-optimism/optimism/l2geth/event"
    27  	"github.com/ethereum-optimism/optimism/l2geth/log"
    28  	"github.com/ethereum-optimism/optimism/l2geth/node"
    29  	"github.com/ethereum-optimism/optimism/l2geth/p2p"
    30  	"github.com/ethereum-optimism/optimism/l2geth/p2p/enode"
    31  	"github.com/ethereum-optimism/optimism/l2geth/p2p/simulations/pipes"
    32  	"github.com/ethereum-optimism/optimism/l2geth/rpc"
    33  	"github.com/gorilla/websocket"
    34  )
    35  
    36  // SimAdapter is a NodeAdapter which creates in-memory simulation nodes and
    37  // connects them using net.Pipe
    38  type SimAdapter struct {
    39  	pipe     func() (net.Conn, net.Conn, error)
    40  	mtx      sync.RWMutex
    41  	nodes    map[enode.ID]*SimNode
    42  	services map[string]ServiceFunc
    43  }
    44  
    45  // NewSimAdapter creates a SimAdapter which is capable of running in-memory
    46  // simulation nodes running any of the given services (the services to run on a
    47  // particular node are passed to the NewNode function in the NodeConfig)
    48  // the adapter uses a net.Pipe for in-memory simulated network connections
    49  func NewSimAdapter(services map[string]ServiceFunc) *SimAdapter {
    50  	return &SimAdapter{
    51  		pipe:     pipes.NetPipe,
    52  		nodes:    make(map[enode.ID]*SimNode),
    53  		services: services,
    54  	}
    55  }
    56  
    57  func NewTCPAdapter(services map[string]ServiceFunc) *SimAdapter {
    58  	return &SimAdapter{
    59  		pipe:     pipes.TCPPipe,
    60  		nodes:    make(map[enode.ID]*SimNode),
    61  		services: services,
    62  	}
    63  }
    64  
    65  // Name returns the name of the adapter for logging purposes
    66  func (s *SimAdapter) Name() string {
    67  	return "sim-adapter"
    68  }
    69  
    70  // NewNode returns a new SimNode using the given config
    71  func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
    72  	s.mtx.Lock()
    73  	defer s.mtx.Unlock()
    74  
    75  	id := config.ID
    76  	// verify that the node has a private key in the config
    77  	if config.PrivateKey == nil {
    78  		return nil, fmt.Errorf("node is missing private key: %s", id)
    79  	}
    80  
    81  	// check a node with the ID doesn't already exist
    82  	if _, exists := s.nodes[id]; exists {
    83  		return nil, fmt.Errorf("node already exists: %s", id)
    84  	}
    85  
    86  	// check the services are valid
    87  	if len(config.Services) == 0 {
    88  		return nil, errors.New("node must have at least one service")
    89  	}
    90  	for _, service := range config.Services {
    91  		if _, exists := s.services[service]; !exists {
    92  			return nil, fmt.Errorf("unknown node service %q", service)
    93  		}
    94  	}
    95  
    96  	err := config.initDummyEnode()
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  
   101  	n, err := node.New(&node.Config{
   102  		P2P: p2p.Config{
   103  			PrivateKey:      config.PrivateKey,
   104  			MaxPeers:        math.MaxInt32,
   105  			NoDiscovery:     true,
   106  			Dialer:          s,
   107  			EnableMsgEvents: config.EnableMsgEvents,
   108  		},
   109  		NoUSB:  true,
   110  		Logger: log.New("node.id", id.String()),
   111  	})
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	simNode := &SimNode{
   117  		ID:      id,
   118  		config:  config,
   119  		node:    n,
   120  		adapter: s,
   121  		running: make(map[string]node.Service),
   122  	}
   123  	s.nodes[id] = simNode
   124  	return simNode, nil
   125  }
   126  
   127  // Dial implements the p2p.NodeDialer interface by connecting to the node using
   128  // an in-memory net.Pipe
   129  func (s *SimAdapter) Dial(dest *enode.Node) (conn net.Conn, err error) {
   130  	node, ok := s.GetNode(dest.ID())
   131  	if !ok {
   132  		return nil, fmt.Errorf("unknown node: %s", dest.ID())
   133  	}
   134  	srv := node.Server()
   135  	if srv == nil {
   136  		return nil, fmt.Errorf("node not running: %s", dest.ID())
   137  	}
   138  	// SimAdapter.pipe is net.Pipe (NewSimAdapter)
   139  	pipe1, pipe2, err := s.pipe()
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  	// this is simulated 'listening'
   144  	// asynchronously call the dialed destination node's p2p server
   145  	// to set up connection on the 'listening' side
   146  	go srv.SetupConn(pipe1, 0, nil)
   147  	return pipe2, nil
   148  }
   149  
   150  // DialRPC implements the RPCDialer interface by creating an in-memory RPC
   151  // client of the given node
   152  func (s *SimAdapter) DialRPC(id enode.ID) (*rpc.Client, error) {
   153  	node, ok := s.GetNode(id)
   154  	if !ok {
   155  		return nil, fmt.Errorf("unknown node: %s", id)
   156  	}
   157  	handler, err := node.node.RPCHandler()
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  	return rpc.DialInProc(handler), nil
   162  }
   163  
   164  // GetNode returns the node with the given ID if it exists
   165  func (s *SimAdapter) GetNode(id enode.ID) (*SimNode, bool) {
   166  	s.mtx.RLock()
   167  	defer s.mtx.RUnlock()
   168  	node, ok := s.nodes[id]
   169  	return node, ok
   170  }
   171  
   172  // SimNode is an in-memory simulation node which connects to other nodes using
   173  // net.Pipe (see SimAdapter.Dial), running devp2p protocols directly over that
   174  // pipe
   175  type SimNode struct {
   176  	lock         sync.RWMutex
   177  	ID           enode.ID
   178  	config       *NodeConfig
   179  	adapter      *SimAdapter
   180  	node         *node.Node
   181  	running      map[string]node.Service
   182  	client       *rpc.Client
   183  	registerOnce sync.Once
   184  }
   185  
   186  // Close closes the underlaying node.Node to release
   187  // acquired resources.
   188  func (sn *SimNode) Close() error {
   189  	return sn.node.Close()
   190  }
   191  
   192  // Addr returns the node's discovery address
   193  func (sn *SimNode) Addr() []byte {
   194  	return []byte(sn.Node().String())
   195  }
   196  
   197  // Node returns a node descriptor representing the SimNode
   198  func (sn *SimNode) Node() *enode.Node {
   199  	return sn.config.Node()
   200  }
   201  
   202  // Client returns an rpc.Client which can be used to communicate with the
   203  // underlying services (it is set once the node has started)
   204  func (sn *SimNode) Client() (*rpc.Client, error) {
   205  	sn.lock.RLock()
   206  	defer sn.lock.RUnlock()
   207  	if sn.client == nil {
   208  		return nil, errors.New("node not started")
   209  	}
   210  	return sn.client, nil
   211  }
   212  
   213  // ServeRPC serves RPC requests over the given connection by creating an
   214  // in-memory client to the node's RPC server.
   215  func (sn *SimNode) ServeRPC(conn *websocket.Conn) error {
   216  	handler, err := sn.node.RPCHandler()
   217  	if err != nil {
   218  		return err
   219  	}
   220  	codec := rpc.NewFuncCodec(conn, conn.WriteJSON, conn.ReadJSON)
   221  	handler.ServeCodec(codec, 0)
   222  	return nil
   223  }
   224  
   225  // Snapshots creates snapshots of the services by calling the
   226  // simulation_snapshot RPC method
   227  func (sn *SimNode) Snapshots() (map[string][]byte, error) {
   228  	sn.lock.RLock()
   229  	services := make(map[string]node.Service, len(sn.running))
   230  	for name, service := range sn.running {
   231  		services[name] = service
   232  	}
   233  	sn.lock.RUnlock()
   234  	if len(services) == 0 {
   235  		return nil, errors.New("no running services")
   236  	}
   237  	snapshots := make(map[string][]byte)
   238  	for name, service := range services {
   239  		if s, ok := service.(interface {
   240  			Snapshot() ([]byte, error)
   241  		}); ok {
   242  			snap, err := s.Snapshot()
   243  			if err != nil {
   244  				return nil, err
   245  			}
   246  			snapshots[name] = snap
   247  		}
   248  	}
   249  	return snapshots, nil
   250  }
   251  
   252  // Start registers the services and starts the underlying devp2p node
   253  func (sn *SimNode) Start(snapshots map[string][]byte) error {
   254  	newService := func(name string) func(ctx *node.ServiceContext) (node.Service, error) {
   255  		return func(nodeCtx *node.ServiceContext) (node.Service, error) {
   256  			ctx := &ServiceContext{
   257  				RPCDialer:   sn.adapter,
   258  				NodeContext: nodeCtx,
   259  				Config:      sn.config,
   260  			}
   261  			if snapshots != nil {
   262  				ctx.Snapshot = snapshots[name]
   263  			}
   264  			serviceFunc := sn.adapter.services[name]
   265  			service, err := serviceFunc(ctx)
   266  			if err != nil {
   267  				return nil, err
   268  			}
   269  			sn.running[name] = service
   270  			return service, nil
   271  		}
   272  	}
   273  
   274  	// ensure we only register the services once in the case of the node
   275  	// being stopped and then started again
   276  	var regErr error
   277  	sn.registerOnce.Do(func() {
   278  		for _, name := range sn.config.Services {
   279  			if err := sn.node.Register(newService(name)); err != nil {
   280  				regErr = err
   281  				break
   282  			}
   283  		}
   284  	})
   285  	if regErr != nil {
   286  		return regErr
   287  	}
   288  
   289  	if err := sn.node.Start(); err != nil {
   290  		return err
   291  	}
   292  
   293  	// create an in-process RPC client
   294  	handler, err := sn.node.RPCHandler()
   295  	if err != nil {
   296  		return err
   297  	}
   298  
   299  	sn.lock.Lock()
   300  	sn.client = rpc.DialInProc(handler)
   301  	sn.lock.Unlock()
   302  
   303  	return nil
   304  }
   305  
   306  // Stop closes the RPC client and stops the underlying devp2p node
   307  func (sn *SimNode) Stop() error {
   308  	sn.lock.Lock()
   309  	if sn.client != nil {
   310  		sn.client.Close()
   311  		sn.client = nil
   312  	}
   313  	sn.lock.Unlock()
   314  	return sn.node.Stop()
   315  }
   316  
   317  // Service returns a running service by name
   318  func (sn *SimNode) Service(name string) node.Service {
   319  	sn.lock.RLock()
   320  	defer sn.lock.RUnlock()
   321  	return sn.running[name]
   322  }
   323  
   324  // Services returns a copy of the underlying services
   325  func (sn *SimNode) Services() []node.Service {
   326  	sn.lock.RLock()
   327  	defer sn.lock.RUnlock()
   328  	services := make([]node.Service, 0, len(sn.running))
   329  	for _, service := range sn.running {
   330  		services = append(services, service)
   331  	}
   332  	return services
   333  }
   334  
   335  // ServiceMap returns a map by names of the underlying services
   336  func (sn *SimNode) ServiceMap() map[string]node.Service {
   337  	sn.lock.RLock()
   338  	defer sn.lock.RUnlock()
   339  	services := make(map[string]node.Service, len(sn.running))
   340  	for name, service := range sn.running {
   341  		services[name] = service
   342  	}
   343  	return services
   344  }
   345  
   346  // Server returns the underlying p2p.Server
   347  func (sn *SimNode) Server() *p2p.Server {
   348  	return sn.node.Server()
   349  }
   350  
   351  // SubscribeEvents subscribes the given channel to peer events from the
   352  // underlying p2p.Server
   353  func (sn *SimNode) SubscribeEvents(ch chan *p2p.PeerEvent) event.Subscription {
   354  	srv := sn.Server()
   355  	if srv == nil {
   356  		panic("node not running")
   357  	}
   358  	return srv.SubscribeEvents(ch)
   359  }
   360  
   361  // NodeInfo returns information about the node
   362  func (sn *SimNode) NodeInfo() *p2p.NodeInfo {
   363  	server := sn.Server()
   364  	if server == nil {
   365  		return &p2p.NodeInfo{
   366  			ID:    sn.ID.String(),
   367  			Enode: sn.Node().String(),
   368  		}
   369  	}
   370  	return server.NodeInfo()
   371  }