github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/p2p/simulations/adapters/types.go (about) 1 // This file is part of the go-sberex library. The go-sberex library is 2 // free software: you can redistribute it and/or modify it under the terms 3 // of the GNU Lesser General Public License as published by the Free 4 // Software Foundation, either version 3 of the License, or (at your option) 5 // any later version. 6 // 7 // The go-sberex library is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 // General Public License <http://www.gnu.org/licenses/> for more details. 11 12 package adapters 13 14 import ( 15 "crypto/ecdsa" 16 "encoding/hex" 17 "encoding/json" 18 "fmt" 19 "net" 20 "os" 21 22 "github.com/docker/docker/pkg/reexec" 23 "github.com/Sberex/go-sberex/crypto" 24 "github.com/Sberex/go-sberex/node" 25 "github.com/Sberex/go-sberex/p2p" 26 "github.com/Sberex/go-sberex/p2p/discover" 27 "github.com/Sberex/go-sberex/rpc" 28 ) 29 30 // Node represents a node in a simulation network which is created by a 31 // NodeAdapter, for example: 32 // 33 // * SimNode - An in-memory node 34 // * ExecNode - A child process node 35 // * DockerNode - A Docker container node 36 // 37 type Node interface { 38 // Addr returns the node's address (e.g. an Enode URL) 39 Addr() []byte 40 41 // Client returns the RPC client which is created once the node is 42 // up and running 43 Client() (*rpc.Client, error) 44 45 // ServeRPC serves RPC requests over the given connection 46 ServeRPC(net.Conn) error 47 48 // Start starts the node with the given snapshots 49 Start(snapshots map[string][]byte) error 50 51 // Stop stops the node 52 Stop() error 53 54 // NodeInfo returns information about the node 55 NodeInfo() *p2p.NodeInfo 56 57 // Snapshots creates snapshots of the running services 58 Snapshots() (map[string][]byte, error) 59 } 60 61 // NodeAdapter is used to create Nodes in a simulation network 62 type NodeAdapter interface { 63 // Name returns the name of the adapter for logging purposes 64 Name() string 65 66 // NewNode creates a new node with the given configuration 67 NewNode(config *NodeConfig) (Node, error) 68 } 69 70 // NodeConfig is the configuration used to start a node in a simulation 71 // network 72 type NodeConfig struct { 73 // ID is the node's ID which is used to identify the node in the 74 // simulation network 75 ID discover.NodeID 76 77 // PrivateKey is the node's private key which is used by the devp2p 78 // stack to encrypt communications 79 PrivateKey *ecdsa.PrivateKey 80 81 // Enable peer events for Msgs 82 EnableMsgEvents bool 83 84 // Name is a human friendly name for the node like "node01" 85 Name string 86 87 // Services are the names of the services which should be run when 88 // starting the node (for SimNodes it should be the names of services 89 // contained in SimAdapter.services, for other nodes it should be 90 // services registered by calling the RegisterService function) 91 Services []string 92 93 // function to sanction or prevent suggesting a peer 94 Reachable func(id discover.NodeID) bool 95 } 96 97 // nodeConfigJSON is used to encode and decode NodeConfig as JSON by encoding 98 // all fields as strings 99 type nodeConfigJSON struct { 100 ID string `json:"id"` 101 PrivateKey string `json:"private_key"` 102 Name string `json:"name"` 103 Services []string `json:"services"` 104 } 105 106 // MarshalJSON implements the json.Marshaler interface by encoding the config 107 // fields as strings 108 func (n *NodeConfig) MarshalJSON() ([]byte, error) { 109 confJSON := nodeConfigJSON{ 110 ID: n.ID.String(), 111 Name: n.Name, 112 Services: n.Services, 113 } 114 if n.PrivateKey != nil { 115 confJSON.PrivateKey = hex.EncodeToString(crypto.FromECDSA(n.PrivateKey)) 116 } 117 return json.Marshal(confJSON) 118 } 119 120 // UnmarshalJSON implements the json.Unmarshaler interface by decoding the json 121 // string values into the config fields 122 func (n *NodeConfig) UnmarshalJSON(data []byte) error { 123 var confJSON nodeConfigJSON 124 if err := json.Unmarshal(data, &confJSON); err != nil { 125 return err 126 } 127 128 if confJSON.ID != "" { 129 nodeID, err := discover.HexID(confJSON.ID) 130 if err != nil { 131 return err 132 } 133 n.ID = nodeID 134 } 135 136 if confJSON.PrivateKey != "" { 137 key, err := hex.DecodeString(confJSON.PrivateKey) 138 if err != nil { 139 return err 140 } 141 privKey, err := crypto.ToECDSA(key) 142 if err != nil { 143 return err 144 } 145 n.PrivateKey = privKey 146 } 147 148 n.Name = confJSON.Name 149 n.Services = confJSON.Services 150 151 return nil 152 } 153 154 // RandomNodeConfig returns node configuration with a randomly generated ID and 155 // PrivateKey 156 func RandomNodeConfig() *NodeConfig { 157 key, err := crypto.GenerateKey() 158 if err != nil { 159 panic("unable to generate key") 160 } 161 var id discover.NodeID 162 pubkey := crypto.FromECDSAPub(&key.PublicKey) 163 copy(id[:], pubkey[1:]) 164 return &NodeConfig{ 165 ID: id, 166 PrivateKey: key, 167 } 168 } 169 170 // ServiceContext is a collection of options and methods which can be utilised 171 // when starting services 172 type ServiceContext struct { 173 RPCDialer 174 175 NodeContext *node.ServiceContext 176 Config *NodeConfig 177 Snapshot []byte 178 } 179 180 // RPCDialer is used when initialising services which need to connect to 181 // other nodes in the network (for example a simulated Swarm node which needs 182 // to connect to a Geth node to resolve ENS names) 183 type RPCDialer interface { 184 DialRPC(id discover.NodeID) (*rpc.Client, error) 185 } 186 187 // Services is a collection of services which can be run in a simulation 188 type Services map[string]ServiceFunc 189 190 // ServiceFunc returns a node.Service which can be used to boot a devp2p node 191 type ServiceFunc func(ctx *ServiceContext) (node.Service, error) 192 193 // serviceFuncs is a map of registered services which are used to boot devp2p 194 // nodes 195 var serviceFuncs = make(Services) 196 197 // RegisterServices registers the given Services which can then be used to 198 // start devp2p nodes using either the Exec or Docker adapters. 199 // 200 // It should be called in an init function so that it has the opportunity to 201 // execute the services before main() is called. 202 func RegisterServices(services Services) { 203 for name, f := range services { 204 if _, exists := serviceFuncs[name]; exists { 205 panic(fmt.Sprintf("node service already exists: %q", name)) 206 } 207 serviceFuncs[name] = f 208 } 209 210 // now we have registered the services, run reexec.Init() which will 211 // potentially start one of the services if the current binary has 212 // been exec'd with argv[0] set to "p2p-node" 213 if reexec.Init() { 214 os.Exit(0) 215 } 216 }