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