run_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. // Copyright 2017 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. "crypto/ecdsa"
  20. "fmt"
  21. "io/ioutil"
  22. "net"
  23. "os"
  24. "path"
  25. "path/filepath"
  26. "runtime"
  27. "sync"
  28. "syscall"
  29. "testing"
  30. "time"
  31. "github.com/docker/docker/pkg/reexec"
  32. "github.com/ethereum/go-ethereum/accounts"
  33. "github.com/ethereum/go-ethereum/accounts/keystore"
  34. "github.com/ethereum/go-ethereum/internal/cmdtest"
  35. "github.com/ethereum/go-ethereum/node"
  36. "github.com/ethereum/go-ethereum/p2p"
  37. "github.com/ethereum/go-ethereum/rpc"
  38. "github.com/ethereum/go-ethereum/swarm"
  39. )
  40. func init() {
  41. // Run the app if we've been exec'd as "swarm-test" in runSwarm.
  42. reexec.Register("swarm-test", func() {
  43. if err := app.Run(os.Args); err != nil {
  44. fmt.Fprintln(os.Stderr, err)
  45. os.Exit(1)
  46. }
  47. os.Exit(0)
  48. })
  49. }
  50. func TestMain(m *testing.M) {
  51. // check if we have been reexec'd
  52. if reexec.Init() {
  53. return
  54. }
  55. os.Exit(m.Run())
  56. }
  57. func runSwarm(t *testing.T, args ...string) *cmdtest.TestCmd {
  58. tt := cmdtest.NewTestCmd(t, nil)
  59. // Boot "swarm". This actually runs the test binary but the TestMain
  60. // function will prevent any tests from running.
  61. tt.Run("swarm-test", args...)
  62. return tt
  63. }
  64. type testCluster struct {
  65. Nodes []*testNode
  66. TmpDir string
  67. }
  68. // newTestCluster starts a test swarm cluster of the given size.
  69. //
  70. // A temporary directory is created and each node gets a data directory inside
  71. // it.
  72. //
  73. // Each node listens on 127.0.0.1 with random ports for both the HTTP and p2p
  74. // ports (assigned by first listening on 127.0.0.1:0 and then passing the ports
  75. // as flags).
  76. //
  77. // When starting more than one node, they are connected together using the
  78. // admin SetPeer RPC method.
  79. func newTestCluster(t *testing.T, size int) *testCluster {
  80. cluster := &testCluster{}
  81. defer func() {
  82. if t.Failed() {
  83. cluster.Shutdown()
  84. }
  85. }()
  86. tmpdir, err := ioutil.TempDir("", "swarm-test")
  87. if err != nil {
  88. t.Fatal(err)
  89. }
  90. cluster.TmpDir = tmpdir
  91. // start the nodes
  92. cluster.StartNewNodes(t, size)
  93. if size == 1 {
  94. return cluster
  95. }
  96. // connect the nodes together
  97. for _, node := range cluster.Nodes {
  98. if err := node.Client.Call(nil, "admin_addPeer", cluster.Nodes[0].Enode); err != nil {
  99. t.Fatal(err)
  100. }
  101. }
  102. // wait until all nodes have the correct number of peers
  103. outer:
  104. for _, node := range cluster.Nodes {
  105. var peers []*p2p.PeerInfo
  106. for start := time.Now(); time.Since(start) < time.Minute; time.Sleep(50 * time.Millisecond) {
  107. if err := node.Client.Call(&peers, "admin_peers"); err != nil {
  108. t.Fatal(err)
  109. }
  110. if len(peers) == len(cluster.Nodes)-1 {
  111. continue outer
  112. }
  113. }
  114. t.Fatalf("%s only has %d / %d peers", node.Name, len(peers), len(cluster.Nodes)-1)
  115. }
  116. return cluster
  117. }
  118. func (c *testCluster) Shutdown() {
  119. for _, node := range c.Nodes {
  120. node.Shutdown()
  121. }
  122. os.RemoveAll(c.TmpDir)
  123. }
  124. func (c *testCluster) Stop() {
  125. for _, node := range c.Nodes {
  126. node.Shutdown()
  127. }
  128. }
  129. func (c *testCluster) StartNewNodes(t *testing.T, size int) {
  130. c.Nodes = make([]*testNode, 0, size)
  131. for i := 0; i < size; i++ {
  132. dir := filepath.Join(c.TmpDir, fmt.Sprintf("swarm%02d", i))
  133. if err := os.Mkdir(dir, 0700); err != nil {
  134. t.Fatal(err)
  135. }
  136. node := newTestNode(t, dir)
  137. node.Name = fmt.Sprintf("swarm%02d", i)
  138. c.Nodes = append(c.Nodes, node)
  139. }
  140. }
  141. func (c *testCluster) StartExistingNodes(t *testing.T, size int, bzzaccount string) {
  142. c.Nodes = make([]*testNode, 0, size)
  143. for i := 0; i < size; i++ {
  144. dir := filepath.Join(c.TmpDir, fmt.Sprintf("swarm%02d", i))
  145. node := existingTestNode(t, dir, bzzaccount)
  146. node.Name = fmt.Sprintf("swarm%02d", i)
  147. c.Nodes = append(c.Nodes, node)
  148. }
  149. }
  150. func (c *testCluster) Cleanup() {
  151. os.RemoveAll(c.TmpDir)
  152. }
  153. type testNode struct {
  154. Name string
  155. Addr string
  156. URL string
  157. Enode string
  158. Dir string
  159. IpcPath string
  160. PrivateKey *ecdsa.PrivateKey
  161. Client *rpc.Client
  162. Cmd *cmdtest.TestCmd
  163. }
  164. const testPassphrase = "swarm-test-passphrase"
  165. func getTestAccount(t *testing.T, dir string) (conf *node.Config, account accounts.Account) {
  166. // create key
  167. conf = &node.Config{
  168. DataDir: dir,
  169. IPCPath: "bzzd.ipc",
  170. NoUSB: true,
  171. }
  172. n, err := node.New(conf)
  173. if err != nil {
  174. t.Fatal(err)
  175. }
  176. account, err = n.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore).NewAccount(testPassphrase)
  177. if err != nil {
  178. t.Fatal(err)
  179. }
  180. // use a unique IPCPath when running tests on Windows
  181. if runtime.GOOS == "windows" {
  182. conf.IPCPath = fmt.Sprintf("bzzd-%s.ipc", account.Address.String())
  183. }
  184. return conf, account
  185. }
  186. func existingTestNode(t *testing.T, dir string, bzzaccount string) *testNode {
  187. conf, _ := getTestAccount(t, dir)
  188. node := &testNode{Dir: dir}
  189. // use a unique IPCPath when running tests on Windows
  190. if runtime.GOOS == "windows" {
  191. conf.IPCPath = fmt.Sprintf("bzzd-%s.ipc", bzzaccount)
  192. }
  193. // assign ports
  194. ports, err := getAvailableTCPPorts(2)
  195. if err != nil {
  196. t.Fatal(err)
  197. }
  198. p2pPort := ports[0]
  199. httpPort := ports[1]
  200. // start the node
  201. node.Cmd = runSwarm(t,
  202. "--port", p2pPort,
  203. "--nodiscover",
  204. "--datadir", dir,
  205. "--ipcpath", conf.IPCPath,
  206. "--ens-api", "",
  207. "--bzzaccount", bzzaccount,
  208. "--bzznetworkid", "321",
  209. "--bzzport", httpPort,
  210. "--verbosity", "6",
  211. )
  212. node.Cmd.InputLine(testPassphrase)
  213. defer func() {
  214. if t.Failed() {
  215. node.Shutdown()
  216. }
  217. }()
  218. ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
  219. defer cancel()
  220. // ensure that all ports have active listeners
  221. // so that the next node will not get the same
  222. // when calling getAvailableTCPPorts
  223. err = waitTCPPorts(ctx, ports...)
  224. if err != nil {
  225. t.Fatal(err)
  226. }
  227. // wait for the node to start
  228. for start := time.Now(); time.Since(start) < 10*time.Second; time.Sleep(50 * time.Millisecond) {
  229. node.Client, err = rpc.Dial(conf.IPCEndpoint())
  230. if err == nil {
  231. break
  232. }
  233. }
  234. if node.Client == nil {
  235. t.Fatal(err)
  236. }
  237. // load info
  238. var info swarm.Info
  239. if err := node.Client.Call(&info, "bzz_info"); err != nil {
  240. t.Fatal(err)
  241. }
  242. node.Addr = net.JoinHostPort("127.0.0.1", info.Port)
  243. node.URL = "http://" + node.Addr
  244. var nodeInfo p2p.NodeInfo
  245. if err := node.Client.Call(&nodeInfo, "admin_nodeInfo"); err != nil {
  246. t.Fatal(err)
  247. }
  248. node.Enode = fmt.Sprintf("enode://%s@127.0.0.1:%s", nodeInfo.ID, p2pPort)
  249. return node
  250. }
  251. func newTestNode(t *testing.T, dir string) *testNode {
  252. conf, account := getTestAccount(t, dir)
  253. ks := keystore.NewKeyStore(path.Join(dir, "keystore"), 1<<18, 1)
  254. pk := decryptStoreAccount(ks, account.Address.Hex(), []string{testPassphrase})
  255. node := &testNode{Dir: dir, PrivateKey: pk}
  256. // assign ports
  257. ports, err := getAvailableTCPPorts(2)
  258. if err != nil {
  259. t.Fatal(err)
  260. }
  261. p2pPort := ports[0]
  262. httpPort := ports[1]
  263. // start the node
  264. node.Cmd = runSwarm(t,
  265. "--port", p2pPort,
  266. "--nodiscover",
  267. "--datadir", dir,
  268. "--ipcpath", conf.IPCPath,
  269. "--ens-api", "",
  270. "--bzzaccount", account.Address.String(),
  271. "--bzznetworkid", "321",
  272. "--bzzport", httpPort,
  273. "--verbosity", "6",
  274. )
  275. node.Cmd.InputLine(testPassphrase)
  276. defer func() {
  277. if t.Failed() {
  278. node.Shutdown()
  279. }
  280. }()
  281. ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
  282. defer cancel()
  283. // ensure that all ports have active listeners
  284. // so that the next node will not get the same
  285. // when calling getAvailableTCPPorts
  286. err = waitTCPPorts(ctx, ports...)
  287. if err != nil {
  288. t.Fatal(err)
  289. }
  290. // wait for the node to start
  291. for start := time.Now(); time.Since(start) < 10*time.Second; time.Sleep(50 * time.Millisecond) {
  292. node.Client, err = rpc.Dial(conf.IPCEndpoint())
  293. if err == nil {
  294. break
  295. }
  296. }
  297. if node.Client == nil {
  298. t.Fatal(err)
  299. }
  300. // load info
  301. var info swarm.Info
  302. if err := node.Client.Call(&info, "bzz_info"); err != nil {
  303. t.Fatal(err)
  304. }
  305. node.Addr = net.JoinHostPort("127.0.0.1", info.Port)
  306. node.URL = "http://" + node.Addr
  307. var nodeInfo p2p.NodeInfo
  308. if err := node.Client.Call(&nodeInfo, "admin_nodeInfo"); err != nil {
  309. t.Fatal(err)
  310. }
  311. node.Enode = fmt.Sprintf("enode://%s@127.0.0.1:%s", nodeInfo.ID, p2pPort)
  312. node.IpcPath = conf.IPCPath
  313. return node
  314. }
  315. func (n *testNode) Shutdown() {
  316. if n.Cmd != nil {
  317. n.Cmd.Kill()
  318. }
  319. }
  320. // getAvailableTCPPorts returns a set of ports that
  321. // nothing is listening on at the time.
  322. //
  323. // Function assignTCPPort cannot be called in sequence
  324. // and guardantee that the same port will be returned in
  325. // different calls as the listener is closed within the function,
  326. // not after all listeners are started and selected unique
  327. // available ports.
  328. func getAvailableTCPPorts(count int) (ports []string, err error) {
  329. for i := 0; i < count; i++ {
  330. l, err := net.Listen("tcp", "127.0.0.1:0")
  331. if err != nil {
  332. return nil, err
  333. }
  334. // defer close in the loop to be sure the same port will not
  335. // be selected in the next iteration
  336. defer l.Close()
  337. _, port, err := net.SplitHostPort(l.Addr().String())
  338. if err != nil {
  339. return nil, err
  340. }
  341. ports = append(ports, port)
  342. }
  343. return ports, nil
  344. }
  345. // waitTCPPorts blocks until tcp connections can be
  346. // established on all provided ports. It runs all
  347. // ports dialers in parallel, and returns the first
  348. // encountered error.
  349. // See waitTCPPort also.
  350. func waitTCPPorts(ctx context.Context, ports ...string) error {
  351. var err error
  352. // mu locks err variable that is assigned in
  353. // other goroutines
  354. var mu sync.Mutex
  355. // cancel is canceling all goroutines
  356. // when the firs error is returned
  357. // to prevent unnecessary waiting
  358. ctx, cancel := context.WithCancel(ctx)
  359. defer cancel()
  360. var wg sync.WaitGroup
  361. for _, port := range ports {
  362. wg.Add(1)
  363. go func(port string) {
  364. defer wg.Done()
  365. e := waitTCPPort(ctx, port)
  366. mu.Lock()
  367. defer mu.Unlock()
  368. if e != nil && err == nil {
  369. err = e
  370. cancel()
  371. }
  372. }(port)
  373. }
  374. wg.Wait()
  375. return err
  376. }
  377. // waitTCPPort blocks until tcp connection can be established
  378. // ona provided port. It has a 3 minute timeout as maximum,
  379. // to prevent long waiting, but it can be shortened with
  380. // a provided context instance. Dialer has a 10 second timeout
  381. // in every iteration, and connection refused error will be
  382. // retried in 100 milliseconds periods.
  383. func waitTCPPort(ctx context.Context, port string) error {
  384. ctx, cancel := context.WithTimeout(ctx, 3*time.Minute)
  385. defer cancel()
  386. for {
  387. c, err := (&net.Dialer{Timeout: 10 * time.Second}).DialContext(ctx, "tcp", "127.0.0.1:"+port)
  388. if err != nil {
  389. if operr, ok := err.(*net.OpError); ok {
  390. if syserr, ok := operr.Err.(*os.SyscallError); ok && syserr.Err == syscall.ECONNREFUSED {
  391. time.Sleep(100 * time.Millisecond)
  392. continue
  393. }
  394. }
  395. return err
  396. }
  397. return c.Close()
  398. }
  399. }