github.com/divan/go-ethereum@v1.8.14-0.20180820134928-1de9ada4016d/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/discover" 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 discover.NodeID 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 // Services are the names of the services which should be run when 94 // starting the node (for SimNodes it should be the names of services 95 // contained in SimAdapter.services, for other nodes it should be 96 // services registered by calling the RegisterService function) 97 Services []string 98 99 // function to sanction or prevent suggesting a peer 100 Reachable func(id discover.NodeID) bool 101 102 Port uint16 103 } 104 105 // nodeConfigJSON is used to encode and decode NodeConfig as JSON by encoding 106 // all fields as strings 107 type nodeConfigJSON struct { 108 ID string `json:"id"` 109 PrivateKey string `json:"private_key"` 110 Name string `json:"name"` 111 Services []string `json:"services"` 112 EnableMsgEvents bool `json:"enable_msg_events"` 113 Port uint16 `json:"port"` 114 } 115 116 // MarshalJSON implements the json.Marshaler interface by encoding the config 117 // fields as strings 118 func (n *NodeConfig) MarshalJSON() ([]byte, error) { 119 confJSON := nodeConfigJSON{ 120 ID: n.ID.String(), 121 Name: n.Name, 122 Services: n.Services, 123 Port: n.Port, 124 EnableMsgEvents: n.EnableMsgEvents, 125 } 126 if n.PrivateKey != nil { 127 confJSON.PrivateKey = hex.EncodeToString(crypto.FromECDSA(n.PrivateKey)) 128 } 129 return json.Marshal(confJSON) 130 } 131 132 // UnmarshalJSON implements the json.Unmarshaler interface by decoding the json 133 // string values into the config fields 134 func (n *NodeConfig) UnmarshalJSON(data []byte) error { 135 var confJSON nodeConfigJSON 136 if err := json.Unmarshal(data, &confJSON); err != nil { 137 return err 138 } 139 140 if confJSON.ID != "" { 141 nodeID, err := discover.HexID(confJSON.ID) 142 if err != nil { 143 return err 144 } 145 n.ID = nodeID 146 } 147 148 if confJSON.PrivateKey != "" { 149 key, err := hex.DecodeString(confJSON.PrivateKey) 150 if err != nil { 151 return err 152 } 153 privKey, err := crypto.ToECDSA(key) 154 if err != nil { 155 return err 156 } 157 n.PrivateKey = privKey 158 } 159 160 n.Name = confJSON.Name 161 n.Services = confJSON.Services 162 n.Port = confJSON.Port 163 n.EnableMsgEvents = confJSON.EnableMsgEvents 164 165 return nil 166 } 167 168 // RandomNodeConfig returns node configuration with a randomly generated ID and 169 // PrivateKey 170 func RandomNodeConfig() *NodeConfig { 171 key, err := crypto.GenerateKey() 172 if err != nil { 173 panic("unable to generate key") 174 } 175 176 id := discover.PubkeyID(&key.PublicKey) 177 port, err := assignTCPPort() 178 if err != nil { 179 panic("unable to assign tcp port") 180 } 181 return &NodeConfig{ 182 ID: id, 183 Name: fmt.Sprintf("node_%s", id.String()), 184 PrivateKey: key, 185 Port: port, 186 EnableMsgEvents: true, 187 } 188 } 189 190 func assignTCPPort() (uint16, error) { 191 l, err := net.Listen("tcp", "127.0.0.1:0") 192 if err != nil { 193 return 0, err 194 } 195 l.Close() 196 _, port, err := net.SplitHostPort(l.Addr().String()) 197 if err != nil { 198 return 0, err 199 } 200 p, err := strconv.ParseInt(port, 10, 32) 201 if err != nil { 202 return 0, err 203 } 204 return uint16(p), nil 205 } 206 207 // ServiceContext is a collection of options and methods which can be utilised 208 // when starting services 209 type ServiceContext struct { 210 RPCDialer 211 212 NodeContext *node.ServiceContext 213 Config *NodeConfig 214 Snapshot []byte 215 } 216 217 // RPCDialer is used when initialising services which need to connect to 218 // other nodes in the network (for example a simulated Swarm node which needs 219 // to connect to a Geth node to resolve ENS names) 220 type RPCDialer interface { 221 DialRPC(id discover.NodeID) (*rpc.Client, error) 222 } 223 224 // Services is a collection of services which can be run in a simulation 225 type Services map[string]ServiceFunc 226 227 // ServiceFunc returns a node.Service which can be used to boot a devp2p node 228 type ServiceFunc func(ctx *ServiceContext) (node.Service, error) 229 230 // serviceFuncs is a map of registered services which are used to boot devp2p 231 // nodes 232 var serviceFuncs = make(Services) 233 234 // RegisterServices registers the given Services which can then be used to 235 // start devp2p nodes using either the Exec or Docker adapters. 236 // 237 // It should be called in an init function so that it has the opportunity to 238 // execute the services before main() is called. 239 func RegisterServices(services Services) { 240 for name, f := range services { 241 if _, exists := serviceFuncs[name]; exists { 242 panic(fmt.Sprintf("node service already exists: %q", name)) 243 } 244 serviceFuncs[name] = f 245 } 246 247 // now we have registered the services, run reexec.Init() which will 248 // potentially start one of the services if the current binary has 249 // been exec'd with argv[0] set to "p2p-node" 250 if reexec.Init() { 251 os.Exit(0) 252 } 253 }