api_test.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  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. "context"
  19. "errors"
  20. "flag"
  21. "io/ioutil"
  22. "math/rand"
  23. "os"
  24. "sync"
  25. "sync/atomic"
  26. "testing"
  27. "time"
  28. "github.com/ethereum/go-ethereum/common"
  29. "github.com/ethereum/go-ethereum/common/hexutil"
  30. "github.com/ethereum/go-ethereum/consensus/ethash"
  31. "github.com/ethereum/go-ethereum/eth"
  32. "github.com/ethereum/go-ethereum/eth/downloader"
  33. "github.com/ethereum/go-ethereum/eth/ethconfig"
  34. "github.com/ethereum/go-ethereum/les/flowcontrol"
  35. "github.com/ethereum/go-ethereum/log"
  36. "github.com/ethereum/go-ethereum/node"
  37. "github.com/ethereum/go-ethereum/p2p/enode"
  38. "github.com/ethereum/go-ethereum/p2p/simulations"
  39. "github.com/ethereum/go-ethereum/p2p/simulations/adapters"
  40. "github.com/ethereum/go-ethereum/rpc"
  41. "github.com/mattn/go-colorable"
  42. )
  43. // Additional command line flags for the test binary.
  44. var (
  45. loglevel = flag.Int("loglevel", 0, "verbosity of logs")
  46. simAdapter = flag.String("adapter", "exec", "type of simulation: sim|socket|exec|docker")
  47. )
  48. func TestMain(m *testing.M) {
  49. flag.Parse()
  50. log.PrintOrigins(true)
  51. log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true))))
  52. // register the Delivery service which will run as a devp2p
  53. // protocol when using the exec adapter
  54. adapters.RegisterLifecycles(services)
  55. os.Exit(m.Run())
  56. }
  57. // This test is not meant to be a part of the automatic testing process because it
  58. // runs for a long time and also requires a large database in order to do a meaningful
  59. // request performance test. When testServerDataDir is empty, the test is skipped.
  60. const (
  61. testServerDataDir = "" // should always be empty on the master branch
  62. testServerCapacity = 200
  63. testMaxClients = 10
  64. testTolerance = 0.1
  65. minRelCap = 0.2
  66. )
  67. func TestCapacityAPI3(t *testing.T) {
  68. testCapacityAPI(t, 3)
  69. }
  70. func TestCapacityAPI6(t *testing.T) {
  71. testCapacityAPI(t, 6)
  72. }
  73. func TestCapacityAPI10(t *testing.T) {
  74. testCapacityAPI(t, 10)
  75. }
  76. // testCapacityAPI runs an end-to-end simulation test connecting one server with
  77. // a given number of clients. It sets different priority capacities to all clients
  78. // except a randomly selected one which runs in free client mode. All clients send
  79. // similar requests at the maximum allowed rate and the test verifies whether the
  80. // ratio of processed requests is close enough to the ratio of assigned capacities.
  81. // Running multiple rounds with different settings ensures that changing capacity
  82. // while connected and going back and forth between free and priority mode with
  83. // the supplied API calls is also thoroughly tested.
  84. func testCapacityAPI(t *testing.T, clientCount int) {
  85. // Skip test if no data dir specified
  86. if testServerDataDir == "" {
  87. return
  88. }
  89. for !testSim(t, 1, clientCount, []string{testServerDataDir}, nil, func(ctx context.Context, net *simulations.Network, servers []*simulations.Node, clients []*simulations.Node) bool {
  90. if len(servers) != 1 {
  91. t.Fatalf("Invalid number of servers: %d", len(servers))
  92. }
  93. server := servers[0]
  94. serverRpcClient, err := server.Client()
  95. if err != nil {
  96. t.Fatalf("Failed to obtain rpc client: %v", err)
  97. }
  98. headNum, headHash := getHead(ctx, t, serverRpcClient)
  99. minCap, totalCap := getCapacityInfo(ctx, t, serverRpcClient)
  100. testCap := totalCap * 3 / 4
  101. t.Logf("Server testCap: %d minCap: %d head number: %d head hash: %064x\n", testCap, minCap, headNum, headHash)
  102. reqMinCap := uint64(float64(testCap) * minRelCap / (minRelCap + float64(len(clients)-1)))
  103. if minCap > reqMinCap {
  104. t.Fatalf("Minimum client capacity (%d) bigger than required minimum for this test (%d)", minCap, reqMinCap)
  105. }
  106. freeIdx := rand.Intn(len(clients))
  107. clientRpcClients := make([]*rpc.Client, len(clients))
  108. for i, client := range clients {
  109. var err error
  110. clientRpcClients[i], err = client.Client()
  111. if err != nil {
  112. t.Fatalf("Failed to obtain rpc client: %v", err)
  113. }
  114. t.Log("connecting client", i)
  115. if i != freeIdx {
  116. setCapacity(ctx, t, serverRpcClient, client.ID(), testCap/uint64(len(clients)))
  117. }
  118. net.Connect(client.ID(), server.ID())
  119. for {
  120. select {
  121. case <-ctx.Done():
  122. t.Fatalf("Timeout")
  123. default:
  124. }
  125. num, hash := getHead(ctx, t, clientRpcClients[i])
  126. if num == headNum && hash == headHash {
  127. t.Log("client", i, "synced")
  128. break
  129. }
  130. time.Sleep(time.Millisecond * 200)
  131. }
  132. }
  133. var wg sync.WaitGroup
  134. stop := make(chan struct{})
  135. reqCount := make([]uint64, len(clientRpcClients))
  136. // Send light request like crazy.
  137. for i, c := range clientRpcClients {
  138. wg.Add(1)
  139. i, c := i, c
  140. go func() {
  141. defer wg.Done()
  142. queue := make(chan struct{}, 100)
  143. reqCount[i] = 0
  144. for {
  145. select {
  146. case queue <- struct{}{}:
  147. select {
  148. case <-stop:
  149. return
  150. case <-ctx.Done():
  151. return
  152. default:
  153. wg.Add(1)
  154. go func() {
  155. ok := testRequest(ctx, t, c)
  156. wg.Done()
  157. <-queue
  158. if ok {
  159. count := atomic.AddUint64(&reqCount[i], 1)
  160. if count%10000 == 0 {
  161. freezeClient(ctx, t, serverRpcClient, clients[i].ID())
  162. }
  163. }
  164. }()
  165. }
  166. case <-stop:
  167. return
  168. case <-ctx.Done():
  169. return
  170. }
  171. }
  172. }()
  173. }
  174. processedSince := func(start []uint64) []uint64 {
  175. res := make([]uint64, len(reqCount))
  176. for i := range reqCount {
  177. res[i] = atomic.LoadUint64(&reqCount[i])
  178. if start != nil {
  179. res[i] -= start[i]
  180. }
  181. }
  182. return res
  183. }
  184. weights := make([]float64, len(clients))
  185. for c := 0; c < 5; c++ {
  186. setCapacity(ctx, t, serverRpcClient, clients[freeIdx].ID(), minCap)
  187. freeIdx = rand.Intn(len(clients))
  188. var sum float64
  189. for i := range clients {
  190. if i == freeIdx {
  191. weights[i] = 0
  192. } else {
  193. weights[i] = rand.Float64()*(1-minRelCap) + minRelCap
  194. }
  195. sum += weights[i]
  196. }
  197. for i, client := range clients {
  198. weights[i] *= float64(testCap-minCap-100) / sum
  199. capacity := uint64(weights[i])
  200. if i != freeIdx && capacity < getCapacity(ctx, t, serverRpcClient, client.ID()) {
  201. setCapacity(ctx, t, serverRpcClient, client.ID(), capacity)
  202. }
  203. }
  204. setCapacity(ctx, t, serverRpcClient, clients[freeIdx].ID(), 0)
  205. for i, client := range clients {
  206. capacity := uint64(weights[i])
  207. if i != freeIdx && capacity > getCapacity(ctx, t, serverRpcClient, client.ID()) {
  208. setCapacity(ctx, t, serverRpcClient, client.ID(), capacity)
  209. }
  210. }
  211. weights[freeIdx] = float64(minCap)
  212. for i := range clients {
  213. weights[i] /= float64(testCap)
  214. }
  215. time.Sleep(flowcontrol.DecParamDelay)
  216. t.Log("Starting measurement")
  217. t.Logf("Relative weights:")
  218. for i := range clients {
  219. t.Logf(" %f", weights[i])
  220. }
  221. t.Log()
  222. start := processedSince(nil)
  223. for {
  224. select {
  225. case <-ctx.Done():
  226. t.Fatalf("Timeout")
  227. default:
  228. }
  229. _, totalCap = getCapacityInfo(ctx, t, serverRpcClient)
  230. if totalCap < testCap {
  231. t.Log("Total capacity underrun")
  232. close(stop)
  233. wg.Wait()
  234. return false
  235. }
  236. processed := processedSince(start)
  237. var avg uint64
  238. t.Logf("Processed")
  239. for i, p := range processed {
  240. t.Logf(" %d", p)
  241. processed[i] = uint64(float64(p) / weights[i])
  242. avg += processed[i]
  243. }
  244. avg /= uint64(len(processed))
  245. if avg >= 10000 {
  246. var maxDev float64
  247. for _, p := range processed {
  248. dev := float64(int64(p-avg)) / float64(avg)
  249. t.Logf(" %7.4f", dev)
  250. if dev < 0 {
  251. dev = -dev
  252. }
  253. if dev > maxDev {
  254. maxDev = dev
  255. }
  256. }
  257. t.Logf(" max deviation: %f totalCap: %d\n", maxDev, totalCap)
  258. if maxDev <= testTolerance {
  259. t.Log("success")
  260. break
  261. }
  262. } else {
  263. t.Log()
  264. }
  265. time.Sleep(time.Millisecond * 200)
  266. }
  267. }
  268. close(stop)
  269. wg.Wait()
  270. for i, count := range reqCount {
  271. t.Log("client", i, "processed", count)
  272. }
  273. return true
  274. }) {
  275. t.Log("restarting test")
  276. }
  277. }
  278. func getHead(ctx context.Context, t *testing.T, client *rpc.Client) (uint64, common.Hash) {
  279. res := make(map[string]interface{})
  280. if err := client.CallContext(ctx, &res, "eth_getBlockByNumber", "latest", false); err != nil {
  281. t.Fatalf("Failed to obtain head block: %v", err)
  282. }
  283. numStr, ok := res["number"].(string)
  284. if !ok {
  285. t.Fatalf("RPC block number field invalid")
  286. }
  287. num, err := hexutil.DecodeUint64(numStr)
  288. if err != nil {
  289. t.Fatalf("Failed to decode RPC block number: %v", err)
  290. }
  291. hashStr, ok := res["hash"].(string)
  292. if !ok {
  293. t.Fatalf("RPC block number field invalid")
  294. }
  295. hash := common.HexToHash(hashStr)
  296. return num, hash
  297. }
  298. func testRequest(ctx context.Context, t *testing.T, client *rpc.Client) bool {
  299. var res string
  300. var addr common.Address
  301. rand.Read(addr[:])
  302. c, cancel := context.WithTimeout(ctx, time.Second*12)
  303. defer cancel()
  304. err := client.CallContext(c, &res, "eth_getBalance", addr, "latest")
  305. if err != nil {
  306. t.Log("request error:", err)
  307. }
  308. return err == nil
  309. }
  310. func freezeClient(ctx context.Context, t *testing.T, server *rpc.Client, clientID enode.ID) {
  311. if err := server.CallContext(ctx, nil, "debug_freezeClient", clientID); err != nil {
  312. t.Fatalf("Failed to freeze client: %v", err)
  313. }
  314. }
  315. func setCapacity(ctx context.Context, t *testing.T, server *rpc.Client, clientID enode.ID, cap uint64) {
  316. params := make(map[string]interface{})
  317. params["capacity"] = cap
  318. if err := server.CallContext(ctx, nil, "les_setClientParams", []enode.ID{clientID}, []string{}, params); err != nil {
  319. t.Fatalf("Failed to set client capacity: %v", err)
  320. }
  321. }
  322. func getCapacity(ctx context.Context, t *testing.T, server *rpc.Client, clientID enode.ID) uint64 {
  323. var res map[enode.ID]map[string]interface{}
  324. if err := server.CallContext(ctx, &res, "les_clientInfo", []enode.ID{clientID}, []string{}); err != nil {
  325. t.Fatalf("Failed to get client info: %v", err)
  326. }
  327. info, ok := res[clientID]
  328. if !ok {
  329. t.Fatalf("Missing client info")
  330. }
  331. v, ok := info["capacity"]
  332. if !ok {
  333. t.Fatalf("Missing field in client info: capacity")
  334. }
  335. vv, ok := v.(float64)
  336. if !ok {
  337. t.Fatalf("Failed to decode capacity field")
  338. }
  339. return uint64(vv)
  340. }
  341. func getCapacityInfo(ctx context.Context, t *testing.T, server *rpc.Client) (minCap, totalCap uint64) {
  342. var res map[string]interface{}
  343. if err := server.CallContext(ctx, &res, "les_serverInfo"); err != nil {
  344. t.Fatalf("Failed to query server info: %v", err)
  345. }
  346. decode := func(s string) uint64 {
  347. v, ok := res[s]
  348. if !ok {
  349. t.Fatalf("Missing field in server info: %s", s)
  350. }
  351. vv, ok := v.(float64)
  352. if !ok {
  353. t.Fatalf("Failed to decode server info field: %s", s)
  354. }
  355. return uint64(vv)
  356. }
  357. minCap = decode("minimumCapacity")
  358. totalCap = decode("totalCapacity")
  359. return
  360. }
  361. var services = adapters.LifecycleConstructors{
  362. "lesclient": newLesClientService,
  363. "lesserver": newLesServerService,
  364. }
  365. func NewNetwork() (*simulations.Network, func(), error) {
  366. adapter, adapterTeardown, err := NewAdapter(*simAdapter, services)
  367. if err != nil {
  368. return nil, adapterTeardown, err
  369. }
  370. defaultService := "streamer"
  371. net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{
  372. ID: "0",
  373. DefaultService: defaultService,
  374. })
  375. teardown := func() {
  376. adapterTeardown()
  377. net.Shutdown()
  378. }
  379. return net, teardown, nil
  380. }
  381. func NewAdapter(adapterType string, services adapters.LifecycleConstructors) (adapter adapters.NodeAdapter, teardown func(), err error) {
  382. teardown = func() {}
  383. switch adapterType {
  384. case "sim":
  385. adapter = adapters.NewSimAdapter(services)
  386. // case "socket":
  387. // adapter = adapters.NewSocketAdapter(services)
  388. case "exec":
  389. baseDir, err0 := ioutil.TempDir("", "les-test")
  390. if err0 != nil {
  391. return nil, teardown, err0
  392. }
  393. teardown = func() { os.RemoveAll(baseDir) }
  394. adapter = adapters.NewExecAdapter(baseDir)
  395. /*case "docker":
  396. adapter, err = adapters.NewDockerAdapter()
  397. if err != nil {
  398. return nil, teardown, err
  399. }*/
  400. default:
  401. return nil, teardown, errors.New("adapter needs to be one of sim, socket, exec, docker")
  402. }
  403. return adapter, teardown, nil
  404. }
  405. func testSim(t *testing.T, serverCount, clientCount int, serverDir, clientDir []string, test func(ctx context.Context, net *simulations.Network, servers []*simulations.Node, clients []*simulations.Node) bool) bool {
  406. net, teardown, err := NewNetwork()
  407. defer teardown()
  408. if err != nil {
  409. t.Fatalf("Failed to create network: %v", err)
  410. }
  411. timeout := 1800 * time.Second
  412. ctx, cancel := context.WithTimeout(context.Background(), timeout)
  413. defer cancel()
  414. servers := make([]*simulations.Node, serverCount)
  415. clients := make([]*simulations.Node, clientCount)
  416. for i := range clients {
  417. clientconf := adapters.RandomNodeConfig()
  418. clientconf.Lifecycles = []string{"lesclient"}
  419. if len(clientDir) == clientCount {
  420. clientconf.DataDir = clientDir[i]
  421. }
  422. client, err := net.NewNodeWithConfig(clientconf)
  423. if err != nil {
  424. t.Fatalf("Failed to create client: %v", err)
  425. }
  426. clients[i] = client
  427. }
  428. for i := range servers {
  429. serverconf := adapters.RandomNodeConfig()
  430. serverconf.Lifecycles = []string{"lesserver"}
  431. if len(serverDir) == serverCount {
  432. serverconf.DataDir = serverDir[i]
  433. }
  434. server, err := net.NewNodeWithConfig(serverconf)
  435. if err != nil {
  436. t.Fatalf("Failed to create server: %v", err)
  437. }
  438. servers[i] = server
  439. }
  440. for _, client := range clients {
  441. if err := net.Start(client.ID()); err != nil {
  442. t.Fatalf("Failed to start client node: %v", err)
  443. }
  444. }
  445. for _, server := range servers {
  446. if err := net.Start(server.ID()); err != nil {
  447. t.Fatalf("Failed to start server node: %v", err)
  448. }
  449. }
  450. return test(ctx, net, servers, clients)
  451. }
  452. func newLesClientService(ctx *adapters.ServiceContext, stack *node.Node) (node.Lifecycle, error) {
  453. config := ethconfig.Defaults
  454. config.SyncMode = downloader.LightSync
  455. config.Ethash.PowMode = ethash.ModeFake
  456. return New(stack, &config)
  457. }
  458. func newLesServerService(ctx *adapters.ServiceContext, stack *node.Node) (node.Lifecycle, error) {
  459. config := ethconfig.Defaults
  460. config.SyncMode = downloader.FullSync
  461. config.LightServ = testServerCapacity
  462. config.LightPeers = testMaxClients
  463. ethereum, err := eth.New(stack, &config)
  464. if err != nil {
  465. return nil, err
  466. }
  467. _, err = NewLesServer(stack, ethereum, &config)
  468. if err != nil {
  469. return nil, err
  470. }
  471. return ethereum, nil
  472. }