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