github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/p2p/simulations/adapters/inproc.go (about)

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