github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/p2p/simulations/adapters/types.go (about) 1 // Copyright 2017 The Spectrum Authors 2 // This file is part of the Spectrum library. 3 // 4 // The Spectrum 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 Spectrum 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 Spectrum 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 27 "github.com/SmartMeshFoundation/Spectrum/crypto" 28 "github.com/SmartMeshFoundation/Spectrum/node" 29 "github.com/SmartMeshFoundation/Spectrum/p2p" 30 "github.com/SmartMeshFoundation/Spectrum/p2p/discover" 31 "github.com/SmartMeshFoundation/Spectrum/rpc" 32 "github.com/docker/docker/pkg/reexec" 33 ) 34 35 // Node represents a node in a simulation network which is created by a 36 // NodeAdapter, for example: 37 // 38 // * SimNode - An in-memory node 39 // * ExecNode - A child process node 40 // * DockerNode - A Docker container node 41 // 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 102 // nodeConfigJSON is used to encode and decode NodeConfig as JSON by encoding 103 // all fields as strings 104 type nodeConfigJSON struct { 105 ID string `json:"id"` 106 PrivateKey string `json:"private_key"` 107 Name string `json:"name"` 108 Services []string `json:"services"` 109 } 110 111 // MarshalJSON implements the json.Marshaler interface by encoding the config 112 // fields as strings 113 func (n *NodeConfig) MarshalJSON() ([]byte, error) { 114 confJSON := nodeConfigJSON{ 115 ID: n.ID.String(), 116 Name: n.Name, 117 Services: n.Services, 118 } 119 if n.PrivateKey != nil { 120 confJSON.PrivateKey = hex.EncodeToString(crypto.FromECDSA(n.PrivateKey)) 121 } 122 return json.Marshal(confJSON) 123 } 124 125 // UnmarshalJSON implements the json.Unmarshaler interface by decoding the json 126 // string values into the config fields 127 func (n *NodeConfig) UnmarshalJSON(data []byte) error { 128 var confJSON nodeConfigJSON 129 if err := json.Unmarshal(data, &confJSON); err != nil { 130 return err 131 } 132 133 if confJSON.ID != "" { 134 nodeID, err := discover.HexID(confJSON.ID) 135 if err != nil { 136 return err 137 } 138 n.ID = nodeID 139 } 140 141 if confJSON.PrivateKey != "" { 142 key, err := hex.DecodeString(confJSON.PrivateKey) 143 if err != nil { 144 return err 145 } 146 privKey, err := crypto.ToECDSA(key) 147 if err != nil { 148 return err 149 } 150 n.PrivateKey = privKey 151 } 152 153 n.Name = confJSON.Name 154 n.Services = confJSON.Services 155 156 return nil 157 } 158 159 // RandomNodeConfig returns node configuration with a randomly generated ID and 160 // PrivateKey 161 func RandomNodeConfig() *NodeConfig { 162 key, err := crypto.GenerateKey() 163 if err != nil { 164 panic("unable to generate key") 165 } 166 var id discover.NodeID 167 pubkey := crypto.FromECDSAPub(&key.PublicKey) 168 copy(id[:], pubkey[1:]) 169 return &NodeConfig{ 170 ID: id, 171 PrivateKey: key, 172 } 173 } 174 175 // ServiceContext is a collection of options and methods which can be utilised 176 // when starting services 177 type ServiceContext struct { 178 RPCDialer 179 180 NodeContext *node.ServiceContext 181 Config *NodeConfig 182 Snapshot []byte 183 } 184 185 // RPCDialer is used when initialising services which need to connect to 186 // other nodes in the network (for example a simulated Swarm node which needs 187 // to connect to a Geth node to resolve ENS names) 188 type RPCDialer interface { 189 DialRPC(id discover.NodeID) (*rpc.Client, error) 190 } 191 192 // Services is a collection of services which can be run in a simulation 193 type Services map[string]ServiceFunc 194 195 // ServiceFunc returns a node.Service which can be used to boot a devp2p node 196 type ServiceFunc func(ctx *ServiceContext) (node.Service, error) 197 198 // serviceFuncs is a map of registered services which are used to boot devp2p 199 // nodes 200 var serviceFuncs = make(Services) 201 202 // RegisterServices registers the given Services which can then be used to 203 // start devp2p nodes using either the Exec or Docker adapters. 204 // 205 // It should be called in an init function so that it has the opportunity to 206 // execute the services before main() is called. 207 func RegisterServices(services Services) { 208 for name, f := range services { 209 if _, exists := serviceFuncs[name]; exists { 210 panic(fmt.Sprintf("node service already exists: %q", name)) 211 } 212 serviceFuncs[name] = f 213 } 214 215 // now we have registered the services, run reexec.Init() which will 216 // potentially start one of the services if the current binary has 217 // been exec'd with argv[0] set to "p2p-node" 218 if reexec.Init() { 219 os.Exit(0) 220 } 221 }