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