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