github.com/intfoundation/intchain@v0.0.0-20220727031208-4316ad31ca73/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/intfoundation/intchain/event"
    27  	"github.com/intfoundation/intchain/log"
    28  	"github.com/intfoundation/intchain/node"
    29  	"github.com/intfoundation/intchain/p2p"
    30  	"github.com/intfoundation/intchain/p2p/discover"
    31  	"github.com/intfoundation/intchain/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  // Close closes the underlaying node.Node to release
   157  // acquired resources.
   158  func (sn *SimNode) Close() error {
   159  	return sn.node.Close()
   160  }
   161  
   162  // Addr returns the node's discovery address
   163  func (self *SimNode) Addr() []byte {
   164  	return []byte(self.Node().String())
   165  }
   166  
   167  // Node returns a discover.Node representing the SimNode
   168  func (self *SimNode) Node() *discover.Node {
   169  	return discover.NewNode(self.ID, net.IP{127, 0, 0, 1}, 30303, 30303)
   170  }
   171  
   172  // Client returns an rpc.Client which can be used to communicate with the
   173  // underlying services (it is set once the node has started)
   174  func (self *SimNode) Client() (*rpc.Client, error) {
   175  	self.lock.RLock()
   176  	defer self.lock.RUnlock()
   177  	if self.client == nil {
   178  		return nil, errors.New("node not started")
   179  	}
   180  	return self.client, nil
   181  }
   182  
   183  // ServeRPC serves RPC requests over the given connection by creating an
   184  // in-memory client to the node's RPC server
   185  func (self *SimNode) ServeRPC(conn net.Conn) error {
   186  	handler, err := self.node.RPCHandler()
   187  	if err != nil {
   188  		return err
   189  	}
   190  	handler.ServeCodec(rpc.NewJSONCodec(conn), rpc.OptionMethodInvocation|rpc.OptionSubscriptions)
   191  	return nil
   192  }
   193  
   194  // Snapshots creates snapshots of the services by calling the
   195  // simulation_snapshot RPC method
   196  func (self *SimNode) Snapshots() (map[string][]byte, error) {
   197  	self.lock.RLock()
   198  	services := make(map[string]node.Service, len(self.running))
   199  	for name, service := range self.running {
   200  		services[name] = service
   201  	}
   202  	self.lock.RUnlock()
   203  	if len(services) == 0 {
   204  		return nil, errors.New("no running services")
   205  	}
   206  	snapshots := make(map[string][]byte)
   207  	for name, service := range services {
   208  		if s, ok := service.(interface {
   209  			Snapshot() ([]byte, error)
   210  		}); ok {
   211  			snap, err := s.Snapshot()
   212  			if err != nil {
   213  				return nil, err
   214  			}
   215  			snapshots[name] = snap
   216  		}
   217  	}
   218  	return snapshots, nil
   219  }
   220  
   221  // Start registers the services and starts the underlying devp2p node
   222  func (self *SimNode) Start(snapshots map[string][]byte) error {
   223  	newService := func(name string) func(ctx *node.ServiceContext) (node.Service, error) {
   224  		return func(nodeCtx *node.ServiceContext) (node.Service, error) {
   225  			ctx := &ServiceContext{
   226  				RPCDialer:   self.adapter,
   227  				NodeContext: nodeCtx,
   228  				Config:      self.config,
   229  			}
   230  			if snapshots != nil {
   231  				ctx.Snapshot = snapshots[name]
   232  			}
   233  			serviceFunc := self.adapter.services[name]
   234  			service, err := serviceFunc(ctx)
   235  			if err != nil {
   236  				return nil, err
   237  			}
   238  			self.running[name] = service
   239  			return service, nil
   240  		}
   241  	}
   242  
   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  	self.registerOnce.Do(func() {
   247  		for _, name := range self.config.Services {
   248  			if err := self.node.Register(newService(name)); err != nil {
   249  				regErr = err
   250  				return
   251  			}
   252  		}
   253  	})
   254  	if regErr != nil {
   255  		return regErr
   256  	}
   257  
   258  	if err := self.node.Start(); err != nil {
   259  		return err
   260  	}
   261  
   262  	// create an in-process RPC client
   263  	handler, err := self.node.RPCHandler()
   264  	if err != nil {
   265  		return err
   266  	}
   267  
   268  	self.lock.Lock()
   269  	self.client = rpc.DialInProc(handler)
   270  	self.lock.Unlock()
   271  
   272  	return nil
   273  }
   274  
   275  // Stop closes the RPC client and stops the underlying devp2p node
   276  func (self *SimNode) Stop() error {
   277  	self.lock.Lock()
   278  	if self.client != nil {
   279  		self.client.Close()
   280  		self.client = nil
   281  	}
   282  	self.lock.Unlock()
   283  	return self.node.Stop()
   284  }
   285  
   286  // Services returns a copy of the underlying services
   287  func (self *SimNode) Services() []node.Service {
   288  	self.lock.RLock()
   289  	defer self.lock.RUnlock()
   290  	services := make([]node.Service, 0, len(self.running))
   291  	for _, service := range self.running {
   292  		services = append(services, service)
   293  	}
   294  	return services
   295  }
   296  
   297  // Server returns the underlying p2p.Server
   298  func (self *SimNode) Server() *p2p.Server {
   299  	return self.node.Server()
   300  }
   301  
   302  // SubscribeEvents subscribes the given channel to peer events from the
   303  // underlying p2p.Server
   304  func (self *SimNode) SubscribeEvents(ch chan *p2p.PeerEvent) event.Subscription {
   305  	srv := self.Server()
   306  	if srv == nil {
   307  		panic("node not running")
   308  	}
   309  	return srv.SubscribeEvents(ch)
   310  }
   311  
   312  // NodeInfo returns information about the node
   313  func (self *SimNode) NodeInfo() *p2p.NodeInfo {
   314  	server := self.Server()
   315  	if server == nil {
   316  		return &p2p.NodeInfo{
   317  			ID:    self.ID.String(),
   318  			Enode: self.Node().String(),
   319  		}
   320  	}
   321  	return server.NodeInfo()
   322  }