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