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