api.go 13 KB

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