github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/p2p/simulations/adapters/types.go (about)

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