github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/swarm/network/stream/visualized_snapshot_sync_sim_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:44</date>
    10  //</624450116014575616>
    11  
    12  
    13  //+使用服务器生成
    14  
    15  package stream
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"os"
    24  	"sync"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/ethereum/go-ethereum/node"
    29  	"github.com/ethereum/go-ethereum/p2p"
    30  	"github.com/ethereum/go-ethereum/p2p/enode"
    31  	"github.com/ethereum/go-ethereum/p2p/protocols"
    32  	"github.com/ethereum/go-ethereum/p2p/simulations"
    33  	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
    34  	"github.com/ethereum/go-ethereum/rlp"
    35  	"github.com/ethereum/go-ethereum/swarm/log"
    36  	"github.com/ethereum/go-ethereum/swarm/network"
    37  	"github.com/ethereum/go-ethereum/swarm/network/simulation"
    38  	"github.com/ethereum/go-ethereum/swarm/state"
    39  	"github.com/ethereum/go-ethereum/swarm/storage"
    40  )
    41  
    42  /*
    43  此文件中的测试需要使用
    44  
    45     -tags=带服务器
    46  
    47  另外,如果单独执行,它们将暂停,因为它们等待
    48  用于可视化前端发送post/runsim消息。
    49  **/
    50  
    51  
    52  //设置SIM,评估nodeCount和chunkCount并创建SIM
    53  func setupSim(serviceMap map[string]simulation.ServiceFunc) (int, int, *simulation.Simulation) {
    54  	nodeCount := *nodes
    55  	chunkCount := *chunks
    56  
    57  	if nodeCount == 0 || chunkCount == 0 {
    58  		nodeCount = 32
    59  		chunkCount = 1
    60  	}
    61  
    62  //使用服务器设置模拟,这意味着SIM卡无法运行
    63  //直到它从前端接收到一个post/runsim
    64  	sim := simulation.New(serviceMap).WithServer(":8888")
    65  	return nodeCount, chunkCount, sim
    66  }
    67  
    68  //注意断开连接并等待健康
    69  func watchSim(sim *simulation.Simulation) (context.Context, context.CancelFunc) {
    70  	ctx, cancelSimRun := context.WithTimeout(context.Background(), 1*time.Minute)
    71  
    72  	if _, err := sim.WaitTillHealthy(ctx); err != nil {
    73  		panic(err)
    74  	}
    75  
    76  	disconnections := sim.PeerEvents(
    77  		context.Background(),
    78  		sim.NodeIDs(),
    79  		simulation.NewPeerEventsFilter().Drop(),
    80  	)
    81  
    82  	go func() {
    83  		for d := range disconnections {
    84  			log.Error("peer drop", "node", d.NodeID, "peer", d.PeerID)
    85  			panic("unexpected disconnect")
    86  			cancelSimRun()
    87  		}
    88  	}()
    89  
    90  	return ctx, cancelSimRun
    91  }
    92  
    93  //此测试请求网络中存在伪造哈希
    94  func TestNonExistingHashesWithServer(t *testing.T) {
    95  
    96  	nodeCount, _, sim := setupSim(retrievalSimServiceMap)
    97  	defer sim.Close()
    98  
    99  	err := sim.UploadSnapshot(fmt.Sprintf("testing/snapshot_%d.json", nodeCount))
   100  	if err != nil {
   101  		panic(err)
   102  	}
   103  
   104  	ctx, cancelSimRun := watchSim(sim)
   105  	defer cancelSimRun()
   106  
   107  //为了获得一些有意义的可视化效果,这是有益的
   108  //定义此测试的最短持续时间
   109  	testDuration := 20 * time.Second
   110  
   111  	result := sim.Run(ctx, func(ctx context.Context, sim *simulation.Simulation) error {
   112  //检查节点的文件存储(netstore)
   113  		id := sim.Net.GetRandomUpNode().ID()
   114  		item, ok := sim.NodeItem(id, bucketKeyFileStore)
   115  		if !ok {
   116  			t.Fatalf("No filestore")
   117  		}
   118  		fileStore := item.(*storage.FileStore)
   119  //创建伪哈希
   120  		fakeHash := storage.GenerateRandomChunk(1000).Address()
   121  //尝试检索它-将把retrieverequestmsg传播到网络中
   122  		reader, _ := fileStore.Retrieve(context.TODO(), fakeHash)
   123  		if _, err := reader.Size(ctx, nil); err != nil {
   124  			log.Debug("expected error for non-existing chunk")
   125  		}
   126  //睡眠,以便前端可以显示一些内容
   127  		time.Sleep(testDuration)
   128  
   129  		return nil
   130  	})
   131  	if result.Error != nil {
   132  		sendSimTerminatedEvent(sim)
   133  		t.Fatal(result.Error)
   134  	}
   135  
   136  	sendSimTerminatedEvent(sim)
   137  
   138  }
   139  
   140  //向前端发送终止事件
   141  func sendSimTerminatedEvent(sim *simulation.Simulation) {
   142  	evt := &simulations.Event{
   143  		Type:    EventTypeSimTerminated,
   144  		Control: false,
   145  	}
   146  	sim.Net.Events().Send(evt)
   147  }
   148  
   149  //此测试与快照同步测试相同,
   150  //但是使用HTTP服务器
   151  //它还发送一些自定义事件,以便前端
   152  //可以可视化消息,如sendOfferedMsg、wantedHashesMsg、deliveryMsg
   153  func TestSnapshotSyncWithServer(t *testing.T) {
   154  //t.skip(“暂时禁用为Simulations.WaittillHealthy,不可信任”)。
   155  
   156  //定义一个包装对象以便能够传递数据
   157  	wrapper := &netWrapper{}
   158  
   159  	nodeCount := *nodes
   160  	chunkCount := *chunks
   161  
   162  	if nodeCount == 0 || chunkCount == 0 {
   163  		nodeCount = 32
   164  		chunkCount = 1
   165  	}
   166  
   167  	log.Info(fmt.Sprintf("Running the simulation with %d nodes and %d chunks", nodeCount, chunkCount))
   168  
   169  	sim := simulation.New(map[string]simulation.ServiceFunc{
   170  		"streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
   171  			n := ctx.Config.Node()
   172  			addr := network.NewAddr(n)
   173  			store, datadir, err := createTestLocalStorageForID(n.ID(), addr)
   174  			if err != nil {
   175  				return nil, nil, err
   176  			}
   177  			bucket.Store(bucketKeyStore, store)
   178  			localStore := store.(*storage.LocalStore)
   179  			netStore, err := storage.NewNetStore(localStore, nil)
   180  			if err != nil {
   181  				return nil, nil, err
   182  			}
   183  			kad := network.NewKademlia(addr.Over(), network.NewKadParams())
   184  			delivery := NewDelivery(kad, netStore)
   185  			netStore.NewNetFetcherFunc = network.NewFetcherFactory(dummyRequestFromPeers, true).New
   186  
   187  			r := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
   188  				Retrieval:       RetrievalDisabled,
   189  				Syncing:         SyncingAutoSubscribe,
   190  				SyncUpdateDelay: 3 * time.Second,
   191  			}, nil)
   192  
   193  			tr := &testRegistry{
   194  				Registry: r,
   195  				w:        wrapper,
   196  			}
   197  
   198  			bucket.Store(bucketKeyRegistry, tr)
   199  
   200  			cleanup = func() {
   201  				netStore.Close()
   202  				tr.Close()
   203  				os.RemoveAll(datadir)
   204  			}
   205  
   206  			return tr, cleanup, nil
   207  		},
   208  }).WithServer(":8888") //从HTTP服务器开始
   209  
   210  	nodeCount, chunkCount, sim := setupSim(simServiceMap)
   211  	defer sim.Close()
   212  
   213  	log.Info("Initializing test config")
   214  
   215  	conf := &synctestConfig{}
   216  //发现ID到该ID处预期的块索引的映射
   217  	conf.idToChunksMap = make(map[enode.ID][]int)
   218  //发现ID的覆盖地址映射
   219  	conf.addrToIDMap = make(map[string]enode.ID)
   220  //存储生成的块哈希的数组
   221  	conf.hashes = make([]storage.Address, 0)
   222  //将网络传递到包装对象
   223  	wrapper.setNetwork(sim.Net)
   224  	err := sim.UploadSnapshot(fmt.Sprintf("testing/snapshot_%d.json", nodeCount))
   225  	if err != nil {
   226  		panic(err)
   227  	}
   228  
   229  	ctx, cancelSimRun := watchSim(sim)
   230  	defer cancelSimRun()
   231  
   232  //运行SIM
   233  	result := runSim(conf, ctx, sim, chunkCount)
   234  
   235  //发送终止事件
   236  	evt := &simulations.Event{
   237  		Type:    EventTypeSimTerminated,
   238  		Control: false,
   239  	}
   240  	go sim.Net.Events().Send(evt)
   241  
   242  	if result.Error != nil {
   243  		panic(result.Error)
   244  	}
   245  	log.Info("Simulation ended")
   246  }
   247  
   248  //TestRegistry嵌入注册表
   249  //它允许替换协议运行功能
   250  type testRegistry struct {
   251  	*Registry
   252  	w *netWrapper
   253  }
   254  
   255  //协议替换协议的运行功能
   256  func (tr *testRegistry) Protocols() []p2p.Protocol {
   257  	regProto := tr.Registry.Protocols()
   258  //用testregistry的run函数设置'stream'协议的run函数
   259  	regProto[0].Run = tr.runProto
   260  	return regProto
   261  }
   262  
   263  //runproto是此测试的新覆盖协议的run函数
   264  func (tr *testRegistry) runProto(p *p2p.Peer, rw p2p.MsgReadWriter) error {
   265  //创建自定义的rw消息读写器
   266  	testRw := &testMsgReadWriter{
   267  		MsgReadWriter: rw,
   268  		Peer:          p,
   269  		w:             tr.w,
   270  		Registry:      tr.Registry,
   271  	}
   272  //现在运行实际的上层“注册表”的协议函数
   273  	return tr.runProtocol(p, testRw)
   274  }
   275  
   276  //testmsgreadwriter是一个自定义的rw
   277  //它将允许我们重复使用该消息两次
   278  type testMsgReadWriter struct {
   279  	*Registry
   280  	p2p.MsgReadWriter
   281  	*p2p.Peer
   282  	w *netWrapper
   283  }
   284  
   285  //NetRapper包装器对象,以便我们可以传递数据
   286  type netWrapper struct {
   287  	net *simulations.Network
   288  }
   289  
   290  //将网络设置为包装器以供以后使用(在自定义rw中使用)
   291  func (w *netWrapper) setNetwork(n *simulations.Network) {
   292  	w.net = n
   293  }
   294  
   295  //从包装器获取网络(在自定义rw中使用)
   296  func (w *netWrapper) getNetwork() *simulations.Network {
   297  	return w.net
   298  }
   299  
   300  //readmsg从基础msgreadwriter读取消息并发出
   301  //“收到消息”事件
   302  //我们这样做是因为我们对定制使用的消息的有效负载感兴趣
   303  //在此测试中,但消息只能使用一次(stream io.reader)
   304  func (ev *testMsgReadWriter) ReadMsg() (p2p.Msg, error) {
   305  //从底层的rw读取消息
   306  	msg, err := ev.MsgReadWriter.ReadMsg()
   307  	if err != nil {
   308  		return msg, err
   309  	}
   310  
   311  //不要对我们实际上不需要/不需要阅读的消息代码做任何事情
   312  	subCodes := []uint64{1, 2, 10}
   313  	found := false
   314  	for _, c := range subCodes {
   315  		if c == msg.Code {
   316  			found = true
   317  		}
   318  	}
   319  //如果不是我们感兴趣的消息代码,请返回
   320  	if !found {
   321  		return msg, nil
   322  	}
   323  
   324  //我们使用IO.teeReader,这样我们可以读两次消息
   325  //有效负载是一个IO.reader,所以如果我们从中读取,实际的协议处理程序
   326  //无法再访问它。
   327  //但是我们需要这个处理程序能够正常地使用消息,
   328  //好像我们不会在这里用那个信息做任何事情
   329  	var buf bytes.Buffer
   330  	tee := io.TeeReader(msg.Payload, &buf)
   331  
   332  	mcp := &p2p.Msg{
   333  		Code:       msg.Code,
   334  		Size:       msg.Size,
   335  		ReceivedAt: msg.ReceivedAt,
   336  		Payload:    tee,
   337  	}
   338  //分配副本供以后使用
   339  	msg.Payload = &buf
   340  
   341  //现在让我们看看这个消息
   342  	var wmsg protocols.WrappedMsg
   343  	err = mcp.Decode(&wmsg)
   344  	if err != nil {
   345  		log.Error(err.Error())
   346  		return msg, err
   347  	}
   348  //从代码创建新消息
   349  	val, ok := ev.Registry.GetSpec().NewMsg(mcp.Code)
   350  	if !ok {
   351  		return msg, errors.New(fmt.Sprintf("Invalid message code: %v", msg.Code))
   352  	}
   353  //解码它
   354  	if err := rlp.DecodeBytes(wmsg.Payload, val); err != nil {
   355  		return msg, errors.New(fmt.Sprintf("Decoding error <= %v: %v", msg, err))
   356  	}
   357  //现在,对于我们感兴趣的每种消息类型,创建一个自定义事件并发送它
   358  	var evt *simulations.Event
   359  	switch val := val.(type) {
   360  	case *OfferedHashesMsg:
   361  		evt = &simulations.Event{
   362  			Type:    EventTypeChunkOffered,
   363  			Node:    ev.w.getNetwork().GetNode(ev.ID()),
   364  			Control: false,
   365  			Data:    val.Hashes,
   366  		}
   367  	case *WantedHashesMsg:
   368  		evt = &simulations.Event{
   369  			Type:    EventTypeChunkWanted,
   370  			Node:    ev.w.getNetwork().GetNode(ev.ID()),
   371  			Control: false,
   372  		}
   373  	case *ChunkDeliveryMsgSyncing:
   374  		evt = &simulations.Event{
   375  			Type:    EventTypeChunkDelivered,
   376  			Node:    ev.w.getNetwork().GetNode(ev.ID()),
   377  			Control: false,
   378  			Data:    val.Addr.String(),
   379  		}
   380  	}
   381  	if evt != nil {
   382  //将自定义事件发送到订阅源;前端将侦听该事件并显示
   383  		ev.w.getNetwork().Events().Send(evt)
   384  	}
   385  	return msg, nil
   386  }
   387