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