github.com/theQRL/go-zond@v0.1.1/les/api.go (about) 1 // Copyright 2019 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 les 18 19 import ( 20 "errors" 21 "fmt" 22 "time" 23 24 "github.com/theQRL/go-zond/common/mclock" 25 vfs "github.com/theQRL/go-zond/les/vflux/server" 26 "github.com/theQRL/go-zond/p2p/enode" 27 ) 28 29 var errUnknownBenchmarkType = errors.New("unknown benchmark type") 30 31 // LightServerAPI provides an API to access the LES light server. 32 type LightServerAPI struct { 33 server *LesServer 34 defaultPosFactors, defaultNegFactors vfs.PriceFactors 35 } 36 37 // NewLightServerAPI creates a new LES light server API. 38 func NewLightServerAPI(server *LesServer) *LightServerAPI { 39 return &LightServerAPI{ 40 server: server, 41 defaultPosFactors: defaultPosFactors, 42 defaultNegFactors: defaultNegFactors, 43 } 44 } 45 46 // parseNode parses either an enode address a raw hex node id 47 func parseNode(node string) (enode.ID, error) { 48 if id, err := enode.ParseID(node); err == nil { 49 return id, nil 50 } 51 if node, err := enode.Parse(enode.ValidSchemes, node); err == nil { 52 return node.ID(), nil 53 } else { 54 return enode.ID{}, err 55 } 56 } 57 58 // ServerInfo returns global server parameters 59 func (api *LightServerAPI) ServerInfo() map[string]interface{} { 60 res := make(map[string]interface{}) 61 res["minimumCapacity"] = api.server.minCapacity 62 res["maximumCapacity"] = api.server.maxCapacity 63 _, res["totalCapacity"] = api.server.clientPool.Limits() 64 _, res["totalConnectedCapacity"] = api.server.clientPool.Active() 65 res["priorityConnectedCapacity"] = 0 //TODO connect when token sale module is added 66 return res 67 } 68 69 // ClientInfo returns information about clients listed in the ids list or matching the given tags 70 func (api *LightServerAPI) ClientInfo(nodes []string) map[enode.ID]map[string]interface{} { 71 var ids []enode.ID 72 for _, node := range nodes { 73 if id, err := parseNode(node); err == nil { 74 ids = append(ids, id) 75 } 76 } 77 78 res := make(map[enode.ID]map[string]interface{}) 79 if len(ids) == 0 { 80 ids = api.server.peers.ids() 81 } 82 for _, id := range ids { 83 if peer := api.server.peers.peer(id); peer != nil { 84 res[id] = api.clientInfo(peer, peer.balance) 85 } else { 86 api.server.clientPool.BalanceOperation(id, "", func(balance vfs.AtomicBalanceOperator) { 87 res[id] = api.clientInfo(nil, balance) 88 }) 89 } 90 } 91 return res 92 } 93 94 // PriorityClientInfo returns information about clients with a positive balance 95 // in the given ID range (stop excluded). If stop is null then the iterator stops 96 // only at the end of the ID space. MaxCount limits the number of results returned. 97 // If maxCount limit is applied but there are more potential results then the ID 98 // of the next potential result is included in the map with an empty structure 99 // assigned to it. 100 func (api *LightServerAPI) PriorityClientInfo(start, stop enode.ID, maxCount int) map[enode.ID]map[string]interface{} { 101 res := make(map[enode.ID]map[string]interface{}) 102 ids := api.server.clientPool.GetPosBalanceIDs(start, stop, maxCount+1) 103 if len(ids) > maxCount { 104 res[ids[maxCount]] = make(map[string]interface{}) 105 ids = ids[:maxCount] 106 } 107 for _, id := range ids { 108 if peer := api.server.peers.peer(id); peer != nil { 109 res[id] = api.clientInfo(peer, peer.balance) 110 } else { 111 api.server.clientPool.BalanceOperation(id, "", func(balance vfs.AtomicBalanceOperator) { 112 res[id] = api.clientInfo(nil, balance) 113 }) 114 } 115 } 116 return res 117 } 118 119 // clientInfo creates a client info data structure 120 func (api *LightServerAPI) clientInfo(peer *clientPeer, balance vfs.ReadOnlyBalance) map[string]interface{} { 121 info := make(map[string]interface{}) 122 pb, nb := balance.GetBalance() 123 info["isConnected"] = peer != nil 124 info["pricing/balance"] = pb 125 info["priority"] = pb != 0 126 // cb := api.server.clientPool.ndb.getCurrencyBalance(id) 127 // info["pricing/currency"] = cb.amount 128 if peer != nil { 129 info["connectionTime"] = float64(mclock.Now()-peer.connectedAt) / float64(time.Second) 130 info["capacity"] = peer.getCapacity() 131 info["pricing/negBalance"] = nb 132 } 133 return info 134 } 135 136 // setParams either sets the given parameters for a single connected client (if specified) 137 // or the default parameters applicable to clients connected in the future 138 func (api *LightServerAPI) setParams(params map[string]interface{}, client *clientPeer, posFactors, negFactors *vfs.PriceFactors) (updateFactors bool, err error) { 139 defParams := client == nil 140 for name, value := range params { 141 errValue := func() error { 142 return fmt.Errorf("invalid value for parameter '%s'", name) 143 } 144 setFactor := func(v *float64) { 145 if val, ok := value.(float64); ok && val >= 0 { 146 *v = val / float64(time.Second) 147 updateFactors = true 148 } else { 149 err = errValue() 150 } 151 } 152 153 switch { 154 case name == "pricing/timeFactor": 155 setFactor(&posFactors.TimeFactor) 156 case name == "pricing/capacityFactor": 157 setFactor(&posFactors.CapacityFactor) 158 case name == "pricing/requestCostFactor": 159 setFactor(&posFactors.RequestFactor) 160 case name == "pricing/negative/timeFactor": 161 setFactor(&negFactors.TimeFactor) 162 case name == "pricing/negative/capacityFactor": 163 setFactor(&negFactors.CapacityFactor) 164 case name == "pricing/negative/requestCostFactor": 165 setFactor(&negFactors.RequestFactor) 166 case !defParams && name == "capacity": 167 if capacity, ok := value.(float64); ok && uint64(capacity) >= api.server.minCapacity { 168 _, err = api.server.clientPool.SetCapacity(client.Node(), uint64(capacity), 0, false) 169 // time factor recalculation is performed automatically by the balance tracker 170 } else { 171 err = errValue() 172 } 173 default: 174 if defParams { 175 err = fmt.Errorf("invalid default parameter '%s'", name) 176 } else { 177 err = fmt.Errorf("invalid client parameter '%s'", name) 178 } 179 } 180 if err != nil { 181 return 182 } 183 } 184 return 185 } 186 187 // SetClientParams sets client parameters for all clients listed in the ids list 188 // or all connected clients if the list is empty 189 func (api *LightServerAPI) SetClientParams(nodes []string, params map[string]interface{}) error { 190 var err error 191 for _, node := range nodes { 192 var id enode.ID 193 if id, err = parseNode(node); err != nil { 194 return err 195 } 196 if peer := api.server.peers.peer(id); peer != nil { 197 posFactors, negFactors := peer.balance.GetPriceFactors() 198 update, e := api.setParams(params, peer, &posFactors, &negFactors) 199 if update { 200 peer.balance.SetPriceFactors(posFactors, negFactors) 201 } 202 if e != nil { 203 err = e 204 } 205 } else { 206 err = fmt.Errorf("client %064x is not connected", id) 207 } 208 } 209 return err 210 } 211 212 // SetDefaultParams sets the default parameters applicable to clients connected in the future 213 func (api *LightServerAPI) SetDefaultParams(params map[string]interface{}) error { 214 update, err := api.setParams(params, nil, &api.defaultPosFactors, &api.defaultNegFactors) 215 if update { 216 api.server.clientPool.SetDefaultFactors(api.defaultPosFactors, api.defaultNegFactors) 217 } 218 return err 219 } 220 221 // SetConnectedBias set the connection bias, which is applied to already connected clients 222 // So that already connected client won't be kicked out very soon and we can ensure all 223 // connected clients can have enough time to request or sync some data. 224 // When the input parameter `bias` < 0 (illegal), return error. 225 func (api *LightServerAPI) SetConnectedBias(bias time.Duration) error { 226 if bias < time.Duration(0) { 227 return fmt.Errorf("bias illegal: %v less than 0", bias) 228 } 229 api.server.clientPool.SetConnectedBias(bias) 230 return nil 231 } 232 233 // AddBalance adds the given amount to the balance of a client if possible and returns 234 // the balance before and after the operation 235 func (api *LightServerAPI) AddBalance(node string, amount int64) (balance [2]uint64, err error) { 236 var id enode.ID 237 if id, err = parseNode(node); err != nil { 238 return 239 } 240 api.server.clientPool.BalanceOperation(id, "", func(nb vfs.AtomicBalanceOperator) { 241 balance[0], balance[1], err = nb.AddBalance(amount) 242 }) 243 return 244 } 245 246 // Benchmark runs a request performance benchmark with a given set of measurement setups 247 // in multiple passes specified by passCount. The measurement time for each setup in each 248 // pass is specified in milliseconds by length. 249 // 250 // Note: measurement time is adjusted for each pass depending on the previous ones. 251 // Therefore a controlled total measurement time is achievable in multiple passes. 252 func (api *LightServerAPI) Benchmark(setups []map[string]interface{}, passCount, length int) ([]map[string]interface{}, error) { 253 benchmarks := make([]requestBenchmark, len(setups)) 254 for i, setup := range setups { 255 if t, ok := setup["type"].(string); ok { 256 getInt := func(field string, def int) int { 257 if value, ok := setup[field].(float64); ok { 258 return int(value) 259 } 260 return def 261 } 262 getBool := func(field string, def bool) bool { 263 if value, ok := setup[field].(bool); ok { 264 return value 265 } 266 return def 267 } 268 switch t { 269 case "header": 270 benchmarks[i] = &benchmarkBlockHeaders{ 271 amount: getInt("amount", 1), 272 skip: getInt("skip", 1), 273 byHash: getBool("byHash", false), 274 reverse: getBool("reverse", false), 275 } 276 case "body": 277 benchmarks[i] = &benchmarkBodiesOrReceipts{receipts: false} 278 case "receipts": 279 benchmarks[i] = &benchmarkBodiesOrReceipts{receipts: true} 280 case "proof": 281 benchmarks[i] = &benchmarkProofsOrCode{code: false} 282 case "code": 283 benchmarks[i] = &benchmarkProofsOrCode{code: true} 284 case "cht": 285 benchmarks[i] = &benchmarkHelperTrie{ 286 bloom: false, 287 reqCount: getInt("amount", 1), 288 } 289 case "bloom": 290 benchmarks[i] = &benchmarkHelperTrie{ 291 bloom: true, 292 reqCount: getInt("amount", 1), 293 } 294 case "txSend": 295 benchmarks[i] = &benchmarkTxSend{} 296 case "txStatus": 297 benchmarks[i] = &benchmarkTxStatus{} 298 default: 299 return nil, errUnknownBenchmarkType 300 } 301 } else { 302 return nil, errUnknownBenchmarkType 303 } 304 } 305 rs := api.server.handler.runBenchmark(benchmarks, passCount, time.Millisecond*time.Duration(length)) 306 result := make([]map[string]interface{}, len(setups)) 307 for i, r := range rs { 308 res := make(map[string]interface{}) 309 if r.err == nil { 310 res["totalCount"] = r.totalCount 311 res["avgTime"] = r.avgTime 312 res["maxInSize"] = r.maxInSize 313 res["maxOutSize"] = r.maxOutSize 314 } else { 315 res["error"] = r.err.Error() 316 } 317 result[i] = res 318 } 319 return result, nil 320 } 321 322 // DebugAPI provides an API to debug LES light server functionality. 323 type DebugAPI struct { 324 server *LesServer 325 } 326 327 // NewDebugAPI creates a new LES light server debug API. 328 func NewDebugAPI(server *LesServer) *DebugAPI { 329 return &DebugAPI{ 330 server: server, 331 } 332 } 333 334 // FreezeClient forces a temporary client freeze which normally happens when the server is overloaded 335 func (api *DebugAPI) FreezeClient(node string) error { 336 var ( 337 id enode.ID 338 err error 339 ) 340 if id, err = parseNode(node); err != nil { 341 return err 342 } 343 if peer := api.server.peers.peer(id); peer != nil { 344 peer.freeze() 345 return nil 346 } else { 347 return fmt.Errorf("client %064x is not connected", id[:]) 348 } 349 }