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