github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/p2p/simulations/network_test.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 19:16:42</date>
    10  //</624450107168788480>
    11  
    12  
    13  package simulations
    14  
    15  import (
    16  	"context"
    17  	"encoding/json"
    18  	"fmt"
    19  	"strconv"
    20  	"strings"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/ethereum/go-ethereum/log"
    25  	"github.com/ethereum/go-ethereum/node"
    26  	"github.com/ethereum/go-ethereum/p2p/enode"
    27  	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
    28  )
    29  
    30  //测试使用最小服务创建的快照只包含预期的连接
    31  //当加载此快照时,网络仅包含这些相同的连接
    32  func TestSnapshot(t *testing.T) {
    33  
    34  //第一部分
    35  //从环网创建快照
    36  
    37  //这是一个最小的服务,其协议在退出前只接受一条消息或关闭连接
    38  	adapter := adapters.NewSimAdapter(adapters.Services{
    39  		"noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) {
    40  			return NewNoopService(nil), nil
    41  		},
    42  	})
    43  
    44  //创建网络
    45  	network := NewNetwork(adapter, &NetworkConfig{
    46  		DefaultService: "noopwoop",
    47  	})
    48  //\t要考虑成为网络成员,请在关机时设置为true threadsafe
    49  	runningOne := true
    50  	defer func() {
    51  		if runningOne {
    52  			network.Shutdown()
    53  		}
    54  	}()
    55  
    56  //创建和启动节点
    57  	nodeCount := 20
    58  	ids := make([]enode.ID, nodeCount)
    59  	for i := 0; i < nodeCount; i++ {
    60  		conf := adapters.RandomNodeConfig()
    61  		node, err := network.NewNodeWithConfig(conf)
    62  		if err != nil {
    63  			t.Fatalf("error creating node: %s", err)
    64  		}
    65  		if err := network.Start(node.ID()); err != nil {
    66  			t.Fatalf("error starting node: %s", err)
    67  		}
    68  		ids[i] = node.ID()
    69  	}
    70  
    71  //订阅对等事件
    72  	evC := make(chan *Event)
    73  	sub := network.Events().Subscribe(evC)
    74  	defer sub.Unsubscribe()
    75  
    76  //连接环中的节点
    77  //生成单独的线程以避免事件侦听器中的死锁
    78  	go func() {
    79  		for i, id := range ids {
    80  			peerID := ids[(i+1)%len(ids)]
    81  			if err := network.Connect(id, peerID); err != nil {
    82  				t.Fatal(err)
    83  			}
    84  		}
    85  	}()
    86  
    87  //收集达到预期数量的连接事件
    88  	ctx, cancel := context.WithTimeout(context.TODO(), time.Second)
    89  	defer cancel()
    90  	checkIds := make(map[enode.ID][]enode.ID)
    91  	connEventCount := nodeCount
    92  OUTER:
    93  	for {
    94  		select {
    95  		case <-ctx.Done():
    96  			t.Fatal(ctx.Err())
    97  		case ev := <-evC:
    98  			if ev.Type == EventTypeConn && !ev.Control {
    99  
   100  //断开时失败
   101  				if !ev.Conn.Up {
   102  					t.Fatalf("unexpected disconnect: %v -> %v", ev.Conn.One, ev.Conn.Other)
   103  				}
   104  				checkIds[ev.Conn.One] = append(checkIds[ev.Conn.One], ev.Conn.Other)
   105  				checkIds[ev.Conn.Other] = append(checkIds[ev.Conn.Other], ev.Conn.One)
   106  				connEventCount--
   107  				log.Debug("ev", "count", connEventCount)
   108  				if connEventCount == 0 {
   109  					break OUTER
   110  				}
   111  			}
   112  		}
   113  	}
   114  
   115  //创建当前网络的快照
   116  	snap, err := network.Snapshot()
   117  	if err != nil {
   118  		t.Fatal(err)
   119  	}
   120  	j, err := json.Marshal(snap)
   121  	if err != nil {
   122  		t.Fatal(err)
   123  	}
   124  	log.Debug("snapshot taken", "nodes", len(snap.Nodes), "conns", len(snap.Conns), "json", string(j))
   125  
   126  //验证对齐元素编号是否签出
   127  	if len(checkIds) != len(snap.Conns) || len(checkIds) != len(snap.Nodes) {
   128  		t.Fatalf("snapshot wrong node,conn counts %d,%d != %d", len(snap.Nodes), len(snap.Conns), len(checkIds))
   129  	}
   130  
   131  //关闭SIM网络
   132  	runningOne = false
   133  	sub.Unsubscribe()
   134  	network.Shutdown()
   135  
   136  //检查快照中是否有所有预期的连接
   137  	for nodid, nodConns := range checkIds {
   138  		for _, nodConn := range nodConns {
   139  			var match bool
   140  			for _, snapConn := range snap.Conns {
   141  				if snapConn.One == nodid && snapConn.Other == nodConn {
   142  					match = true
   143  					break
   144  				} else if snapConn.Other == nodid && snapConn.One == nodConn {
   145  					match = true
   146  					break
   147  				}
   148  			}
   149  			if !match {
   150  				t.Fatalf("snapshot missing conn %v -> %v", nodid, nodConn)
   151  			}
   152  		}
   153  	}
   154  	log.Info("snapshot checked")
   155  
   156  //第二部分
   157  //加载快照并验证是否形成完全相同的连接
   158  
   159  	adapter = adapters.NewSimAdapter(adapters.Services{
   160  		"noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) {
   161  			return NewNoopService(nil), nil
   162  		},
   163  	})
   164  	network = NewNetwork(adapter, &NetworkConfig{
   165  		DefaultService: "noopwoop",
   166  	})
   167  	defer func() {
   168  		network.Shutdown()
   169  	}()
   170  
   171  //订阅对等事件
   172  //每个节点启动和连接事件将生成一个额外的控制事件
   173  //因此,将计数乘以2
   174  	evC = make(chan *Event, (len(snap.Conns)*2)+(len(snap.Nodes)*2))
   175  	sub = network.Events().Subscribe(evC)
   176  	defer sub.Unsubscribe()
   177  
   178  //加载快照
   179  //生成单独的线程以避免事件侦听器中的死锁
   180  	err = network.Load(snap)
   181  	if err != nil {
   182  		t.Fatal(err)
   183  	}
   184  
   185  //收集达到预期数量的连接事件
   186  	ctx, cancel = context.WithTimeout(context.TODO(), time.Second*3)
   187  	defer cancel()
   188  
   189  	connEventCount = nodeCount
   190  
   191  OUTER_TWO:
   192  	for {
   193  		select {
   194  		case <-ctx.Done():
   195  			t.Fatal(ctx.Err())
   196  		case ev := <-evC:
   197  			if ev.Type == EventTypeConn && !ev.Control {
   198  
   199  //断开时失败
   200  				if !ev.Conn.Up {
   201  					t.Fatalf("unexpected disconnect: %v -> %v", ev.Conn.One, ev.Conn.Other)
   202  				}
   203  				log.Debug("conn", "on", ev.Conn.One, "other", ev.Conn.Other)
   204  				checkIds[ev.Conn.One] = append(checkIds[ev.Conn.One], ev.Conn.Other)
   205  				checkIds[ev.Conn.Other] = append(checkIds[ev.Conn.Other], ev.Conn.One)
   206  				connEventCount--
   207  				log.Debug("ev", "count", connEventCount)
   208  				if connEventCount == 0 {
   209  					break OUTER_TWO
   210  				}
   211  			}
   212  		}
   213  	}
   214  
   215  //检查网络中的所有预期连接
   216  	for _, snapConn := range snap.Conns {
   217  		var match bool
   218  		for nodid, nodConns := range checkIds {
   219  			for _, nodConn := range nodConns {
   220  				if snapConn.One == nodid && snapConn.Other == nodConn {
   221  					match = true
   222  					break
   223  				} else if snapConn.Other == nodid && snapConn.One == nodConn {
   224  					match = true
   225  					break
   226  				}
   227  			}
   228  		}
   229  		if !match {
   230  			t.Fatalf("network missing conn %v -> %v", snapConn.One, snapConn.Other)
   231  		}
   232  	}
   233  
   234  //验证在合理时间内收集到的连接事件之后,网络没有生成任何其他附加连接事件。
   235  	ctx, cancel = context.WithTimeout(context.TODO(), time.Second)
   236  	defer cancel()
   237  	select {
   238  	case <-ctx.Done():
   239  	case ev := <-evC:
   240  		if ev.Type == EventTypeConn {
   241  			t.Fatalf("Superfluous conn found %v -> %v", ev.Conn.One, ev.Conn.Other)
   242  		}
   243  	}
   244  
   245  //此测试验证快照中的所有连接
   246  //在网络中创建。
   247  	t.Run("conns after load", func(t *testing.T) {
   248  //创建新网络。
   249  		n := NewNetwork(
   250  			adapters.NewSimAdapter(adapters.Services{
   251  				"noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) {
   252  					return NewNoopService(nil), nil
   253  				},
   254  			}),
   255  			&NetworkConfig{
   256  				DefaultService: "noopwoop",
   257  			},
   258  		)
   259  		defer n.Shutdown()
   260  
   261  //加载同一快照。
   262  		err := n.Load(snap)
   263  		if err != nil {
   264  			t.Fatal(err)
   265  		}
   266  
   267  //检查快照中的每个连接
   268  //如果它也在网络中。
   269  		for _, c := range snap.Conns {
   270  			if n.GetConn(c.One, c.Other) == nil {
   271  				t.Errorf("missing connection: %s -> %s", c.One, c.Other)
   272  			}
   273  		}
   274  	})
   275  }
   276  
   277  //TestNetworkSimulation使用每个节点创建多节点仿真网络
   278  //在环形拓扑中连接,检查所有节点是否成功握手
   279  //彼此之间,快照完全代表所需的拓扑
   280  func TestNetworkSimulation(t *testing.T) {
   281  //使用20个testservice节点创建模拟网络
   282  	adapter := adapters.NewSimAdapter(adapters.Services{
   283  		"test": newTestService,
   284  	})
   285  	network := NewNetwork(adapter, &NetworkConfig{
   286  		DefaultService: "test",
   287  	})
   288  	defer network.Shutdown()
   289  	nodeCount := 20
   290  	ids := make([]enode.ID, nodeCount)
   291  	for i := 0; i < nodeCount; i++ {
   292  		conf := adapters.RandomNodeConfig()
   293  		node, err := network.NewNodeWithConfig(conf)
   294  		if err != nil {
   295  			t.Fatalf("error creating node: %s", err)
   296  		}
   297  		if err := network.Start(node.ID()); err != nil {
   298  			t.Fatalf("error starting node: %s", err)
   299  		}
   300  		ids[i] = node.ID()
   301  	}
   302  
   303  //执行连接环中节点的检查(因此每个节点
   304  //然后检查所有节点
   305  //通过检查他们的对等计数进行了两次握手
   306  	action := func(_ context.Context) error {
   307  		for i, id := range ids {
   308  			peerID := ids[(i+1)%len(ids)]
   309  			if err := network.Connect(id, peerID); err != nil {
   310  				return err
   311  			}
   312  		}
   313  		return nil
   314  	}
   315  	check := func(ctx context.Context, id enode.ID) (bool, error) {
   316  //检查一下我们的时间没有用完
   317  		select {
   318  		case <-ctx.Done():
   319  			return false, ctx.Err()
   320  		default:
   321  		}
   322  
   323  //获取节点
   324  		node := network.GetNode(id)
   325  		if node == nil {
   326  			return false, fmt.Errorf("unknown node: %s", id)
   327  		}
   328  
   329  //检查它是否有两个同龄人
   330  		client, err := node.Client()
   331  		if err != nil {
   332  			return false, err
   333  		}
   334  		var peerCount int64
   335  		if err := client.CallContext(ctx, &peerCount, "test_peerCount"); err != nil {
   336  			return false, err
   337  		}
   338  		switch {
   339  		case peerCount < 2:
   340  			return false, nil
   341  		case peerCount == 2:
   342  			return true, nil
   343  		default:
   344  			return false, fmt.Errorf("unexpected peerCount: %d", peerCount)
   345  		}
   346  	}
   347  
   348  	timeout := 30 * time.Second
   349  	ctx, cancel := context.WithTimeout(context.Background(), timeout)
   350  	defer cancel()
   351  
   352  //每100毫秒触发一次检查
   353  	trigger := make(chan enode.ID)
   354  	go triggerChecks(ctx, ids, trigger, 100*time.Millisecond)
   355  
   356  	result := NewSimulation(network).Run(ctx, &Step{
   357  		Action:  action,
   358  		Trigger: trigger,
   359  		Expect: &Expectation{
   360  			Nodes: ids,
   361  			Check: check,
   362  		},
   363  	})
   364  	if result.Error != nil {
   365  		t.Fatalf("simulation failed: %s", result.Error)
   366  	}
   367  
   368  //获取网络快照并检查它是否包含正确的拓扑
   369  	snap, err := network.Snapshot()
   370  	if err != nil {
   371  		t.Fatal(err)
   372  	}
   373  	if len(snap.Nodes) != nodeCount {
   374  		t.Fatalf("expected snapshot to contain %d nodes, got %d", nodeCount, len(snap.Nodes))
   375  	}
   376  	if len(snap.Conns) != nodeCount {
   377  		t.Fatalf("expected snapshot to contain %d connections, got %d", nodeCount, len(snap.Conns))
   378  	}
   379  	for i, id := range ids {
   380  		conn := snap.Conns[i]
   381  		if conn.One != id {
   382  			t.Fatalf("expected conn[%d].One to be %s, got %s", i, id, conn.One)
   383  		}
   384  		peerID := ids[(i+1)%len(ids)]
   385  		if conn.Other != peerID {
   386  			t.Fatalf("expected conn[%d].Other to be %s, got %s", i, peerID, conn.Other)
   387  		}
   388  	}
   389  }
   390  
   391  func triggerChecks(ctx context.Context, ids []enode.ID, trigger chan enode.ID, interval time.Duration) {
   392  	tick := time.NewTicker(interval)
   393  	defer tick.Stop()
   394  	for {
   395  		select {
   396  		case <-tick.C:
   397  			for _, id := range ids {
   398  				select {
   399  				case trigger <- id:
   400  				case <-ctx.Done():
   401  					return
   402  				}
   403  			}
   404  		case <-ctx.Done():
   405  			return
   406  		}
   407  	}
   408  }
   409  
   410  //\todo:重构以实现shapshots
   411  //一旦将配置方法从
   412  //swarm/网络/仿真/connect.go
   413  func BenchmarkMinimalService(b *testing.B) {
   414  	b.Run("ring/32", benchmarkMinimalServiceTmp)
   415  }
   416  
   417  func benchmarkMinimalServiceTmp(b *testing.B) {
   418  
   419  //停止计时器以丢弃设置时间污染
   420  	args := strings.Split(b.Name(), "/")
   421  	nodeCount, err := strconv.ParseInt(args[2], 10, 16)
   422  	if err != nil {
   423  		b.Fatal(err)
   424  	}
   425  
   426  	for i := 0; i < b.N; i++ {
   427  //这是一个最小的服务,其协议将在协议运行时关闭通道。
   428  //使服务启动和协议实际运行所需的时间成为可能
   429  		protoCMap := make(map[enode.ID]map[enode.ID]chan struct{})
   430  		adapter := adapters.NewSimAdapter(adapters.Services{
   431  			"noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) {
   432  				protoCMap[ctx.Config.ID] = make(map[enode.ID]chan struct{})
   433  				svc := NewNoopService(protoCMap[ctx.Config.ID])
   434  				return svc, nil
   435  			},
   436  		})
   437  
   438  //创建网络
   439  		network := NewNetwork(adapter, &NetworkConfig{
   440  			DefaultService: "noopwoop",
   441  		})
   442  		defer network.Shutdown()
   443  
   444  //创建和启动节点
   445  		ids := make([]enode.ID, nodeCount)
   446  		for i := 0; i < int(nodeCount); i++ {
   447  			conf := adapters.RandomNodeConfig()
   448  			node, err := network.NewNodeWithConfig(conf)
   449  			if err != nil {
   450  				b.Fatalf("error creating node: %s", err)
   451  			}
   452  			if err := network.Start(node.ID()); err != nil {
   453  				b.Fatalf("error starting node: %s", err)
   454  			}
   455  			ids[i] = node.ID()
   456  		}
   457  
   458  //准备,设置,去
   459  		b.ResetTimer()
   460  
   461  //连接环中的节点
   462  		for i, id := range ids {
   463  			peerID := ids[(i+1)%len(ids)]
   464  			if err := network.Connect(id, peerID); err != nil {
   465  				b.Fatal(err)
   466  			}
   467  		}
   468  
   469  //等待所有协议发出关闭信号
   470  		ctx, cancel := context.WithTimeout(context.TODO(), time.Second)
   471  		defer cancel()
   472  		for nodid, peers := range protoCMap {
   473  			for peerid, peerC := range peers {
   474  				log.Debug("getting ", "node", nodid, "peer", peerid)
   475  				select {
   476  				case <-ctx.Done():
   477  					b.Fatal(ctx.Err())
   478  				case <-peerC:
   479  				}
   480  			}
   481  		}
   482  	}
   483  }
   484