api.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  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. package les
  17. import (
  18. "errors"
  19. "fmt"
  20. "math"
  21. "time"
  22. "github.com/ethereum/go-ethereum/common/hexutil"
  23. "github.com/ethereum/go-ethereum/common/mclock"
  24. "github.com/ethereum/go-ethereum/p2p/enode"
  25. )
  26. var (
  27. errNoCheckpoint = errors.New("no local checkpoint provided")
  28. errNotActivated = errors.New("checkpoint registrar is not activated")
  29. errUnknownBenchmarkType = errors.New("unknown benchmark type")
  30. errBalanceOverflow = errors.New("balance overflow")
  31. errNoPriority = errors.New("priority too low to raise capacity")
  32. )
  33. const maxBalance = math.MaxInt64
  34. // PrivateLightServerAPI provides an API to access the LES light server.
  35. type PrivateLightServerAPI struct {
  36. server *LesServer
  37. defaultPosFactors, defaultNegFactors priceFactors
  38. }
  39. // NewPrivateLightServerAPI creates a new LES light server API.
  40. func NewPrivateLightServerAPI(server *LesServer) *PrivateLightServerAPI {
  41. return &PrivateLightServerAPI{
  42. server: server,
  43. defaultPosFactors: server.clientPool.defaultPosFactors,
  44. defaultNegFactors: server.clientPool.defaultNegFactors,
  45. }
  46. }
  47. // ServerInfo returns global server parameters
  48. func (api *PrivateLightServerAPI) ServerInfo() map[string]interface{} {
  49. res := make(map[string]interface{})
  50. res["minimumCapacity"] = api.server.minCapacity
  51. res["maximumCapacity"] = api.server.maxCapacity
  52. res["freeClientCapacity"] = api.server.freeCapacity
  53. res["totalCapacity"], res["totalConnectedCapacity"], res["priorityConnectedCapacity"] = api.server.clientPool.capacityInfo()
  54. return res
  55. }
  56. // ClientInfo returns information about clients listed in the ids list or matching the given tags
  57. func (api *PrivateLightServerAPI) ClientInfo(ids []enode.ID) map[enode.ID]map[string]interface{} {
  58. res := make(map[enode.ID]map[string]interface{})
  59. api.server.clientPool.forClients(ids, func(client *clientInfo, id enode.ID) error {
  60. res[id] = api.clientInfo(client, id)
  61. return nil
  62. })
  63. return res
  64. }
  65. // PriorityClientInfo returns information about clients with a positive balance
  66. // in the given ID range (stop excluded). If stop is null then the iterator stops
  67. // only at the end of the ID space. MaxCount limits the number of results returned.
  68. // If maxCount limit is applied but there are more potential results then the ID
  69. // of the next potential result is included in the map with an empty structure
  70. // assigned to it.
  71. func (api *PrivateLightServerAPI) PriorityClientInfo(start, stop enode.ID, maxCount int) map[enode.ID]map[string]interface{} {
  72. res := make(map[enode.ID]map[string]interface{})
  73. ids := api.server.clientPool.ndb.getPosBalanceIDs(start, stop, maxCount+1)
  74. if len(ids) > maxCount {
  75. res[ids[maxCount]] = make(map[string]interface{})
  76. ids = ids[:maxCount]
  77. }
  78. if len(ids) != 0 {
  79. api.server.clientPool.forClients(ids, func(client *clientInfo, id enode.ID) error {
  80. res[id] = api.clientInfo(client, id)
  81. return nil
  82. })
  83. }
  84. return res
  85. }
  86. // clientInfo creates a client info data structure
  87. func (api *PrivateLightServerAPI) clientInfo(c *clientInfo, id enode.ID) map[string]interface{} {
  88. info := make(map[string]interface{})
  89. if c != nil {
  90. now := mclock.Now()
  91. info["isConnected"] = true
  92. info["connectionTime"] = float64(now-c.connectedAt) / float64(time.Second)
  93. info["capacity"] = c.capacity
  94. pb, nb := c.balanceTracker.getBalance(now)
  95. info["pricing/balance"], info["pricing/negBalance"] = pb, nb
  96. info["pricing/balanceMeta"] = c.balanceMetaInfo
  97. info["priority"] = pb != 0
  98. } else {
  99. info["isConnected"] = false
  100. pb := api.server.clientPool.ndb.getOrNewPB(id)
  101. info["pricing/balance"], info["pricing/balanceMeta"] = pb.value, pb.meta
  102. info["priority"] = pb.value != 0
  103. }
  104. return info
  105. }
  106. // setParams either sets the given parameters for a single connected client (if specified)
  107. // or the default parameters applicable to clients connected in the future
  108. func (api *PrivateLightServerAPI) setParams(params map[string]interface{}, client *clientInfo, posFactors, negFactors *priceFactors) (updateFactors bool, err error) {
  109. defParams := client == nil
  110. if !defParams {
  111. posFactors, negFactors = &client.posFactors, &client.negFactors
  112. }
  113. for name, value := range params {
  114. errValue := func() error {
  115. return fmt.Errorf("invalid value for parameter '%s'", name)
  116. }
  117. setFactor := func(v *float64) {
  118. if val, ok := value.(float64); ok && val >= 0 {
  119. *v = val / float64(time.Second)
  120. updateFactors = true
  121. } else {
  122. err = errValue()
  123. }
  124. }
  125. switch {
  126. case name == "pricing/timeFactor":
  127. setFactor(&posFactors.timeFactor)
  128. case name == "pricing/capacityFactor":
  129. setFactor(&posFactors.capacityFactor)
  130. case name == "pricing/requestCostFactor":
  131. setFactor(&posFactors.requestFactor)
  132. case name == "pricing/negative/timeFactor":
  133. setFactor(&negFactors.timeFactor)
  134. case name == "pricing/negative/capacityFactor":
  135. setFactor(&negFactors.capacityFactor)
  136. case name == "pricing/negative/requestCostFactor":
  137. setFactor(&negFactors.requestFactor)
  138. case !defParams && name == "capacity":
  139. if capacity, ok := value.(float64); ok && uint64(capacity) >= api.server.minCapacity {
  140. err = api.server.clientPool.setCapacity(client, uint64(capacity))
  141. // Don't have to call factor update explicitly. It's already done
  142. // in setCapacity function.
  143. } else {
  144. err = errValue()
  145. }
  146. default:
  147. if defParams {
  148. err = fmt.Errorf("invalid default parameter '%s'", name)
  149. } else {
  150. err = fmt.Errorf("invalid client parameter '%s'", name)
  151. }
  152. }
  153. if err != nil {
  154. return
  155. }
  156. }
  157. return
  158. }
  159. // AddBalance updates the balance of a client (either overwrites it or adds to it).
  160. // It also updates the balance meta info string.
  161. func (api *PrivateLightServerAPI) AddBalance(id enode.ID, value int64, meta string) ([2]uint64, error) {
  162. oldBalance, newBalance, err := api.server.clientPool.addBalance(id, value, meta)
  163. return [2]uint64{oldBalance, newBalance}, err
  164. }
  165. // SetClientParams sets client parameters for all clients listed in the ids list
  166. // or all connected clients if the list is empty
  167. func (api *PrivateLightServerAPI) SetClientParams(ids []enode.ID, params map[string]interface{}) error {
  168. return api.server.clientPool.forClients(ids, func(client *clientInfo, id enode.ID) error {
  169. if client != nil {
  170. update, err := api.setParams(params, client, nil, nil)
  171. if update {
  172. client.updatePriceFactors()
  173. }
  174. return err
  175. } else {
  176. return fmt.Errorf("client %064x is not connected", id[:])
  177. }
  178. })
  179. }
  180. // SetDefaultParams sets the default parameters applicable to clients connected in the future
  181. func (api *PrivateLightServerAPI) SetDefaultParams(params map[string]interface{}) error {
  182. update, err := api.setParams(params, nil, &api.defaultPosFactors, &api.defaultNegFactors)
  183. if update {
  184. api.server.clientPool.setDefaultFactors(api.defaultPosFactors, api.defaultNegFactors)
  185. }
  186. return err
  187. }
  188. // Benchmark runs a request performance benchmark with a given set of measurement setups
  189. // in multiple passes specified by passCount. The measurement time for each setup in each
  190. // pass is specified in milliseconds by length.
  191. //
  192. // Note: measurement time is adjusted for each pass depending on the previous ones.
  193. // Therefore a controlled total measurement time is achievable in multiple passes.
  194. func (api *PrivateLightServerAPI) Benchmark(setups []map[string]interface{}, passCount, length int) ([]map[string]interface{}, error) {
  195. benchmarks := make([]requestBenchmark, len(setups))
  196. for i, setup := range setups {
  197. if t, ok := setup["type"].(string); ok {
  198. getInt := func(field string, def int) int {
  199. if value, ok := setup[field].(float64); ok {
  200. return int(value)
  201. }
  202. return def
  203. }
  204. getBool := func(field string, def bool) bool {
  205. if value, ok := setup[field].(bool); ok {
  206. return value
  207. }
  208. return def
  209. }
  210. switch t {
  211. case "header":
  212. benchmarks[i] = &benchmarkBlockHeaders{
  213. amount: getInt("amount", 1),
  214. skip: getInt("skip", 1),
  215. byHash: getBool("byHash", false),
  216. reverse: getBool("reverse", false),
  217. }
  218. case "body":
  219. benchmarks[i] = &benchmarkBodiesOrReceipts{receipts: false}
  220. case "receipts":
  221. benchmarks[i] = &benchmarkBodiesOrReceipts{receipts: true}
  222. case "proof":
  223. benchmarks[i] = &benchmarkProofsOrCode{code: false}
  224. case "code":
  225. benchmarks[i] = &benchmarkProofsOrCode{code: true}
  226. case "cht":
  227. benchmarks[i] = &benchmarkHelperTrie{
  228. bloom: false,
  229. reqCount: getInt("amount", 1),
  230. }
  231. case "bloom":
  232. benchmarks[i] = &benchmarkHelperTrie{
  233. bloom: true,
  234. reqCount: getInt("amount", 1),
  235. }
  236. case "txSend":
  237. benchmarks[i] = &benchmarkTxSend{}
  238. case "txStatus":
  239. benchmarks[i] = &benchmarkTxStatus{}
  240. default:
  241. return nil, errUnknownBenchmarkType
  242. }
  243. } else {
  244. return nil, errUnknownBenchmarkType
  245. }
  246. }
  247. rs := api.server.handler.runBenchmark(benchmarks, passCount, time.Millisecond*time.Duration(length))
  248. result := make([]map[string]interface{}, len(setups))
  249. for i, r := range rs {
  250. res := make(map[string]interface{})
  251. if r.err == nil {
  252. res["totalCount"] = r.totalCount
  253. res["avgTime"] = r.avgTime
  254. res["maxInSize"] = r.maxInSize
  255. res["maxOutSize"] = r.maxOutSize
  256. } else {
  257. res["error"] = r.err.Error()
  258. }
  259. result[i] = res
  260. }
  261. return result, nil
  262. }
  263. // PrivateDebugAPI provides an API to debug LES light server functionality.
  264. type PrivateDebugAPI struct {
  265. server *LesServer
  266. }
  267. // NewPrivateDebugAPI creates a new LES light server debug API.
  268. func NewPrivateDebugAPI(server *LesServer) *PrivateDebugAPI {
  269. return &PrivateDebugAPI{
  270. server: server,
  271. }
  272. }
  273. // FreezeClient forces a temporary client freeze which normally happens when the server is overloaded
  274. func (api *PrivateDebugAPI) FreezeClient(id enode.ID) error {
  275. return api.server.clientPool.forClients([]enode.ID{id}, func(c *clientInfo, id enode.ID) error {
  276. if c == nil {
  277. return fmt.Errorf("client %064x is not connected", id[:])
  278. }
  279. c.peer.freezeClient()
  280. return nil
  281. })
  282. }
  283. // PrivateLightAPI provides an API to access the LES light server or light client.
  284. type PrivateLightAPI struct {
  285. backend *lesCommons
  286. }
  287. // NewPrivateLightAPI creates a new LES service API.
  288. func NewPrivateLightAPI(backend *lesCommons) *PrivateLightAPI {
  289. return &PrivateLightAPI{backend: backend}
  290. }
  291. // LatestCheckpoint returns the latest local checkpoint package.
  292. //
  293. // The checkpoint package consists of 4 strings:
  294. // result[0], hex encoded latest section index
  295. // result[1], 32 bytes hex encoded latest section head hash
  296. // result[2], 32 bytes hex encoded latest section canonical hash trie root hash
  297. // result[3], 32 bytes hex encoded latest section bloom trie root hash
  298. func (api *PrivateLightAPI) LatestCheckpoint() ([4]string, error) {
  299. var res [4]string
  300. cp := api.backend.latestLocalCheckpoint()
  301. if cp.Empty() {
  302. return res, errNoCheckpoint
  303. }
  304. res[0] = hexutil.EncodeUint64(cp.SectionIndex)
  305. res[1], res[2], res[3] = cp.SectionHead.Hex(), cp.CHTRoot.Hex(), cp.BloomRoot.Hex()
  306. return res, nil
  307. }
  308. // GetLocalCheckpoint returns the specific local checkpoint package.
  309. //
  310. // The checkpoint package consists of 3 strings:
  311. // result[0], 32 bytes hex encoded latest section head hash
  312. // result[1], 32 bytes hex encoded latest section canonical hash trie root hash
  313. // result[2], 32 bytes hex encoded latest section bloom trie root hash
  314. func (api *PrivateLightAPI) GetCheckpoint(index uint64) ([3]string, error) {
  315. var res [3]string
  316. cp := api.backend.localCheckpoint(index)
  317. if cp.Empty() {
  318. return res, errNoCheckpoint
  319. }
  320. res[0], res[1], res[2] = cp.SectionHead.Hex(), cp.CHTRoot.Hex(), cp.BloomRoot.Hex()
  321. return res, nil
  322. }
  323. // GetCheckpointContractAddress returns the contract contract address in hex format.
  324. func (api *PrivateLightAPI) GetCheckpointContractAddress() (string, error) {
  325. if api.backend.oracle == nil {
  326. return "", errNotActivated
  327. }
  328. return api.backend.oracle.Contract().ContractAddr().Hex(), nil
  329. }