github.com/ethereum-optimism/optimism/l2geth@v0.0.0-20230612200230-50b04ade19e3/p2p/simulations/adapters/inproc.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 "errors" 21 "fmt" 22 "math" 23 "net" 24 "sync" 25 26 "github.com/ethereum-optimism/optimism/l2geth/event" 27 "github.com/ethereum-optimism/optimism/l2geth/log" 28 "github.com/ethereum-optimism/optimism/l2geth/node" 29 "github.com/ethereum-optimism/optimism/l2geth/p2p" 30 "github.com/ethereum-optimism/optimism/l2geth/p2p/enode" 31 "github.com/ethereum-optimism/optimism/l2geth/p2p/simulations/pipes" 32 "github.com/ethereum-optimism/optimism/l2geth/rpc" 33 "github.com/gorilla/websocket" 34 ) 35 36 // SimAdapter is a NodeAdapter which creates in-memory simulation nodes and 37 // connects them using net.Pipe 38 type SimAdapter struct { 39 pipe func() (net.Conn, net.Conn, error) 40 mtx sync.RWMutex 41 nodes map[enode.ID]*SimNode 42 services map[string]ServiceFunc 43 } 44 45 // NewSimAdapter creates a SimAdapter which is capable of running in-memory 46 // simulation nodes running any of the given services (the services to run on a 47 // particular node are passed to the NewNode function in the NodeConfig) 48 // the adapter uses a net.Pipe for in-memory simulated network connections 49 func NewSimAdapter(services map[string]ServiceFunc) *SimAdapter { 50 return &SimAdapter{ 51 pipe: pipes.NetPipe, 52 nodes: make(map[enode.ID]*SimNode), 53 services: services, 54 } 55 } 56 57 func NewTCPAdapter(services map[string]ServiceFunc) *SimAdapter { 58 return &SimAdapter{ 59 pipe: pipes.TCPPipe, 60 nodes: make(map[enode.ID]*SimNode), 61 services: services, 62 } 63 } 64 65 // Name returns the name of the adapter for logging purposes 66 func (s *SimAdapter) Name() string { 67 return "sim-adapter" 68 } 69 70 // NewNode returns a new SimNode using the given config 71 func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) { 72 s.mtx.Lock() 73 defer s.mtx.Unlock() 74 75 id := config.ID 76 // verify that the node has a private key in the config 77 if config.PrivateKey == nil { 78 return nil, fmt.Errorf("node is missing private key: %s", id) 79 } 80 81 // check a node with the ID doesn't already exist 82 if _, exists := s.nodes[id]; exists { 83 return nil, fmt.Errorf("node already exists: %s", id) 84 } 85 86 // check the services are valid 87 if len(config.Services) == 0 { 88 return nil, errors.New("node must have at least one service") 89 } 90 for _, service := range config.Services { 91 if _, exists := s.services[service]; !exists { 92 return nil, fmt.Errorf("unknown node service %q", service) 93 } 94 } 95 96 err := config.initDummyEnode() 97 if err != nil { 98 return nil, err 99 } 100 101 n, err := node.New(&node.Config{ 102 P2P: p2p.Config{ 103 PrivateKey: config.PrivateKey, 104 MaxPeers: math.MaxInt32, 105 NoDiscovery: true, 106 Dialer: s, 107 EnableMsgEvents: config.EnableMsgEvents, 108 }, 109 NoUSB: true, 110 Logger: log.New("node.id", id.String()), 111 }) 112 if err != nil { 113 return nil, err 114 } 115 116 simNode := &SimNode{ 117 ID: id, 118 config: config, 119 node: n, 120 adapter: s, 121 running: make(map[string]node.Service), 122 } 123 s.nodes[id] = simNode 124 return simNode, nil 125 } 126 127 // Dial implements the p2p.NodeDialer interface by connecting to the node using 128 // an in-memory net.Pipe 129 func (s *SimAdapter) Dial(dest *enode.Node) (conn net.Conn, err error) { 130 node, ok := s.GetNode(dest.ID()) 131 if !ok { 132 return nil, fmt.Errorf("unknown node: %s", dest.ID()) 133 } 134 srv := node.Server() 135 if srv == nil { 136 return nil, fmt.Errorf("node not running: %s", dest.ID()) 137 } 138 // SimAdapter.pipe is net.Pipe (NewSimAdapter) 139 pipe1, pipe2, err := s.pipe() 140 if err != nil { 141 return nil, err 142 } 143 // this is simulated 'listening' 144 // asynchronously call the dialed destination node's p2p server 145 // to set up connection on the 'listening' side 146 go srv.SetupConn(pipe1, 0, nil) 147 return pipe2, nil 148 } 149 150 // DialRPC implements the RPCDialer interface by creating an in-memory RPC 151 // client of the given node 152 func (s *SimAdapter) DialRPC(id enode.ID) (*rpc.Client, error) { 153 node, ok := s.GetNode(id) 154 if !ok { 155 return nil, fmt.Errorf("unknown node: %s", id) 156 } 157 handler, err := node.node.RPCHandler() 158 if err != nil { 159 return nil, err 160 } 161 return rpc.DialInProc(handler), nil 162 } 163 164 // GetNode returns the node with the given ID if it exists 165 func (s *SimAdapter) GetNode(id enode.ID) (*SimNode, bool) { 166 s.mtx.RLock() 167 defer s.mtx.RUnlock() 168 node, ok := s.nodes[id] 169 return node, ok 170 } 171 172 // SimNode is an in-memory simulation node which connects to other nodes using 173 // net.Pipe (see SimAdapter.Dial), running devp2p protocols directly over that 174 // pipe 175 type SimNode struct { 176 lock sync.RWMutex 177 ID enode.ID 178 config *NodeConfig 179 adapter *SimAdapter 180 node *node.Node 181 running map[string]node.Service 182 client *rpc.Client 183 registerOnce sync.Once 184 } 185 186 // Close closes the underlaying node.Node to release 187 // acquired resources. 188 func (sn *SimNode) Close() error { 189 return sn.node.Close() 190 } 191 192 // Addr returns the node's discovery address 193 func (sn *SimNode) Addr() []byte { 194 return []byte(sn.Node().String()) 195 } 196 197 // Node returns a node descriptor representing the SimNode 198 func (sn *SimNode) Node() *enode.Node { 199 return sn.config.Node() 200 } 201 202 // Client returns an rpc.Client which can be used to communicate with the 203 // underlying services (it is set once the node has started) 204 func (sn *SimNode) Client() (*rpc.Client, error) { 205 sn.lock.RLock() 206 defer sn.lock.RUnlock() 207 if sn.client == nil { 208 return nil, errors.New("node not started") 209 } 210 return sn.client, nil 211 } 212 213 // ServeRPC serves RPC requests over the given connection by creating an 214 // in-memory client to the node's RPC server. 215 func (sn *SimNode) ServeRPC(conn *websocket.Conn) error { 216 handler, err := sn.node.RPCHandler() 217 if err != nil { 218 return err 219 } 220 codec := rpc.NewFuncCodec(conn, conn.WriteJSON, conn.ReadJSON) 221 handler.ServeCodec(codec, 0) 222 return nil 223 } 224 225 // Snapshots creates snapshots of the services by calling the 226 // simulation_snapshot RPC method 227 func (sn *SimNode) Snapshots() (map[string][]byte, error) { 228 sn.lock.RLock() 229 services := make(map[string]node.Service, len(sn.running)) 230 for name, service := range sn.running { 231 services[name] = service 232 } 233 sn.lock.RUnlock() 234 if len(services) == 0 { 235 return nil, errors.New("no running services") 236 } 237 snapshots := make(map[string][]byte) 238 for name, service := range services { 239 if s, ok := service.(interface { 240 Snapshot() ([]byte, error) 241 }); ok { 242 snap, err := s.Snapshot() 243 if err != nil { 244 return nil, err 245 } 246 snapshots[name] = snap 247 } 248 } 249 return snapshots, nil 250 } 251 252 // Start registers the services and starts the underlying devp2p node 253 func (sn *SimNode) Start(snapshots map[string][]byte) error { 254 newService := func(name string) func(ctx *node.ServiceContext) (node.Service, error) { 255 return func(nodeCtx *node.ServiceContext) (node.Service, error) { 256 ctx := &ServiceContext{ 257 RPCDialer: sn.adapter, 258 NodeContext: nodeCtx, 259 Config: sn.config, 260 } 261 if snapshots != nil { 262 ctx.Snapshot = snapshots[name] 263 } 264 serviceFunc := sn.adapter.services[name] 265 service, err := serviceFunc(ctx) 266 if err != nil { 267 return nil, err 268 } 269 sn.running[name] = service 270 return service, nil 271 } 272 } 273 274 // ensure we only register the services once in the case of the node 275 // being stopped and then started again 276 var regErr error 277 sn.registerOnce.Do(func() { 278 for _, name := range sn.config.Services { 279 if err := sn.node.Register(newService(name)); err != nil { 280 regErr = err 281 break 282 } 283 } 284 }) 285 if regErr != nil { 286 return regErr 287 } 288 289 if err := sn.node.Start(); err != nil { 290 return err 291 } 292 293 // create an in-process RPC client 294 handler, err := sn.node.RPCHandler() 295 if err != nil { 296 return err 297 } 298 299 sn.lock.Lock() 300 sn.client = rpc.DialInProc(handler) 301 sn.lock.Unlock() 302 303 return nil 304 } 305 306 // Stop closes the RPC client and stops the underlying devp2p node 307 func (sn *SimNode) Stop() error { 308 sn.lock.Lock() 309 if sn.client != nil { 310 sn.client.Close() 311 sn.client = nil 312 } 313 sn.lock.Unlock() 314 return sn.node.Stop() 315 } 316 317 // Service returns a running service by name 318 func (sn *SimNode) Service(name string) node.Service { 319 sn.lock.RLock() 320 defer sn.lock.RUnlock() 321 return sn.running[name] 322 } 323 324 // Services returns a copy of the underlying services 325 func (sn *SimNode) Services() []node.Service { 326 sn.lock.RLock() 327 defer sn.lock.RUnlock() 328 services := make([]node.Service, 0, len(sn.running)) 329 for _, service := range sn.running { 330 services = append(services, service) 331 } 332 return services 333 } 334 335 // ServiceMap returns a map by names of the underlying services 336 func (sn *SimNode) ServiceMap() map[string]node.Service { 337 sn.lock.RLock() 338 defer sn.lock.RUnlock() 339 services := make(map[string]node.Service, len(sn.running)) 340 for name, service := range sn.running { 341 services[name] = service 342 } 343 return services 344 } 345 346 // Server returns the underlying p2p.Server 347 func (sn *SimNode) Server() *p2p.Server { 348 return sn.node.Server() 349 } 350 351 // SubscribeEvents subscribes the given channel to peer events from the 352 // underlying p2p.Server 353 func (sn *SimNode) SubscribeEvents(ch chan *p2p.PeerEvent) event.Subscription { 354 srv := sn.Server() 355 if srv == nil { 356 panic("node not running") 357 } 358 return srv.SubscribeEvents(ch) 359 } 360 361 // NodeInfo returns information about the node 362 func (sn *SimNode) NodeInfo() *p2p.NodeInfo { 363 server := sn.Server() 364 if server == nil { 365 return &p2p.NodeInfo{ 366 ID: sn.ID.String(), 367 Enode: sn.Node().String(), 368 } 369 } 370 return server.NodeInfo() 371 }