les_test.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. // Copyright 2020 The go-ethereum Authors
  2. // This file is part of go-ethereum.
  3. //
  4. // go-ethereum is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU 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. // go-ethereum 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 General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
  16. package main
  17. import (
  18. "context"
  19. "fmt"
  20. "os"
  21. "path/filepath"
  22. "runtime"
  23. "strings"
  24. "sync/atomic"
  25. "testing"
  26. "time"
  27. "github.com/ethereum/go-ethereum/p2p"
  28. "github.com/ethereum/go-ethereum/rpc"
  29. )
  30. type gethrpc struct {
  31. name string
  32. rpc *rpc.Client
  33. geth *testgeth
  34. nodeInfo *p2p.NodeInfo
  35. }
  36. func (g *gethrpc) killAndWait() {
  37. g.geth.Kill()
  38. g.geth.WaitExit()
  39. }
  40. func (g *gethrpc) callRPC(result interface{}, method string, args ...interface{}) {
  41. if err := g.rpc.Call(&result, method, args...); err != nil {
  42. g.geth.Fatalf("callRPC %v: %v", method, err)
  43. }
  44. }
  45. func (g *gethrpc) addPeer(peer *gethrpc) {
  46. g.geth.Logf("%v.addPeer(%v)", g.name, peer.name)
  47. enode := peer.getNodeInfo().Enode
  48. peerCh := make(chan *p2p.PeerEvent)
  49. sub, err := g.rpc.Subscribe(context.Background(), "admin", peerCh, "peerEvents")
  50. if err != nil {
  51. g.geth.Fatalf("subscribe %v: %v", g.name, err)
  52. }
  53. defer sub.Unsubscribe()
  54. g.callRPC(nil, "admin_addPeer", enode)
  55. dur := 14 * time.Second
  56. timeout := time.After(dur)
  57. select {
  58. case ev := <-peerCh:
  59. g.geth.Logf("%v received event: type=%v, peer=%v", g.name, ev.Type, ev.Peer)
  60. case err := <-sub.Err():
  61. g.geth.Fatalf("%v sub error: %v", g.name, err)
  62. case <-timeout:
  63. g.geth.Error("timeout adding peer after", dur)
  64. }
  65. }
  66. // Use this function instead of `g.nodeInfo` directly
  67. func (g *gethrpc) getNodeInfo() *p2p.NodeInfo {
  68. if g.nodeInfo != nil {
  69. return g.nodeInfo
  70. }
  71. g.nodeInfo = &p2p.NodeInfo{}
  72. g.callRPC(&g.nodeInfo, "admin_nodeInfo")
  73. return g.nodeInfo
  74. }
  75. // ipcEndpoint resolves an IPC endpoint based on a configured value, taking into
  76. // account the set data folders as well as the designated platform we're currently
  77. // running on.
  78. func ipcEndpoint(ipcPath, datadir string) string {
  79. // On windows we can only use plain top-level pipes
  80. if runtime.GOOS == "windows" {
  81. if strings.HasPrefix(ipcPath, `\\.\pipe\`) {
  82. return ipcPath
  83. }
  84. return `\\.\pipe\` + ipcPath
  85. }
  86. // Resolve names into the data directory full paths otherwise
  87. if filepath.Base(ipcPath) == ipcPath {
  88. if datadir == "" {
  89. return filepath.Join(os.TempDir(), ipcPath)
  90. }
  91. return filepath.Join(datadir, ipcPath)
  92. }
  93. return ipcPath
  94. }
  95. // nextIPC ensures that each ipc pipe gets a unique name.
  96. // On linux, it works well to use ipc pipes all over the filesystem (in datadirs),
  97. // but windows require pipes to sit in "\\.\pipe\". Therefore, to run several
  98. // nodes simultaneously, we need to distinguish between them, which we do by
  99. // the pipe filename instead of folder.
  100. var nextIPC = uint32(0)
  101. func startGethWithIpc(t *testing.T, name string, args ...string) *gethrpc {
  102. ipcName := fmt.Sprintf("geth-%d.ipc", atomic.AddUint32(&nextIPC, 1))
  103. args = append([]string{"--networkid=42", "--port=0", "--authrpc.port", "0", "--ipcpath", ipcName}, args...)
  104. t.Logf("Starting %v with rpc: %v", name, args)
  105. g := &gethrpc{
  106. name: name,
  107. geth: runGeth(t, args...),
  108. }
  109. ipcpath := ipcEndpoint(ipcName, g.geth.Datadir)
  110. // We can't know exactly how long geth will take to start, so we try 10
  111. // times over a 5 second period.
  112. var err error
  113. for i := 0; i < 10; i++ {
  114. time.Sleep(500 * time.Millisecond)
  115. if g.rpc, err = rpc.Dial(ipcpath); err == nil {
  116. return g
  117. }
  118. }
  119. t.Fatalf("%v rpc connect to %v: %v", name, ipcpath, err)
  120. return nil
  121. }
  122. func initGeth(t *testing.T) string {
  123. args := []string{"--networkid=42", "init", "./testdata/clique.json"}
  124. t.Logf("Initializing geth: %v ", args)
  125. g := runGeth(t, args...)
  126. datadir := g.Datadir
  127. g.WaitExit()
  128. return datadir
  129. }
  130. func startLightServer(t *testing.T) *gethrpc {
  131. datadir := initGeth(t)
  132. t.Logf("Importing keys to geth")
  133. runGeth(t, "account", "import", "--datadir", datadir, "--password", "./testdata/password.txt", "--lightkdf", "./testdata/key.prv").WaitExit()
  134. account := "0x02f0d131f1f97aef08aec6e3291b957d9efe7105"
  135. server := startGethWithIpc(t, "lightserver", "--allow-insecure-unlock", "--datadir", datadir, "--password", "./testdata/password.txt", "--unlock", account, "--mine", "--light.serve=100", "--light.maxpeers=1", "--nodiscover", "--nat=extip:127.0.0.1", "--verbosity=4")
  136. return server
  137. }
  138. func startClient(t *testing.T, name string) *gethrpc {
  139. datadir := initGeth(t)
  140. return startGethWithIpc(t, name, "--datadir", datadir, "--nodiscover", "--syncmode=light", "--nat=extip:127.0.0.1", "--verbosity=4")
  141. }
  142. func TestPriorityClient(t *testing.T) {
  143. lightServer := startLightServer(t)
  144. defer lightServer.killAndWait()
  145. // Start client and add lightServer as peer
  146. freeCli := startClient(t, "freeCli")
  147. defer freeCli.killAndWait()
  148. freeCli.addPeer(lightServer)
  149. var peers []*p2p.PeerInfo
  150. freeCli.callRPC(&peers, "admin_peers")
  151. if len(peers) != 1 {
  152. t.Errorf("Expected: # of client peers == 1, actual: %v", len(peers))
  153. return
  154. }
  155. // Set up priority client, get its nodeID, increase its balance on the lightServer
  156. prioCli := startClient(t, "prioCli")
  157. defer prioCli.killAndWait()
  158. // 3_000_000_000 once we move to Go 1.13
  159. tokens := uint64(3000000000)
  160. lightServer.callRPC(nil, "les_addBalance", prioCli.getNodeInfo().ID, tokens)
  161. prioCli.addPeer(lightServer)
  162. // Check if priority client is actually syncing and the regular client got kicked out
  163. prioCli.callRPC(&peers, "admin_peers")
  164. if len(peers) != 1 {
  165. t.Errorf("Expected: # of prio peers == 1, actual: %v", len(peers))
  166. }
  167. nodes := map[string]*gethrpc{
  168. lightServer.getNodeInfo().ID: lightServer,
  169. freeCli.getNodeInfo().ID: freeCli,
  170. prioCli.getNodeInfo().ID: prioCli,
  171. }
  172. time.Sleep(1 * time.Second)
  173. lightServer.callRPC(&peers, "admin_peers")
  174. peersWithNames := make(map[string]string)
  175. for _, p := range peers {
  176. peersWithNames[nodes[p.ID].name] = p.ID
  177. }
  178. if _, freeClientFound := peersWithNames[freeCli.name]; freeClientFound {
  179. t.Error("client is still a peer of lightServer", peersWithNames)
  180. }
  181. if _, prioClientFound := peersWithNames[prioCli.name]; !prioClientFound {
  182. t.Error("prio client is not among lightServer peers", peersWithNames)
  183. }
  184. }