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