api.go 14 KB

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