github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/swarm/network/stream/snapshot_retrieval_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  //</624450115662254080>
    11  
    12  package stream
    13  
    14  import (
    15  	"context"
    16  	"fmt"
    17  	"os"
    18  	"sync"
    19  	"testing"
    20  	"time"
    21  
    22  	"github.com/ethereum/go-ethereum/node"
    23  	"github.com/ethereum/go-ethereum/p2p/enode"
    24  	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
    25  	"github.com/ethereum/go-ethereum/swarm/log"
    26  	"github.com/ethereum/go-ethereum/swarm/network"
    27  	"github.com/ethereum/go-ethereum/swarm/network/simulation"
    28  	"github.com/ethereum/go-ethereum/swarm/state"
    29  	"github.com/ethereum/go-ethereum/swarm/storage"
    30  )
    31  
    32  //用于随机文件生成的常量
    33  const (
    34  	minFileSize = 2
    35  	maxFileSize = 40
    36  )
    37  
    38  //此测试是节点的检索测试。
    39  //可以配置多个节点
    40  //提供给测试。
    41  //文件上载到节点,其他节点尝试检索文件
    42  //节点数量也可以通过命令行提供。
    43  func TestFileRetrieval(t *testing.T) {
    44  	if *nodes != 0 {
    45  		err := runFileRetrievalTest(*nodes)
    46  		if err != nil {
    47  			t.Fatal(err)
    48  		}
    49  	} else {
    50  		nodeCnt := []int{16}
    51  //如果已提供“longrunning”标志
    52  //运行更多测试组合
    53  		if *longrunning {
    54  			nodeCnt = append(nodeCnt, 32, 64, 128)
    55  		}
    56  		for _, n := range nodeCnt {
    57  			err := runFileRetrievalTest(n)
    58  			if err != nil {
    59  				t.Fatal(err)
    60  			}
    61  		}
    62  	}
    63  }
    64  
    65  //此测试是节点的检索测试。
    66  //随机选择一个节点作为轴节点。
    67  //块和节点的可配置数量可以是
    68  //提供给测试时,将上载块的数量
    69  //到透视节点和其他节点,尝试检索块。
    70  //块和节点的数量也可以通过命令行提供。
    71  func TestRetrieval(t *testing.T) {
    72  //如果节点/块是通过命令行提供的,
    73  //使用这些值运行测试
    74  	if *nodes != 0 && *chunks != 0 {
    75  		err := runRetrievalTest(*chunks, *nodes)
    76  		if err != nil {
    77  			t.Fatal(err)
    78  		}
    79  	} else {
    80  		var nodeCnt []int
    81  		var chnkCnt []int
    82  //如果已提供“longrunning”标志
    83  //运行更多测试组合
    84  		if *longrunning {
    85  			nodeCnt = []int{16, 32, 128}
    86  			chnkCnt = []int{4, 32, 256}
    87  		} else {
    88  //缺省测试
    89  			nodeCnt = []int{16}
    90  			chnkCnt = []int{32}
    91  		}
    92  		for _, n := range nodeCnt {
    93  			for _, c := range chnkCnt {
    94  				err := runRetrievalTest(c, n)
    95  				if err != nil {
    96  					t.Fatal(err)
    97  				}
    98  			}
    99  		}
   100  	}
   101  }
   102  
   103  var retrievalSimServiceMap = map[string]simulation.ServiceFunc{
   104  	"streamer": retrievalStreamerFunc,
   105  }
   106  
   107  func retrievalStreamerFunc(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
   108  	n := ctx.Config.Node()
   109  	addr := network.NewAddr(n)
   110  	store, datadir, err := createTestLocalStorageForID(n.ID(), addr)
   111  	if err != nil {
   112  		return nil, nil, err
   113  	}
   114  	bucket.Store(bucketKeyStore, store)
   115  
   116  	localStore := store.(*storage.LocalStore)
   117  	netStore, err := storage.NewNetStore(localStore, nil)
   118  	if err != nil {
   119  		return nil, nil, err
   120  	}
   121  	kad := network.NewKademlia(addr.Over(), network.NewKadParams())
   122  	delivery := NewDelivery(kad, netStore)
   123  	netStore.NewNetFetcherFunc = network.NewFetcherFactory(delivery.RequestFromPeers, true).New
   124  
   125  	r := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
   126  		Retrieval:       RetrievalEnabled,
   127  		Syncing:         SyncingAutoSubscribe,
   128  		SyncUpdateDelay: 3 * time.Second,
   129  	}, nil)
   130  
   131  	fileStore := storage.NewFileStore(netStore, storage.NewFileStoreParams())
   132  	bucket.Store(bucketKeyFileStore, fileStore)
   133  
   134  	cleanup = func() {
   135  		os.RemoveAll(datadir)
   136  		netStore.Close()
   137  		r.Close()
   138  	}
   139  
   140  	return r, cleanup, nil
   141  }
   142  
   143  /*
   144  测试加载快照文件构建群网络,
   145  假设快照文件标识一个健康的
   146  卡德米利亚网络。然而,健康检查运行在
   147  模拟的“action”函数。
   148  
   149  快照的服务列表中应包含“streamer”。
   150  **/
   151  
   152  func runFileRetrievalTest(nodeCount int) error {
   153  	sim := simulation.New(retrievalSimServiceMap)
   154  	defer sim.Close()
   155  
   156  	log.Info("Initializing test config")
   157  
   158  	conf := &synctestConfig{}
   159  //发现ID到该ID处预期的块索引的映射
   160  	conf.idToChunksMap = make(map[enode.ID][]int)
   161  //发现ID的覆盖地址映射
   162  	conf.addrToIDMap = make(map[string]enode.ID)
   163  //存储生成的块哈希的数组
   164  	conf.hashes = make([]storage.Address, 0)
   165  
   166  	err := sim.UploadSnapshot(fmt.Sprintf("testing/snapshot_%d.json", nodeCount))
   167  	if err != nil {
   168  		return err
   169  	}
   170  
   171  	ctx, cancelSimRun := context.WithTimeout(context.Background(), 1*time.Minute)
   172  	defer cancelSimRun()
   173  
   174  	result := sim.Run(ctx, func(ctx context.Context, sim *simulation.Simulation) error {
   175  		nodeIDs := sim.UpNodeIDs()
   176  		for _, n := range nodeIDs {
   177  //从此ID获取Kademlia覆盖地址
   178  			a := n.Bytes()
   179  //将它附加到所有覆盖地址的数组中
   180  			conf.addrs = append(conf.addrs, a)
   181  //邻近度计算在叠加地址上,
   182  //p2p/simulations检查enode.id上的func触发器,
   183  //所以我们需要知道哪个overlay addr映射到哪个nodeid
   184  			conf.addrToIDMap[string(a)] = n
   185  		}
   186  
   187  //随机文件的数组
   188  		var randomFiles []string
   189  //上传完成后的信号通道
   190  //上传完成:=make(chan struct)
   191  //触发新节点检查的通道
   192  
   193  		conf.hashes, randomFiles, err = uploadFilesToNodes(sim)
   194  		if err != nil {
   195  			return err
   196  		}
   197  		if _, err := sim.WaitTillHealthy(ctx); err != nil {
   198  			return err
   199  		}
   200  
   201  //重复文件检索检查,直到从所有节点检索所有上载的文件
   202  //或者直到超时。
   203  	REPEAT:
   204  		for {
   205  			for _, id := range nodeIDs {
   206  //对于每个期望的文件,检查它是否在本地存储区中
   207  				item, ok := sim.NodeItem(id, bucketKeyFileStore)
   208  				if !ok {
   209  					return fmt.Errorf("No filestore")
   210  				}
   211  				fileStore := item.(*storage.FileStore)
   212  //检查所有块
   213  				for i, hash := range conf.hashes {
   214  					reader, _ := fileStore.Retrieve(context.TODO(), hash)
   215  //检查我们是否可以读取文件大小,以及它是否与生成的文件大小相对应。
   216  					if s, err := reader.Size(ctx, nil); err != nil || s != int64(len(randomFiles[i])) {
   217  						log.Debug("Retrieve error", "err", err, "hash", hash, "nodeId", id)
   218  						time.Sleep(500 * time.Millisecond)
   219  						continue REPEAT
   220  					}
   221  					log.Debug(fmt.Sprintf("File with root hash %x successfully retrieved", hash))
   222  				}
   223  			}
   224  			return nil
   225  		}
   226  	})
   227  
   228  	if result.Error != nil {
   229  		return result.Error
   230  	}
   231  
   232  	return nil
   233  }
   234  
   235  /*
   236  测试生成给定数量的块。
   237  
   238  测试加载快照文件构建群网络,
   239  假设快照文件标识一个健康的
   240  卡德米利亚网络。然而,健康检查运行在
   241  模拟的“action”函数。
   242  
   243  快照的服务列表中应包含“streamer”。
   244  **/
   245  
   246  func runRetrievalTest(chunkCount int, nodeCount int) error {
   247  	sim := simulation.New(retrievalSimServiceMap)
   248  	defer sim.Close()
   249  
   250  	conf := &synctestConfig{}
   251  //发现ID到该ID处预期的块索引的映射
   252  	conf.idToChunksMap = make(map[enode.ID][]int)
   253  //发现ID的覆盖地址映射
   254  	conf.addrToIDMap = make(map[string]enode.ID)
   255  //存储生成的块哈希的数组
   256  	conf.hashes = make([]storage.Address, 0)
   257  
   258  	err := sim.UploadSnapshot(fmt.Sprintf("testing/snapshot_%d.json", nodeCount))
   259  	if err != nil {
   260  		return err
   261  	}
   262  
   263  	ctx := context.Background()
   264  	result := sim.Run(ctx, func(ctx context.Context, sim *simulation.Simulation) error {
   265  		nodeIDs := sim.UpNodeIDs()
   266  		for _, n := range nodeIDs {
   267  //从此ID获取Kademlia覆盖地址
   268  			a := n.Bytes()
   269  //将它附加到所有覆盖地址的数组中
   270  			conf.addrs = append(conf.addrs, a)
   271  //邻近度计算在叠加地址上,
   272  //p2p/simulations检查enode.id上的func触发器,
   273  //所以我们需要知道哪个overlay addr映射到哪个nodeid
   274  			conf.addrToIDMap[string(a)] = n
   275  		}
   276  
   277  //这是选择上载的节点
   278  		node := sim.Net.GetRandomUpNode()
   279  		item, ok := sim.NodeItem(node.ID(), bucketKeyStore)
   280  		if !ok {
   281  			return fmt.Errorf("No localstore")
   282  		}
   283  		lstore := item.(*storage.LocalStore)
   284  		conf.hashes, err = uploadFileToSingleNodeStore(node.ID(), chunkCount, lstore)
   285  		if err != nil {
   286  			return err
   287  		}
   288  		if _, err := sim.WaitTillHealthy(ctx); err != nil {
   289  			return err
   290  		}
   291  
   292  //重复文件检索检查,直到从所有节点检索所有上载的文件
   293  //或者直到超时。
   294  	REPEAT:
   295  		for {
   296  			for _, id := range nodeIDs {
   297  //对于每个预期的块,检查它是否在本地存储区中
   298  //检查节点的文件存储(netstore)
   299  				item, ok := sim.NodeItem(id, bucketKeyFileStore)
   300  				if !ok {
   301  					return fmt.Errorf("No filestore")
   302  				}
   303  				fileStore := item.(*storage.FileStore)
   304  //检查所有块
   305  				for _, hash := range conf.hashes {
   306  					reader, _ := fileStore.Retrieve(context.TODO(), hash)
   307  //检查我们是否可以读取块大小,以及它是否与生成的块大小相对应。
   308  					if s, err := reader.Size(ctx, nil); err != nil || s != int64(chunkSize) {
   309  						log.Debug("Retrieve error", "err", err, "hash", hash, "nodeId", id, "size", s)
   310  						time.Sleep(500 * time.Millisecond)
   311  						continue REPEAT
   312  					}
   313  					log.Debug(fmt.Sprintf("Chunk with root hash %x successfully retrieved", hash))
   314  				}
   315  			}
   316  //找到所有节点和文件,退出循环并返回而不出错
   317  			return nil
   318  		}
   319  	})
   320  
   321  	if result.Error != nil {
   322  		return result.Error
   323  	}
   324  
   325  	return nil
   326  }
   327