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