github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/p2p/simulations/adapters/inproc.go (about)

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