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