github.com/aswedchain/aswed@v1.0.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/aswedchain/aswed/crypto" 30 "github.com/aswedchain/aswed/log" 31 "github.com/aswedchain/aswed/node" 32 "github.com/aswedchain/aswed/p2p" 33 "github.com/aswedchain/aswed/p2p/enode" 34 "github.com/aswedchain/aswed/p2p/enr" 35 "github.com/aswedchain/aswed/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 // Enode 111 node *enode.Node 112 113 // ENR Record with entries to overwrite 114 Record enr.Record 115 116 // function to sanction or prevent suggesting a peer 117 Reachable func(id enode.ID) bool 118 119 Port uint16 120 } 121 122 // nodeConfigJSON is used to encode and decode NodeConfig as JSON by encoding 123 // all fields as strings 124 type nodeConfigJSON struct { 125 ID string `json:"id"` 126 PrivateKey string `json:"private_key"` 127 Name string `json:"name"` 128 Services []string `json:"services"` 129 Properties []string `json:"properties"` 130 EnableMsgEvents bool `json:"enable_msg_events"` 131 Port uint16 `json:"port"` 132 } 133 134 // MarshalJSON implements the json.Marshaler interface by encoding the config 135 // fields as strings 136 func (n *NodeConfig) MarshalJSON() ([]byte, error) { 137 confJSON := nodeConfigJSON{ 138 ID: n.ID.String(), 139 Name: n.Name, 140 Services: n.Lifecycles, 141 Properties: n.Properties, 142 Port: n.Port, 143 EnableMsgEvents: n.EnableMsgEvents, 144 } 145 if n.PrivateKey != nil { 146 confJSON.PrivateKey = hex.EncodeToString(crypto.FromECDSA(n.PrivateKey)) 147 } 148 return json.Marshal(confJSON) 149 } 150 151 // UnmarshalJSON implements the json.Unmarshaler interface by decoding the json 152 // string values into the config fields 153 func (n *NodeConfig) UnmarshalJSON(data []byte) error { 154 var confJSON nodeConfigJSON 155 if err := json.Unmarshal(data, &confJSON); err != nil { 156 return err 157 } 158 159 if confJSON.ID != "" { 160 if err := n.ID.UnmarshalText([]byte(confJSON.ID)); err != nil { 161 return err 162 } 163 } 164 165 if confJSON.PrivateKey != "" { 166 key, err := hex.DecodeString(confJSON.PrivateKey) 167 if err != nil { 168 return err 169 } 170 privKey, err := crypto.ToECDSA(key) 171 if err != nil { 172 return err 173 } 174 n.PrivateKey = privKey 175 } 176 177 n.Name = confJSON.Name 178 n.Lifecycles = confJSON.Services 179 n.Properties = confJSON.Properties 180 n.Port = confJSON.Port 181 n.EnableMsgEvents = confJSON.EnableMsgEvents 182 183 return nil 184 } 185 186 // Node returns the node descriptor represented by the config. 187 func (n *NodeConfig) Node() *enode.Node { 188 return n.node 189 } 190 191 // RandomNodeConfig returns node configuration with a randomly generated ID and 192 // PrivateKey 193 func RandomNodeConfig() *NodeConfig { 194 prvkey, err := crypto.GenerateKey() 195 if err != nil { 196 panic("unable to generate key") 197 } 198 199 port, err := assignTCPPort() 200 if err != nil { 201 panic("unable to assign tcp port") 202 } 203 204 enodId := enode.PubkeyToIDV4(&prvkey.PublicKey) 205 return &NodeConfig{ 206 PrivateKey: prvkey, 207 ID: enodId, 208 Name: fmt.Sprintf("node_%s", enodId.String()), 209 Port: port, 210 EnableMsgEvents: true, 211 } 212 } 213 214 func assignTCPPort() (uint16, error) { 215 l, err := net.Listen("tcp", "127.0.0.1:0") 216 if err != nil { 217 return 0, err 218 } 219 l.Close() 220 _, port, err := net.SplitHostPort(l.Addr().String()) 221 if err != nil { 222 return 0, err 223 } 224 p, err := strconv.ParseInt(port, 10, 32) 225 if err != nil { 226 return 0, err 227 } 228 return uint16(p), nil 229 } 230 231 // ServiceContext is a collection of options and methods which can be utilised 232 // when starting services 233 type ServiceContext struct { 234 RPCDialer 235 236 Config *NodeConfig 237 Snapshot []byte 238 } 239 240 // RPCDialer is used when initialising services which need to connect to 241 // other nodes in the network (for example a simulated Swarm node which needs 242 // to connect to a Geth node to resolve ENS names) 243 type RPCDialer interface { 244 DialRPC(id enode.ID) (*rpc.Client, error) 245 } 246 247 // LifecycleConstructor allows a Lifecycle to be constructed during node start-up. 248 // While the service-specific package usually takes care of Lifecycle creation and registration, 249 // for testing purposes, it is useful to be able to construct a Lifecycle on spot. 250 type LifecycleConstructor func(ctx *ServiceContext, stack *node.Node) (node.Lifecycle, error) 251 252 // LifecycleConstructors stores LifecycleConstructor functions to call during node start-up. 253 type LifecycleConstructors map[string]LifecycleConstructor 254 255 // lifecycleConstructorFuncs is a map of registered services which are used to boot devp2p 256 // nodes 257 var lifecycleConstructorFuncs = make(LifecycleConstructors) 258 259 // RegisterLifecycles registers the given Services which can then be used to 260 // start devp2p nodes using either the Exec or Docker adapters. 261 // 262 // It should be called in an init function so that it has the opportunity to 263 // execute the services before main() is called. 264 func RegisterLifecycles(lifecycles LifecycleConstructors) { 265 for name, f := range lifecycles { 266 if _, exists := lifecycleConstructorFuncs[name]; exists { 267 panic(fmt.Sprintf("node service already exists: %q", name)) 268 } 269 lifecycleConstructorFuncs[name] = f 270 } 271 272 // now we have registered the services, run reexec.Init() which will 273 // potentially start one of the services if the current binary has 274 // been exec'd with argv[0] set to "p2p-node" 275 if reexec.Init() { 276 os.Exit(0) 277 } 278 } 279 280 // adds the host part to the configuration's ENR, signs it 281 // creates and the corresponding enode object to the configuration 282 func (n *NodeConfig) initEnode(ip net.IP, tcpport int, udpport int) error { 283 enrIp := enr.IP(ip) 284 n.Record.Set(&enrIp) 285 enrTcpPort := enr.TCP(tcpport) 286 n.Record.Set(&enrTcpPort) 287 enrUdpPort := enr.UDP(udpport) 288 n.Record.Set(&enrUdpPort) 289 290 err := enode.SignV4(&n.Record, n.PrivateKey) 291 if err != nil { 292 return fmt.Errorf("unable to generate ENR: %v", err) 293 } 294 nod, err := enode.New(enode.V4ID{}, &n.Record) 295 if err != nil { 296 return fmt.Errorf("unable to create enode: %v", err) 297 } 298 log.Trace("simnode new", "record", n.Record) 299 n.node = nod 300 return nil 301 } 302 303 func (n *NodeConfig) initDummyEnode() error { 304 return n.initEnode(net.IPv4(127, 0, 0, 1), int(n.Port), 0) 305 }