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