github.com/hardtosaygoodbye/go-ethereum@v1.10.16-0.20220122011429-97003b9e6c15/les/vflux/server/service.go (about) 1 // Copyright 2020 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 server 18 19 import ( 20 "net" 21 "strings" 22 "sync" 23 "time" 24 25 "github.com/hardtosaygoodbye/go-ethereum/les/utils" 26 "github.com/hardtosaygoodbye/go-ethereum/les/vflux" 27 "github.com/hardtosaygoodbye/go-ethereum/log" 28 "github.com/hardtosaygoodbye/go-ethereum/p2p/enode" 29 "github.com/hardtosaygoodbye/go-ethereum/rlp" 30 ) 31 32 type ( 33 // Server serves vflux requests 34 Server struct { 35 limiter *utils.Limiter 36 lock sync.Mutex 37 services map[string]*serviceEntry 38 delayPerRequest time.Duration 39 } 40 41 // Service is a service registered at the Server and identified by a string id 42 Service interface { 43 Handle(id enode.ID, address string, name string, data []byte) []byte // never called concurrently 44 } 45 46 serviceEntry struct { 47 id, desc string 48 backend Service 49 } 50 ) 51 52 // NewServer creates a new Server 53 func NewServer(delayPerRequest time.Duration) *Server { 54 return &Server{ 55 limiter: utils.NewLimiter(1000), 56 delayPerRequest: delayPerRequest, 57 services: make(map[string]*serviceEntry), 58 } 59 } 60 61 // Register registers a Service 62 func (s *Server) Register(b Service, id, desc string) { 63 srv := &serviceEntry{backend: b, id: id, desc: desc} 64 if strings.Contains(srv.id, ":") { 65 // srv.id + ":" will be used as a service database prefix 66 log.Error("Service ID contains ':'", "id", srv.id) 67 return 68 } 69 s.lock.Lock() 70 s.services[srv.id] = srv 71 s.lock.Unlock() 72 } 73 74 // Serve serves a vflux request batch 75 // Note: requests are served by the Handle functions of the registered services. Serve 76 // may be called concurrently but the Handle functions are called sequentially and 77 // therefore thread safety is guaranteed. 78 func (s *Server) Serve(id enode.ID, address string, requests vflux.Requests) vflux.Replies { 79 reqLen := uint(len(requests)) 80 if reqLen == 0 || reqLen > vflux.MaxRequestLength { 81 return nil 82 } 83 // Note: the value parameter will be supplied by the token sale module (total amount paid) 84 ch := <-s.limiter.Add(id, address, 0, reqLen) 85 if ch == nil { 86 return nil 87 } 88 // Note: the limiter ensures that the following section is not running concurrently, 89 // the lock only protects against contention caused by new service registration 90 s.lock.Lock() 91 results := make(vflux.Replies, len(requests)) 92 for i, req := range requests { 93 if service := s.services[req.Service]; service != nil { 94 results[i] = service.backend.Handle(id, address, req.Name, req.Params) 95 } 96 } 97 s.lock.Unlock() 98 time.Sleep(s.delayPerRequest * time.Duration(reqLen)) 99 close(ch) 100 return results 101 } 102 103 // ServeEncoded serves an encoded vflux request batch and returns the encoded replies 104 func (s *Server) ServeEncoded(id enode.ID, addr *net.UDPAddr, req []byte) []byte { 105 var requests vflux.Requests 106 if err := rlp.DecodeBytes(req, &requests); err != nil { 107 return nil 108 } 109 results := s.Serve(id, addr.String(), requests) 110 if results == nil { 111 return nil 112 } 113 res, _ := rlp.EncodeToBytes(&results) 114 return res 115 } 116 117 // Stop shuts down the server 118 func (s *Server) Stop() { 119 s.limiter.Stop() 120 }