github.com/codingfuture/orig-energi3@v0.8.4/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 // 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 enode.ID) 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 if err := n.ID.UnmarshalText([]byte(confJSON.ID)); err != nil { 142 return err 143 } 144 } 145 146 if confJSON.PrivateKey != "" { 147 key, err := hex.DecodeString(confJSON.PrivateKey) 148 if err != nil { 149 return err 150 } 151 privKey, err := crypto.ToECDSA(key) 152 if err != nil { 153 return err 154 } 155 n.PrivateKey = privKey 156 } 157 158 n.Name = confJSON.Name 159 n.Services = confJSON.Services 160 n.Port = confJSON.Port 161 n.EnableMsgEvents = confJSON.EnableMsgEvents 162 163 return nil 164 } 165 166 // Node returns the node descriptor represented by the config. 167 func (n *NodeConfig) Node() *enode.Node { 168 return enode.NewV4(&n.PrivateKey.PublicKey, net.IP{127, 0, 0, 1}, int(n.Port), int(n.Port)) 169 } 170 171 // RandomNodeConfig returns node configuration with a randomly generated ID and 172 // PrivateKey 173 func RandomNodeConfig() *NodeConfig { 174 key, err := crypto.GenerateKey() 175 if err != nil { 176 panic("unable to generate key") 177 } 178 179 id := enode.PubkeyToIDV4(&key.PublicKey) 180 port, err := assignTCPPort() 181 if err != nil { 182 panic("unable to assign tcp port") 183 } 184 return &NodeConfig{ 185 ID: id, 186 Name: fmt.Sprintf("node_%s", id.String()), 187 PrivateKey: key, 188 Port: port, 189 EnableMsgEvents: true, 190 } 191 } 192 193 func assignTCPPort() (uint16, error) { 194 l, err := net.Listen("tcp", "127.0.0.1:0") 195 if err != nil { 196 return 0, err 197 } 198 l.Close() 199 _, port, err := net.SplitHostPort(l.Addr().String()) 200 if err != nil { 201 return 0, err 202 } 203 p, err := strconv.ParseInt(port, 10, 32) 204 if err != nil { 205 return 0, err 206 } 207 return uint16(p), nil 208 } 209 210 // ServiceContext is a collection of options and methods which can be utilised 211 // when starting services 212 type ServiceContext struct { 213 RPCDialer 214 215 NodeContext *node.ServiceContext 216 Config *NodeConfig 217 Snapshot []byte 218 } 219 220 // RPCDialer is used when initialising services which need to connect to 221 // other nodes in the network (for example a simulated Swarm node which needs 222 // to connect to a Geth node to resolve ENS names) 223 type RPCDialer interface { 224 DialRPC(id enode.ID) (*rpc.Client, error) 225 } 226 227 // Services is a collection of services which can be run in a simulation 228 type Services map[string]ServiceFunc 229 230 // ServiceFunc returns a node.Service which can be used to boot a devp2p node 231 type ServiceFunc func(ctx *ServiceContext) (node.Service, error) 232 233 // serviceFuncs is a map of registered services which are used to boot devp2p 234 // nodes 235 var serviceFuncs = make(Services) 236 237 // RegisterServices registers the given Services which can then be used to 238 // start devp2p nodes using either the Exec or Docker adapters. 239 // 240 // It should be called in an init function so that it has the opportunity to 241 // execute the services before main() is called. 242 func RegisterServices(services Services) { 243 for name, f := range services { 244 if _, exists := serviceFuncs[name]; exists { 245 panic(fmt.Sprintf("node service already exists: %q", name)) 246 } 247 serviceFuncs[name] = f 248 } 249 250 // now we have registered the services, run reexec.Init() which will 251 // potentially start one of the services if the current binary has 252 // been exec'd with argv[0] set to "p2p-node" 253 if reexec.Init() { 254 os.Exit(0) 255 } 256 }