github.com/FUSIONFoundation/efsn@v3.6.2-0.20200916075423-dbb5dd5d2cc7+incompatible/swarm/storage/netstore_test.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package storage 18 19 import ( 20 "bytes" 21 "context" 22 "crypto/rand" 23 "io/ioutil" 24 "sync" 25 "testing" 26 "time" 27 28 "github.com/FusionFoundation/efsn/p2p/discover" 29 ch "github.com/FusionFoundation/efsn/swarm/chunk" 30 31 "github.com/FusionFoundation/efsn/common" 32 ) 33 34 var sourcePeerID = discover.MustHexID("2dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439") 35 36 type mockNetFetcher struct { 37 peers *sync.Map 38 sources []*discover.NodeID 39 peersPerRequest [][]Address 40 requestCalled bool 41 offerCalled bool 42 quit <-chan struct{} 43 ctx context.Context 44 } 45 46 func (m *mockNetFetcher) Offer(ctx context.Context, source *discover.NodeID) { 47 m.offerCalled = true 48 m.sources = append(m.sources, source) 49 } 50 51 func (m *mockNetFetcher) Request(ctx context.Context) { 52 m.requestCalled = true 53 var peers []Address 54 m.peers.Range(func(key interface{}, _ interface{}) bool { 55 peers = append(peers, common.FromHex(key.(string))) 56 return true 57 }) 58 m.peersPerRequest = append(m.peersPerRequest, peers) 59 } 60 61 type mockNetFetchFuncFactory struct { 62 fetcher *mockNetFetcher 63 } 64 65 func (m *mockNetFetchFuncFactory) newMockNetFetcher(ctx context.Context, _ Address, peers *sync.Map) NetFetcher { 66 m.fetcher.peers = peers 67 m.fetcher.quit = ctx.Done() 68 m.fetcher.ctx = ctx 69 return m.fetcher 70 } 71 72 func mustNewNetStore(t *testing.T) *NetStore { 73 netStore, _ := mustNewNetStoreWithFetcher(t) 74 return netStore 75 } 76 77 func mustNewNetStoreWithFetcher(t *testing.T) (*NetStore, *mockNetFetcher) { 78 t.Helper() 79 80 datadir, err := ioutil.TempDir("", "netstore") 81 if err != nil { 82 t.Fatal(err) 83 } 84 naddr := make([]byte, 32) 85 params := NewDefaultLocalStoreParams() 86 params.Init(datadir) 87 params.BaseKey = naddr 88 localStore, err := NewTestLocalStoreForAddr(params) 89 if err != nil { 90 t.Fatal(err) 91 } 92 93 fetcher := &mockNetFetcher{} 94 mockNetFetchFuncFactory := &mockNetFetchFuncFactory{ 95 fetcher: fetcher, 96 } 97 netStore, err := NewNetStore(localStore, mockNetFetchFuncFactory.newMockNetFetcher) 98 if err != nil { 99 t.Fatal(err) 100 } 101 return netStore, fetcher 102 } 103 104 // TestNetStoreGetAndPut tests calling NetStore.Get which is blocked until the same chunk is Put. 105 // After the Put there should no active fetchers, and the context created for the fetcher should 106 // be cancelled. 107 func TestNetStoreGetAndPut(t *testing.T) { 108 netStore, fetcher := mustNewNetStoreWithFetcher(t) 109 110 chunk := GenerateRandomChunk(ch.DefaultSize) 111 112 ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) 113 defer cancel() 114 115 c := make(chan struct{}) // this channel ensures that the gouroutine with the Put does not run earlier than the Get 116 go func() { 117 <-c // wait for the Get to be called 118 time.Sleep(200 * time.Millisecond) // and a little more so it is surely called 119 120 // check if netStore created a fetcher in the Get call for the unavailable chunk 121 if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil { 122 t.Fatal("Expected netStore to use a fetcher for the Get call") 123 } 124 125 err := netStore.Put(ctx, chunk) 126 if err != nil { 127 t.Fatalf("Expected no err got %v", err) 128 } 129 }() 130 131 close(c) 132 recChunk, err := netStore.Get(ctx, chunk.Address()) // this is blocked until the Put above is done 133 if err != nil { 134 t.Fatalf("Expected no err got %v", err) 135 } 136 // the retrieved chunk should be the same as what we Put 137 if !bytes.Equal(recChunk.Address(), chunk.Address()) || !bytes.Equal(recChunk.Data(), chunk.Data()) { 138 t.Fatalf("Different chunk received than what was put") 139 } 140 // the chunk is already available locally, so there should be no active fetchers waiting for it 141 if netStore.fetchers.Len() != 0 { 142 t.Fatal("Expected netStore to remove the fetcher after delivery") 143 } 144 145 // A fetcher was created when the Get was called (and the chunk was not available). The chunk 146 // was delivered with the Put call, so the fetcher should be cancelled now. 147 select { 148 case <-fetcher.ctx.Done(): 149 default: 150 t.Fatal("Expected fetcher context to be cancelled") 151 } 152 153 } 154 155 // TestNetStoreGetAndPut tests calling NetStore.Put and then NetStore.Get. 156 // After the Put the chunk is available locally, so the Get can just retrieve it from LocalStore, 157 // there is no need to create fetchers. 158 func TestNetStoreGetAfterPut(t *testing.T) { 159 netStore, fetcher := mustNewNetStoreWithFetcher(t) 160 161 chunk := GenerateRandomChunk(ch.DefaultSize) 162 163 ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) 164 defer cancel() 165 166 // First we Put the chunk, so the chunk will be available locally 167 err := netStore.Put(ctx, chunk) 168 if err != nil { 169 t.Fatalf("Expected no err got %v", err) 170 } 171 172 // Get should retrieve the chunk from LocalStore, without creating fetcher 173 recChunk, err := netStore.Get(ctx, chunk.Address()) 174 if err != nil { 175 t.Fatalf("Expected no err got %v", err) 176 } 177 // the retrieved chunk should be the same as what we Put 178 if !bytes.Equal(recChunk.Address(), chunk.Address()) || !bytes.Equal(recChunk.Data(), chunk.Data()) { 179 t.Fatalf("Different chunk received than what was put") 180 } 181 // no fetcher offer or request should be created for a locally available chunk 182 if fetcher.offerCalled || fetcher.requestCalled { 183 t.Fatal("NetFetcher.offerCalled or requestCalled not expected to be called") 184 } 185 // no fetchers should be created for a locally available chunk 186 if netStore.fetchers.Len() != 0 { 187 t.Fatal("Expected netStore to not have fetcher") 188 } 189 190 } 191 192 // TestNetStoreGetTimeout tests a Get call for an unavailable chunk and waits for timeout 193 func TestNetStoreGetTimeout(t *testing.T) { 194 netStore, fetcher := mustNewNetStoreWithFetcher(t) 195 196 chunk := GenerateRandomChunk(ch.DefaultSize) 197 198 ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) 199 defer cancel() 200 201 c := make(chan struct{}) // this channel ensures that the gouroutine does not run earlier than the Get 202 go func() { 203 <-c // wait for the Get to be called 204 time.Sleep(200 * time.Millisecond) // and a little more so it is surely called 205 206 // check if netStore created a fetcher in the Get call for the unavailable chunk 207 if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil { 208 t.Fatal("Expected netStore to use a fetcher for the Get call") 209 } 210 }() 211 212 close(c) 213 // We call Get on this chunk, which is not in LocalStore. We don't Put it at all, so there will 214 // be a timeout 215 _, err := netStore.Get(ctx, chunk.Address()) 216 217 // Check if the timeout happened 218 if err != context.DeadlineExceeded { 219 t.Fatalf("Expected context.DeadLineExceeded err got %v", err) 220 } 221 222 // A fetcher was created, check if it has been removed after timeout 223 if netStore.fetchers.Len() != 0 { 224 t.Fatal("Expected netStore to remove the fetcher after timeout") 225 } 226 227 // Check if the fetcher context has been cancelled after the timeout 228 select { 229 case <-fetcher.ctx.Done(): 230 default: 231 t.Fatal("Expected fetcher context to be cancelled") 232 } 233 } 234 235 // TestNetStoreGetCancel tests a Get call for an unavailable chunk, then cancels the context and checks 236 // the errors 237 func TestNetStoreGetCancel(t *testing.T) { 238 netStore, fetcher := mustNewNetStoreWithFetcher(t) 239 240 chunk := GenerateRandomChunk(ch.DefaultSize) 241 242 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) 243 244 c := make(chan struct{}) // this channel ensures that the gouroutine with the cancel does not run earlier than the Get 245 go func() { 246 <-c // wait for the Get to be called 247 time.Sleep(200 * time.Millisecond) // and a little more so it is surely called 248 // check if netStore created a fetcher in the Get call for the unavailable chunk 249 if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil { 250 t.Fatal("Expected netStore to use a fetcher for the Get call") 251 } 252 cancel() 253 }() 254 255 close(c) 256 // We call Get with an unavailable chunk, so it will create a fetcher and wait for delivery 257 _, err := netStore.Get(ctx, chunk.Address()) 258 259 // After the context is cancelled above Get should return with an error 260 if err != context.Canceled { 261 t.Fatalf("Expected context.Canceled err got %v", err) 262 } 263 264 // A fetcher was created, check if it has been removed after cancel 265 if netStore.fetchers.Len() != 0 { 266 t.Fatal("Expected netStore to remove the fetcher after cancel") 267 } 268 269 // Check if the fetcher context has been cancelled after the request context cancel 270 select { 271 case <-fetcher.ctx.Done(): 272 default: 273 t.Fatal("Expected fetcher context to be cancelled") 274 } 275 } 276 277 // TestNetStoreMultipleGetAndPut tests four Get calls for the same unavailable chunk. The chunk is 278 // delivered with a Put, we have to make sure all Get calls return, and they use a single fetcher 279 // for the chunk retrieval 280 func TestNetStoreMultipleGetAndPut(t *testing.T) { 281 netStore, fetcher := mustNewNetStoreWithFetcher(t) 282 283 chunk := GenerateRandomChunk(ch.DefaultSize) 284 285 ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) 286 defer cancel() 287 288 go func() { 289 // sleep to make sure Put is called after all the Get 290 time.Sleep(500 * time.Millisecond) 291 // check if netStore created exactly one fetcher for all Get calls 292 if netStore.fetchers.Len() != 1 { 293 t.Fatal("Expected netStore to use one fetcher for all Get calls") 294 } 295 err := netStore.Put(ctx, chunk) 296 if err != nil { 297 t.Fatalf("Expected no err got %v", err) 298 } 299 }() 300 301 // call Get 4 times for the same unavailable chunk. The calls will be blocked until the Put above. 302 getWG := sync.WaitGroup{} 303 for i := 0; i < 4; i++ { 304 getWG.Add(1) 305 go func() { 306 defer getWG.Done() 307 recChunk, err := netStore.Get(ctx, chunk.Address()) 308 if err != nil { 309 t.Fatalf("Expected no err got %v", err) 310 } 311 if !bytes.Equal(recChunk.Address(), chunk.Address()) || !bytes.Equal(recChunk.Data(), chunk.Data()) { 312 t.Fatalf("Different chunk received than what was put") 313 } 314 }() 315 } 316 317 finishedC := make(chan struct{}) 318 go func() { 319 getWG.Wait() 320 close(finishedC) 321 }() 322 323 // The Get calls should return after Put, so no timeout expected 324 select { 325 case <-finishedC: 326 case <-time.After(1 * time.Second): 327 t.Fatalf("Timeout waiting for Get calls to return") 328 } 329 330 // A fetcher was created, check if it has been removed after cancel 331 if netStore.fetchers.Len() != 0 { 332 t.Fatal("Expected netStore to remove the fetcher after delivery") 333 } 334 335 // A fetcher was created, check if it has been removed after delivery 336 select { 337 case <-fetcher.ctx.Done(): 338 default: 339 t.Fatal("Expected fetcher context to be cancelled") 340 } 341 342 } 343 344 // TestNetStoreFetchFuncTimeout tests a FetchFunc call for an unavailable chunk and waits for timeout 345 func TestNetStoreFetchFuncTimeout(t *testing.T) { 346 netStore, fetcher := mustNewNetStoreWithFetcher(t) 347 348 chunk := GenerateRandomChunk(ch.DefaultSize) 349 350 ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) 351 defer cancel() 352 353 // FetchFunc is called for an unavaible chunk, so the returned wait function should not be nil 354 wait := netStore.FetchFunc(ctx, chunk.Address()) 355 if wait == nil { 356 t.Fatal("Expected wait function to be not nil") 357 } 358 359 // There should an active fetcher for the chunk after the FetchFunc call 360 if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil { 361 t.Fatalf("Expected netStore to have one fetcher for the requested chunk") 362 } 363 364 // wait function should timeout because we don't deliver the chunk with a Put 365 err := wait(ctx) 366 if err != context.DeadlineExceeded { 367 t.Fatalf("Expected context.DeadLineExceeded err got %v", err) 368 } 369 370 // the fetcher should be removed after timeout 371 if netStore.fetchers.Len() != 0 { 372 t.Fatal("Expected netStore to remove the fetcher after timeout") 373 } 374 375 // the fetcher context should be cancelled after timeout 376 select { 377 case <-fetcher.ctx.Done(): 378 default: 379 t.Fatal("Expected fetcher context to be cancelled") 380 } 381 } 382 383 // TestNetStoreFetchFuncAfterPut tests that the FetchFunc should return nil for a locally available chunk 384 func TestNetStoreFetchFuncAfterPut(t *testing.T) { 385 netStore := mustNewNetStore(t) 386 387 chunk := GenerateRandomChunk(ch.DefaultSize) 388 389 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) 390 defer cancel() 391 392 // We deliver the created the chunk with a Put 393 err := netStore.Put(ctx, chunk) 394 if err != nil { 395 t.Fatalf("Expected no err got %v", err) 396 } 397 398 // FetchFunc should return nil, because the chunk is available locally, no need to fetch it 399 wait := netStore.FetchFunc(ctx, chunk.Address()) 400 if wait != nil { 401 t.Fatal("Expected wait to be nil") 402 } 403 404 // No fetchers should be created at all 405 if netStore.fetchers.Len() != 0 { 406 t.Fatal("Expected netStore to not have fetcher") 407 } 408 } 409 410 // TestNetStoreGetCallsRequest tests if Get created a request on the NetFetcher for an unavailable chunk 411 func TestNetStoreGetCallsRequest(t *testing.T) { 412 netStore, fetcher := mustNewNetStoreWithFetcher(t) 413 414 chunk := GenerateRandomChunk(ch.DefaultSize) 415 416 ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) 417 defer cancel() 418 419 // We call get for a not available chunk, it will timeout because the chunk is not delivered 420 _, err := netStore.Get(ctx, chunk.Address()) 421 422 if err != context.DeadlineExceeded { 423 t.Fatalf("Expected context.DeadlineExceeded err got %v", err) 424 } 425 426 // NetStore should call NetFetcher.Request and wait for the chunk 427 if !fetcher.requestCalled { 428 t.Fatal("Expected NetFetcher.Request to be called") 429 } 430 } 431 432 // TestNetStoreGetCallsOffer tests if Get created a request on the NetFetcher for an unavailable chunk 433 // in case of a source peer provided in the context. 434 func TestNetStoreGetCallsOffer(t *testing.T) { 435 netStore, fetcher := mustNewNetStoreWithFetcher(t) 436 437 chunk := GenerateRandomChunk(ch.DefaultSize) 438 439 // If a source peer is added to the context, NetStore will handle it as an offer 440 ctx := context.WithValue(context.Background(), "source", sourcePeerID.String()) 441 ctx, cancel := context.WithTimeout(ctx, 200*time.Millisecond) 442 defer cancel() 443 444 // We call get for a not available chunk, it will timeout because the chunk is not delivered 445 chunk, err := netStore.Get(ctx, chunk.Address()) 446 447 if err != context.DeadlineExceeded { 448 t.Fatalf("Expect error %v got %v", context.DeadlineExceeded, err) 449 } 450 451 // NetStore should call NetFetcher.Offer with the source peer 452 if !fetcher.offerCalled { 453 t.Fatal("Expected NetFetcher.Request to be called") 454 } 455 456 if len(fetcher.sources) != 1 { 457 t.Fatalf("Expected fetcher sources length 1 got %v", len(fetcher.sources)) 458 } 459 460 if fetcher.sources[0].String() != sourcePeerID.String() { 461 t.Fatalf("Expected fetcher source %v got %v", sourcePeerID, fetcher.sources[0]) 462 } 463 464 } 465 466 // TestNetStoreFetcherCountPeers tests multiple NetStore.Get calls with peer in the context. 467 // There is no Put call, so the Get calls timeout 468 func TestNetStoreFetcherCountPeers(t *testing.T) { 469 470 netStore, fetcher := mustNewNetStoreWithFetcher(t) 471 472 addr := randomAddr() 473 peers := []string{randomAddr().Hex(), randomAddr().Hex(), randomAddr().Hex()} 474 475 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) 476 defer cancel() 477 errC := make(chan error) 478 nrGets := 3 479 480 // Call Get 3 times with a peer in context 481 for i := 0; i < nrGets; i++ { 482 peer := peers[i] 483 go func() { 484 ctx := context.WithValue(ctx, "peer", peer) 485 _, err := netStore.Get(ctx, addr) 486 errC <- err 487 }() 488 } 489 490 // All 3 Get calls should timeout 491 for i := 0; i < nrGets; i++ { 492 err := <-errC 493 if err != context.DeadlineExceeded { 494 t.Fatalf("Expected \"%v\" error got \"%v\"", context.DeadlineExceeded, err) 495 } 496 } 497 498 // fetcher should be closed after timeout 499 select { 500 case <-fetcher.quit: 501 case <-time.After(3 * time.Second): 502 t.Fatalf("mockNetFetcher not closed after timeout") 503 } 504 505 // All 3 peers should be given to NetFetcher after the 3 Get calls 506 if len(fetcher.peersPerRequest) != nrGets { 507 t.Fatalf("Expected 3 got %v", len(fetcher.peersPerRequest)) 508 } 509 510 for i, peers := range fetcher.peersPerRequest { 511 if len(peers) < i+1 { 512 t.Fatalf("Expected at least %v got %v", i+1, len(peers)) 513 } 514 } 515 } 516 517 // TestNetStoreFetchFuncCalledMultipleTimes calls the wait function given by FetchFunc three times, 518 // and checks there is still exactly one fetcher for one chunk. Afthe chunk is delivered, it checks 519 // if the fetcher is closed. 520 func TestNetStoreFetchFuncCalledMultipleTimes(t *testing.T) { 521 netStore, fetcher := mustNewNetStoreWithFetcher(t) 522 523 chunk := GenerateRandomChunk(ch.DefaultSize) 524 525 ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) 526 defer cancel() 527 528 // FetchFunc should return a non-nil wait function, because the chunk is not available 529 wait := netStore.FetchFunc(ctx, chunk.Address()) 530 if wait == nil { 531 t.Fatal("Expected wait function to be not nil") 532 } 533 534 // There should be exactly one fetcher for the chunk 535 if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil { 536 t.Fatalf("Expected netStore to have one fetcher for the requested chunk") 537 } 538 539 // Call wait three times parallelly 540 wg := sync.WaitGroup{} 541 for i := 0; i < 3; i++ { 542 wg.Add(1) 543 go func() { 544 err := wait(ctx) 545 if err != nil { 546 t.Fatalf("Expected no err got %v", err) 547 } 548 wg.Done() 549 }() 550 } 551 552 // sleep a little so the wait functions are called above 553 time.Sleep(100 * time.Millisecond) 554 555 // there should be still only one fetcher, because all wait calls are for the same chunk 556 if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil { 557 t.Fatal("Expected netStore to have one fetcher for the requested chunk") 558 } 559 560 // Deliver the chunk with a Put 561 err := netStore.Put(ctx, chunk) 562 if err != nil { 563 t.Fatalf("Expected no err got %v", err) 564 } 565 566 // wait until all wait calls return (because the chunk is delivered) 567 wg.Wait() 568 569 // There should be no more fetchers for the delivered chunk 570 if netStore.fetchers.Len() != 0 { 571 t.Fatal("Expected netStore to remove the fetcher after delivery") 572 } 573 574 // The context for the fetcher should be cancelled after delivery 575 select { 576 case <-fetcher.ctx.Done(): 577 default: 578 t.Fatal("Expected fetcher context to be cancelled") 579 } 580 } 581 582 // TestNetStoreFetcherLifeCycleWithTimeout is similar to TestNetStoreFetchFuncCalledMultipleTimes, 583 // the only difference is that we don't deilver the chunk, just wait for timeout 584 func TestNetStoreFetcherLifeCycleWithTimeout(t *testing.T) { 585 netStore, fetcher := mustNewNetStoreWithFetcher(t) 586 587 chunk := GenerateRandomChunk(ch.DefaultSize) 588 589 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) 590 defer cancel() 591 592 // FetchFunc should return a non-nil wait function, because the chunk is not available 593 wait := netStore.FetchFunc(ctx, chunk.Address()) 594 if wait == nil { 595 t.Fatal("Expected wait function to be not nil") 596 } 597 598 // There should be exactly one fetcher for the chunk 599 if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil { 600 t.Fatalf("Expected netStore to have one fetcher for the requested chunk") 601 } 602 603 // Call wait three times parallelly 604 wg := sync.WaitGroup{} 605 for i := 0; i < 3; i++ { 606 wg.Add(1) 607 go func() { 608 defer wg.Done() 609 rctx, rcancel := context.WithTimeout(context.Background(), 100*time.Millisecond) 610 defer rcancel() 611 err := wait(rctx) 612 if err != context.DeadlineExceeded { 613 t.Fatalf("Expected err %v got %v", context.DeadlineExceeded, err) 614 } 615 }() 616 } 617 618 // wait until all wait calls timeout 619 wg.Wait() 620 621 // There should be no more fetchers after timeout 622 if netStore.fetchers.Len() != 0 { 623 t.Fatal("Expected netStore to remove the fetcher after delivery") 624 } 625 626 // The context for the fetcher should be cancelled after timeout 627 select { 628 case <-fetcher.ctx.Done(): 629 default: 630 t.Fatal("Expected fetcher context to be cancelled") 631 } 632 } 633 634 func randomAddr() Address { 635 addr := make([]byte, 32) 636 rand.Read(addr) 637 return Address(addr) 638 }