api.go 14 KB

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