github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/cmd/swarm/run_test.go (about)

     1  
     2  //此源码被清华学神尹成大魔王专业翻译分析并修改
     3  //尹成QQ77025077
     4  //尹成微信18510341407
     5  //尹成所在QQ群721929980
     6  //尹成邮箱 yinc13@mails.tsinghua.edu.cn
     7  //尹成毕业于清华大学,微软区块链领域全球最有价值专家
     8  //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620
     9  //版权所有2017 Go Ethereum作者
    10  //此文件是Go以太坊的一部分。
    11  //
    12  //Go以太坊是免费软件:您可以重新发布和/或修改它
    13  //根据GNU通用公共许可证的条款
    14  //自由软件基金会,或者许可证的第3版,或者
    15  //(由您选择)任何更高版本。
    16  //
    17  //Go以太坊的分布希望它会有用,
    18  //但没有任何保证;甚至没有
    19  //适销性或特定用途的适用性。见
    20  //GNU通用公共许可证了解更多详细信息。
    21  //
    22  //你应该已经收到一份GNU通用公共许可证的副本
    23  //一起去以太坊吧。如果没有,请参见<http://www.gnu.org/licenses/>。
    24  
    25  package main
    26  
    27  import (
    28  	"context"
    29  	"crypto/ecdsa"
    30  	"fmt"
    31  	"io/ioutil"
    32  	"net"
    33  	"os"
    34  	"path"
    35  	"path/filepath"
    36  	"runtime"
    37  	"sync"
    38  	"syscall"
    39  	"testing"
    40  	"time"
    41  
    42  	"github.com/docker/docker/pkg/reexec"
    43  	"github.com/ethereum/go-ethereum/accounts"
    44  	"github.com/ethereum/go-ethereum/accounts/keystore"
    45  	"github.com/ethereum/go-ethereum/internal/cmdtest"
    46  	"github.com/ethereum/go-ethereum/node"
    47  	"github.com/ethereum/go-ethereum/p2p"
    48  	"github.com/ethereum/go-ethereum/rpc"
    49  	"github.com/ethereum/go-ethereum/swarm"
    50  )
    51  
    52  func init() {
    53  //如果我们在run swarm中被执行为“swarm测试”,就运行这个应用程序。
    54  	reexec.Register("swarm-test", func() {
    55  		if err := app.Run(os.Args); err != nil {
    56  			fmt.Fprintln(os.Stderr, err)
    57  			os.Exit(1)
    58  		}
    59  		os.Exit(0)
    60  	})
    61  }
    62  
    63  func TestMain(m *testing.M) {
    64  //检查我们是否被重新执行了
    65  	if reexec.Init() {
    66  		return
    67  	}
    68  	os.Exit(m.Run())
    69  }
    70  
    71  func runSwarm(t *testing.T, args ...string) *cmdtest.TestCmd {
    72  	tt := cmdtest.NewTestCmd(t, nil)
    73  
    74  //
    75  //函数将阻止任何测试运行。
    76  	tt.Run("swarm-test", args...)
    77  
    78  	return tt
    79  }
    80  
    81  type testCluster struct {
    82  	Nodes  []*testNode
    83  	TmpDir string
    84  }
    85  
    86  //
    87  //
    88  //
    89  //它。
    90  //
    91  //
    92  //
    93  //
    94  //
    95  //
    96  //
    97  
    98  func newTestCluster(t *testing.T, size int) *testCluster {
    99  	cluster := &testCluster{}
   100  	defer func() {
   101  		if t.Failed() {
   102  			cluster.Shutdown()
   103  		}
   104  	}()
   105  
   106  	tmpdir, err := ioutil.TempDir("", "swarm-test")
   107  	if err != nil {
   108  		t.Fatal(err)
   109  	}
   110  	cluster.TmpDir = tmpdir
   111  
   112  //
   113  	cluster.StartNewNodes(t, size)
   114  
   115  	if size == 1 {
   116  		return cluster
   117  	}
   118  
   119  //
   120  	for _, node := range cluster.Nodes {
   121  		if err := node.Client.Call(nil, "admin_addPeer", cluster.Nodes[0].Enode); err != nil {
   122  			t.Fatal(err)
   123  		}
   124  	}
   125  
   126  //
   127  outer:
   128  	for _, node := range cluster.Nodes {
   129  		var peers []*p2p.PeerInfo
   130  		for start := time.Now(); time.Since(start) < time.Minute; time.Sleep(50 * time.Millisecond) {
   131  			if err := node.Client.Call(&peers, "admin_peers"); err != nil {
   132  				t.Fatal(err)
   133  			}
   134  			if len(peers) == len(cluster.Nodes)-1 {
   135  				continue outer
   136  			}
   137  		}
   138  		t.Fatalf("%s only has %d / %d peers", node.Name, len(peers), len(cluster.Nodes)-1)
   139  	}
   140  
   141  	return cluster
   142  }
   143  
   144  func (c *testCluster) Shutdown() {
   145  	for _, node := range c.Nodes {
   146  		node.Shutdown()
   147  	}
   148  	os.RemoveAll(c.TmpDir)
   149  }
   150  
   151  func (c *testCluster) Stop() {
   152  	for _, node := range c.Nodes {
   153  		node.Shutdown()
   154  	}
   155  }
   156  
   157  func (c *testCluster) StartNewNodes(t *testing.T, size int) {
   158  	c.Nodes = make([]*testNode, 0, size)
   159  	for i := 0; i < size; i++ {
   160  		dir := filepath.Join(c.TmpDir, fmt.Sprintf("swarm%02d", i))
   161  		if err := os.Mkdir(dir, 0700); err != nil {
   162  			t.Fatal(err)
   163  		}
   164  
   165  		node := newTestNode(t, dir)
   166  		node.Name = fmt.Sprintf("swarm%02d", i)
   167  
   168  		c.Nodes = append(c.Nodes, node)
   169  	}
   170  }
   171  
   172  func (c *testCluster) StartExistingNodes(t *testing.T, size int, bzzaccount string) {
   173  	c.Nodes = make([]*testNode, 0, size)
   174  	for i := 0; i < size; i++ {
   175  		dir := filepath.Join(c.TmpDir, fmt.Sprintf("swarm%02d", i))
   176  		node := existingTestNode(t, dir, bzzaccount)
   177  		node.Name = fmt.Sprintf("swarm%02d", i)
   178  
   179  		c.Nodes = append(c.Nodes, node)
   180  	}
   181  }
   182  
   183  func (c *testCluster) Cleanup() {
   184  	os.RemoveAll(c.TmpDir)
   185  }
   186  
   187  type testNode struct {
   188  	Name       string
   189  	Addr       string
   190  	URL        string
   191  	Enode      string
   192  	Dir        string
   193  	IpcPath    string
   194  	PrivateKey *ecdsa.PrivateKey
   195  	Client     *rpc.Client
   196  	Cmd        *cmdtest.TestCmd
   197  }
   198  
   199  const testPassphrase = "swarm-test-passphrase"
   200  
   201  func getTestAccount(t *testing.T, dir string) (conf *node.Config, account accounts.Account) {
   202  //创建密钥
   203  	conf = &node.Config{
   204  		DataDir: dir,
   205  		IPCPath: "bzzd.ipc",
   206  		NoUSB:   true,
   207  	}
   208  	n, err := node.New(conf)
   209  	if err != nil {
   210  		t.Fatal(err)
   211  	}
   212  	account, err = n.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore).NewAccount(testPassphrase)
   213  	if err != nil {
   214  		t.Fatal(err)
   215  	}
   216  
   217  //在Windows上运行测试时使用唯一的ipcpath
   218  	if runtime.GOOS == "windows" {
   219  		conf.IPCPath = fmt.Sprintf("bzzd-%s.ipc", account.Address.String())
   220  	}
   221  
   222  	return conf, account
   223  }
   224  
   225  func existingTestNode(t *testing.T, dir string, bzzaccount string) *testNode {
   226  	conf, _ := getTestAccount(t, dir)
   227  	node := &testNode{Dir: dir}
   228  
   229  //在Windows上运行测试时使用唯一的ipcpath
   230  	if runtime.GOOS == "windows" {
   231  		conf.IPCPath = fmt.Sprintf("bzzd-%s.ipc", bzzaccount)
   232  	}
   233  
   234  //指定端口
   235  	ports, err := getAvailableTCPPorts(2)
   236  	if err != nil {
   237  		t.Fatal(err)
   238  	}
   239  	p2pPort := ports[0]
   240  	httpPort := ports[1]
   241  
   242  //启动节点
   243  	node.Cmd = runSwarm(t,
   244  		"--port", p2pPort,
   245  		"--nodiscover",
   246  		"--datadir", dir,
   247  		"--ipcpath", conf.IPCPath,
   248  		"--ens-api", "",
   249  		"--bzzaccount", bzzaccount,
   250  		"--bzznetworkid", "321",
   251  		"--bzzport", httpPort,
   252  		"--verbosity", "6",
   253  	)
   254  	node.Cmd.InputLine(testPassphrase)
   255  	defer func() {
   256  		if t.Failed() {
   257  			node.Shutdown()
   258  		}
   259  	}()
   260  
   261  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
   262  	defer cancel()
   263  
   264  //确保所有端口都有活动的侦听器
   265  //这样下一个节点就不会得到相同的
   266  //调用GetAvailableTCPPorts时
   267  	err = waitTCPPorts(ctx, ports...)
   268  	if err != nil {
   269  		t.Fatal(err)
   270  	}
   271  
   272  //等待节点启动
   273  	for start := time.Now(); time.Since(start) < 10*time.Second; time.Sleep(50 * time.Millisecond) {
   274  		node.Client, err = rpc.Dial(conf.IPCEndpoint())
   275  		if err == nil {
   276  			break
   277  		}
   278  	}
   279  	if node.Client == nil {
   280  		t.Fatal(err)
   281  	}
   282  
   283  //加载信息
   284  	var info swarm.Info
   285  	if err := node.Client.Call(&info, "bzz_info"); err != nil {
   286  		t.Fatal(err)
   287  	}
   288  	node.Addr = net.JoinHostPort("127.0.0.1", info.Port)
   289  node.URL = "http://“+No.ADDR”
   290  
   291  	var nodeInfo p2p.NodeInfo
   292  	if err := node.Client.Call(&nodeInfo, "admin_nodeInfo"); err != nil {
   293  		t.Fatal(err)
   294  	}
   295  node.Enode = fmt.Sprintf("enode://%s@127.0.0.1:%s”,nodeinfo.id,p2pport)
   296  
   297  	return node
   298  }
   299  
   300  func newTestNode(t *testing.T, dir string) *testNode {
   301  
   302  	conf, account := getTestAccount(t, dir)
   303  	ks := keystore.NewKeyStore(path.Join(dir, "keystore"), 1<<18, 1)
   304  
   305  	pk := decryptStoreAccount(ks, account.Address.Hex(), []string{testPassphrase})
   306  
   307  	node := &testNode{Dir: dir, PrivateKey: pk}
   308  
   309  //指定端口
   310  	ports, err := getAvailableTCPPorts(2)
   311  	if err != nil {
   312  		t.Fatal(err)
   313  	}
   314  	p2pPort := ports[0]
   315  	httpPort := ports[1]
   316  
   317  //启动节点
   318  	node.Cmd = runSwarm(t,
   319  		"--port", p2pPort,
   320  		"--nodiscover",
   321  		"--datadir", dir,
   322  		"--ipcpath", conf.IPCPath,
   323  		"--ens-api", "",
   324  		"--bzzaccount", account.Address.String(),
   325  		"--bzznetworkid", "321",
   326  		"--bzzport", httpPort,
   327  		"--verbosity", "6",
   328  	)
   329  	node.Cmd.InputLine(testPassphrase)
   330  	defer func() {
   331  		if t.Failed() {
   332  			node.Shutdown()
   333  		}
   334  	}()
   335  
   336  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
   337  	defer cancel()
   338  
   339  //确保所有端口都有活动的侦听器
   340  //这样下一个节点就不会得到相同的
   341  //调用GetAvailableTCPPorts时
   342  	err = waitTCPPorts(ctx, ports...)
   343  	if err != nil {
   344  		t.Fatal(err)
   345  	}
   346  
   347  //等待节点启动
   348  	for start := time.Now(); time.Since(start) < 10*time.Second; time.Sleep(50 * time.Millisecond) {
   349  		node.Client, err = rpc.Dial(conf.IPCEndpoint())
   350  		if err == nil {
   351  			break
   352  		}
   353  	}
   354  	if node.Client == nil {
   355  		t.Fatal(err)
   356  	}
   357  
   358  //加载信息
   359  	var info swarm.Info
   360  	if err := node.Client.Call(&info, "bzz_info"); err != nil {
   361  		t.Fatal(err)
   362  	}
   363  	node.Addr = net.JoinHostPort("127.0.0.1", info.Port)
   364  node.URL = "http://“+No.ADDR”
   365  
   366  	var nodeInfo p2p.NodeInfo
   367  	if err := node.Client.Call(&nodeInfo, "admin_nodeInfo"); err != nil {
   368  		t.Fatal(err)
   369  	}
   370  node.Enode = fmt.Sprintf("enode://%s@127.0.0.1:%s”,nodeinfo.id,p2pport)
   371  	node.IpcPath = conf.IPCPath
   372  
   373  	return node
   374  }
   375  
   376  func (n *testNode) Shutdown() {
   377  	if n.Cmd != nil {
   378  		n.Cmd.Kill()
   379  	}
   380  }
   381  
   382  //
   383  //
   384  //
   385  //
   386  //并保证同一港口将被运回
   387  //不同的调用,因为侦听器在函数内关闭,
   388  //
   389  //可用端口。
   390  func getAvailableTCPPorts(count int) (ports []string, err error) {
   391  	for i := 0; i < count; i++ {
   392  		l, err := net.Listen("tcp", "127.0.0.1:0")
   393  		if err != nil {
   394  			return nil, err
   395  		}
   396  //在循环中延迟关闭以确保同一端口不会
   397  //在下一个迭代中被选择
   398  		defer l.Close()
   399  
   400  		_, port, err := net.SplitHostPort(l.Addr().String())
   401  		if err != nil {
   402  			return nil, err
   403  		}
   404  		ports = append(ports, port)
   405  	}
   406  	return ports, nil
   407  }
   408  
   409  //
   410  //
   411  //
   412  //遇到错误。
   413  //另请参见waitcpport。
   414  func waitTCPPorts(ctx context.Context, ports ...string) error {
   415  	var err error
   416  //在中分配的mu locks err变量
   417  //其他Goroutines
   418  	var mu sync.Mutex
   419  
   420  //
   421  //
   422  //防止不必要的等待
   423  	ctx, cancel := context.WithCancel(ctx)
   424  	defer cancel()
   425  
   426  	var wg sync.WaitGroup
   427  	for _, port := range ports {
   428  		wg.Add(1)
   429  		go func(port string) {
   430  			defer wg.Done()
   431  
   432  			e := waitTCPPort(ctx, port)
   433  
   434  			mu.Lock()
   435  			defer mu.Unlock()
   436  			if e != nil && err == nil {
   437  				err = e
   438  				cancel()
   439  			}
   440  		}(port)
   441  	}
   442  	wg.Wait()
   443  
   444  	return err
   445  }
   446  
   447  //
   448  //ONA提供的端口。它最多有3分钟的超时时间,
   449  //
   450  //提供的上下文实例。拨号程序超时10秒
   451  //在每次迭代中,连接被拒绝的错误将
   452  //在100毫秒内重试。
   453  func waitTCPPort(ctx context.Context, port string) error {
   454  	ctx, cancel := context.WithTimeout(ctx, 3*time.Minute)
   455  	defer cancel()
   456  
   457  	for {
   458  		c, err := (&net.Dialer{Timeout: 10 * time.Second}).DialContext(ctx, "tcp", "127.0.0.1:"+port)
   459  		if err != nil {
   460  			if operr, ok := err.(*net.OpError); ok {
   461  				if syserr, ok := operr.Err.(*os.SyscallError); ok && syserr.Err == syscall.ECONNREFUSED {
   462  					time.Sleep(100 * time.Millisecond)
   463  					continue
   464  				}
   465  			}
   466  			return err
   467  		}
   468  		return c.Close()
   469  	}
   470  }