run_test.go 11 KB

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