github.com/core-coin/go-core/v2@v2.1.9/p2p/simulations/adapters/inproc.go (about)

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