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