github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/p2p/simulations/adapters/types.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  	"crypto/ecdsa"
    16  	"encoding/hex"
    17  	"encoding/json"
    18  	"fmt"
    19  	"net"
    20  	"os"
    21  
    22  	"github.com/docker/docker/pkg/reexec"
    23  	"github.com/Sberex/go-sberex/crypto"
    24  	"github.com/Sberex/go-sberex/node"
    25  	"github.com/Sberex/go-sberex/p2p"
    26  	"github.com/Sberex/go-sberex/p2p/discover"
    27  	"github.com/Sberex/go-sberex/rpc"
    28  )
    29  
    30  // Node represents a node in a simulation network which is created by a
    31  // NodeAdapter, for example:
    32  //
    33  // * SimNode    - An in-memory node
    34  // * ExecNode   - A child process node
    35  // * DockerNode - A Docker container node
    36  //
    37  type Node interface {
    38  	// Addr returns the node's address (e.g. an Enode URL)
    39  	Addr() []byte
    40  
    41  	// Client returns the RPC client which is created once the node is
    42  	// up and running
    43  	Client() (*rpc.Client, error)
    44  
    45  	// ServeRPC serves RPC requests over the given connection
    46  	ServeRPC(net.Conn) error
    47  
    48  	// Start starts the node with the given snapshots
    49  	Start(snapshots map[string][]byte) error
    50  
    51  	// Stop stops the node
    52  	Stop() error
    53  
    54  	// NodeInfo returns information about the node
    55  	NodeInfo() *p2p.NodeInfo
    56  
    57  	// Snapshots creates snapshots of the running services
    58  	Snapshots() (map[string][]byte, error)
    59  }
    60  
    61  // NodeAdapter is used to create Nodes in a simulation network
    62  type NodeAdapter interface {
    63  	// Name returns the name of the adapter for logging purposes
    64  	Name() string
    65  
    66  	// NewNode creates a new node with the given configuration
    67  	NewNode(config *NodeConfig) (Node, error)
    68  }
    69  
    70  // NodeConfig is the configuration used to start a node in a simulation
    71  // network
    72  type NodeConfig struct {
    73  	// ID is the node's ID which is used to identify the node in the
    74  	// simulation network
    75  	ID discover.NodeID
    76  
    77  	// PrivateKey is the node's private key which is used by the devp2p
    78  	// stack to encrypt communications
    79  	PrivateKey *ecdsa.PrivateKey
    80  
    81  	// Enable peer events for Msgs
    82  	EnableMsgEvents bool
    83  
    84  	// Name is a human friendly name for the node like "node01"
    85  	Name string
    86  
    87  	// Services are the names of the services which should be run when
    88  	// starting the node (for SimNodes it should be the names of services
    89  	// contained in SimAdapter.services, for other nodes it should be
    90  	// services registered by calling the RegisterService function)
    91  	Services []string
    92  
    93  	// function to sanction or prevent suggesting a peer
    94  	Reachable func(id discover.NodeID) bool
    95  }
    96  
    97  // nodeConfigJSON is used to encode and decode NodeConfig as JSON by encoding
    98  // all fields as strings
    99  type nodeConfigJSON struct {
   100  	ID         string   `json:"id"`
   101  	PrivateKey string   `json:"private_key"`
   102  	Name       string   `json:"name"`
   103  	Services   []string `json:"services"`
   104  }
   105  
   106  // MarshalJSON implements the json.Marshaler interface by encoding the config
   107  // fields as strings
   108  func (n *NodeConfig) MarshalJSON() ([]byte, error) {
   109  	confJSON := nodeConfigJSON{
   110  		ID:       n.ID.String(),
   111  		Name:     n.Name,
   112  		Services: n.Services,
   113  	}
   114  	if n.PrivateKey != nil {
   115  		confJSON.PrivateKey = hex.EncodeToString(crypto.FromECDSA(n.PrivateKey))
   116  	}
   117  	return json.Marshal(confJSON)
   118  }
   119  
   120  // UnmarshalJSON implements the json.Unmarshaler interface by decoding the json
   121  // string values into the config fields
   122  func (n *NodeConfig) UnmarshalJSON(data []byte) error {
   123  	var confJSON nodeConfigJSON
   124  	if err := json.Unmarshal(data, &confJSON); err != nil {
   125  		return err
   126  	}
   127  
   128  	if confJSON.ID != "" {
   129  		nodeID, err := discover.HexID(confJSON.ID)
   130  		if err != nil {
   131  			return err
   132  		}
   133  		n.ID = nodeID
   134  	}
   135  
   136  	if confJSON.PrivateKey != "" {
   137  		key, err := hex.DecodeString(confJSON.PrivateKey)
   138  		if err != nil {
   139  			return err
   140  		}
   141  		privKey, err := crypto.ToECDSA(key)
   142  		if err != nil {
   143  			return err
   144  		}
   145  		n.PrivateKey = privKey
   146  	}
   147  
   148  	n.Name = confJSON.Name
   149  	n.Services = confJSON.Services
   150  
   151  	return nil
   152  }
   153  
   154  // RandomNodeConfig returns node configuration with a randomly generated ID and
   155  // PrivateKey
   156  func RandomNodeConfig() *NodeConfig {
   157  	key, err := crypto.GenerateKey()
   158  	if err != nil {
   159  		panic("unable to generate key")
   160  	}
   161  	var id discover.NodeID
   162  	pubkey := crypto.FromECDSAPub(&key.PublicKey)
   163  	copy(id[:], pubkey[1:])
   164  	return &NodeConfig{
   165  		ID:         id,
   166  		PrivateKey: key,
   167  	}
   168  }
   169  
   170  // ServiceContext is a collection of options and methods which can be utilised
   171  // when starting services
   172  type ServiceContext struct {
   173  	RPCDialer
   174  
   175  	NodeContext *node.ServiceContext
   176  	Config      *NodeConfig
   177  	Snapshot    []byte
   178  }
   179  
   180  // RPCDialer is used when initialising services which need to connect to
   181  // other nodes in the network (for example a simulated Swarm node which needs
   182  // to connect to a Geth node to resolve ENS names)
   183  type RPCDialer interface {
   184  	DialRPC(id discover.NodeID) (*rpc.Client, error)
   185  }
   186  
   187  // Services is a collection of services which can be run in a simulation
   188  type Services map[string]ServiceFunc
   189  
   190  // ServiceFunc returns a node.Service which can be used to boot a devp2p node
   191  type ServiceFunc func(ctx *ServiceContext) (node.Service, error)
   192  
   193  // serviceFuncs is a map of registered services which are used to boot devp2p
   194  // nodes
   195  var serviceFuncs = make(Services)
   196  
   197  // RegisterServices registers the given Services which can then be used to
   198  // start devp2p nodes using either the Exec or Docker adapters.
   199  //
   200  // It should be called in an init function so that it has the opportunity to
   201  // execute the services before main() is called.
   202  func RegisterServices(services Services) {
   203  	for name, f := range services {
   204  		if _, exists := serviceFuncs[name]; exists {
   205  			panic(fmt.Sprintf("node service already exists: %q", name))
   206  		}
   207  		serviceFuncs[name] = f
   208  	}
   209  
   210  	// now we have registered the services, run reexec.Init() which will
   211  	// potentially start one of the services if the current binary has
   212  	// been exec'd with argv[0] set to "p2p-node"
   213  	if reexec.Init() {
   214  		os.Exit(0)
   215  	}
   216  }