gitlab.com/SiaPrime/SiaPrime@v1.4.1/node/api/renterhost_test.go (about) 1 package api 2 3 // renterhost_test.go sets up larger integration tests between renters and 4 // hosts, checking that the whole storage ecosystem is functioning cohesively. 5 6 import ( 7 "bytes" 8 "errors" 9 "fmt" 10 "io/ioutil" 11 "net/url" 12 "os" 13 "path/filepath" 14 "strconv" 15 "sync" 16 "testing" 17 "time" 18 19 "gitlab.com/SiaPrime/SiaPrime/build" 20 "gitlab.com/SiaPrime/SiaPrime/crypto" 21 "gitlab.com/SiaPrime/SiaPrime/modules" 22 "gitlab.com/SiaPrime/SiaPrime/types" 23 ) 24 25 // TestHostObligationAcceptingContracts verifies that the host will complete 26 // storage proofs and the renter will successfully download even if the host 27 // has set accepting contracts to false. 28 func TestHostObligationAcceptingContracts(t *testing.T) { 29 if testing.Short() { 30 t.SkipNow() 31 } 32 st, err := createServerTester(t.Name()) 33 if err != nil { 34 t.Fatal(err) 35 } 36 defer st.server.Close() 37 err = st.setHostStorage() 38 if err != nil { 39 t.Fatal(err) 40 } 41 err = st.acceptContracts() 42 if err != nil { 43 t.Fatal(err) 44 } 45 err = st.announceHost() 46 if err != nil { 47 t.Fatal(err) 48 } 49 allowanceValues := url.Values{} 50 allowanceValues.Set("funds", "50000000000000000000000000000") // 50k SC 51 allowanceValues.Set("hosts", "1") 52 allowanceValues.Set("period", "10") 53 allowanceValues.Set("renewwindow", "5") 54 err = st.stdPostAPI("/renter", allowanceValues) 55 if err != nil { 56 t.Fatal(err) 57 } 58 59 // Block until the allowance has finished forming contracts. 60 err = build.Retry(50, time.Millisecond*250, func() error { 61 var rc RenterContracts 62 err = st.getAPI("/renter/contracts", &rc) 63 if err != nil { 64 return errors.New("couldn't get renter stats") 65 } 66 if len(rc.Contracts) != 1 { 67 return errors.New("no contracts") 68 } 69 return nil 70 }) 71 if err != nil { 72 t.Fatal("allowance setting failed") 73 } 74 75 filesize := int(1024) 76 path := filepath.Join(st.dir, "test.dat") 77 err = createRandFile(path, filesize) 78 if err != nil { 79 t.Fatal(err) 80 } 81 82 // upload the file 83 uploadValues := url.Values{} 84 uploadValues.Set("source", path) 85 err = st.stdPostAPI("/renter/upload/test", uploadValues) 86 if err != nil { 87 t.Fatal(err) 88 } 89 90 // redundancy should reach 1 91 var rf RenterFiles 92 err = build.Retry(120, time.Millisecond*250, func() error { 93 st.getAPI("/renter/files", &rf) 94 if len(rf.Files) >= 1 && rf.Files[0].Available { 95 return nil 96 } 97 return errors.New("file not uploaded") 98 }) 99 if err != nil { 100 t.Fatal(err) 101 } 102 103 // Get contracts via API call 104 var cts ContractInfoGET 105 err = st.getAPI("/host/contracts", &cts) 106 if err != nil { 107 t.Fatal(err) 108 } 109 110 // There should be some contracts returned 111 if len(cts.Contracts) == 0 { 112 t.Fatal("No contracts returned from /host/contracts API call.") 113 } 114 115 // Check if the number of contracts are equal to the number of storage obligations 116 if len(cts.Contracts) != len(st.host.StorageObligations()) { 117 t.Fatal("Number of contracts returned by API call and host method don't match.") 118 } 119 120 // set acceptingcontracts = false, mine some blocks, verify we can download 121 settings := st.host.InternalSettings() 122 settings.AcceptingContracts = false 123 st.host.SetInternalSettings(settings) 124 for i := 0; i < 3; i++ { 125 _, err := st.miner.AddBlock() 126 if err != nil { 127 t.Fatal(err) 128 } 129 time.Sleep(time.Millisecond * 100) 130 } 131 downloadPath := filepath.Join(st.dir, "test-downloaded-verify.dat") 132 err = st.stdGetAPI("/renter/download/test?destination=" + downloadPath) 133 if err != nil { 134 t.Fatal(err) 135 } 136 137 // mine blocks to cause the host to submit storage proofs to the blockchain. 138 for i := 0; i < 15; i++ { 139 _, err := st.miner.AddBlock() 140 if err != nil { 141 t.Fatal(err) 142 } 143 time.Sleep(time.Millisecond * 100) 144 } 145 146 // should have successful proofs 147 success := false 148 for _, so := range st.host.StorageObligations() { 149 if so.ProofConfirmed { 150 success = true 151 break 152 } 153 } 154 if !success { 155 t.Fatal("no successful storage proofs") 156 } 157 } 158 159 // TestHostAndRentVanilla sets up an integration test where a host and renter 160 // do basic uploads and downloads. 161 func TestHostAndRentVanilla(t *testing.T) { 162 if testing.Short() { 163 t.SkipNow() 164 } 165 t.Parallel() 166 st, err := createServerTester(t.Name()) 167 if err != nil { 168 t.Fatal(err) 169 } 170 defer st.server.panicClose() 171 172 // Announce the host and start accepting contracts. 173 err = st.setHostStorage() 174 if err != nil { 175 t.Fatal(err) 176 } 177 err = st.acceptContracts() 178 if err != nil { 179 t.Fatal(err) 180 } 181 err = st.announceHost() 182 if err != nil { 183 t.Fatal(err) 184 } 185 186 // Set an allowance for the renter, allowing a contract to be formed. 187 allowanceValues := url.Values{} 188 testFunds := "100000000000000000000000000000" // 100k SC 189 testPeriod := "20" 190 renewWindow := "10" 191 testPeriodInt := 20 192 allowanceValues.Set("funds", testFunds) 193 allowanceValues.Set("period", testPeriod) 194 allowanceValues.Set("renewwindow", renewWindow) 195 allowanceValues.Set("hosts", fmt.Sprint(modules.DefaultAllowance.Hosts)) 196 err = st.stdPostAPI("/renter", allowanceValues) 197 if err != nil { 198 t.Fatal(err) 199 } 200 201 // Block until the allowance has finished forming contracts. 202 err = build.Retry(50, time.Millisecond*250, func() error { 203 var rc RenterContracts 204 err = st.getAPI("/renter/contracts", &rc) 205 if err != nil { 206 return errors.New("couldn't get renter stats") 207 } 208 if len(rc.Contracts) != 1 { 209 return errors.New("no contracts") 210 } 211 return nil 212 }) 213 if err != nil { 214 t.Fatal("allowance setting failed") 215 } 216 217 // Check the host, who should now be reporting file contracts. 218 var cts ContractInfoGET 219 err = st.getAPI("/host/contracts", &cts) 220 if err != nil { 221 t.Fatal(err) 222 } 223 224 if len(cts.Contracts) != 1 { 225 t.Error("Host has wrong number of obligations:", len(cts.Contracts)) 226 } 227 // Check if the obligation status is unresolved 228 if cts.Contracts[0].ObligationStatus != "obligationUnresolved" { 229 t.Error("Wrong obligation status for new contract:", cts.Contracts[0].ObligationStatus) 230 } 231 // Check if there are no sector roots on a new contract 232 if cts.Contracts[0].SectorRootsCount != 0 { 233 t.Error("Wrong number of sector roots for new contract:", cts.Contracts[0].SectorRootsCount) 234 } 235 // Check if there is locked collateral 236 if cts.Contracts[0].LockedCollateral.IsZero() { 237 t.Error("No locked collateral in contract.") 238 } 239 // Check if risked collateral is not equal to zero 240 if !cts.Contracts[0].RiskedCollateral.IsZero() { 241 t.Error("Risked collateral not zero in new contract.") 242 } 243 // Check if all potential revenues are zero 244 if !(cts.Contracts[0].PotentialDownloadRevenue.IsZero() && cts.Contracts[0].PotentialUploadRevenue.IsZero() && cts.Contracts[0].PotentialStorageRevenue.IsZero()) { 245 t.Error("Potential values not zero in new contract.") 246 } 247 248 // Create a file. 249 path := filepath.Join(st.dir, "test.dat") 250 err = createRandFile(path, 1024) 251 if err != nil { 252 t.Fatal(err) 253 } 254 255 // Upload the file to the renter. 256 uploadValues := url.Values{} 257 uploadValues.Set("source", path) 258 err = st.stdPostAPI("/renter/upload/test", uploadValues) 259 if err != nil { 260 t.Fatal(err) 261 } 262 // Only one piece will be uploaded (10% at current redundancy). 263 var rf RenterFiles 264 for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ { 265 st.getAPI("/renter/files", &rf) 266 time.Sleep(100 * time.Millisecond) 267 } 268 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 { 269 t.Fatal("the uploading is not succeeding for some reason:", rf.Files[0]) 270 } 271 272 // On a second connection, upload another file. 273 path2 := filepath.Join(st.dir, "test2.dat") 274 test2Size := modules.SectorSize*2 + 1 275 err = createRandFile(path2, int(test2Size)) 276 if err != nil { 277 t.Fatal(err) 278 } 279 uploadValues = url.Values{} 280 uploadValues.Set("source", path2) 281 err = st.stdPostAPI("/renter/upload/test2", uploadValues) 282 if err != nil { 283 t.Fatal(err) 284 } 285 // Only one piece will be uploaded (10% at current redundancy). 286 for i := 0; i < 200 && (len(rf.Files) != 2 || rf.Files[0].UploadProgress < 10 || rf.Files[1].UploadProgress < 10); i++ { 287 st.getAPI("/renter/files", &rf) 288 time.Sleep(100 * time.Millisecond) 289 } 290 if len(rf.Files) != 2 || rf.Files[0].UploadProgress < 10 || rf.Files[1].UploadProgress < 10 { 291 t.Fatal("the uploading is not succeeding for some reason:", rf.Files[0], rf.Files[1]) 292 } 293 294 // Try downloading the first file. 295 downpath := filepath.Join(st.dir, "testdown.dat") 296 err = st.stdGetAPI("/renter/download/test?destination=" + downpath) 297 if err != nil { 298 t.Fatal(err) 299 } 300 // Check that the download has the right contents. 301 orig, err := ioutil.ReadFile(path) 302 if err != nil { 303 t.Fatal(err) 304 } 305 download, err := ioutil.ReadFile(downpath) 306 if err != nil { 307 t.Fatal(err) 308 } 309 if bytes.Compare(orig, download) != 0 { 310 t.Fatal("data mismatch when downloading a file") 311 } 312 313 // The renter's downloads queue should have 1 entry now. 314 var queue RenterDownloadQueue 315 if err = st.getAPI("/renter/downloads", &queue); err != nil { 316 t.Fatal(err) 317 } 318 if len(queue.Downloads) != 1 { 319 t.Fatalf("expected renter to have 1 download in the queue; got %v", len(queue.Downloads)) 320 } 321 322 // Try downloading the second file. 323 downpath2 := filepath.Join(st.dir, "testdown2.dat") 324 err = st.stdGetAPI("/renter/download/test2?destination=" + downpath2) 325 if err != nil { 326 t.Fatal(err) 327 } 328 // Check that the download has the right contents. 329 orig2, err := ioutil.ReadFile(path2) 330 if err != nil { 331 t.Fatal(err) 332 } 333 download2, err := ioutil.ReadFile(downpath2) 334 if err != nil { 335 t.Fatal(err) 336 } 337 if bytes.Compare(orig2, download2) != 0 { 338 t.Fatal("data mismatch when downloading a file") 339 } 340 341 // The renter's downloads queue should have 2 entries now. 342 if err = st.getAPI("/renter/downloads", &queue); err != nil { 343 t.Fatal(err) 344 } 345 if len(queue.Downloads) != 2 { 346 t.Fatalf("expected renter to have 1 download in the queue; got %v", len(queue.Downloads)) 347 } 348 349 // Mine two blocks, which should cause the host to submit the storage 350 // obligation to the blockchain. 351 for i := 0; i < 2; i++ { 352 _, err := st.miner.AddBlock() 353 if err != nil { 354 t.Fatal(err) 355 } 356 time.Sleep(time.Millisecond * 200) 357 } 358 359 // Check that the host was able to get the file contract confirmed on the 360 // blockchain. 361 cts = ContractInfoGET{} 362 err = st.getAPI("/host/contracts", &cts) 363 if err != nil { 364 t.Fatal(err) 365 } 366 367 if len(cts.Contracts) != 1 { 368 t.Error("Host has wrong number of obligations:", len(cts.Contracts)) 369 } 370 if !cts.Contracts[0].OriginConfirmed { 371 t.Error("Host has not seen the file contract on the blockchain.") 372 } 373 // Check if there are sector roots 374 if cts.Contracts[0].SectorRootsCount == 0 { 375 t.Error("Sector roots count is zero for used obligation.") 376 } 377 // Check if risked collateral is not equal to zero 378 if cts.Contracts[0].RiskedCollateral.IsZero() { 379 t.Error("Risked collateral is zero for used obligation.") 380 } 381 // There should be some potential revenues in this contract 382 if cts.Contracts[0].PotentialDownloadRevenue.IsZero() || cts.Contracts[0].PotentialUploadRevenue.IsZero() || cts.Contracts[0].PotentialStorageRevenue.IsZero() { 383 t.Error("Potential revenue value is zero for used obligation.") 384 } 385 386 // Mine blocks until the host should have submitted a storage proof. 387 for i := 0; i <= testPeriodInt+5; i++ { 388 _, err := st.miner.AddBlock() 389 if err != nil { 390 t.Fatal(err) 391 } 392 time.Sleep(time.Millisecond * 200) 393 } 394 395 cts = ContractInfoGET{} 396 err = st.getAPI("/host/contracts", &cts) 397 if err != nil { 398 t.Fatal(err) 399 } 400 401 success := false 402 for _, contract := range cts.Contracts { 403 if contract.ProofConfirmed { 404 // Sector roots should be removed from storage obligation 405 if contract.SectorRootsCount > 0 { 406 t.Error("There are sector roots on completed storage obligation.") 407 } 408 success = true 409 break 410 } 411 } 412 if !success { 413 t.Error("does not seem like the host has submitted a storage proof successfully to the network") 414 } 415 } 416 417 // TestHostAndRentMultiHost sets up an integration test where three hosts and a 418 // renter do basic (parallel) uploads and downloads. 419 func TestHostAndRentMultiHost(t *testing.T) { 420 if testing.Short() || !build.VLONG { 421 t.SkipNow() 422 } 423 t.Parallel() 424 st, err := createServerTester(t.Name()) 425 if err != nil { 426 t.Fatal(err) 427 } 428 defer st.server.panicClose() 429 stH1, err := blankServerTester(t.Name() + " - Host 2") 430 if err != nil { 431 t.Fatal(err) 432 } 433 defer stH1.server.panicClose() 434 stH2, err := blankServerTester(t.Name() + " - Host 3") 435 if err != nil { 436 t.Fatal(err) 437 } 438 defer stH2.server.panicClose() 439 testGroup := []*serverTester{st, stH1, stH2} 440 441 // Connect the testers to eachother so that they are all on the same 442 // blockchain. 443 err = fullyConnectNodes(testGroup) 444 if err != nil { 445 t.Fatal(err) 446 } 447 448 // Make sure that every wallet has money in it. 449 err = fundAllNodes(testGroup) 450 if err != nil { 451 t.Fatal(err) 452 } 453 454 // Add storage to every host. 455 err = addStorageToAllHosts(testGroup) 456 if err != nil { 457 t.Fatal(err) 458 } 459 460 // Announce every host. 461 err = announceAllHosts(testGroup) 462 if err != nil { 463 t.Fatal(err) 464 } 465 466 // Set an allowance with three hosts. 467 allowanceValues := url.Values{} 468 allowanceValues.Set("funds", "50000000000000000000000000000") // 50k SC 469 allowanceValues.Set("hosts", "3") 470 allowanceValues.Set("period", "10") 471 allowanceValues.Set("renewwindow", "2") 472 err = st.stdPostAPI("/renter", allowanceValues) 473 if err != nil { 474 t.Fatal(err) 475 } 476 477 // Create a file to upload. 478 filesize := int(45678) 479 path := filepath.Join(st.dir, "test.dat") 480 err = createRandFile(path, filesize) 481 if err != nil { 482 t.Fatal(err) 483 } 484 485 // Upload a file with 2-of-6 redundancy. 486 uploadValues := url.Values{} 487 uploadValues.Set("source", path) 488 uploadValues.Set("datapieces", "2") 489 uploadValues.Set("paritypieces", "4") 490 err = st.stdPostAPI("/renter/upload/test", uploadValues) 491 if err != nil { 492 t.Fatal(err) 493 } 494 // Three pieces should get uploaded. 495 var rf RenterFiles 496 for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 50); i++ { 497 st.getAPI("/renter/files", &rf) 498 time.Sleep(100 * time.Millisecond) 499 } 500 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 50 { 501 t.Fatal("the uploading is not succeeding for some reason:", rf.Files[0]) 502 } 503 504 // Try downloading the file. 505 downpath := filepath.Join(st.dir, "testdown.dat") 506 err = st.stdGetAPI("/renter/download/test?destination=" + downpath) 507 if err != nil { 508 t.Fatal(err) 509 } 510 // Check that the download has the right contents. 511 orig, err := ioutil.ReadFile(path) 512 if err != nil { 513 t.Fatal(err) 514 } 515 download, err := ioutil.ReadFile(downpath) 516 if err != nil { 517 t.Fatal(err) 518 } 519 if bytes.Compare(orig, download) != 0 { 520 t.Fatal("data mismatch when downloading a file") 521 } 522 523 // The renter's downloads queue should have 1 entry now. 524 var queue RenterDownloadQueue 525 if err = st.getAPI("/renter/downloads", &queue); err != nil { 526 t.Fatal(err) 527 } 528 if len(queue.Downloads) != 1 { 529 t.Fatalf("expected renter to have 1 download in the queue; got %v", len(queue.Downloads)) 530 } 531 } 532 533 // TestHostAndRentManyFiles sets up an integration test where a single renter 534 // is uploading many files to the network. 535 func TestHostAndRentManyFiles(t *testing.T) { 536 if testing.Short() || !build.VLONG { 537 t.SkipNow() 538 } 539 t.Parallel() 540 st, err := createServerTester(t.Name()) 541 if err != nil { 542 t.Fatal(err) 543 } 544 defer st.server.panicClose() 545 stH1, err := blankServerTester(t.Name() + " - Host 2") 546 if err != nil { 547 t.Fatal(err) 548 } 549 defer stH1.server.panicClose() 550 stH2, err := blankServerTester(t.Name() + " - Host 3") 551 if err != nil { 552 t.Fatal(err) 553 } 554 defer stH2.server.panicClose() 555 stH3, err := blankServerTester(t.Name() + " - Host 4") 556 if err != nil { 557 t.Fatal(err) 558 } 559 defer stH3.server.panicClose() 560 testGroup := []*serverTester{st, stH1, stH2, stH3} 561 562 // Connect the testers to eachother so that they are all on the same 563 // blockchain. 564 err = fullyConnectNodes(testGroup) 565 if err != nil { 566 t.Fatal(err) 567 } 568 569 // Make sure that every wallet has money in it. 570 err = fundAllNodes(testGroup) 571 if err != nil { 572 t.Fatal(err) 573 } 574 575 // Add storage to every host. 576 err = addStorageToAllHosts(testGroup) 577 if err != nil { 578 t.Fatal(err) 579 } 580 581 // Announce every host. 582 err = announceAllHosts(testGroup) 583 if err != nil { 584 t.Fatal(err) 585 } 586 587 // Set an allowance with four hosts. 588 allowanceValues := url.Values{} 589 allowanceValues.Set("funds", "50000000000000000000000000000") // 50k SC 590 allowanceValues.Set("hosts", "4") 591 allowanceValues.Set("period", "5") 592 allowanceValues.Set("renewwindow", "2") 593 err = st.stdPostAPI("/renter", allowanceValues) 594 if err != nil { 595 t.Fatal(err) 596 } 597 598 // Create 3 files to upload at the same time. 599 filesize1 := int(12347) 600 filesize2 := int(22343) 601 filesize3 := int(32349) 602 path1 := filepath.Join(st.dir, "test1.dat") 603 path2 := filepath.Join(st.dir, "test2.dat") 604 path3 := filepath.Join(st.dir, "test3.dat") 605 err = createRandFile(path1, filesize1) 606 if err != nil { 607 t.Fatal(err) 608 } 609 err = createRandFile(path2, filesize2) 610 if err != nil { 611 t.Fatal(err) 612 } 613 err = createRandFile(path3, filesize3) 614 if err != nil { 615 t.Fatal(err) 616 } 617 618 // Concurrently upload a file with 1-of-4 redundancy, 2-of-4 redundancy, 619 // and 3-of-4 redundancy. 620 var wg sync.WaitGroup 621 wg.Add(3) 622 go func() { 623 defer wg.Done() 624 uploadValues := url.Values{} 625 uploadValues.Set("source", path1) 626 uploadValues.Set("datapieces", "1") 627 uploadValues.Set("paritypieces", "3") 628 err := st.stdPostAPI("/renter/upload/test1", uploadValues) 629 if err != nil { 630 t.Error(err) 631 } 632 }() 633 go func() { 634 defer wg.Done() 635 uploadValues := url.Values{} 636 uploadValues.Set("source", path2) 637 uploadValues.Set("datapieces", "2") 638 uploadValues.Set("paritypieces", "2") 639 err := st.stdPostAPI("/renter/upload/test2", uploadValues) 640 if err != nil { 641 t.Error(err) 642 } 643 }() 644 go func() { 645 defer wg.Done() 646 uploadValues := url.Values{} 647 uploadValues.Set("source", path3) 648 uploadValues.Set("datapieces", "3") 649 uploadValues.Set("paritypieces", "1") 650 err := st.stdPostAPI("/renter/upload/test3", uploadValues) 651 if err != nil { 652 t.Error(err) 653 } 654 }() 655 656 // Block until the upload call is complete for all three files. 657 wg.Wait() 658 659 // Block until all files hit 100% uploaded. 660 var rf RenterFiles 661 for i := 0; i < 200 && (len(rf.Files) != 3 || rf.Files[0].UploadProgress < 100 || rf.Files[1].UploadProgress < 100 || rf.Files[2].UploadProgress < 100); i++ { 662 st.getAPI("/renter/files", &rf) 663 time.Sleep(500 * time.Millisecond) 664 } 665 if len(rf.Files) != 3 || rf.Files[0].UploadProgress < 100 || rf.Files[1].UploadProgress < 100 || rf.Files[2].UploadProgress < 100 { 666 t.Fatal("the uploading is not succeeding for some reason:", rf.Files[0], rf.Files[1], rf.Files[2]) 667 } 668 669 // Download all three files in parallel. 670 wg.Add(3) 671 go func() { 672 defer wg.Done() 673 downpath := filepath.Join(st.dir, "testdown1.dat") 674 err := st.stdGetAPI("/renter/download/test1?destination=" + downpath) 675 if err != nil { 676 t.Error(err) 677 } 678 // Check that the download has the right contents. 679 orig, err := ioutil.ReadFile(path1) 680 if err != nil { 681 t.Error(err) 682 } 683 download, err := ioutil.ReadFile(downpath) 684 if err != nil { 685 t.Error(err) 686 } 687 if bytes.Compare(orig, download) != 0 { 688 t.Error("data mismatch when downloading a file") 689 } 690 }() 691 go func() { 692 defer wg.Done() 693 downpath := filepath.Join(st.dir, "testdown2.dat") 694 err := st.stdGetAPI("/renter/download/test2?destination=" + downpath) 695 if err != nil { 696 t.Error(err) 697 } 698 // Check that the download has the right contents. 699 orig, err := ioutil.ReadFile(path2) 700 if err != nil { 701 t.Error(err) 702 } 703 download, err := ioutil.ReadFile(downpath) 704 if err != nil { 705 t.Error(err) 706 } 707 if bytes.Compare(orig, download) != 0 { 708 t.Error("data mismatch when downloading a file") 709 } 710 }() 711 go func() { 712 defer wg.Done() 713 downpath := filepath.Join(st.dir, "testdown3.dat") 714 err := st.stdGetAPI("/renter/download/test3?destination=" + downpath) 715 if err != nil { 716 t.Error(err) 717 } 718 // Check that the download has the right contents. 719 orig, err := ioutil.ReadFile(path3) 720 if err != nil { 721 t.Error(err) 722 } 723 download, err := ioutil.ReadFile(downpath) 724 if err != nil { 725 t.Error(err) 726 } 727 if bytes.Compare(orig, download) != 0 { 728 t.Error("data mismatch when downloading a file") 729 } 730 }() 731 wg.Wait() 732 733 // The renter's downloads queue should have 3 entries now. 734 var queue RenterDownloadQueue 735 if err = st.getAPI("/renter/downloads", &queue); err != nil { 736 t.Fatal(err) 737 } 738 if len(queue.Downloads) != 3 { 739 t.Fatalf("expected renter to have 1 download in the queue; got %v", len(queue.Downloads)) 740 } 741 } 742 743 // TestRenterUploadDownload tests that downloading and uploading in parallel 744 // does not result in failures or stalling. 745 func TestRenterUploadDownload(t *testing.T) { 746 if testing.Short() { 747 t.SkipNow() 748 } 749 st, err := createServerTester(t.Name()) 750 if err != nil { 751 t.Fatal(err) 752 } 753 defer st.server.panicClose() 754 755 // Announce the host and start accepting contracts. 756 err = st.setHostStorage() 757 if err != nil { 758 t.Fatal(err) 759 } 760 err = st.announceHost() 761 if err != nil { 762 t.Fatal(err) 763 } 764 err = st.acceptContracts() 765 if err != nil { 766 t.Fatal(err) 767 } 768 769 // Set an allowance for the renter, allowing a contract to be formed. 770 allowanceValues := url.Values{} 771 testFunds := "10000000000000000000000000000" // 10k SC 772 testPeriod := "10" 773 allowanceValues.Set("funds", testFunds) 774 allowanceValues.Set("period", testPeriod) 775 allowanceValues.Set("renewwindow", testRenewWindow) 776 allowanceValues.Set("hosts", fmt.Sprint(modules.DefaultAllowance.Hosts)) 777 err = st.stdPostAPI("/renter", allowanceValues) 778 if err != nil { 779 t.Fatal(err) 780 } 781 782 // Block until the allowance has finished forming contracts. 783 err = build.Retry(50, time.Millisecond*250, func() error { 784 var rc RenterContracts 785 err = st.getAPI("/renter/contracts", &rc) 786 if err != nil { 787 return errors.New("couldn't get renter stats") 788 } 789 if len(rc.Contracts) != 1 { 790 return errors.New("no contracts") 791 } 792 return nil 793 }) 794 if err != nil { 795 t.Fatal("allowance setting failed") 796 } 797 798 // Check financial metrics; coins should have been spent on contracts 799 var rg RenterGET 800 err = st.getAPI("/renter", &rg) 801 if err != nil { 802 t.Fatal(err) 803 } 804 spent := rg.Settings.Allowance.Funds.Sub(rg.FinancialMetrics.Unspent) 805 if spent.IsZero() { 806 t.Fatal("financial metrics do not reflect contract spending") 807 } 808 809 // Create a file. 810 path := filepath.Join(st.dir, "test.dat") 811 err = createRandFile(path, 1024) 812 if err != nil { 813 t.Fatal(err) 814 } 815 816 // Upload to host. 817 uploadValues := url.Values{} 818 uploadValues.Set("source", path) 819 err = st.stdPostAPI("/renter/upload/test", uploadValues) 820 if err != nil { 821 t.Fatal(err) 822 } 823 // Only one piece will be uploaded (10% at current redundancy). 824 var rf RenterFiles 825 for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ { 826 st.getAPI("/renter/files", &rf) 827 time.Sleep(100 * time.Millisecond) 828 } 829 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 { 830 t.Fatal("the uploading is not succeeding for some reason:", rf.Files[0]) 831 } 832 833 // In parallel, upload another file and download the first file. 834 path2 := filepath.Join(st.dir, "test2.dat") 835 test2Size := modules.SectorSize*2 + 1 836 err = createRandFile(path2, int(test2Size)) 837 if err != nil { 838 t.Fatal(err) 839 } 840 uploadValues = url.Values{} 841 uploadValues.Set("source", path2) 842 err = st.stdPostAPI("/renter/upload/test2", uploadValues) 843 if err != nil { 844 t.Fatal(err) 845 } 846 downpath := filepath.Join(st.dir, "testdown.dat") 847 err = st.stdGetAPI("/renter/download/test?destination=" + downpath) 848 if err != nil { 849 t.Fatal(err) 850 } 851 // Check that the download has the right contents. 852 orig, err := ioutil.ReadFile(path) 853 if err != nil { 854 t.Fatal(err) 855 } 856 download, err := ioutil.ReadFile(downpath) 857 if err != nil { 858 t.Fatal(err) 859 } 860 if bytes.Compare(orig, download) != 0 { 861 t.Fatal("data mismatch when downloading a file") 862 } 863 864 // Wait for upload to complete. 865 for i := 0; i < 200 && (len(rf.Files) != 2 || rf.Files[0].UploadProgress < 10 || rf.Files[1].UploadProgress < 10); i++ { 866 st.getAPI("/renter/files", &rf) 867 time.Sleep(100 * time.Millisecond) 868 } 869 if len(rf.Files) != 2 || rf.Files[0].UploadProgress < 10 || rf.Files[1].UploadProgress < 10 { 870 t.Fatal("the uploading is not succeeding for some reason:", rf.Files[0], rf.Files[1]) 871 } 872 873 // Check financial metrics; funds should have been spent on uploads/downloads 874 err = st.getAPI("/renter", &rg) 875 if err != nil { 876 t.Fatal(err) 877 } 878 fm := rg.FinancialMetrics 879 newSpent := rg.Settings.Allowance.Funds.Sub(fm.Unspent) 880 // all new spending should be reflected in upload/download/storage spending 881 diff := fm.UploadSpending.Add(fm.DownloadSpending).Add(fm.StorageSpending) 882 if !diff.Equals(newSpent.Sub(spent)) { 883 t.Fatal("all new spending should be reflected in metrics:", diff, newSpent.Sub(spent)) 884 } 885 } 886 887 // TestRenterParallelDelete tests that uploading and deleting parallel does not 888 // result in failures or stalling. 889 func TestRenterParallelDelete(t *testing.T) { 890 if testing.Short() { 891 t.SkipNow() 892 } 893 st, err := createServerTester(t.Name()) 894 if err != nil { 895 t.Fatal(err) 896 } 897 defer st.server.panicClose() 898 899 // Announce the host and start accepting contracts. 900 err = st.setHostStorage() 901 if err != nil { 902 t.Fatal(err) 903 } 904 err = st.announceHost() 905 if err != nil { 906 t.Fatal(err) 907 } 908 err = st.acceptContracts() 909 if err != nil { 910 t.Fatal(err) 911 } 912 913 // Set an allowance for the renter, allowing a contract to be formed. 914 allowanceValues := url.Values{} 915 testFunds := "10000000000000000000000000000" // 10k SC 916 testPeriod := "10" 917 allowanceValues.Set("funds", testFunds) 918 allowanceValues.Set("period", testPeriod) 919 allowanceValues.Set("renewwindow", testRenewWindow) 920 allowanceValues.Set("hosts", fmt.Sprint(modules.DefaultAllowance.Hosts)) 921 err = st.stdPostAPI("/renter", allowanceValues) 922 if err != nil { 923 t.Fatal(err) 924 } 925 926 // Create two files. 927 path := filepath.Join(st.dir, "test.dat") 928 err = createRandFile(path, 1024) 929 if err != nil { 930 t.Fatal(err) 931 } 932 path2 := filepath.Join(st.dir, "test2.dat") 933 err = createRandFile(path2, 1024) 934 if err != nil { 935 t.Fatal(err) 936 } 937 938 // Upload the first file to host. 939 uploadValues := url.Values{} 940 uploadValues.Set("source", path) 941 err = st.stdPostAPI("/renter/upload/test", uploadValues) 942 if err != nil { 943 t.Fatal(err) 944 } 945 // Wait for the first file to be registered in the renter. 946 var rf RenterFiles 947 for i := 0; i < 200 && len(rf.Files) != 1; i++ { 948 st.getAPI("/renter/files", &rf) 949 time.Sleep(100 * time.Millisecond) 950 } 951 if len(rf.Files) != 1 { 952 t.Fatal("file is not being registered:", rf.Files) 953 } 954 955 // In parallel, start uploading the other file, and delete the first file. 956 uploadValues = url.Values{} 957 uploadValues.Set("source", path2) 958 err = st.stdPostAPI("/renter/upload/test2", uploadValues) 959 if err != nil { 960 t.Fatal(err) 961 } 962 963 err = st.stdPostAPI("/renter/delete/test", url.Values{}) 964 if err != nil { 965 t.Fatal(err) 966 } 967 // Only the second file should be present 968 st.getAPI("/renter/files", &rf) 969 if len(rf.Files) != 1 || rf.Files[0].SiaPath.String() != "test2" { 970 t.Fatal("file was not deleted properly:", rf.Files) 971 } 972 973 // Wait for the second upload to complete. 974 var file RenterFile 975 for i := 0; i < 200 && file.File.UploadProgress < 10; i++ { 976 st.getAPI("/renter/file/test2", &file) 977 time.Sleep(100 * time.Millisecond) 978 } 979 if file.File.UploadProgress < 10 { 980 t.Fatal("Expected upload progress to be >=10 but was", file.File.UploadProgress) 981 } 982 983 // In parallel, download and delete the second file. 984 go st.stdPostAPI("/renter/delete/test2", url.Values{}) 985 time.Sleep(100 * time.Millisecond) 986 downpath := filepath.Join(st.dir, "testdown.dat") 987 err = st.stdGetAPI("/renter/download/test2?destination=" + downpath) 988 if err == nil { 989 t.Fatal("download should fail after delete") 990 } 991 992 // No files should be present 993 st.getAPI("/renter/files", &rf) 994 if len(rf.Files) != 0 { 995 t.Fatal("file was not deleted properly:", rf.Files) 996 } 997 } 998 999 // TestRenterRenew sets up an integration test where a renter renews a 1000 // contract with a host. 1001 func TestRenterRenew(t *testing.T) { 1002 if testing.Short() { 1003 t.SkipNow() 1004 } 1005 st, err := createServerTester(t.Name()) 1006 if err != nil { 1007 t.Fatal(err) 1008 } 1009 defer st.server.panicClose() 1010 1011 // Announce the host and start accepting contracts. 1012 err = st.setHostStorage() 1013 if err != nil { 1014 t.Fatal(err) 1015 } 1016 err = st.announceHost() 1017 if err != nil { 1018 t.Fatal(err) 1019 } 1020 err = st.acceptContracts() 1021 if err != nil { 1022 t.Fatal(err) 1023 } 1024 var ah HostdbActiveGET 1025 for i := 0; i < 50; i++ { 1026 if err = st.getAPI("/hostdb/active", &ah); err != nil { 1027 t.Fatal(err) 1028 } 1029 if len(ah.Hosts) == 1 { 1030 break 1031 } 1032 time.Sleep(time.Millisecond * 100) 1033 } 1034 if len(ah.Hosts) != 1 { 1035 t.Fatalf("expected 1 host, got %v", len(ah.Hosts)) 1036 } 1037 1038 // Set an allowance for the renter, allowing a contract to be formed. 1039 allowanceValues := url.Values{} 1040 testFunds := "10000000000000000000000000000" // 10k SC 1041 testPeriod := 10 1042 allowanceValues.Set("funds", testFunds) 1043 allowanceValues.Set("period", strconv.Itoa(testPeriod)) 1044 allowanceValues.Set("renewwindow", strconv.Itoa(testPeriod/2)) 1045 allowanceValues.Set("hosts", fmt.Sprint(modules.DefaultAllowance.Hosts)) 1046 err = st.stdPostAPI("/renter", allowanceValues) 1047 if err != nil { 1048 t.Fatal(err) 1049 } 1050 1051 // Block until the allowance has finished forming contracts. 1052 err = build.Retry(50, time.Millisecond*250, func() error { 1053 var rc RenterContracts 1054 err = st.getAPI("/renter/contracts", &rc) 1055 if err != nil { 1056 return errors.New("couldn't get renter stats") 1057 } 1058 if len(rc.Contracts) != 1 { 1059 return errors.New("no contracts") 1060 } 1061 return nil 1062 }) 1063 if err != nil { 1064 t.Fatal("allowance setting failed") 1065 } 1066 1067 // Create a file. 1068 path := filepath.Join(st.dir, "test.dat") 1069 err = createRandFile(path, 1024) 1070 if err != nil { 1071 t.Fatal(err) 1072 } 1073 1074 // Upload the file to the renter. 1075 uploadValues := url.Values{} 1076 uploadValues.Set("source", path) 1077 err = st.stdPostAPI("/renter/upload/test", uploadValues) 1078 if err != nil { 1079 t.Fatal(err) 1080 } 1081 // Only one piece will be uploaded (10% at current redundancy). 1082 var rf RenterFiles 1083 for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ { 1084 st.getAPI("/renter/files", &rf) 1085 time.Sleep(100 * time.Millisecond) 1086 } 1087 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 { 1088 t.Fatal("the uploading is not succeeding for some reason:", rf.Files[0]) 1089 } 1090 1091 // Get current contract ID. 1092 var rc RenterContracts 1093 err = st.getAPI("/renter/contracts", &rc) 1094 if err != nil { 1095 t.Fatal(err) 1096 } 1097 contractID := rc.Contracts[0].ID 1098 1099 // Mine enough blocks to enter the renewal window. 1100 testWindow := testPeriod / 2 1101 for i := 0; i < testWindow+1; i++ { 1102 _, err = st.miner.AddBlock() 1103 if err != nil { 1104 t.Fatal(err) 1105 } 1106 } 1107 // Wait for the contract to be renewed. 1108 for i := 0; i < 200 && (len(rc.Contracts) != 1 || rc.Contracts[0].ID == contractID); i++ { 1109 st.getAPI("/renter/contracts", &rc) 1110 time.Sleep(100 * time.Millisecond) 1111 } 1112 if rc.Contracts[0].ID == contractID { 1113 t.Fatal("contract was not renewed:", rc.Contracts[0]) 1114 } 1115 1116 // Try downloading the file. 1117 downpath := filepath.Join(st.dir, "testdown.dat") 1118 err = st.stdGetAPI("/renter/download/test?destination=" + downpath) 1119 if err != nil { 1120 t.Fatal(err) 1121 } 1122 // Check that the download has the right contents. 1123 orig, err := ioutil.ReadFile(path) 1124 if err != nil { 1125 t.Fatal(err) 1126 } 1127 download, err := ioutil.ReadFile(downpath) 1128 if err != nil { 1129 t.Fatal(err) 1130 } 1131 if bytes.Compare(orig, download) != 0 { 1132 t.Fatal("data mismatch when downloading a file") 1133 } 1134 } 1135 1136 // TestRenterAllowance sets up an integration test where a renter attempts to 1137 // download a file after changing the allowance. 1138 func TestRenterAllowance(t *testing.T) { 1139 t.Skip("bypassing NDF") 1140 if testing.Short() { 1141 t.SkipNow() 1142 } 1143 t.Parallel() 1144 1145 st, err := createServerTester(t.Name()) 1146 if err != nil { 1147 t.Fatal(err) 1148 } 1149 defer st.server.panicClose() 1150 1151 // Announce the host and start accepting contracts. 1152 err = st.setHostStorage() 1153 if err != nil { 1154 t.Fatal(err) 1155 } 1156 err = st.announceHost() 1157 if err != nil { 1158 t.Fatal(err) 1159 } 1160 err = st.acceptContracts() 1161 if err != nil { 1162 t.Fatal(err) 1163 } 1164 1165 // Set an allowance for the renter, allowing a contract to be formed. 1166 allowanceValues := url.Values{} 1167 testFunds := types.SiacoinPrecision.Mul64(10000) // 10k SC 1168 testPeriod := 20 1169 allowanceValues.Set("funds", testFunds.String()) 1170 allowanceValues.Set("period", strconv.Itoa(testPeriod)) 1171 err = st.stdPostAPI("/renter", allowanceValues) 1172 if err != nil { 1173 t.Fatal(err) 1174 } 1175 1176 // Create a file. 1177 path := filepath.Join(st.dir, "test.dat") 1178 err = createRandFile(path, 1024) 1179 if err != nil { 1180 t.Fatal(err) 1181 } 1182 1183 // Upload the file to the renter. 1184 uploadValues := url.Values{} 1185 uploadValues.Set("source", path) 1186 err = st.stdPostAPI("/renter/upload/test", uploadValues) 1187 if err != nil { 1188 t.Fatal(err) 1189 } 1190 // Only one piece will be uploaded (10% at current redundancy). 1191 var rf RenterFiles 1192 for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ { 1193 st.getAPI("/renter/files", &rf) 1194 time.Sleep(100 * time.Millisecond) 1195 } 1196 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 { 1197 t.Fatal("the uploading is not succeeding for some reason:", rf.Files[0]) 1198 } 1199 1200 t.Skip("ndf - re-enable after contractor overhaul") 1201 1202 // Try downloading the file after modifying the allowance in various ways. 1203 allowances := []struct { 1204 funds types.Currency 1205 period int 1206 }{ 1207 {testFunds.Mul64(10), testPeriod / 2}, 1208 {testFunds, testPeriod / 2}, 1209 {testFunds.Div64(10), testPeriod / 2}, 1210 {testFunds.Mul64(10), testPeriod}, 1211 {testFunds, testPeriod}, 1212 {testFunds.Div64(10), testPeriod}, 1213 {testFunds.Mul64(10), testPeriod * 2}, 1214 {testFunds, testPeriod * 2}, 1215 {testFunds.Div64(10), testPeriod * 2}, 1216 } 1217 1218 for _, a := range allowances { 1219 allowanceValues.Set("funds", a.funds.String()) 1220 allowanceValues.Set("period", strconv.Itoa(a.period)) 1221 err = st.stdPostAPI("/renter", allowanceValues) 1222 if err != nil { 1223 t.Fatal(err) 1224 } 1225 time.Sleep(100 * time.Millisecond) 1226 1227 // Try downloading the file. 1228 downpath := filepath.Join(st.dir, "testdown.dat") 1229 err = st.stdGetAPI("/renter/download/test?destination=" + downpath) 1230 if err != nil { 1231 t.Fatal(err) 1232 } 1233 // Check that the download has the right contents. 1234 orig, err := ioutil.ReadFile(path) 1235 if err != nil { 1236 t.Fatal(err) 1237 } 1238 download, err := ioutil.ReadFile(downpath) 1239 if err != nil { 1240 t.Fatal(err) 1241 } 1242 if bytes.Compare(orig, download) != 0 { 1243 t.Fatal("data mismatch when downloading a file") 1244 } 1245 } 1246 } 1247 1248 // TestHostAndRentReload sets up an integration test where a host and renter 1249 // do basic uploads and downloads, with an intervening shutdown+startup. 1250 func TestHostAndRentReload(t *testing.T) { 1251 if testing.Short() { 1252 t.SkipNow() 1253 } 1254 t.Parallel() 1255 st, err := createServerTester(t.Name()) 1256 if err != nil { 1257 t.Fatal(err) 1258 } 1259 1260 // Announce the host and start accepting contracts. 1261 err = st.setHostStorage() 1262 if err != nil { 1263 t.Fatal(err) 1264 } 1265 err = st.announceHost() 1266 if err != nil { 1267 t.Fatal(err) 1268 } 1269 err = st.acceptContracts() 1270 if err != nil { 1271 t.Fatal(err) 1272 } 1273 // Mine a block so that the wallet reclaims refund outputs 1274 _, err = st.miner.AddBlock() 1275 if err != nil { 1276 t.Fatal(err) 1277 } 1278 1279 // Set an allowance for the renter, allowing a contract to be formed. 1280 allowanceValues := url.Values{} 1281 testFunds := "100000000000000000000000000000" // 100k SC 1282 testPeriod := "10" 1283 allowanceValues.Set("funds", testFunds) 1284 allowanceValues.Set("period", testPeriod) 1285 allowanceValues.Set("renewwindow", testRenewWindow) 1286 allowanceValues.Set("hosts", fmt.Sprint(modules.DefaultAllowance.Hosts)) 1287 err = st.stdPostAPI("/renter", allowanceValues) 1288 if err != nil { 1289 t.Fatal(err) 1290 } 1291 1292 // Block until the allowance has finished forming contracts. 1293 err = build.Retry(50, time.Millisecond*250, func() error { 1294 var rc RenterContracts 1295 err = st.getAPI("/renter/contracts", &rc) 1296 if err != nil { 1297 return errors.New("couldn't get renter stats") 1298 } 1299 if len(rc.Contracts) != 1 { 1300 return errors.New("no contracts") 1301 } 1302 return nil 1303 }) 1304 if err != nil { 1305 t.Fatal("allowance setting failed") 1306 } 1307 1308 // Create a file. 1309 path := filepath.Join(st.dir, "test.dat") 1310 err = createRandFile(path, 1024) 1311 if err != nil { 1312 t.Fatal(err) 1313 } 1314 1315 // Upload the file to the renter. 1316 uploadValues := url.Values{} 1317 uploadValues.Set("source", path) 1318 err = st.stdPostAPI("/renter/upload/test", uploadValues) 1319 if err != nil { 1320 t.Fatal(err) 1321 } 1322 // Only one piece will be uploaded (10% at current redundancy). 1323 var rf RenterFiles 1324 for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ { 1325 st.getAPI("/renter/files", &rf) 1326 time.Sleep(100 * time.Millisecond) 1327 } 1328 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 { 1329 rflen := len(rf.Files) 1330 t.Logf("rf.Files has %v elements\n", rflen) 1331 1332 t.Fatal("the uploading is not succeeding for some reason:", rf.Files[0]) 1333 } 1334 1335 // Try downloading the file. 1336 downpath := filepath.Join(st.dir, "testdown.dat") 1337 err = st.stdGetAPI("/renter/download/test?destination=" + downpath) 1338 if err != nil { 1339 t.Fatal(err) 1340 } 1341 // Check that the download has the right contents. 1342 orig, err := ioutil.ReadFile(path) 1343 if err != nil { 1344 t.Fatal(err) 1345 } 1346 download, err := ioutil.ReadFile(downpath) 1347 if err != nil { 1348 t.Fatal(err) 1349 } 1350 if bytes.Compare(orig, download) != 0 { 1351 t.Fatal("data mismatch when downloading a file") 1352 } 1353 1354 // The renter's downloads queue should have 1 entry now. 1355 var queue RenterDownloadQueue 1356 if err = st.getAPI("/renter/downloads", &queue); err != nil { 1357 t.Fatal(err) 1358 } 1359 if len(queue.Downloads) != 1 { 1360 t.Fatalf("expected renter to have 1 download in the queue; got %v", len(queue.Downloads)) 1361 } 1362 1363 // close and reopen the server 1364 err = st.server.Close() 1365 if err != nil { 1366 t.Fatal(err) 1367 } 1368 st, err = assembleServerTester(st.walletKey, st.dir) 1369 if err != nil { 1370 t.Fatal(err) 1371 } 1372 defer st.server.panicClose() 1373 1374 // Announce the host again and wait until the host is re-scanned and put 1375 // back into the hostdb as an active host. 1376 announceValues := url.Values{} 1377 announceValues.Set("address", string(st.host.ExternalSettings().NetAddress)) 1378 err = st.stdPostAPI("/host/announce", announceValues) 1379 if err != nil { 1380 t.Fatal(err) 1381 } 1382 // Mine a block. 1383 _, err = st.miner.AddBlock() 1384 if err != nil { 1385 t.Fatal(err) 1386 } 1387 err = build.Retry(100, time.Millisecond*100, func() error { 1388 var hosts HostdbActiveGET 1389 err := st.getAPI("/hostdb/active", &hosts) 1390 if err != nil { 1391 return err 1392 } 1393 if len(hosts.Hosts) != 1 { 1394 return errors.New("host is not in the set of active hosts") 1395 } 1396 return nil 1397 }) 1398 if err != nil { 1399 t.Fatal(err) 1400 } 1401 1402 // Try downloading the file. 1403 err = st.stdGetAPI("/renter/download/test?destination=" + downpath) 1404 if err != nil { 1405 t.Fatal(err) 1406 } 1407 // Check that the download has the right contents. 1408 orig, err = ioutil.ReadFile(path) 1409 if err != nil { 1410 t.Fatal(err) 1411 } 1412 download, err = ioutil.ReadFile(downpath) 1413 if err != nil { 1414 t.Fatal(err) 1415 } 1416 if bytes.Compare(orig, download) != 0 { 1417 t.Fatal("data mismatch when downloading a file") 1418 } 1419 } 1420 1421 // TestHostAndRenterRenewInterrupt 1422 func TestHostAndRenterRenewInterrupt(t *testing.T) { 1423 t.Skip("active test following contractor overhaul") 1424 if testing.Short() { 1425 t.SkipNow() 1426 } 1427 t.Parallel() 1428 st, err := createServerTester(t.Name()) 1429 if err != nil { 1430 t.Fatal(err) 1431 } 1432 stHost, err := blankServerTester(t.Name() + "-Host") 1433 if err != nil { 1434 t.Fatal(err) 1435 } 1436 sts := []*serverTester{st, stHost} 1437 err = fullyConnectNodes(sts) 1438 if err != nil { 1439 t.Fatal(err) 1440 } 1441 err = fundAllNodes(sts) 1442 if err != nil { 1443 t.Fatal(err) 1444 } 1445 1446 // Announce the host. 1447 err = stHost.acceptContracts() 1448 if err != nil { 1449 t.Fatal(err) 1450 } 1451 err = stHost.setHostStorage() 1452 if err != nil { 1453 t.Fatal(err) 1454 } 1455 err = stHost.announceHost() 1456 if err != nil { 1457 t.Fatal(err) 1458 } 1459 1460 // Wait for host to be seen in renter's hostdb 1461 var ah HostdbActiveGET 1462 for i := 0; i < 50; i++ { 1463 if err = st.getAPI("/hostdb/active", &ah); err != nil { 1464 t.Fatal(err) 1465 } 1466 if len(ah.Hosts) == 1 { 1467 break 1468 } 1469 time.Sleep(time.Millisecond * 100) 1470 } 1471 if len(ah.Hosts) != 1 { 1472 t.Fatalf("expected 1 host, got %v", len(ah.Hosts)) 1473 } 1474 1475 // Upload a file to the host. 1476 allowanceValues := url.Values{} 1477 testFunds := "10000000000000000000000000000" // 10k SC 1478 testPeriod := "10" 1479 testPeriodInt := 10 1480 allowanceValues.Set("funds", testFunds) 1481 allowanceValues.Set("period", testPeriod) 1482 err = st.stdPostAPI("/renter", allowanceValues) 1483 if err != nil { 1484 t.Fatal(err) 1485 } 1486 // Create a file. 1487 path := filepath.Join(st.dir, "test.dat") 1488 err = createRandFile(path, 10e3) 1489 if err != nil { 1490 t.Fatal(err) 1491 } 1492 // Upload the file to the renter. 1493 uploadValues := url.Values{} 1494 uploadValues.Set("source", path) 1495 err = st.stdPostAPI("/renter/upload/test", uploadValues) 1496 if err != nil { 1497 t.Fatal(err) 1498 } 1499 1500 // Get current contract ID. 1501 var rc RenterContracts 1502 err = st.getAPI("/renter/contracts", &rc) 1503 if err != nil { 1504 t.Fatal(err) 1505 } 1506 contractID := rc.Contracts[0].ID 1507 1508 // Mine enough blocks to enter the renewal window. 1509 testWindow := testPeriodInt / 2 1510 for i := 0; i < testWindow+1; i++ { 1511 _, err = st.miner.AddBlock() 1512 if err != nil { 1513 t.Fatal(err) 1514 } 1515 } 1516 // Wait for the contract to be renewed. 1517 for i := 0; i < 200 && (len(rc.Contracts) != 1 || rc.Contracts[0].ID == contractID); i++ { 1518 st.getAPI("/renter/contracts", &rc) 1519 time.Sleep(100 * time.Millisecond) 1520 } 1521 if rc.Contracts[0].ID == contractID { 1522 t.Fatal("contract was not renewed:", rc.Contracts[0]) 1523 } 1524 1525 // Only one piece will be uploaded (10% at current redundancy). 1526 var rf RenterFiles 1527 for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ { 1528 st.getAPI("/renter/files", &rf) 1529 time.Sleep(1000 * time.Millisecond) 1530 } 1531 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 { 1532 t.Fatal("the uploading is not succeeding for some reason:", rf.Files[0]) 1533 } 1534 1535 // Try downloading the file. 1536 downpath := filepath.Join(st.dir, "testdown.dat") 1537 err = st.stdGetAPI("/renter/download/test?destination=" + downpath) 1538 if err != nil { 1539 t.Fatal(err) 1540 } 1541 // Check that the download has the right contents. 1542 orig, err := ioutil.ReadFile(path) 1543 if err != nil { 1544 t.Fatal(err) 1545 } 1546 download, err := ioutil.ReadFile(downpath) 1547 if err != nil { 1548 t.Fatal(err) 1549 } 1550 if bytes.Compare(orig, download) != 0 { 1551 t.Fatal("data mismatch when downloading a file") 1552 } 1553 } 1554 1555 // TestUploadedBytesReporting verifies that reporting of how many bytes have 1556 // been uploaded via active contracts is accurate 1557 func TestUploadedBytesReporting(t *testing.T) { 1558 if testing.Short() { 1559 t.SkipNow() 1560 } 1561 t.Parallel() 1562 st, err := createServerTester(t.Name()) 1563 if err != nil { 1564 t.Fatal(err) 1565 } 1566 defer st.server.Close() 1567 stH1, err := blankServerTester(t.Name() + " - Host 2") 1568 if err != nil { 1569 t.Fatal(err) 1570 } 1571 defer stH1.server.Close() 1572 testGroup := []*serverTester{st, stH1} 1573 1574 // Connect the testers to eachother so that they are all on the same 1575 // blockchain. 1576 err = fullyConnectNodes(testGroup) 1577 if err != nil { 1578 t.Fatal(err) 1579 } 1580 // Make sure that every wallet has money in it. 1581 err = fundAllNodes(testGroup) 1582 if err != nil { 1583 t.Fatal(err) 1584 } 1585 // Add storage to every host. 1586 err = addStorageToAllHosts(testGroup) 1587 if err != nil { 1588 t.Fatal(err) 1589 } 1590 // Announce every host. 1591 err = announceAllHosts(testGroup) 1592 if err != nil { 1593 t.Fatal(err) 1594 } 1595 1596 // Set an allowance with two hosts. 1597 allowanceValues := url.Values{} 1598 allowanceValues.Set("funds", "50000000000000000000000000000") // 50k SC 1599 allowanceValues.Set("hosts", "2") 1600 allowanceValues.Set("period", "10") 1601 allowanceValues.Set("renewwindow", "5") 1602 err = st.stdPostAPI("/renter", allowanceValues) 1603 if err != nil { 1604 t.Fatal(err) 1605 } 1606 1607 // Block until the allowance has finished forming contracts. 1608 err = build.Retry(50, time.Millisecond*250, func() error { 1609 var rc RenterContracts 1610 err = st.getAPI("/renter/contracts", &rc) 1611 if err != nil { 1612 return errors.New("couldn't get renter stats") 1613 } 1614 if len(rc.Contracts) != 2 { 1615 return errors.New("no contracts") 1616 } 1617 return nil 1618 }) 1619 if err != nil { 1620 t.Fatal("allowance setting failed") 1621 } 1622 1623 // Create a file to upload. 1624 filesize := int(modules.SectorSize * 2) 1625 path := filepath.Join(st.dir, "test.dat") 1626 err = createRandFile(path, filesize) 1627 if err != nil { 1628 t.Fatal(err) 1629 } 1630 1631 // Upload the file 1632 dataPieces := 1 1633 parityPieces := 1 1634 uploadValues := url.Values{} 1635 uploadValues.Set("source", path) 1636 uploadValues.Set("datapieces", fmt.Sprint(dataPieces)) 1637 uploadValues.Set("paritypieces", fmt.Sprint(parityPieces)) 1638 err = st.stdPostAPI("/renter/upload/test", uploadValues) 1639 if err != nil { 1640 t.Fatal(err) 1641 } 1642 1643 // Calculate the encrypted size of our fully redundant encoded file 1644 fullyRedundantSize := func(cipherType string) uint64 { 1645 var ct crypto.CipherType 1646 if err := ct.FromString(cipherType); err != nil { 1647 t.Fatal(err) 1648 } 1649 pieceSize := modules.SectorSize - ct.Overhead() 1650 chunkSize := pieceSize * uint64(dataPieces) 1651 numChunks := uint64(filesize) / chunkSize 1652 if uint64(filesize)%chunkSize != 0 { 1653 numChunks++ 1654 } 1655 return modules.SectorSize * uint64(dataPieces+parityPieces) * uint64(numChunks) 1656 } 1657 1658 // Monitor the file as it uploads. Ensure that the UploadProgress times 1659 // the fully redundant file size always equals UploadedBytes reported 1660 var rf RenterFile 1661 for i := 0; i < 60 && rf.File.UploadProgress < 100; i++ { 1662 st.getAPI("/renter/file/test", &rf) 1663 uploadProgressBytes := uint64(float64(fullyRedundantSize(rf.File.CipherType)) * rf.File.UploadProgress / 100.0) 1664 // Note: in Go 1.10 we will be able to write Math.Round(uploadProgressBytes) != rf.Files[0].UploadedBytes 1665 if uploadProgressBytes != rf.File.UploadedBytes && (uploadProgressBytes+1) != rf.File.UploadedBytes { 1666 t.Fatalf("api reports having uploaded %v bytes when upload progress is %v%%, but the actual uploaded bytes count should be %v\n", 1667 rf.File.UploadedBytes, rf.File.UploadProgress, uploadProgressBytes) 1668 } 1669 1670 time.Sleep(time.Second) 1671 } 1672 if err != nil { 1673 t.Fatal(err) 1674 } 1675 1676 // Upload progress should be 100% and redundancy should reach 2 1677 err = build.Retry(100, 100*time.Millisecond, func() error { 1678 if err := st.getAPI("/renter/file/test", &rf); err != nil { 1679 return err 1680 } 1681 if rf.File.UploadProgress < 100 { 1682 return fmt.Errorf("Expected UploadProgress to be 100 but was %v", rf.File.UploadProgress) 1683 } 1684 if rf.File.Redundancy != 2 { 1685 return fmt.Errorf("Expected Redundancy to be 2 but was %v", rf.File.Redundancy) 1686 } 1687 return nil 1688 }) 1689 if err != nil { 1690 t.Fatal(err) 1691 } 1692 1693 // When the file is fully redundantly uploaded, UploadedBytes should 1694 // equal the file's fully redundant size 1695 if rf.File.UploadedBytes != fullyRedundantSize(rf.File.CipherType) { 1696 t.Fatalf("api reports having uploaded %v bytes when upload progress is 100%%, but the actual fully redundant file size is %v\n", 1697 rf.File.UploadedBytes, fullyRedundantSize(rf.File.CipherType)) 1698 } 1699 1700 } 1701 1702 // TestRepairLoopBlocking checks if the repair loop blocks operations while a 1703 // non local file is being downloaded for repair. 1704 func TestRepairLoopBlocking(t *testing.T) { 1705 // TODO: Refactor dependency management to block download 1706 t.Skip("Test requires refactoring") 1707 if testing.Short() || !build.VLONG { 1708 t.SkipNow() 1709 } 1710 st, err := createServerTester(t.Name()) 1711 if err != nil { 1712 t.Fatal(err) 1713 } 1714 //st.renter.SetDependencies(renter.BlockRepairUpload{}) 1715 defer st.server.Close() 1716 stH1, err := blankServerTester(t.Name() + " - Host 1") 1717 if err != nil { 1718 t.Fatal(err) 1719 } 1720 defer stH1.server.Close() 1721 testGroup := []*serverTester{st, stH1} 1722 1723 // Connect the testers to eachother so that they are all on the same 1724 // blockchain. 1725 err = fullyConnectNodes(testGroup) 1726 if err != nil { 1727 t.Fatal(err) 1728 } 1729 // Make sure that every wallet has money in it. 1730 err = fundAllNodes(testGroup) 1731 if err != nil { 1732 t.Fatal(err) 1733 } 1734 1735 // Add storage to every host. 1736 err = addStorageToAllHosts(testGroup) 1737 if err != nil { 1738 t.Fatal(err) 1739 } 1740 err = announceAllHosts(testGroup) 1741 if err != nil { 1742 t.Fatal(err) 1743 } 1744 1745 // Set an allowance with two hosts. 1746 allowanceValues := url.Values{} 1747 allowanceValues.Set("funds", "50000000000000000000000000000") // 50k SC 1748 allowanceValues.Set("hosts", "2") 1749 allowanceValues.Set("period", "10") 1750 err = st.stdPostAPI("/renter", allowanceValues) 1751 if err != nil { 1752 t.Fatal(err) 1753 } 1754 1755 // Create a file with 1 chunk to upload. 1756 filesize := int(1) 1757 path := filepath.Join(st.dir, "test.dat") 1758 err = createRandFile(path, filesize) 1759 if err != nil { 1760 t.Fatal(err) 1761 } 1762 1763 // upload the file 1764 uploadValues := url.Values{} 1765 uploadValues.Set("source", path) 1766 err = st.stdPostAPI("/renter/upload/test", uploadValues) 1767 if err != nil { 1768 t.Fatal(err) 1769 } 1770 1771 // redundancy should reach 2 1772 var rf RenterFiles 1773 err = build.Retry(60, time.Second, func() error { 1774 st.getAPI("/renter/files", &rf) 1775 if len(rf.Files) >= 1 && rf.Files[0].Redundancy == 2 { 1776 return nil 1777 } 1778 return errors.New("file not uploaded") 1779 }) 1780 if err != nil { 1781 t.Fatal(err) 1782 } 1783 1784 // verify we can download 1785 downloadPath := filepath.Join(st.dir, "test-downloaded-verify.dat") 1786 err = st.stdGetAPI("/renter/download/test?destination=" + downloadPath) 1787 if err != nil { 1788 t.Fatal(err) 1789 } 1790 1791 // remove the local copy of the file 1792 err = os.Remove(path) 1793 if err != nil { 1794 t.Fatal(err) 1795 } 1796 1797 // take down one of the hosts 1798 err = stH1.server.Close() 1799 if err != nil { 1800 t.Fatal(err) 1801 } 1802 1803 // wait for the redundancy to decrement 1804 err = build.Retry(60, time.Second, func() error { 1805 st.getAPI("/renter/files", &rf) 1806 if len(rf.Files) >= 1 && rf.Files[0].Redundancy == 1 { 1807 return nil 1808 } 1809 return errors.New("file redundancy not decremented") 1810 }) 1811 if err != nil { 1812 t.Fatal(err) 1813 } 1814 1815 // verify we still can download 1816 downloadPath = filepath.Join(st.dir, "test-downloaded-verify2.dat") 1817 err = st.stdGetAPI("/renter/download/test?destination=" + downloadPath) 1818 if err != nil { 1819 t.Fatal(err) 1820 } 1821 1822 // bring up a few new hosts 1823 testGroup = []*serverTester{st} 1824 for i := 0; i < 3; i++ { 1825 stNewHost, err := blankServerTester(t.Name() + fmt.Sprintf("-newhost%d", i)) 1826 if err != nil { 1827 t.Fatal(err) 1828 } 1829 defer stNewHost.server.Close() 1830 testGroup = append(testGroup, stNewHost) 1831 } 1832 1833 // Connect the testers to eachother so that they are all on the same 1834 // blockchain. 1835 err = fullyConnectNodes(testGroup) 1836 if err != nil { 1837 t.Fatal(err) 1838 } 1839 _, err = synchronizationCheck(testGroup) 1840 if err != nil { 1841 t.Fatal(err) 1842 } 1843 1844 // Make sure that every wallet has money in it. 1845 err = fundAllNodes(testGroup) 1846 if err != nil { 1847 t.Fatal(err) 1848 } 1849 1850 for _, stNewHost := range testGroup[1 : len(testGroup)-1] { 1851 err = stNewHost.setHostStorage() 1852 if err != nil { 1853 t.Fatal(err) 1854 } 1855 err = stNewHost.announceHost() 1856 if err != nil { 1857 t.Fatal(err) 1858 } 1859 err = waitForBlock(stNewHost.cs.CurrentBlock().ID(), st) 1860 if err != nil { 1861 t.Fatal(err) 1862 } 1863 1864 // add a few new blocks in order to cause the renter to form contracts with the new host 1865 for i := 0; i < 10; i++ { 1866 b, err := testGroup[0].miner.AddBlock() 1867 if err != nil { 1868 t.Fatal(err) 1869 } 1870 tipID, err := synchronizationCheck(testGroup) 1871 if err != nil { 1872 t.Fatal(err) 1873 } 1874 if b.ID() != tipID { 1875 t.Fatal("test group does not have the tip block") 1876 } 1877 } 1878 } 1879 1880 // wait a few seconds for the the repair to be queued and started 1881 time.Sleep(3 * time.Second) 1882 1883 // redundancy should not increment back to 2 because the renter should be blocked 1884 st.getAPI("/renter/files", &rf) 1885 if len(rf.Files) >= 1 && rf.Files[0].Redundancy >= 2 && rf.Files[0].Available { 1886 t.Error("The file's redundancy incremented back to 2 but shouldn't") 1887 } 1888 1889 // create a second file to upload 1890 filesize = int(1) 1891 path2 := filepath.Join(st.dir, "test2.dat") 1892 err = createRandFile(path2, filesize) 1893 if err != nil { 1894 t.Fatal(err) 1895 } 1896 1897 // upload the second file 1898 uploadValues = url.Values{} 1899 uploadValues.Set("source", path2) 1900 1901 wait := make(chan error) 1902 go func() { 1903 wait <- st.stdPostAPI("/renter/upload/test2", uploadValues) 1904 }() 1905 select { 1906 case <-time.After(time.Minute): 1907 t.Fatal("/renter/upload API call didn't return within 60 seconds") 1908 case err = <-wait: 1909 } 1910 if err != nil { 1911 t.Fatal(err) 1912 } 1913 1914 // redundancy should reach 2 for the second file 1915 err = build.Retry(60, time.Second, func() error { 1916 st.getAPI("/renter/files", &rf) 1917 if len(rf.Files) >= 2 && rf.Files[1].Redundancy >= 2 { 1918 return nil 1919 } 1920 return errors.New("file 2 not uploaded") 1921 }) 1922 if err != nil { 1923 t.Fatal(err) 1924 } 1925 1926 // verify we can download the second file 1927 downloadPath = filepath.Join(st.dir, "test-downloaded-verify2.dat") 1928 err = st.stdGetAPI("/renter/download/test2?destination=" + downloadPath) 1929 if err != nil { 1930 t.Fatal(err) 1931 } 1932 } 1933 1934 // TestRemoteFileRepairMassive is similar to TestRemoteFileRepair but uploads 1935 // more files to find potential deadlocks or crashes 1936 func TestRemoteFileRepairMassive(t *testing.T) { 1937 if testing.Short() || !build.VLONG { 1938 t.SkipNow() 1939 } 1940 st, err := createServerTester(t.Name()) 1941 if err != nil { 1942 t.Fatal(err) 1943 } 1944 defer st.server.Close() 1945 stH1, err := blankServerTester(t.Name() + " - Host 1") 1946 if err != nil { 1947 t.Fatal(err) 1948 } 1949 defer stH1.server.Close() 1950 testGroup := []*serverTester{st, stH1} 1951 1952 // Connect the testers to eachother so that they are all on the same 1953 // blockchain. 1954 err = fullyConnectNodes(testGroup) 1955 if err != nil { 1956 t.Fatal(err) 1957 } 1958 // Make sure that every wallet has money in it. 1959 err = fundAllNodes(testGroup) 1960 if err != nil { 1961 t.Fatal(err) 1962 } 1963 1964 // Add storage to every host. 1965 err = addStorageToAllHosts(testGroup) 1966 if err != nil { 1967 t.Fatal(err) 1968 } 1969 err = announceAllHosts(testGroup) 1970 if err != nil { 1971 t.Fatal(err) 1972 } 1973 1974 // Set an allowance with two hosts. 1975 allowanceValues := url.Values{} 1976 allowanceValues.Set("funds", "50000000000000000000000000000") // 50k SC 1977 allowanceValues.Set("hosts", "2") 1978 allowanceValues.Set("period", "10") 1979 err = st.stdPostAPI("/renter", allowanceValues) 1980 if err != nil { 1981 t.Fatal(err) 1982 } 1983 1984 // Create a file to upload. 1985 filesize := int(4000) 1986 path := filepath.Join(st.dir, "test.dat") 1987 err = createRandFile(path, filesize) 1988 if err != nil { 1989 t.Fatal(err) 1990 } 1991 1992 // upload the file numUploads times 1993 numUploads := 10 1994 uploadValues := url.Values{} 1995 uploadValues.Set("source", path) 1996 1997 for i := 0; i < numUploads; i++ { 1998 err = st.stdPostAPI(fmt.Sprintf("/renter/upload/test%v", i), uploadValues) 1999 if err != nil { 2000 t.Fatal(err) 2001 } 2002 } 2003 2004 // redundancy should reach 2 for all files 2005 var rf RenterFiles 2006 err = build.Retry(600, time.Second, func() error { 2007 st.getAPI("/renter/files", &rf) 2008 if len(rf.Files) != numUploads { 2009 return errors.New("file not uploaded") 2010 } 2011 for i, f := range rf.Files { 2012 if f.Redundancy != 2 { 2013 return fmt.Errorf("file %v only reached %v redundancy", i, f.Redundancy) 2014 } 2015 } 2016 return nil 2017 }) 2018 if err != nil { 2019 t.Fatal(err) 2020 } 2021 2022 // remove the local copy of the file 2023 err = os.Remove(path) 2024 if err != nil { 2025 t.Fatal(err) 2026 } 2027 2028 // take down one of the hosts 2029 err = stH1.server.Close() 2030 if err != nil { 2031 t.Fatal(err) 2032 } 2033 2034 // wait for the redundancy to decrement 2035 err = build.Retry(60, time.Second, func() error { 2036 st.getAPI("/renter/files", &rf) 2037 if len(rf.Files) != numUploads { 2038 return errors.New("file not uploaded") 2039 } 2040 for _, f := range rf.Files { 2041 if f.Redundancy != 1 { 2042 return errors.New("file redudancy didn't decrement to x1") 2043 } 2044 } 2045 return nil 2046 }) 2047 if err != nil { 2048 t.Fatal(err) 2049 } 2050 2051 // bring up a new host 2052 stNewHost, err := blankServerTester(t.Name() + "-newhost") 2053 if err != nil { 2054 t.Fatal(err) 2055 } 2056 defer stNewHost.server.Close() 2057 2058 testGroup = []*serverTester{st, stNewHost} 2059 2060 // Connect the testers to eachother so that they are all on the same 2061 // blockchain. 2062 err = fullyConnectNodes(testGroup) 2063 if err != nil { 2064 t.Fatal(err) 2065 } 2066 _, err = synchronizationCheck(testGroup) 2067 if err != nil { 2068 t.Fatal(err) 2069 } 2070 2071 // Make sure that every wallet has money in it. 2072 err = fundAllNodes(testGroup) 2073 if err != nil { 2074 t.Fatal(err) 2075 } 2076 2077 err = stNewHost.setHostStorage() 2078 if err != nil { 2079 t.Fatal(err) 2080 } 2081 err = stNewHost.announceHost() 2082 if err != nil { 2083 t.Fatal(err) 2084 } 2085 err = waitForBlock(stNewHost.cs.CurrentBlock().ID(), st) 2086 if err != nil { 2087 t.Fatal(err) 2088 } 2089 2090 // add a few new blocks in order to cause the renter to form contracts with the new host 2091 for i := 0; i < 10; i++ { 2092 b, err := testGroup[0].miner.AddBlock() 2093 if err != nil { 2094 t.Fatal(err) 2095 } 2096 tipID, err := synchronizationCheck(testGroup) 2097 if err != nil { 2098 t.Fatal(err) 2099 } 2100 if b.ID() != tipID { 2101 t.Fatal("test group does not have the tip block") 2102 } 2103 } 2104 2105 // redundancy should increment back to 2 as the renter uploads to the new 2106 // host using the download-to-upload strategy 2107 err = build.Retry(300, time.Second, func() error { 2108 st.getAPI("/renter/files", &rf) 2109 if len(rf.Files) != numUploads { 2110 return errors.New("file not uploaded") 2111 } 2112 for i, f := range rf.Files { 2113 if f.Redundancy != 2 { 2114 return fmt.Errorf("file %v only reached %v redundancy", i, f.Redundancy) 2115 } 2116 } 2117 return nil 2118 }) 2119 if err != nil { 2120 t.Fatal(err) 2121 } 2122 }