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