github.com/theQRL/go-zond@v0.1.1/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/gorilla/websocket" 30 "github.com/theQRL/go-zond/crypto" 31 "github.com/theQRL/go-zond/log" 32 "github.com/theQRL/go-zond/node" 33 "github.com/theQRL/go-zond/p2p" 34 "github.com/theQRL/go-zond/p2p/enode" 35 "github.com/theQRL/go-zond/p2p/enr" 36 "github.com/theQRL/go-zond/rpc" 37 ) 38 39 // Node represents a node in a simulation network which is created by a 40 // NodeAdapter, for example: 41 // 42 // - SimNode, an in-memory node in the same process 43 // - ExecNode, a child process node 44 // - DockerNode, a node running in a Docker container 45 type Node interface { 46 // Addr returns the node's address (e.g. an Enode URL) 47 Addr() []byte 48 49 // Client returns the RPC client which is created once the node is 50 // up and running 51 Client() (*rpc.Client, error) 52 53 // ServeRPC serves RPC requests over the given connection 54 ServeRPC(*websocket.Conn) error 55 56 // Start starts the node with the given snapshots 57 Start(snapshots map[string][]byte) error 58 59 // Stop stops the node 60 Stop() error 61 62 // NodeInfo returns information about the node 63 NodeInfo() *p2p.NodeInfo 64 65 // Snapshots creates snapshots of the running services 66 Snapshots() (map[string][]byte, error) 67 } 68 69 // NodeAdapter is used to create Nodes in a simulation network 70 type NodeAdapter interface { 71 // Name returns the name of the adapter for logging purposes 72 Name() string 73 74 // NewNode creates a new node with the given configuration 75 NewNode(config *NodeConfig) (Node, error) 76 } 77 78 // NodeConfig is the configuration used to start a node in a simulation 79 // network 80 type NodeConfig struct { 81 // ID is the node's ID which is used to identify the node in the 82 // simulation network 83 ID enode.ID 84 85 // PrivateKey is the node's private key which is used by the devp2p 86 // stack to encrypt communications 87 PrivateKey *ecdsa.PrivateKey 88 89 // Enable peer events for Msgs 90 EnableMsgEvents bool 91 92 // Name is a human friendly name for the node like "node01" 93 Name string 94 95 // Use an existing database instead of a temporary one if non-empty 96 DataDir string 97 98 // Lifecycles are the names of the service lifecycles which should be run when 99 // starting the node (for SimNodes it should be the names of service lifecycles 100 // contained in SimAdapter.lifecycles, for other nodes it should be 101 // service lifecycles registered by calling the RegisterLifecycle function) 102 Lifecycles []string 103 104 // Properties are the names of the properties this node should hold 105 // within running services (e.g. "bootnode", "lightnode" or any custom values) 106 // These values need to be checked and acted upon by node Services 107 Properties []string 108 109 // ExternalSigner specifies an external URI for a clef-type signer 110 ExternalSigner string 111 112 // Enode 113 node *enode.Node 114 115 // ENR Record with entries to overwrite 116 Record enr.Record 117 118 // function to sanction or prevent suggesting a peer 119 Reachable func(id enode.ID) bool 120 121 Port uint16 122 123 // LogFile is the log file name of the p2p node at runtime. 124 // 125 // The default value is empty so that the default log writer 126 // is the system standard output. 127 LogFile string 128 129 // LogVerbosity is the log verbosity of the p2p node at runtime. 130 // 131 // The default verbosity is INFO. 132 LogVerbosity log.Lvl 133 } 134 135 // nodeConfigJSON is used to encode and decode NodeConfig as JSON by encoding 136 // all fields as strings 137 type nodeConfigJSON struct { 138 ID string `json:"id"` 139 PrivateKey string `json:"private_key"` 140 Name string `json:"name"` 141 Lifecycles []string `json:"lifecycles"` 142 Properties []string `json:"properties"` 143 EnableMsgEvents bool `json:"enable_msg_events"` 144 Port uint16 `json:"port"` 145 LogFile string `json:"logfile"` 146 LogVerbosity int `json:"log_verbosity"` 147 } 148 149 // MarshalJSON implements the json.Marshaler interface by encoding the config 150 // fields as strings 151 func (n *NodeConfig) MarshalJSON() ([]byte, error) { 152 confJSON := nodeConfigJSON{ 153 ID: n.ID.String(), 154 Name: n.Name, 155 Lifecycles: n.Lifecycles, 156 Properties: n.Properties, 157 Port: n.Port, 158 EnableMsgEvents: n.EnableMsgEvents, 159 LogFile: n.LogFile, 160 LogVerbosity: int(n.LogVerbosity), 161 } 162 if n.PrivateKey != nil { 163 confJSON.PrivateKey = hex.EncodeToString(crypto.FromECDSA(n.PrivateKey)) 164 } 165 return json.Marshal(confJSON) 166 } 167 168 // UnmarshalJSON implements the json.Unmarshaler interface by decoding the json 169 // string values into the config fields 170 func (n *NodeConfig) UnmarshalJSON(data []byte) error { 171 var confJSON nodeConfigJSON 172 if err := json.Unmarshal(data, &confJSON); err != nil { 173 return err 174 } 175 176 if confJSON.ID != "" { 177 if err := n.ID.UnmarshalText([]byte(confJSON.ID)); err != nil { 178 return err 179 } 180 } 181 182 if confJSON.PrivateKey != "" { 183 key, err := hex.DecodeString(confJSON.PrivateKey) 184 if err != nil { 185 return err 186 } 187 privKey, err := crypto.ToECDSA(key) 188 if err != nil { 189 return err 190 } 191 n.PrivateKey = privKey 192 } 193 194 n.Name = confJSON.Name 195 n.Lifecycles = confJSON.Lifecycles 196 n.Properties = confJSON.Properties 197 n.Port = confJSON.Port 198 n.EnableMsgEvents = confJSON.EnableMsgEvents 199 n.LogFile = confJSON.LogFile 200 n.LogVerbosity = log.Lvl(confJSON.LogVerbosity) 201 202 return nil 203 } 204 205 // Node returns the node descriptor represented by the config. 206 func (n *NodeConfig) Node() *enode.Node { 207 return n.node 208 } 209 210 // RandomNodeConfig returns node configuration with a randomly generated ID and 211 // PrivateKey 212 func RandomNodeConfig() *NodeConfig { 213 prvkey, err := crypto.GenerateKey() 214 if err != nil { 215 panic("unable to generate key") 216 } 217 218 port, err := assignTCPPort() 219 if err != nil { 220 panic("unable to assign tcp port") 221 } 222 223 enodId := enode.PubkeyToIDV4(&prvkey.PublicKey) 224 return &NodeConfig{ 225 PrivateKey: prvkey, 226 ID: enodId, 227 Name: fmt.Sprintf("node_%s", enodId.String()), 228 Port: port, 229 EnableMsgEvents: true, 230 LogVerbosity: log.LvlInfo, 231 } 232 } 233 234 func assignTCPPort() (uint16, error) { 235 l, err := net.Listen("tcp", "127.0.0.1:0") 236 if err != nil { 237 return 0, err 238 } 239 l.Close() 240 _, port, err := net.SplitHostPort(l.Addr().String()) 241 if err != nil { 242 return 0, err 243 } 244 p, err := strconv.ParseUint(port, 10, 16) 245 if err != nil { 246 return 0, err 247 } 248 return uint16(p), nil 249 } 250 251 // ServiceContext is a collection of options and methods which can be utilised 252 // when starting services 253 type ServiceContext struct { 254 RPCDialer 255 256 Config *NodeConfig 257 Snapshot []byte 258 } 259 260 // RPCDialer is used when initialising services which need to connect to 261 // other nodes in the network (for example a simulated Swarm node which needs 262 // to connect to a Geth node to resolve ENS names) 263 type RPCDialer interface { 264 DialRPC(id enode.ID) (*rpc.Client, error) 265 } 266 267 // LifecycleConstructor allows a Lifecycle to be constructed during node start-up. 268 // While the service-specific package usually takes care of Lifecycle creation and registration, 269 // for testing purposes, it is useful to be able to construct a Lifecycle on spot. 270 type LifecycleConstructor func(ctx *ServiceContext, stack *node.Node) (node.Lifecycle, error) 271 272 // LifecycleConstructors stores LifecycleConstructor functions to call during node start-up. 273 type LifecycleConstructors map[string]LifecycleConstructor 274 275 // lifecycleConstructorFuncs is a map of registered services which are used to boot devp2p 276 // nodes 277 var lifecycleConstructorFuncs = make(LifecycleConstructors) 278 279 // RegisterLifecycles registers the given Services which can then be used to 280 // start devp2p nodes using either the Exec or Docker adapters. 281 // 282 // It should be called in an init function so that it has the opportunity to 283 // execute the services before main() is called. 284 func RegisterLifecycles(lifecycles LifecycleConstructors) { 285 for name, f := range lifecycles { 286 if _, exists := lifecycleConstructorFuncs[name]; exists { 287 panic(fmt.Sprintf("node service already exists: %q", name)) 288 } 289 lifecycleConstructorFuncs[name] = f 290 } 291 292 // now we have registered the services, run reexec.Init() which will 293 // potentially start one of the services if the current binary has 294 // been exec'd with argv[0] set to "p2p-node" 295 if reexec.Init() { 296 os.Exit(0) 297 } 298 } 299 300 // adds the host part to the configuration's ENR, signs it 301 // creates and the corresponding enode object to the configuration 302 func (n *NodeConfig) initEnode(ip net.IP, tcpport int, udpport int) error { 303 enrIp := enr.IP(ip) 304 n.Record.Set(&enrIp) 305 enrTcpPort := enr.TCP(tcpport) 306 n.Record.Set(&enrTcpPort) 307 enrUdpPort := enr.UDP(udpport) 308 n.Record.Set(&enrUdpPort) 309 310 err := enode.SignV4(&n.Record, n.PrivateKey) 311 if err != nil { 312 return fmt.Errorf("unable to generate ENR: %v", err) 313 } 314 nod, err := enode.New(enode.V4ID{}, &n.Record) 315 if err != nil { 316 return fmt.Errorf("unable to create enode: %v", err) 317 } 318 log.Trace("simnode new", "record", n.Record) 319 n.node = nod 320 return nil 321 } 322 323 func (n *NodeConfig) initDummyEnode() error { 324 return n.initEnode(net.IPv4(127, 0, 0, 1), int(n.Port), 0) 325 }