gitlab.com/jokerrs1/Sia@v1.3.2/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 "strings" 16 "sync" 17 "testing" 18 "time" 19 20 "github.com/NebulousLabs/Sia/build" 21 "github.com/NebulousLabs/Sia/crypto" 22 "github.com/NebulousLabs/Sia/modules" 23 "github.com/NebulousLabs/Sia/types" 24 ) 25 26 // TestHostObligationAcceptingContracts verifies that the host will complete 27 // storage proofs and the renter will successfully download even if the host 28 // has set accepting contracts to false. 29 func TestHostObligationAcceptingContracts(t *testing.T) { 30 if testing.Short() { 31 t.SkipNow() 32 } 33 st, err := createServerTester(t.Name()) 34 if err != nil { 35 t.Fatal(err) 36 } 37 defer st.server.Close() 38 err = st.setHostStorage() 39 if err != nil { 40 t.Fatal(err) 41 } 42 err = st.acceptContracts() 43 if err != nil { 44 t.Fatal(err) 45 } 46 err = st.announceHost() 47 if err != nil { 48 t.Fatal(err) 49 } 50 allowanceValues := url.Values{} 51 allowanceValues.Set("funds", "50000000000000000000000000000") // 50k SC 52 allowanceValues.Set("hosts", "1") 53 allowanceValues.Set("period", "10") 54 allowanceValues.Set("renewwindow", "5") 55 err = st.stdPostAPI("/renter", allowanceValues) 56 if err != nil { 57 t.Fatal(err) 58 } 59 60 // Block until the allowance has finished forming contracts. 61 err = build.Retry(50, time.Millisecond*250, func() error { 62 var rc RenterContracts 63 err = st.getAPI("/renter/contracts", &rc) 64 if err != nil { 65 return errors.New("couldn't get renter stats") 66 } 67 if len(rc.Contracts) != 1 { 68 return errors.New("no contracts") 69 } 70 return nil 71 }) 72 if err != nil { 73 t.Fatal("allowance setting failed") 74 } 75 76 filesize := int(1024) 77 path := filepath.Join(st.dir, "test.dat") 78 err = createRandFile(path, filesize) 79 if err != nil { 80 t.Fatal(err) 81 } 82 83 // upload the file 84 uploadValues := url.Values{} 85 uploadValues.Set("source", path) 86 err = st.stdPostAPI("/renter/upload/test", uploadValues) 87 if err != nil { 88 t.Fatal(err) 89 } 90 91 // redundancy should reach 1 92 var rf RenterFiles 93 err = retry(120, time.Millisecond*250, func() error { 94 st.getAPI("/renter/files", &rf) 95 if len(rf.Files) >= 1 && rf.Files[0].Available { 96 return nil 97 } 98 return errors.New("file not uploaded") 99 }) 100 if err != nil { 101 t.Fatal(err) 102 } 103 104 // set acceptingcontracts = false, mine some blocks, verify we can download 105 settings := st.host.InternalSettings() 106 settings.AcceptingContracts = false 107 st.host.SetInternalSettings(settings) 108 for i := 0; i < 3; i++ { 109 _, err := st.miner.AddBlock() 110 if err != nil { 111 t.Fatal(err) 112 } 113 time.Sleep(time.Millisecond * 100) 114 } 115 downloadPath := filepath.Join(st.dir, "test-downloaded-verify.dat") 116 err = st.stdGetAPI("/renter/download/test?destination=" + downloadPath) 117 if err != nil { 118 t.Fatal(err) 119 } 120 121 // mine blocks to cause the host to submit storage proofs to the blockchain. 122 for i := 0; i < 15; i++ { 123 _, err := st.miner.AddBlock() 124 if err != nil { 125 t.Fatal(err) 126 } 127 time.Sleep(time.Millisecond * 100) 128 } 129 130 // should have successful proofs 131 success := false 132 for _, so := range st.host.StorageObligations() { 133 if so.ProofConfirmed { 134 success = true 135 break 136 } 137 } 138 if !success { 139 t.Fatal("no successful storage proofs") 140 } 141 } 142 143 // TestRenterLocalRepair verifies that the renter will use the local file to 144 // repair if the file exists locally 145 func TestRenterLocalRepair(t *testing.T) { 146 if testing.Short() { 147 t.SkipNow() 148 } 149 st, err := createServerTester(t.Name()) 150 if err != nil { 151 t.Fatal(err) 152 } 153 defer st.server.Close() 154 stH1, err := blankServerTester(t.Name() + " - Host 1") 155 if err != nil { 156 t.Fatal(err) 157 } 158 defer stH1.server.Close() 159 testGroup := []*serverTester{st, stH1} 160 161 // Connect the testers to eachother so that they are all on the same 162 // blockchain. 163 err = fullyConnectNodes(testGroup) 164 if err != nil { 165 t.Fatal(err) 166 } 167 // Make sure that every wallet has money in it. 168 err = fundAllNodes(testGroup) 169 if err != nil { 170 t.Fatal(err) 171 } 172 173 // Add storage to every host. 174 err = addStorageToAllHosts(testGroup) 175 if err != nil { 176 t.Fatal(err) 177 } 178 err = announceAllHosts(testGroup) 179 if err != nil { 180 t.Fatal(err) 181 } 182 183 // Set an allowance with two hosts. 184 allowanceValues := url.Values{} 185 allowanceValues.Set("funds", "50000000000000000000000000000") // 50k SC 186 allowanceValues.Set("hosts", "2") 187 allowanceValues.Set("period", "10") 188 allowanceValues.Set("renewwindow", "5") 189 err = st.stdPostAPI("/renter", allowanceValues) 190 if err != nil { 191 t.Fatal(err) 192 } 193 194 // Block until the allowance has finished forming contracts. 195 err = build.Retry(50, time.Millisecond*250, func() error { 196 var rc RenterContracts 197 err = st.getAPI("/renter/contracts", &rc) 198 if err != nil { 199 return errors.New("couldn't get renter stats") 200 } 201 if len(rc.Contracts) != 2 { 202 return errors.New("no contracts") 203 } 204 return nil 205 }) 206 if err != nil { 207 t.Fatal("allowance setting failed") 208 } 209 210 // Create a file to upload. 211 filesize := int(1024) 212 path := filepath.Join(st.dir, "test.dat") 213 err = createRandFile(path, filesize) 214 if err != nil { 215 t.Fatal(err) 216 } 217 218 // upload the file 219 uploadValues := url.Values{} 220 uploadValues.Set("source", path) 221 err = st.stdPostAPI("/renter/upload/test", uploadValues) 222 if err != nil { 223 t.Fatal(err) 224 } 225 226 // redundancy should reach 2 227 var rf RenterFiles 228 err = retry(60, time.Second, func() error { 229 st.getAPI("/renter/files", &rf) 230 if len(rf.Files) >= 1 && rf.Files[0].Redundancy >= 2 { 231 return nil 232 } 233 return errors.New("file not uploaded") 234 }) 235 if err != nil { 236 t.Fatal(err) 237 } 238 239 // download spending should not have increased 240 var rg RenterGET 241 err = st.getAPI("/renter", &rg) 242 if err != nil { 243 t.Fatal(err) 244 } 245 if rg.FinancialMetrics.DownloadSpending.Cmp(types.NewCurrency64(0)) > 0 { 246 t.Fatalf("expected no download spending, got %v instead\n", rg.FinancialMetrics.DownloadSpending) 247 } 248 249 // take down one of the hosts 250 err = stH1.server.Close() 251 if err != nil { 252 t.Fatal(err) 253 } 254 // Mine a block so the renter realizes the host is offline. 255 b, err := st.miner.AddBlock() 256 if err != nil { 257 t.Fatal(err) 258 } 259 err = waitForBlock(b.ID(), st) 260 if err != nil { 261 t.Fatal(err) 262 } 263 264 // wait for the redundancy to decrement 265 err = retry(60, time.Second, func() error { 266 if err := st.getAPI("/renter/files", &rf); err != nil { 267 return err 268 } 269 if len(rf.Files) >= 1 && rf.Files[0].Redundancy == 1 { 270 return nil 271 } 272 return errors.New("file redundancy not decremented") 273 }) 274 if err != nil { 275 t.Fatal(err, len(rf.Files), rf.Files[0].Redundancy) 276 } 277 278 // bring up a new host 279 stNewHost, err := blankServerTester(t.Name() + "-newhost") 280 if err != nil { 281 t.Fatal(err) 282 } 283 defer stNewHost.server.Close() 284 testGroup = []*serverTester{st, stNewHost} 285 286 // Connect the testers to eachother so that they are all on the same 287 // blockchain. 288 err = fullyConnectNodes(testGroup) 289 if err != nil { 290 t.Fatal(err) 291 } 292 _, err = synchronizationCheck(testGroup) 293 if err != nil { 294 t.Fatal(err) 295 } 296 297 // Make sure that every wallet has money in it. 298 err = fundAllNodes(testGroup) 299 if err != nil { 300 t.Fatal(err) 301 } 302 303 // Set up storage on the new host. 304 err = stNewHost.setHostStorage() 305 if err != nil { 306 t.Fatal(err) 307 } 308 err = stNewHost.announceHost() 309 if err != nil { 310 t.Fatal(err) 311 } 312 // Mine a block so the renter sees the host in its hostdb. Need to mine two 313 // blocks because after the announcement, the renter node's blockchain is 314 // actually behind. 315 b, err = stNewHost.miner.AddBlock() 316 if err != nil { 317 t.Fatal(err) 318 } 319 err = waitForBlock(b.ID(), testGroup[0]) 320 if err != nil { 321 t.Fatal(err) 322 } 323 _, err = synchronizationCheck(testGroup) 324 if err != nil { 325 t.Fatal(err) 326 } 327 328 // Wait for host to be seen in renter's hostdb 329 var ah HostdbActiveGET 330 err = build.Retry(250, time.Millisecond*250, func() error { 331 if err = st.getAPI("/hostdb/active", &ah); err != nil { 332 t.Fatal(err) 333 } 334 if len(ah.Hosts) != 2 { 335 return errors.New("not enough hosts in hostdb") 336 } 337 for _, host := range ah.Hosts { 338 if len(host.ScanHistory) < 2 { 339 return errors.New("hosts are not scanned") 340 } 341 } 342 return nil 343 }) 344 if err != nil { 345 t.Fatal(err, ah) 346 } 347 348 // Mine a block so the contractor sees that it can form a new contract. 349 b, err = testGroup[0].miner.AddBlock() 350 if err != nil { 351 t.Fatal(err) 352 } 353 err = waitForBlock(b.ID(), testGroup[0]) 354 if err != nil { 355 t.Fatal(err) 356 } 357 _, err = synchronizationCheck(testGroup) 358 if err != nil { 359 t.Fatal(err) 360 } 361 362 // Block until we formed a contract with the new host. 363 err = build.Retry(50, time.Millisecond*250, func() error { 364 var rc RenterContracts 365 err = st.getAPI("/renter/contracts", &rc) 366 if err != nil { 367 return errors.New("couldn't get renter stats") 368 } 369 if len(rc.Contracts) != 3 { 370 return fmt.Errorf("Insufficient contracts: expected %v was %v", 3, len(rc.Contracts)) 371 } 372 return nil 373 }) 374 if err != nil { 375 t.Fatalf("Failed to form new contract: %v", err) 376 } 377 378 // redundancy should increment back to 2 as the renter uploads to the new 379 // host using the download-to-upload strategy 380 err = retry(1000, 250*time.Millisecond, func() error { 381 if err := st.getAPI("/renter/files", &rf); err != nil { 382 return err 383 } 384 if len(rf.Files) >= 1 && rf.Files[0].Redundancy == 2 && rf.Files[0].Available { 385 return nil 386 } 387 return errors.New("file redundancy not incremented") 388 }) 389 if err != nil { 390 t.Log(len(rf.Files)) 391 t.Log(rf.Files[0].Redundancy) 392 t.Log(rf.Files[0].Available) 393 t.Fatal(err) 394 } 395 if rg.FinancialMetrics.DownloadSpending.Cmp(types.NewCurrency64(0)) > 0 { 396 t.Fatalf("expected no download spending, got %v instead\n", rg.FinancialMetrics.DownloadSpending) 397 } 398 } 399 400 // TestRemoteFileRepair verifies that if a trackedFile is made unavailable 401 // locally by being deleted, the repair loop will download the necessary chunks 402 // from the living hosts and upload them to new hosts. 403 func TestRemoteFileRepair(t *testing.T) { 404 if testing.Short() { 405 t.SkipNow() 406 } 407 st, err := createServerTester(t.Name()) 408 if err != nil { 409 t.Fatal(err) 410 } 411 defer st.server.Close() 412 stH1, err := blankServerTester(t.Name() + " - Host 1") 413 if err != nil { 414 t.Fatal(err) 415 } 416 defer stH1.server.Close() 417 testGroup := []*serverTester{st, stH1} 418 419 // Connect the testers to eachother so that they are all on the same 420 // blockchain. 421 err = fullyConnectNodes(testGroup) 422 if err != nil { 423 t.Fatal(err) 424 } 425 // Make sure that every wallet has money in it. 426 err = fundAllNodes(testGroup) 427 if err != nil { 428 t.Fatal(err) 429 } 430 431 // Add storage to every host. 432 err = addStorageToAllHosts(testGroup) 433 if err != nil { 434 t.Fatal(err) 435 } 436 err = announceAllHosts(testGroup) 437 if err != nil { 438 t.Fatal(err) 439 } 440 441 // Set an allowance with two hosts. 442 allowanceValues := url.Values{} 443 allowanceValues.Set("funds", "50000000000000000000000000000") // 50k SC 444 allowanceValues.Set("hosts", "2") 445 allowanceValues.Set("period", "10") 446 allowanceValues.Set("renewwindow", "5") 447 err = st.stdPostAPI("/renter", allowanceValues) 448 if err != nil { 449 t.Fatal(err) 450 } 451 452 // Create a file to upload. 453 filesize := int(45767) 454 path := filepath.Join(st.dir, "test.dat") 455 err = createRandFile(path, filesize) 456 if err != nil { 457 t.Fatal(err) 458 } 459 460 // upload the file 461 uploadValues := url.Values{} 462 uploadValues.Set("source", path) 463 err = st.stdPostAPI("/renter/upload/test", uploadValues) 464 if err != nil { 465 t.Fatal(err) 466 } 467 468 // redundancy should reach 2 469 var rf RenterFiles 470 err = retry(60, time.Second, func() error { 471 if err := st.getAPI("/renter/files", &rf); err != nil { 472 return err 473 } 474 if len(rf.Files) >= 1 && rf.Files[0].Redundancy == 2 { 475 return nil 476 } 477 return errors.New("file not uploaded") 478 }) 479 if err != nil { 480 t.Fatal(err) 481 } 482 483 // verify we can download 484 downloadPath := filepath.Join(st.dir, "test-downloaded-verify.dat") 485 err = st.stdGetAPI("/renter/download/test?destination=" + downloadPath) 486 if err != nil { 487 t.Fatal(err) 488 } 489 // save a copy of the file contents in memory for verification later 490 orig, err := ioutil.ReadFile(path) 491 if err != nil { 492 t.Fatal(err) 493 } 494 downloadedFile, err := ioutil.ReadFile(downloadPath) 495 if err != nil { 496 t.Fatal(err) 497 } 498 if !bytes.Equal(orig, downloadedFile) { 499 t.Log("Files not equal, lengths:", len(orig), len(downloadedFile)) 500 t.Fatal("Uploaded and downloaded file do not have matching data.") 501 } 502 503 // remove the local copy of the file 504 err = build.Retry(50, time.Millisecond*200, func() error { 505 return os.Remove(path) 506 }) 507 if err != nil { 508 t.Fatal(err) 509 } 510 511 // take down one of the hosts 512 err = stH1.server.Close() 513 if err != nil { 514 t.Fatal(err) 515 } 516 // wait for the redundancy to decrement 517 err = retry(120, time.Millisecond*250, func() error { 518 st.getAPI("/renter/files", &rf) 519 if len(rf.Files) >= 1 && rf.Files[0].Redundancy == 1 { 520 return nil 521 } 522 return errors.New("file redundancy not decremented") 523 }) 524 if err != nil { 525 t.Fatal(err) 526 } 527 // verify we still can download 528 downloadPath2 := filepath.Join(st.dir, "test-downloaded-verify2.dat") 529 err = st.stdGetAPI("/renter/download/test?destination=" + downloadPath2) 530 if err != nil { 531 t.Fatal(err) 532 } 533 downloadedFile2, err := ioutil.ReadFile(downloadPath) 534 if err != nil { 535 t.Fatal(err) 536 } 537 if !bytes.Equal(orig, downloadedFile2) { 538 t.Log("Files not equal, lengths:", len(orig), len(downloadedFile2)) 539 t.Fatal("Uploaded and downloaded file do not have matching data.") 540 } 541 542 // bring up a new host 543 stNewHost, err := blankServerTester(t.Name() + "-newhost") 544 if err != nil { 545 t.Fatal(err) 546 } 547 defer stNewHost.server.Close() 548 testGroup = []*serverTester{st, stNewHost} 549 // Connect the testers to eachother so that they are all on the same 550 // blockchain. 551 err = fullyConnectNodes(testGroup) 552 if err != nil { 553 t.Fatal(err) 554 } 555 _, err = synchronizationCheck(testGroup) 556 if err != nil { 557 t.Fatal(err) 558 } 559 560 // Make sure that every wallet has money in it, then set the storage and 561 // perform announcements. 562 err = fundAllNodes(testGroup) 563 if err != nil { 564 t.Fatal(err) 565 } 566 err = stNewHost.setHostStorage() 567 if err != nil { 568 t.Fatal(err) 569 } 570 err = stNewHost.announceHost() 571 if err != nil { 572 t.Fatal(err) 573 } 574 _, err = stNewHost.miner.AddBlock() 575 if err != nil { 576 t.Fatal(err) 577 } 578 err = waitForBlock(stNewHost.cs.CurrentBlock().ID(), st) 579 if err != nil { 580 t.Fatal(err) 581 } 582 _, err = synchronizationCheck(testGroup) 583 if err != nil { 584 t.Fatal(err) 585 } 586 587 // Wait for host to be seen in renter's hostdb 588 var ah HostdbActiveGET 589 err = build.Retry(250, time.Millisecond*250, func() error { 590 if err = st.getAPI("/hostdb/active", &ah); err != nil { 591 t.Fatal(err) 592 } 593 if len(ah.Hosts) != 2 { 594 return errors.New("not enough hosts in hostdb") 595 } 596 for _, host := range ah.Hosts { 597 if len(host.ScanHistory) < 2 { 598 return errors.New("hosts are not scanned") 599 } 600 } 601 return nil 602 }) 603 if err != nil { 604 t.Fatal(err) 605 } 606 607 // Mine a block so the contractor sees that it can form a new contract. 608 _, err = testGroup[0].miner.AddBlock() 609 if err != nil { 610 t.Fatal(err) 611 } 612 _, err = synchronizationCheck(testGroup) 613 if err != nil { 614 t.Fatal(err) 615 } 616 617 // Block until we formed a contract with the new host. 618 err = build.Retry(50, time.Millisecond*250, func() error { 619 var rc RenterContracts 620 err = st.getAPI("/renter/contracts", &rc) 621 if err != nil { 622 return errors.New("couldn't get renter stats") 623 } 624 if len(rc.Contracts) != 3 { 625 return fmt.Errorf("Insufficient contracts: expected %v was %v", 3, len(rc.Contracts)) 626 } 627 return nil 628 }) 629 if err != nil { 630 t.Fatalf("Failed to form new contract: %v", err) 631 } 632 633 // redundancy should increment back to 2 as the renter uploads to the new 634 // host using the download-to-upload strategy 635 err = retry(500, 250*time.Millisecond, func() error { 636 if err := st.getAPI("/renter/files", &rf); err != nil { 637 return err 638 } 639 640 if len(rf.Files) >= 1 && rf.Files[0].Redundancy == 2 && rf.Files[0].Available { 641 return nil 642 } 643 return errors.New("file redundancy not incremented") 644 }) 645 if err != nil { 646 t.Log(len(rf.Files), rf.Files[0].Redundancy, rf.Files[0].Available) 647 t.Fatal(err) 648 } 649 650 // Try to download the repaired file. 651 downloadPath = filepath.Join(st.dir, "test-downloaded3.dat") 652 err = st.stdGetAPI("/renter/download/test?destination=" + downloadPath) 653 if err != nil { 654 t.Fatal(err) 655 } 656 657 // Check that the download has the right contents. 658 downloaded, err := ioutil.ReadFile(downloadPath) 659 if err != nil { 660 t.Fatal(err) 661 } 662 if !bytes.Equal(orig, downloaded) { 663 t.Fatal("data mismatch when downloading a file") 664 } 665 } 666 667 // TestHostAndRentVanilla sets up an integration test where a host and renter 668 // do basic uploads and downloads. 669 func TestHostAndRentVanilla(t *testing.T) { 670 if testing.Short() { 671 t.SkipNow() 672 } 673 t.Parallel() 674 st, err := createServerTester(t.Name()) 675 if err != nil { 676 t.Fatal(err) 677 } 678 defer st.server.panicClose() 679 680 // Announce the host and start accepting contracts. 681 err = st.announceHost() 682 if err != nil { 683 t.Fatal(err) 684 } 685 err = st.setHostStorage() 686 if err != nil { 687 t.Fatal(err) 688 } 689 err = st.acceptContracts() 690 if err != nil { 691 t.Fatal(err) 692 } 693 694 // Set an allowance for the renter, allowing a contract to be formed. 695 allowanceValues := url.Values{} 696 testFunds := "10000000000000000000000000000" // 10k SC 697 testPeriod := "20" 698 renewWindow := "10" 699 testPeriodInt := 20 700 allowanceValues.Set("funds", testFunds) 701 allowanceValues.Set("period", testPeriod) 702 allowanceValues.Set("renewwindow", renewWindow) 703 allowanceValues.Set("hosts", fmt.Sprint(recommendedHosts)) 704 err = st.stdPostAPI("/renter", allowanceValues) 705 if err != nil { 706 t.Fatal(err) 707 } 708 709 // Block until the allowance has finished forming contracts. 710 err = build.Retry(50, time.Millisecond*250, func() error { 711 var rc RenterContracts 712 err = st.getAPI("/renter/contracts", &rc) 713 if err != nil { 714 return errors.New("couldn't get renter stats") 715 } 716 if len(rc.Contracts) != 1 { 717 return errors.New("no contracts") 718 } 719 return nil 720 }) 721 if err != nil { 722 t.Fatal("allowance setting failed") 723 } 724 725 // Check the host, who should now be reporting file contracts. 726 // 727 // TODO: Switch to using an API call. 728 obligations := st.host.StorageObligations() 729 if len(obligations) != 1 { 730 t.Error("Host has wrong number of obligations:", len(obligations)) 731 } 732 733 // Create a file. 734 path := filepath.Join(st.dir, "test.dat") 735 err = createRandFile(path, 1024) 736 if err != nil { 737 t.Fatal(err) 738 } 739 740 // Upload the file to the renter. 741 uploadValues := url.Values{} 742 uploadValues.Set("source", path) 743 err = st.stdPostAPI("/renter/upload/test", uploadValues) 744 if err != nil { 745 t.Fatal(err) 746 } 747 // Only one piece will be uploaded (10% at current redundancy). 748 var rf RenterFiles 749 for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ { 750 st.getAPI("/renter/files", &rf) 751 time.Sleep(100 * time.Millisecond) 752 } 753 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 { 754 t.Fatal("the uploading is not succeeding for some reason:", rf.Files[0]) 755 } 756 757 // On a second connection, upload another file. 758 path2 := filepath.Join(st.dir, "test2.dat") 759 test2Size := modules.SectorSize*2 + 1 760 err = createRandFile(path2, int(test2Size)) 761 if err != nil { 762 t.Fatal(err) 763 } 764 uploadValues = url.Values{} 765 uploadValues.Set("source", path2) 766 err = st.stdPostAPI("/renter/upload/test2", uploadValues) 767 if err != nil { 768 t.Fatal(err) 769 } 770 // Only one piece will be uploaded (10% at current redundancy). 771 for i := 0; i < 200 && (len(rf.Files) != 2 || rf.Files[0].UploadProgress < 10 || rf.Files[1].UploadProgress < 10); i++ { 772 st.getAPI("/renter/files", &rf) 773 time.Sleep(100 * time.Millisecond) 774 } 775 if len(rf.Files) != 2 || rf.Files[0].UploadProgress < 10 || rf.Files[1].UploadProgress < 10 { 776 t.Fatal("the uploading is not succeeding for some reason:", rf.Files[0], rf.Files[1]) 777 } 778 779 // Try downloading the first file. 780 downpath := filepath.Join(st.dir, "testdown.dat") 781 err = st.stdGetAPI("/renter/download/test?destination=" + downpath) 782 if err != nil { 783 t.Fatal(err) 784 } 785 // Check that the download has the right contents. 786 orig, err := ioutil.ReadFile(path) 787 if err != nil { 788 t.Fatal(err) 789 } 790 download, err := ioutil.ReadFile(downpath) 791 if err != nil { 792 t.Fatal(err) 793 } 794 if bytes.Compare(orig, download) != 0 { 795 t.Fatal("data mismatch when downloading a file") 796 } 797 798 // The renter's downloads queue should have 1 entry now. 799 var queue RenterDownloadQueue 800 if err = st.getAPI("/renter/downloads", &queue); err != nil { 801 t.Fatal(err) 802 } 803 if len(queue.Downloads) != 1 { 804 t.Fatalf("expected renter to have 1 download in the queue; got %v", len(queue.Downloads)) 805 } 806 807 // Try downloading the second file. 808 downpath2 := filepath.Join(st.dir, "testdown2.dat") 809 err = st.stdGetAPI("/renter/download/test2?destination=" + downpath2) 810 if err != nil { 811 t.Fatal(err) 812 } 813 // Check that the download has the right contents. 814 orig2, err := ioutil.ReadFile(path2) 815 if err != nil { 816 t.Fatal(err) 817 } 818 download2, err := ioutil.ReadFile(downpath2) 819 if err != nil { 820 t.Fatal(err) 821 } 822 if bytes.Compare(orig2, download2) != 0 { 823 t.Fatal("data mismatch when downloading a file") 824 } 825 826 // The renter's downloads queue should have 2 entries now. 827 if err = st.getAPI("/renter/downloads", &queue); err != nil { 828 t.Fatal(err) 829 } 830 if len(queue.Downloads) != 2 { 831 t.Fatalf("expected renter to have 1 download in the queue; got %v", len(queue.Downloads)) 832 } 833 834 // Mine two blocks, which should cause the host to submit the storage 835 // obligation to the blockchain. 836 for i := 0; i < 2; i++ { 837 _, err := st.miner.AddBlock() 838 if err != nil { 839 t.Fatal(err) 840 } 841 time.Sleep(time.Millisecond * 200) 842 } 843 844 // Check that the host was able to get the file contract confirmed on the 845 // blockchain. 846 obligations = st.host.StorageObligations() 847 if len(obligations) != 1 { 848 t.Error("Host has wrong number of obligations:", len(obligations)) 849 } 850 if !obligations[0].OriginConfirmed { 851 t.Error("host has not seen the file contract on the blockchain") 852 } 853 854 // Mine blocks until the host should have submitted a storage proof. 855 for i := 0; i <= testPeriodInt+5; i++ { 856 _, err := st.miner.AddBlock() 857 if err != nil { 858 t.Fatal(err) 859 } 860 time.Sleep(time.Millisecond * 200) 861 } 862 863 success := false 864 obligations = st.host.StorageObligations() 865 for _, obligation := range obligations { 866 if obligation.ProofConfirmed { 867 success = true 868 break 869 } 870 } 871 if !success { 872 t.Error("does not seem like the host has submitted a storage proof successfully to the network") 873 } 874 } 875 876 // TestHostAndRentMultiHost sets up an integration test where three hosts and a 877 // renter do basic (parallel) uploads and downloads. 878 func TestHostAndRentMultiHost(t *testing.T) { 879 if testing.Short() || !build.VLONG { 880 t.SkipNow() 881 } 882 t.Parallel() 883 st, err := createServerTester(t.Name()) 884 if err != nil { 885 t.Fatal(err) 886 } 887 defer st.server.panicClose() 888 stH1, err := blankServerTester(t.Name() + " - Host 2") 889 if err != nil { 890 t.Fatal(err) 891 } 892 defer stH1.server.panicClose() 893 stH2, err := blankServerTester(t.Name() + " - Host 3") 894 if err != nil { 895 t.Fatal(err) 896 } 897 defer stH2.server.panicClose() 898 testGroup := []*serverTester{st, stH1, stH2} 899 900 // Connect the testers to eachother so that they are all on the same 901 // blockchain. 902 err = fullyConnectNodes(testGroup) 903 if err != nil { 904 t.Fatal(err) 905 } 906 907 // Make sure that every wallet has money in it. 908 err = fundAllNodes(testGroup) 909 if err != nil { 910 t.Fatal(err) 911 } 912 913 // Add storage to every host. 914 err = addStorageToAllHosts(testGroup) 915 if err != nil { 916 t.Fatal(err) 917 } 918 919 // Announce every host. 920 err = announceAllHosts(testGroup) 921 if err != nil { 922 t.Fatal(err) 923 } 924 925 // Set an allowance with three hosts. 926 allowanceValues := url.Values{} 927 allowanceValues.Set("funds", "50000000000000000000000000000") // 50k SC 928 allowanceValues.Set("hosts", "3") 929 allowanceValues.Set("period", "10") 930 allowanceValues.Set("renewwindow", "2") 931 err = st.stdPostAPI("/renter", allowanceValues) 932 if err != nil { 933 t.Fatal(err) 934 } 935 936 // Create a file to upload. 937 filesize := int(45678) 938 path := filepath.Join(st.dir, "test.dat") 939 err = createRandFile(path, filesize) 940 if err != nil { 941 t.Fatal(err) 942 } 943 944 // Upload a file with 2-of-6 redundancy. 945 uploadValues := url.Values{} 946 uploadValues.Set("source", path) 947 uploadValues.Set("datapieces", "2") 948 uploadValues.Set("paritypieces", "4") 949 err = st.stdPostAPI("/renter/upload/test", uploadValues) 950 if err != nil { 951 t.Fatal(err) 952 } 953 // Three pieces should get uploaded. 954 var rf RenterFiles 955 for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 50); i++ { 956 st.getAPI("/renter/files", &rf) 957 time.Sleep(100 * time.Millisecond) 958 } 959 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 50 { 960 t.Fatal("the uploading is not succeeding for some reason:", rf.Files[0]) 961 } 962 963 // Try downloading the file. 964 downpath := filepath.Join(st.dir, "testdown.dat") 965 err = st.stdGetAPI("/renter/download/test?destination=" + downpath) 966 if err != nil { 967 t.Fatal(err) 968 } 969 // Check that the download has the right contents. 970 orig, err := ioutil.ReadFile(path) 971 if err != nil { 972 t.Fatal(err) 973 } 974 download, err := ioutil.ReadFile(downpath) 975 if err != nil { 976 t.Fatal(err) 977 } 978 if bytes.Compare(orig, download) != 0 { 979 t.Fatal("data mismatch when downloading a file") 980 } 981 982 // The renter's downloads queue should have 1 entry now. 983 var queue RenterDownloadQueue 984 if err = st.getAPI("/renter/downloads", &queue); err != nil { 985 t.Fatal(err) 986 } 987 if len(queue.Downloads) != 1 { 988 t.Fatalf("expected renter to have 1 download in the queue; got %v", len(queue.Downloads)) 989 } 990 } 991 992 // TestHostAndRentManyFiles sets up an integration test where a single renter 993 // is uploading many files to the network. 994 func TestHostAndRentManyFiles(t *testing.T) { 995 if testing.Short() || !build.VLONG { 996 t.SkipNow() 997 } 998 t.Parallel() 999 st, err := createServerTester(t.Name()) 1000 if err != nil { 1001 t.Fatal(err) 1002 } 1003 defer st.server.panicClose() 1004 stH1, err := blankServerTester(t.Name() + " - Host 2") 1005 if err != nil { 1006 t.Fatal(err) 1007 } 1008 defer stH1.server.panicClose() 1009 stH2, err := blankServerTester(t.Name() + " - Host 3") 1010 if err != nil { 1011 t.Fatal(err) 1012 } 1013 defer stH2.server.panicClose() 1014 stH3, err := blankServerTester(t.Name() + " - Host 4") 1015 if err != nil { 1016 t.Fatal(err) 1017 } 1018 defer stH3.server.panicClose() 1019 testGroup := []*serverTester{st, stH1, stH2, stH3} 1020 1021 // Connect the testers to eachother so that they are all on the same 1022 // blockchain. 1023 err = fullyConnectNodes(testGroup) 1024 if err != nil { 1025 t.Fatal(err) 1026 } 1027 1028 // Make sure that every wallet has money in it. 1029 err = fundAllNodes(testGroup) 1030 if err != nil { 1031 t.Fatal(err) 1032 } 1033 1034 // Add storage to every host. 1035 err = addStorageToAllHosts(testGroup) 1036 if err != nil { 1037 t.Fatal(err) 1038 } 1039 1040 // Announce every host. 1041 err = announceAllHosts(testGroup) 1042 if err != nil { 1043 t.Fatal(err) 1044 } 1045 1046 // Set an allowance with four hosts. 1047 allowanceValues := url.Values{} 1048 allowanceValues.Set("funds", "50000000000000000000000000000") // 50k SC 1049 allowanceValues.Set("hosts", "4") 1050 allowanceValues.Set("period", "5") 1051 allowanceValues.Set("renewwindow", "2") 1052 err = st.stdPostAPI("/renter", allowanceValues) 1053 if err != nil { 1054 t.Fatal(err) 1055 } 1056 1057 // Create 3 files to upload at the same time. 1058 filesize1 := int(12347) 1059 filesize2 := int(22343) 1060 filesize3 := int(32349) 1061 path1 := filepath.Join(st.dir, "test1.dat") 1062 path2 := filepath.Join(st.dir, "test2.dat") 1063 path3 := filepath.Join(st.dir, "test3.dat") 1064 err = createRandFile(path1, filesize1) 1065 if err != nil { 1066 t.Fatal(err) 1067 } 1068 err = createRandFile(path2, filesize2) 1069 if err != nil { 1070 t.Fatal(err) 1071 } 1072 err = createRandFile(path3, filesize3) 1073 if err != nil { 1074 t.Fatal(err) 1075 } 1076 1077 // Concurrently upload a file with 1-of-4 redundancy, 2-of-4 redundancy, 1078 // and 3-of-4 redundancy. 1079 var wg sync.WaitGroup 1080 wg.Add(3) 1081 go func() { 1082 defer wg.Done() 1083 uploadValues := url.Values{} 1084 uploadValues.Set("source", path1) 1085 uploadValues.Set("datapieces", "1") 1086 uploadValues.Set("paritypieces", "3") 1087 err := st.stdPostAPI("/renter/upload/test1", uploadValues) 1088 if err != nil { 1089 t.Error(err) 1090 } 1091 }() 1092 go func() { 1093 defer wg.Done() 1094 uploadValues := url.Values{} 1095 uploadValues.Set("source", path2) 1096 uploadValues.Set("datapieces", "2") 1097 uploadValues.Set("paritypieces", "2") 1098 err := st.stdPostAPI("/renter/upload/test2", uploadValues) 1099 if err != nil { 1100 t.Error(err) 1101 } 1102 }() 1103 go func() { 1104 defer wg.Done() 1105 uploadValues := url.Values{} 1106 uploadValues.Set("source", path3) 1107 uploadValues.Set("datapieces", "3") 1108 uploadValues.Set("paritypieces", "1") 1109 err := st.stdPostAPI("/renter/upload/test3", uploadValues) 1110 if err != nil { 1111 t.Error(err) 1112 } 1113 }() 1114 1115 // Block until the upload call is complete for all three files. 1116 wg.Wait() 1117 1118 // Block until all files hit 100% uploaded. 1119 var rf RenterFiles 1120 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++ { 1121 st.getAPI("/renter/files", &rf) 1122 time.Sleep(500 * time.Millisecond) 1123 } 1124 if len(rf.Files) != 3 || rf.Files[0].UploadProgress < 100 || rf.Files[1].UploadProgress < 100 || rf.Files[2].UploadProgress < 100 { 1125 t.Fatal("the uploading is not succeeding for some reason:", rf.Files[0], rf.Files[1], rf.Files[2]) 1126 } 1127 1128 // Download all three files in parallel. 1129 wg.Add(3) 1130 go func() { 1131 defer wg.Done() 1132 downpath := filepath.Join(st.dir, "testdown1.dat") 1133 err := st.stdGetAPI("/renter/download/test1?destination=" + downpath) 1134 if err != nil { 1135 t.Error(err) 1136 } 1137 // Check that the download has the right contents. 1138 orig, err := ioutil.ReadFile(path1) 1139 if err != nil { 1140 t.Error(err) 1141 } 1142 download, err := ioutil.ReadFile(downpath) 1143 if err != nil { 1144 t.Error(err) 1145 } 1146 if bytes.Compare(orig, download) != 0 { 1147 t.Error("data mismatch when downloading a file") 1148 } 1149 }() 1150 go func() { 1151 defer wg.Done() 1152 downpath := filepath.Join(st.dir, "testdown2.dat") 1153 err := st.stdGetAPI("/renter/download/test2?destination=" + downpath) 1154 if err != nil { 1155 t.Error(err) 1156 } 1157 // Check that the download has the right contents. 1158 orig, err := ioutil.ReadFile(path2) 1159 if err != nil { 1160 t.Error(err) 1161 } 1162 download, err := ioutil.ReadFile(downpath) 1163 if err != nil { 1164 t.Error(err) 1165 } 1166 if bytes.Compare(orig, download) != 0 { 1167 t.Error("data mismatch when downloading a file") 1168 } 1169 }() 1170 go func() { 1171 defer wg.Done() 1172 downpath := filepath.Join(st.dir, "testdown3.dat") 1173 err := st.stdGetAPI("/renter/download/test3?destination=" + downpath) 1174 if err != nil { 1175 t.Error(err) 1176 } 1177 // Check that the download has the right contents. 1178 orig, err := ioutil.ReadFile(path3) 1179 if err != nil { 1180 t.Error(err) 1181 } 1182 download, err := ioutil.ReadFile(downpath) 1183 if err != nil { 1184 t.Error(err) 1185 } 1186 if bytes.Compare(orig, download) != 0 { 1187 t.Error("data mismatch when downloading a file") 1188 } 1189 }() 1190 wg.Wait() 1191 1192 // The renter's downloads queue should have 3 entries now. 1193 var queue RenterDownloadQueue 1194 if err = st.getAPI("/renter/downloads", &queue); err != nil { 1195 t.Fatal(err) 1196 } 1197 if len(queue.Downloads) != 3 { 1198 t.Fatalf("expected renter to have 1 download in the queue; got %v", len(queue.Downloads)) 1199 } 1200 } 1201 1202 // TestRenterUploadDownload tests that downloading and uploading in parallel 1203 // does not result in failures or stalling. 1204 func TestRenterUploadDownload(t *testing.T) { 1205 if testing.Short() { 1206 t.SkipNow() 1207 } 1208 st, err := createServerTester(t.Name()) 1209 if err != nil { 1210 t.Fatal(err) 1211 } 1212 defer st.server.panicClose() 1213 1214 // Announce the host and start accepting contracts. 1215 err = st.announceHost() 1216 if err != nil { 1217 t.Fatal(err) 1218 } 1219 err = st.acceptContracts() 1220 if err != nil { 1221 t.Fatal(err) 1222 } 1223 err = st.setHostStorage() 1224 if err != nil { 1225 t.Fatal(err) 1226 } 1227 1228 // Set an allowance for the renter, allowing a contract to be formed. 1229 allowanceValues := url.Values{} 1230 testFunds := "10000000000000000000000000000" // 10k SC 1231 testPeriod := "10" 1232 allowanceValues.Set("funds", testFunds) 1233 allowanceValues.Set("period", testPeriod) 1234 allowanceValues.Set("renewwindow", testRenewWindow) 1235 allowanceValues.Set("hosts", fmt.Sprint(recommendedHosts)) 1236 err = st.stdPostAPI("/renter", allowanceValues) 1237 if err != nil { 1238 t.Fatal(err) 1239 } 1240 1241 // Block until the allowance has finished forming contracts. 1242 err = build.Retry(50, time.Millisecond*250, func() error { 1243 var rc RenterContracts 1244 err = st.getAPI("/renter/contracts", &rc) 1245 if err != nil { 1246 return errors.New("couldn't get renter stats") 1247 } 1248 if len(rc.Contracts) != 1 { 1249 return errors.New("no contracts") 1250 } 1251 return nil 1252 }) 1253 if err != nil { 1254 t.Fatal("allowance setting failed") 1255 } 1256 1257 // Check financial metrics; coins should have been spent on contracts 1258 var rg RenterGET 1259 err = st.getAPI("/renter", &rg) 1260 if err != nil { 1261 t.Fatal(err) 1262 } 1263 spent := rg.Settings.Allowance.Funds.Sub(rg.FinancialMetrics.Unspent) 1264 if spent.IsZero() { 1265 t.Fatal("financial metrics do not reflect contract spending") 1266 } 1267 1268 // Create a file. 1269 path := filepath.Join(st.dir, "test.dat") 1270 err = createRandFile(path, 1024) 1271 if err != nil { 1272 t.Fatal(err) 1273 } 1274 1275 // Upload to host. 1276 uploadValues := url.Values{} 1277 uploadValues.Set("source", path) 1278 err = st.stdPostAPI("/renter/upload/test", uploadValues) 1279 if err != nil { 1280 t.Fatal(err) 1281 } 1282 // Only one piece will be uploaded (10% at current redundancy). 1283 var rf RenterFiles 1284 for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ { 1285 st.getAPI("/renter/files", &rf) 1286 time.Sleep(100 * time.Millisecond) 1287 } 1288 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 { 1289 t.Fatal("the uploading is not succeeding for some reason:", rf.Files[0]) 1290 } 1291 1292 // In parallel, upload another file and download the first file. 1293 path2 := filepath.Join(st.dir, "test2.dat") 1294 test2Size := modules.SectorSize*2 + 1 1295 err = createRandFile(path2, int(test2Size)) 1296 if err != nil { 1297 t.Fatal(err) 1298 } 1299 uploadValues = url.Values{} 1300 uploadValues.Set("source", path2) 1301 err = st.stdPostAPI("/renter/upload/test2", uploadValues) 1302 if err != nil { 1303 t.Fatal(err) 1304 } 1305 downpath := filepath.Join(st.dir, "testdown.dat") 1306 err = st.stdGetAPI("/renter/download/test?destination=" + downpath) 1307 if err != nil { 1308 t.Fatal(err) 1309 } 1310 // Check that the download has the right contents. 1311 orig, err := ioutil.ReadFile(path) 1312 if err != nil { 1313 t.Fatal(err) 1314 } 1315 download, err := ioutil.ReadFile(downpath) 1316 if err != nil { 1317 t.Fatal(err) 1318 } 1319 if bytes.Compare(orig, download) != 0 { 1320 t.Fatal("data mismatch when downloading a file") 1321 } 1322 1323 // Wait for upload to complete. 1324 for i := 0; i < 200 && (len(rf.Files) != 2 || rf.Files[0].UploadProgress < 10 || rf.Files[1].UploadProgress < 10); i++ { 1325 st.getAPI("/renter/files", &rf) 1326 time.Sleep(100 * time.Millisecond) 1327 } 1328 if len(rf.Files) != 2 || rf.Files[0].UploadProgress < 10 || rf.Files[1].UploadProgress < 10 { 1329 t.Fatal("the uploading is not succeeding for some reason:", rf.Files[0], rf.Files[1]) 1330 } 1331 1332 // Check financial metrics; funds should have been spent on uploads/downloads 1333 err = st.getAPI("/renter", &rg) 1334 if err != nil { 1335 t.Fatal(err) 1336 } 1337 fm := rg.FinancialMetrics 1338 newSpent := rg.Settings.Allowance.Funds.Sub(fm.Unspent) 1339 // all new spending should be reflected in upload/download/storage spending 1340 diff := fm.UploadSpending.Add(fm.DownloadSpending).Add(fm.StorageSpending) 1341 if !diff.Equals(newSpent.Sub(spent)) { 1342 t.Fatal("all new spending should be reflected in metrics:", diff, newSpent.Sub(spent)) 1343 } 1344 } 1345 1346 // TestRenterCancelAllowance tests that setting an empty allowance causes 1347 // uploads, downloads, and renewals to cease. 1348 func TestRenterCancelAllowance(t *testing.T) { 1349 if testing.Short() { 1350 t.SkipNow() 1351 } 1352 st, err := createServerTester(t.Name()) 1353 if err != nil { 1354 t.Fatal(err) 1355 } 1356 defer st.server.panicClose() 1357 1358 // Announce the host and start accepting contracts. 1359 err = st.announceHost() 1360 if err != nil { 1361 t.Fatal(err) 1362 } 1363 err = st.setHostStorage() 1364 if err != nil { 1365 t.Fatal(err) 1366 } 1367 err = st.acceptContracts() 1368 if err != nil { 1369 t.Fatal(err) 1370 } 1371 1372 // Set an allowance for the renter, allowing a contract to be formed. 1373 allowanceValues := url.Values{} 1374 testFunds := "10000000000000000000000000000" // 10k SC 1375 testPeriod := "20" 1376 allowanceValues.Set("funds", testFunds) 1377 allowanceValues.Set("period", testPeriod) 1378 allowanceValues.Set("renewwindow", testRenewWindow) 1379 allowanceValues.Set("hosts", fmt.Sprint(recommendedHosts)) 1380 err = st.stdPostAPI("/renter", allowanceValues) 1381 if err != nil { 1382 t.Fatal(err) 1383 } 1384 1385 // Block until the allowance has finished forming contracts. 1386 err = build.Retry(50, time.Millisecond*250, func() error { 1387 var rc RenterContracts 1388 err = st.getAPI("/renter/contracts", &rc) 1389 if err != nil { 1390 return errors.New("couldn't get renter stats") 1391 } 1392 if len(rc.Contracts) != 1 { 1393 return errors.New("no contracts") 1394 } 1395 return nil 1396 }) 1397 if err != nil { 1398 t.Fatal("allowance setting failed") 1399 } 1400 1401 // Create a file. 1402 path := filepath.Join(st.dir, "test.dat") 1403 err = createRandFile(path, 1024) 1404 if err != nil { 1405 t.Fatal(err) 1406 } 1407 1408 // Upload the file to the renter. 1409 uploadValues := url.Values{} 1410 uploadValues.Set("source", path) 1411 err = st.stdPostAPI("/renter/upload/test", uploadValues) 1412 if err != nil { 1413 t.Fatal(err) 1414 } 1415 // Only one piece will be uploaded (10% at current redundancy). 1416 var rf RenterFiles 1417 for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ { 1418 st.getAPI("/renter/files", &rf) 1419 time.Sleep(100 * time.Millisecond) 1420 } 1421 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 { 1422 t.Fatal("the uploading is not succeeding for some reason:", rf.Files[0]) 1423 } 1424 1425 // Cancel the allowance 1426 allowanceValues = url.Values{} 1427 allowanceValues.Set("funds", "0") 1428 allowanceValues.Set("hosts", "0") 1429 allowanceValues.Set("period", "0") 1430 allowanceValues.Set("renewwindow", "0") 1431 err = st.stdPostAPI("/renter", allowanceValues) 1432 if err != nil { 1433 t.Fatal(err) 1434 } 1435 1436 // Try downloading the file; should fail 1437 downpath := filepath.Join(st.dir, "testdown.dat") 1438 err = st.stdGetAPI("/renter/download/test?destination=" + downpath) 1439 if err == nil || !strings.Contains(err.Error(), "download failed") { 1440 t.Fatal("expected insufficient hosts error, got", err) 1441 } 1442 } 1443 1444 // TestRenterParallelDelete tests that uploading and deleting parallel does not 1445 // result in failures or stalling. 1446 func TestRenterParallelDelete(t *testing.T) { 1447 if testing.Short() { 1448 t.SkipNow() 1449 } 1450 st, err := createServerTester(t.Name()) 1451 if err != nil { 1452 t.Fatal(err) 1453 } 1454 defer st.server.panicClose() 1455 1456 // Announce the host and start accepting contracts. 1457 err = st.announceHost() 1458 if err != nil { 1459 t.Fatal(err) 1460 } 1461 err = st.acceptContracts() 1462 if err != nil { 1463 t.Fatal(err) 1464 } 1465 err = st.setHostStorage() 1466 if err != nil { 1467 t.Fatal(err) 1468 } 1469 1470 // Set an allowance for the renter, allowing a contract to be formed. 1471 allowanceValues := url.Values{} 1472 testFunds := "10000000000000000000000000000" // 10k SC 1473 testPeriod := "10" 1474 allowanceValues.Set("funds", testFunds) 1475 allowanceValues.Set("period", testPeriod) 1476 allowanceValues.Set("renewwindow", testRenewWindow) 1477 allowanceValues.Set("hosts", fmt.Sprint(recommendedHosts)) 1478 err = st.stdPostAPI("/renter", allowanceValues) 1479 if err != nil { 1480 t.Fatal(err) 1481 } 1482 1483 // Create two files. 1484 path := filepath.Join(st.dir, "test.dat") 1485 err = createRandFile(path, 1024) 1486 if err != nil { 1487 t.Fatal(err) 1488 } 1489 path2 := filepath.Join(st.dir, "test2.dat") 1490 err = createRandFile(path2, 1024) 1491 if err != nil { 1492 t.Fatal(err) 1493 } 1494 1495 // Upload the first file to host. 1496 uploadValues := url.Values{} 1497 uploadValues.Set("source", path) 1498 err = st.stdPostAPI("/renter/upload/test", uploadValues) 1499 if err != nil { 1500 t.Fatal(err) 1501 } 1502 // Wait for the first file to be registered in the renter. 1503 var rf RenterFiles 1504 for i := 0; i < 200 && len(rf.Files) != 1; i++ { 1505 st.getAPI("/renter/files", &rf) 1506 time.Sleep(100 * time.Millisecond) 1507 } 1508 if len(rf.Files) != 1 { 1509 t.Fatal("file is not being registered:", rf.Files) 1510 } 1511 1512 // In parallel, start uploading the other file, and delete the first file. 1513 uploadValues = url.Values{} 1514 uploadValues.Set("source", path2) 1515 err = st.stdPostAPI("/renter/upload/test2", uploadValues) 1516 if err != nil { 1517 t.Fatal(err) 1518 } 1519 1520 err = st.stdPostAPI("/renter/delete/test", url.Values{}) 1521 if err != nil { 1522 t.Fatal(err) 1523 } 1524 // Only the second file should be present 1525 st.getAPI("/renter/files", &rf) 1526 if len(rf.Files) != 1 || rf.Files[0].SiaPath != "test2" { 1527 t.Fatal("file was not deleted properly:", rf.Files) 1528 } 1529 1530 // Wait for the second upload to complete. 1531 for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ { 1532 st.getAPI("/renter/files", &rf) 1533 time.Sleep(100 * time.Millisecond) 1534 } 1535 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 { 1536 t.Fatal("the uploading is not succeeding for some reason:", rf.Files) 1537 } 1538 1539 // In parallel, download and delete the second file. 1540 go st.stdPostAPI("/renter/delete/test2", url.Values{}) 1541 time.Sleep(100 * time.Millisecond) 1542 downpath := filepath.Join(st.dir, "testdown.dat") 1543 err = st.stdGetAPI("/renter/download/test2?destination=" + downpath) 1544 if err == nil { 1545 t.Fatal("download should fail after delete") 1546 } 1547 1548 // No files should be present 1549 st.getAPI("/renter/files", &rf) 1550 if len(rf.Files) != 0 { 1551 t.Fatal("file was not deleted properly:", rf.Files) 1552 } 1553 } 1554 1555 // TestRenterRenew sets up an integration test where a renter renews a 1556 // contract with a host. 1557 func TestRenterRenew(t *testing.T) { 1558 if testing.Short() { 1559 t.SkipNow() 1560 } 1561 st, err := createServerTester(t.Name()) 1562 if err != nil { 1563 t.Fatal(err) 1564 } 1565 defer st.server.panicClose() 1566 1567 // Announce the host and start accepting contracts. 1568 err = st.announceHost() 1569 if err != nil { 1570 t.Fatal(err) 1571 } 1572 err = st.acceptContracts() 1573 if err != nil { 1574 t.Fatal(err) 1575 } 1576 err = st.setHostStorage() 1577 if err != nil { 1578 t.Fatal(err) 1579 } 1580 var ah HostdbActiveGET 1581 for i := 0; i < 50; i++ { 1582 if err = st.getAPI("/hostdb/active", &ah); err != nil { 1583 t.Fatal(err) 1584 } 1585 if len(ah.Hosts) == 1 { 1586 break 1587 } 1588 time.Sleep(time.Millisecond * 100) 1589 } 1590 if len(ah.Hosts) != 1 { 1591 t.Fatalf("expected 1 host, got %v", len(ah.Hosts)) 1592 } 1593 1594 // Set an allowance for the renter, allowing a contract to be formed. 1595 allowanceValues := url.Values{} 1596 testFunds := "10000000000000000000000000000" // 10k SC 1597 testPeriod := 10 1598 allowanceValues.Set("funds", testFunds) 1599 allowanceValues.Set("period", strconv.Itoa(testPeriod)) 1600 allowanceValues.Set("renewwindow", strconv.Itoa(testPeriod/2)) 1601 allowanceValues.Set("hosts", fmt.Sprint(recommendedHosts)) 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) != 1 { 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. 1624 path := filepath.Join(st.dir, "test.dat") 1625 err = createRandFile(path, 1024) 1626 if err != nil { 1627 t.Fatal(err) 1628 } 1629 1630 // Upload the file to the renter. 1631 uploadValues := url.Values{} 1632 uploadValues.Set("source", path) 1633 err = st.stdPostAPI("/renter/upload/test", uploadValues) 1634 if err != nil { 1635 t.Fatal(err) 1636 } 1637 // Only one piece will be uploaded (10% at current redundancy). 1638 var rf RenterFiles 1639 for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ { 1640 st.getAPI("/renter/files", &rf) 1641 time.Sleep(100 * time.Millisecond) 1642 } 1643 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 { 1644 t.Fatal("the uploading is not succeeding for some reason:", rf.Files[0]) 1645 } 1646 1647 // Get current contract ID. 1648 var rc RenterContracts 1649 err = st.getAPI("/renter/contracts", &rc) 1650 if err != nil { 1651 t.Fatal(err) 1652 } 1653 contractID := rc.Contracts[0].ID 1654 1655 // Mine enough blocks to enter the renewal window. 1656 testWindow := testPeriod / 2 1657 for i := 0; i < testWindow+1; i++ { 1658 _, err = st.miner.AddBlock() 1659 if err != nil { 1660 t.Fatal(err) 1661 } 1662 } 1663 // Wait for the contract to be renewed. 1664 for i := 0; i < 200 && (len(rc.Contracts) != 1 || rc.Contracts[0].ID == contractID); i++ { 1665 st.getAPI("/renter/contracts", &rc) 1666 time.Sleep(100 * time.Millisecond) 1667 } 1668 if rc.Contracts[0].ID == contractID { 1669 t.Fatal("contract was not renewed:", rc.Contracts[0]) 1670 } 1671 1672 // Try downloading the file. 1673 downpath := filepath.Join(st.dir, "testdown.dat") 1674 err = st.stdGetAPI("/renter/download/test?destination=" + downpath) 1675 if err != nil { 1676 t.Fatal(err) 1677 } 1678 // Check that the download has the right contents. 1679 orig, err := ioutil.ReadFile(path) 1680 if err != nil { 1681 t.Fatal(err) 1682 } 1683 download, err := ioutil.ReadFile(downpath) 1684 if err != nil { 1685 t.Fatal(err) 1686 } 1687 if bytes.Compare(orig, download) != 0 { 1688 t.Fatal("data mismatch when downloading a file") 1689 } 1690 } 1691 1692 // TestRenterAllowance sets up an integration test where a renter attempts to 1693 // download a file after changing the allowance. 1694 func TestRenterAllowance(t *testing.T) { 1695 t.Skip("bypassing NDF") 1696 if testing.Short() { 1697 t.SkipNow() 1698 } 1699 t.Parallel() 1700 1701 st, err := createServerTester(t.Name()) 1702 if err != nil { 1703 t.Fatal(err) 1704 } 1705 defer st.server.panicClose() 1706 1707 // Announce the host and start accepting contracts. 1708 err = st.announceHost() 1709 if err != nil { 1710 t.Fatal(err) 1711 } 1712 err = st.acceptContracts() 1713 if err != nil { 1714 t.Fatal(err) 1715 } 1716 err = st.setHostStorage() 1717 if err != nil { 1718 t.Fatal(err) 1719 } 1720 1721 // Set an allowance for the renter, allowing a contract to be formed. 1722 allowanceValues := url.Values{} 1723 testFunds := types.SiacoinPrecision.Mul64(10000) // 10k SC 1724 testPeriod := 20 1725 allowanceValues.Set("funds", testFunds.String()) 1726 allowanceValues.Set("period", strconv.Itoa(testPeriod)) 1727 err = st.stdPostAPI("/renter", allowanceValues) 1728 if err != nil { 1729 t.Fatal(err) 1730 } 1731 1732 // Create a file. 1733 path := filepath.Join(st.dir, "test.dat") 1734 err = createRandFile(path, 1024) 1735 if err != nil { 1736 t.Fatal(err) 1737 } 1738 1739 // Upload the file to the renter. 1740 uploadValues := url.Values{} 1741 uploadValues.Set("source", path) 1742 err = st.stdPostAPI("/renter/upload/test", uploadValues) 1743 if err != nil { 1744 t.Fatal(err) 1745 } 1746 // Only one piece will be uploaded (10% at current redundancy). 1747 var rf RenterFiles 1748 for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ { 1749 st.getAPI("/renter/files", &rf) 1750 time.Sleep(100 * time.Millisecond) 1751 } 1752 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 { 1753 t.Fatal("the uploading is not succeeding for some reason:", rf.Files[0]) 1754 } 1755 1756 t.Skip("ndf - re-enable after contractor overhaul") 1757 1758 // Try downloading the file after modifying the allowance in various ways. 1759 allowances := []struct { 1760 funds types.Currency 1761 period int 1762 }{ 1763 {testFunds.Mul64(10), testPeriod / 2}, 1764 {testFunds, testPeriod / 2}, 1765 {testFunds.Div64(10), testPeriod / 2}, 1766 {testFunds.Mul64(10), testPeriod}, 1767 {testFunds, testPeriod}, 1768 {testFunds.Div64(10), testPeriod}, 1769 {testFunds.Mul64(10), testPeriod * 2}, 1770 {testFunds, testPeriod * 2}, 1771 {testFunds.Div64(10), testPeriod * 2}, 1772 } 1773 1774 for _, a := range allowances { 1775 allowanceValues.Set("funds", a.funds.String()) 1776 allowanceValues.Set("period", strconv.Itoa(a.period)) 1777 err = st.stdPostAPI("/renter", allowanceValues) 1778 if err != nil { 1779 t.Fatal(err) 1780 } 1781 time.Sleep(100 * time.Millisecond) 1782 1783 // Try downloading the file. 1784 downpath := filepath.Join(st.dir, "testdown.dat") 1785 err = st.stdGetAPI("/renter/download/test?destination=" + downpath) 1786 if err != nil { 1787 t.Fatal(err) 1788 } 1789 // Check that the download has the right contents. 1790 orig, err := ioutil.ReadFile(path) 1791 if err != nil { 1792 t.Fatal(err) 1793 } 1794 download, err := ioutil.ReadFile(downpath) 1795 if err != nil { 1796 t.Fatal(err) 1797 } 1798 if bytes.Compare(orig, download) != 0 { 1799 t.Fatal("data mismatch when downloading a file") 1800 } 1801 } 1802 } 1803 1804 // TestHostAndRentReload sets up an integration test where a host and renter 1805 // do basic uploads and downloads, with an intervening shutdown+startup. 1806 func TestHostAndRentReload(t *testing.T) { 1807 if testing.Short() { 1808 t.SkipNow() 1809 } 1810 t.Parallel() 1811 st, err := createServerTester(t.Name()) 1812 if err != nil { 1813 t.Fatal(err) 1814 } 1815 1816 // Announce the host and start accepting contracts. 1817 err = st.announceHost() 1818 if err != nil { 1819 t.Fatal(err) 1820 } 1821 err = st.acceptContracts() 1822 if err != nil { 1823 t.Fatal(err) 1824 } 1825 err = st.setHostStorage() 1826 if err != nil { 1827 t.Fatal(err) 1828 } 1829 // Mine a block so that the wallet reclaims refund outputs 1830 _, err = st.miner.AddBlock() 1831 if err != nil { 1832 t.Fatal(err) 1833 } 1834 1835 // Set an allowance for the renter, allowing a contract to be formed. 1836 allowanceValues := url.Values{} 1837 testFunds := "10000000000000000000000000000" // 10k SC 1838 testPeriod := "10" 1839 allowanceValues.Set("funds", testFunds) 1840 allowanceValues.Set("period", testPeriod) 1841 allowanceValues.Set("renewwindow", testRenewWindow) 1842 allowanceValues.Set("hosts", fmt.Sprint(recommendedHosts)) 1843 err = st.stdPostAPI("/renter", allowanceValues) 1844 if err != nil { 1845 t.Fatal(err) 1846 } 1847 1848 // Block until the allowance has finished forming contracts. 1849 err = build.Retry(50, time.Millisecond*250, func() error { 1850 var rc RenterContracts 1851 err = st.getAPI("/renter/contracts", &rc) 1852 if err != nil { 1853 return errors.New("couldn't get renter stats") 1854 } 1855 if len(rc.Contracts) != 1 { 1856 return errors.New("no contracts") 1857 } 1858 return nil 1859 }) 1860 if err != nil { 1861 t.Fatal("allowance setting failed") 1862 } 1863 1864 // Create a file. 1865 path := filepath.Join(st.dir, "test.dat") 1866 err = createRandFile(path, 1024) 1867 if err != nil { 1868 t.Fatal(err) 1869 } 1870 1871 // Upload the file to the renter. 1872 uploadValues := url.Values{} 1873 uploadValues.Set("source", path) 1874 err = st.stdPostAPI("/renter/upload/test", uploadValues) 1875 if err != nil { 1876 t.Fatal(err) 1877 } 1878 // Only one piece will be uploaded (10% at current redundancy). 1879 var rf RenterFiles 1880 for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ { 1881 st.getAPI("/renter/files", &rf) 1882 time.Sleep(100 * time.Millisecond) 1883 } 1884 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 { 1885 t.Fatal("the uploading is not succeeding for some reason:", rf.Files[0]) 1886 } 1887 1888 // Try downloading the file. 1889 downpath := filepath.Join(st.dir, "testdown.dat") 1890 err = st.stdGetAPI("/renter/download/test?destination=" + downpath) 1891 if err != nil { 1892 t.Fatal(err) 1893 } 1894 // Check that the download has the right contents. 1895 orig, err := ioutil.ReadFile(path) 1896 if err != nil { 1897 t.Fatal(err) 1898 } 1899 download, err := ioutil.ReadFile(downpath) 1900 if err != nil { 1901 t.Fatal(err) 1902 } 1903 if bytes.Compare(orig, download) != 0 { 1904 t.Fatal("data mismatch when downloading a file") 1905 } 1906 1907 // The renter's downloads queue should have 1 entry now. 1908 var queue RenterDownloadQueue 1909 if err = st.getAPI("/renter/downloads", &queue); err != nil { 1910 t.Fatal(err) 1911 } 1912 if len(queue.Downloads) != 1 { 1913 t.Fatalf("expected renter to have 1 download in the queue; got %v", len(queue.Downloads)) 1914 } 1915 1916 // close and reopen the server 1917 err = st.server.Close() 1918 if err != nil { 1919 t.Fatal(err) 1920 } 1921 st, err = assembleServerTester(st.walletKey, st.dir) 1922 if err != nil { 1923 t.Fatal(err) 1924 } 1925 defer st.server.panicClose() 1926 1927 // Announce the host again and wait until the host is re-scanned and put 1928 // back into the hostdb as an active host. 1929 announceValues := url.Values{} 1930 announceValues.Set("address", string(st.host.ExternalSettings().NetAddress)) 1931 err = st.stdPostAPI("/host/announce", announceValues) 1932 if err != nil { 1933 t.Fatal(err) 1934 } 1935 // Mine a block. 1936 _, err = st.miner.AddBlock() 1937 if err != nil { 1938 t.Fatal(err) 1939 } 1940 err = retry(100, time.Millisecond*100, func() error { 1941 var hosts HostdbActiveGET 1942 err := st.getAPI("/hostdb/active", &hosts) 1943 if err != nil { 1944 return err 1945 } 1946 if len(hosts.Hosts) != 1 { 1947 return errors.New("host is not in the set of active hosts") 1948 } 1949 return nil 1950 }) 1951 if err != nil { 1952 t.Fatal(err) 1953 } 1954 1955 // Try downloading the file. 1956 err = st.stdGetAPI("/renter/download/test?destination=" + downpath) 1957 if err != nil { 1958 t.Fatal(err) 1959 } 1960 // Check that the download has the right contents. 1961 orig, err = ioutil.ReadFile(path) 1962 if err != nil { 1963 t.Fatal(err) 1964 } 1965 download, err = ioutil.ReadFile(downpath) 1966 if err != nil { 1967 t.Fatal(err) 1968 } 1969 if bytes.Compare(orig, download) != 0 { 1970 t.Fatal("data mismatch when downloading a file") 1971 } 1972 } 1973 1974 // TestHostAndRenterRenewInterrupt 1975 func TestHostAndRenterRenewInterrupt(t *testing.T) { 1976 t.Skip("active test following contractor overhaul") 1977 if testing.Short() { 1978 t.SkipNow() 1979 } 1980 t.Parallel() 1981 st, err := createServerTester(t.Name()) 1982 if err != nil { 1983 t.Fatal(err) 1984 } 1985 stHost, err := blankServerTester(t.Name() + "-Host") 1986 if err != nil { 1987 t.Fatal(err) 1988 } 1989 sts := []*serverTester{st, stHost} 1990 err = fullyConnectNodes(sts) 1991 if err != nil { 1992 t.Fatal(err) 1993 } 1994 err = fundAllNodes(sts) 1995 if err != nil { 1996 t.Fatal(err) 1997 } 1998 1999 // Announce the host. 2000 err = stHost.acceptContracts() 2001 if err != nil { 2002 t.Fatal(err) 2003 } 2004 err = stHost.setHostStorage() 2005 if err != nil { 2006 t.Fatal(err) 2007 } 2008 err = stHost.announceHost() 2009 if err != nil { 2010 t.Fatal(err) 2011 } 2012 2013 // Wait for host to be seen in renter's hostdb 2014 var ah HostdbActiveGET 2015 for i := 0; i < 50; i++ { 2016 if err = st.getAPI("/hostdb/active", &ah); err != nil { 2017 t.Fatal(err) 2018 } 2019 if len(ah.Hosts) == 1 { 2020 break 2021 } 2022 time.Sleep(time.Millisecond * 100) 2023 } 2024 if len(ah.Hosts) != 1 { 2025 t.Fatalf("expected 1 host, got %v", len(ah.Hosts)) 2026 } 2027 2028 // Upload a file to the host. 2029 allowanceValues := url.Values{} 2030 testFunds := "10000000000000000000000000000" // 10k SC 2031 testPeriod := "10" 2032 testPeriodInt := 10 2033 allowanceValues.Set("funds", testFunds) 2034 allowanceValues.Set("period", testPeriod) 2035 err = st.stdPostAPI("/renter", allowanceValues) 2036 if err != nil { 2037 t.Fatal(err) 2038 } 2039 // Create a file. 2040 path := filepath.Join(st.dir, "test.dat") 2041 err = createRandFile(path, 10e3) 2042 if err != nil { 2043 t.Fatal(err) 2044 } 2045 // Upload the file to the renter. 2046 uploadValues := url.Values{} 2047 uploadValues.Set("source", path) 2048 err = st.stdPostAPI("/renter/upload/test", uploadValues) 2049 if err != nil { 2050 t.Fatal(err) 2051 } 2052 2053 // Get current contract ID. 2054 var rc RenterContracts 2055 err = st.getAPI("/renter/contracts", &rc) 2056 if err != nil { 2057 t.Fatal(err) 2058 } 2059 contractID := rc.Contracts[0].ID 2060 2061 // Mine enough blocks to enter the renewal window. 2062 testWindow := testPeriodInt / 2 2063 for i := 0; i < testWindow+1; i++ { 2064 _, err = st.miner.AddBlock() 2065 if err != nil { 2066 t.Fatal(err) 2067 } 2068 } 2069 // Wait for the contract to be renewed. 2070 for i := 0; i < 200 && (len(rc.Contracts) != 1 || rc.Contracts[0].ID == contractID); i++ { 2071 st.getAPI("/renter/contracts", &rc) 2072 time.Sleep(100 * time.Millisecond) 2073 } 2074 if rc.Contracts[0].ID == contractID { 2075 t.Fatal("contract was not renewed:", rc.Contracts[0]) 2076 } 2077 2078 // Only one piece will be uploaded (10% at current redundancy). 2079 var rf RenterFiles 2080 for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ { 2081 st.getAPI("/renter/files", &rf) 2082 time.Sleep(1000 * time.Millisecond) 2083 } 2084 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 { 2085 t.Fatal("the uploading is not succeeding for some reason:", rf.Files[0]) 2086 } 2087 2088 // Try downloading the file. 2089 downpath := filepath.Join(st.dir, "testdown.dat") 2090 err = st.stdGetAPI("/renter/download/test?destination=" + downpath) 2091 if err != nil { 2092 t.Fatal(err) 2093 } 2094 // Check that the download has the right contents. 2095 orig, err := ioutil.ReadFile(path) 2096 if err != nil { 2097 t.Fatal(err) 2098 } 2099 download, err := ioutil.ReadFile(downpath) 2100 if err != nil { 2101 t.Fatal(err) 2102 } 2103 if bytes.Compare(orig, download) != 0 { 2104 t.Fatal("data mismatch when downloading a file") 2105 } 2106 } 2107 2108 // TestRedundancyReporting verifies that redundancy reporting is accurate if 2109 // contracts become offline. 2110 func TestRedundancyReporting(t *testing.T) { 2111 if testing.Short() { 2112 t.SkipNow() 2113 } 2114 t.Parallel() 2115 st, err := createServerTester(t.Name()) 2116 if err != nil { 2117 t.Fatal(err) 2118 } 2119 defer st.server.Close() 2120 stH1, err := blankServerTester(t.Name() + " - Host 2") 2121 if err != nil { 2122 t.Fatal(err) 2123 } 2124 testGroup := []*serverTester{st, stH1} 2125 2126 // Connect the testers to eachother so that they are all on the same 2127 // blockchain. 2128 err = fullyConnectNodes(testGroup) 2129 if err != nil { 2130 t.Fatal(err) 2131 } 2132 // Make sure that every wallet has money in it. 2133 err = fundAllNodes(testGroup) 2134 if err != nil { 2135 t.Fatal(err) 2136 } 2137 // Add storage to every host. 2138 err = addStorageToAllHosts(testGroup) 2139 if err != nil { 2140 t.Fatal(err) 2141 } 2142 // Announce every host. 2143 err = announceAllHosts(testGroup) 2144 if err != nil { 2145 t.Fatal(err) 2146 } 2147 2148 // Set an allowance with two hosts. 2149 allowanceValues := url.Values{} 2150 allowanceValues.Set("funds", "50000000000000000000000000000") // 50k SC 2151 allowanceValues.Set("hosts", "2") 2152 allowanceValues.Set("period", "10") 2153 allowanceValues.Set("renewwindow", "5") 2154 err = st.stdPostAPI("/renter", allowanceValues) 2155 if err != nil { 2156 t.Fatal(err) 2157 } 2158 2159 // Block until the allowance has finished forming contracts. 2160 err = build.Retry(50, time.Millisecond*250, func() error { 2161 var rc RenterContracts 2162 err = st.getAPI("/renter/contracts", &rc) 2163 if err != nil { 2164 return errors.New("couldn't get renter stats") 2165 } 2166 if len(rc.Contracts) != 2 { 2167 return errors.New("no contracts") 2168 } 2169 return nil 2170 }) 2171 if err != nil { 2172 t.Fatal("allowance setting failed") 2173 } 2174 2175 // Create a file to upload. 2176 filesize := int(1024) 2177 path := filepath.Join(st.dir, "test.dat") 2178 err = createRandFile(path, filesize) 2179 if err != nil { 2180 t.Fatal(err) 2181 } 2182 2183 // upload the file 2184 uploadValues := url.Values{} 2185 uploadValues.Set("source", path) 2186 err = st.stdPostAPI("/renter/upload/test", uploadValues) 2187 if err != nil { 2188 t.Fatal(err) 2189 } 2190 2191 // redundancy should reach 2 2192 var rf RenterFiles 2193 err = retry(60, time.Second, func() error { 2194 st.getAPI("/renter/files", &rf) 2195 if len(rf.Files) >= 1 && rf.Files[0].Redundancy == 2 { 2196 return nil 2197 } 2198 return errors.New("file not uploaded") 2199 }) 2200 if err != nil { 2201 t.Fatal(err) 2202 } 2203 2204 // take down one of the hosts 2205 stH1.server.Close() 2206 2207 // wait for the redundancy to decrement 2208 err = retry(60, time.Second, func() error { 2209 st.getAPI("/renter/files", &rf) 2210 if len(rf.Files) >= 1 && rf.Files[0].Redundancy == 1 { 2211 return nil 2212 } 2213 return errors.New("file redundancy not decremented") 2214 }) 2215 if err != nil { 2216 t.Fatal(err) 2217 } 2218 2219 // bring back the host and let it mine a block 2220 stH1, err = assembleServerTester(stH1.walletKey, stH1.dir) 2221 if err != nil { 2222 t.Fatal(err) 2223 } 2224 defer stH1.server.Close() 2225 2226 testGroup = []*serverTester{st, stH1} 2227 // Make sure the leader of the group has the longest chain before 2228 // connecting the nodes 2229 if _, err := st.miner.AddBlock(); err != nil { 2230 t.Fatal(err) 2231 } 2232 err = fullyConnectNodes(testGroup) 2233 if err != nil { 2234 t.Fatal(err) 2235 } 2236 2237 // Add a block to clear the transaction pool and give the host an output to 2238 // make an announcement, and then make the announcement. 2239 _, err = st.miner.AddBlock() 2240 if err != nil { 2241 t.Fatal(err) 2242 } 2243 _, err = synchronizationCheck(testGroup) 2244 if err != nil { 2245 t.Fatal(err) 2246 } 2247 err = announceAllHosts(testGroup) 2248 if err != nil { 2249 t.Fatal(err) 2250 } 2251 _, err = st.miner.AddBlock() 2252 if err != nil { 2253 t.Fatal(err) 2254 } 2255 err = waitForBlock(st.cs.CurrentBlock().ID(), stH1) 2256 if err != nil { 2257 t.Fatal(err) 2258 } 2259 _, err = synchronizationCheck(testGroup) 2260 if err != nil { 2261 t.Fatal(err) 2262 } 2263 2264 // Wait until the host shows back up in the hostdb. 2265 var ah HostdbActiveGET 2266 err = retry(250, time.Millisecond*250, func() error { 2267 if err = st.getAPI("/hostdb/active", &ah); err != nil { 2268 t.Fatal(err) 2269 } 2270 if len(ah.Hosts) != 2 { 2271 return errors.New("not enough hosts in hostdb") 2272 } 2273 for _, host := range ah.Hosts { 2274 if len(host.ScanHistory) < 2 { 2275 return errors.New("hosts are not scanned") 2276 } 2277 } 2278 return nil 2279 }) 2280 if err != nil { 2281 t.Fatal(err) 2282 } 2283 2284 // Mine another block so that the contract checker updates the IsGood status 2285 // of the contracts. 2286 _, err = st.miner.AddBlock() 2287 if err != nil { 2288 t.Fatal(err) 2289 } 2290 _, err = synchronizationCheck(testGroup) 2291 if err != nil { 2292 t.Fatal(err) 2293 } 2294 2295 // Redundancy should re-report at 2. 2296 err = retry(250, 100*time.Millisecond, func() error { 2297 st.getAPI("/renter/files", &rf) 2298 if len(rf.Files) >= 1 && rf.Files[0].Redundancy == 2 { 2299 return nil 2300 } 2301 return errors.New("file redundancy not incremented") 2302 }) 2303 if err != nil { 2304 t.Fatal(err) 2305 } 2306 } 2307 2308 // TestUploadedBytesReporting verifies that reporting of how many bytes have 2309 // been uploaded via active contracts is accurate 2310 func TestUploadedBytesReporting(t *testing.T) { 2311 if testing.Short() { 2312 t.SkipNow() 2313 } 2314 t.Parallel() 2315 st, err := createServerTester(t.Name()) 2316 if err != nil { 2317 t.Fatal(err) 2318 } 2319 defer st.server.Close() 2320 stH1, err := blankServerTester(t.Name() + " - Host 2") 2321 if err != nil { 2322 t.Fatal(err) 2323 } 2324 defer stH1.server.Close() 2325 testGroup := []*serverTester{st, stH1} 2326 2327 // Connect the testers to eachother so that they are all on the same 2328 // blockchain. 2329 err = fullyConnectNodes(testGroup) 2330 if err != nil { 2331 t.Fatal(err) 2332 } 2333 // Make sure that every wallet has money in it. 2334 err = fundAllNodes(testGroup) 2335 if err != nil { 2336 t.Fatal(err) 2337 } 2338 // Add storage to every host. 2339 err = addStorageToAllHosts(testGroup) 2340 if err != nil { 2341 t.Fatal(err) 2342 } 2343 // Announce every host. 2344 err = announceAllHosts(testGroup) 2345 if err != nil { 2346 t.Fatal(err) 2347 } 2348 2349 // Set an allowance with two hosts. 2350 allowanceValues := url.Values{} 2351 allowanceValues.Set("funds", "50000000000000000000000000000") // 50k SC 2352 allowanceValues.Set("hosts", "2") 2353 allowanceValues.Set("period", "10") 2354 allowanceValues.Set("renewwindow", "5") 2355 err = st.stdPostAPI("/renter", allowanceValues) 2356 if err != nil { 2357 t.Fatal(err) 2358 } 2359 2360 // Block until the allowance has finished forming contracts. 2361 err = build.Retry(50, time.Millisecond*250, func() error { 2362 var rc RenterContracts 2363 err = st.getAPI("/renter/contracts", &rc) 2364 if err != nil { 2365 return errors.New("couldn't get renter stats") 2366 } 2367 if len(rc.Contracts) != 2 { 2368 return errors.New("no contracts") 2369 } 2370 return nil 2371 }) 2372 if err != nil { 2373 t.Fatal("allowance setting failed") 2374 } 2375 2376 // Create a file to upload. 2377 filesize := int(modules.SectorSize * 2) 2378 path := filepath.Join(st.dir, "test.dat") 2379 err = createRandFile(path, filesize) 2380 if err != nil { 2381 t.Fatal(err) 2382 } 2383 2384 // Upload the file 2385 dataPieces := 1 2386 parityPieces := 1 2387 uploadValues := url.Values{} 2388 uploadValues.Set("source", path) 2389 uploadValues.Set("datapieces", fmt.Sprint(dataPieces)) 2390 uploadValues.Set("paritypieces", fmt.Sprint(parityPieces)) 2391 err = st.stdPostAPI("/renter/upload/test", uploadValues) 2392 if err != nil { 2393 t.Fatal(err) 2394 } 2395 2396 // Calculate the encrypted size of our fully redundant encoded file 2397 pieceSize := modules.SectorSize - crypto.TwofishOverhead 2398 chunkSize := pieceSize * uint64(dataPieces) 2399 numChunks := uint64(filesize) / chunkSize 2400 if uint64(filesize)%chunkSize != 0 { 2401 numChunks++ 2402 } 2403 fullyRedundantSize := modules.SectorSize * uint64(dataPieces+parityPieces) * uint64(numChunks) 2404 2405 // Monitor the file as it uploads. Ensure that the UploadProgress times 2406 // the fully redundant file size always equals UploadedBytes reported 2407 var rf RenterFiles 2408 for i := 0; i < 60 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 100); i++ { 2409 st.getAPI("/renter/files", &rf) 2410 if len(rf.Files) >= 1 { 2411 uploadProgressBytes := uint64(float64(fullyRedundantSize) * rf.Files[0].UploadProgress / 100.0) 2412 // Note: in Go 1.10 we will be able to write Math.Round(uploadProgressBytes) != rf.Files[0].UploadedBytes 2413 if uploadProgressBytes != rf.Files[0].UploadedBytes && (uploadProgressBytes+1) != rf.Files[0].UploadedBytes { 2414 t.Fatalf("api reports having uploaded %v bytes when upload progress is %v%%, but the actual uploaded bytes count should be %v\n", 2415 rf.Files[0].UploadedBytes, rf.Files[0].UploadProgress, uploadProgressBytes) 2416 } 2417 } 2418 time.Sleep(time.Second) 2419 } 2420 if err != nil { 2421 t.Fatal(err) 2422 } 2423 2424 // Upload progress should be 100% and redundancy should reach 2 2425 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 100 || rf.Files[0].Redundancy != 2 { 2426 t.Fatal("the uploading is not succeeding for some reason:", rf.Files[0]) 2427 } 2428 2429 // When the file is fully redundantly uploaded, UploadedBytes should 2430 // equal the file's fully redundant size 2431 if rf.Files[0].UploadedBytes != fullyRedundantSize { 2432 t.Fatalf("api reports having uploaded %v bytes when upload progress is 100%%, but the actual fully redundant file size is %v\n", 2433 rf.Files[0].UploadedBytes, fullyRedundantSize) 2434 } 2435 2436 } 2437 2438 // TestRenterMissingHosts verifies that if hosts are taken offline, downloads 2439 // fail. 2440 func TestRenterMissingHosts(t *testing.T) { 2441 if testing.Short() || !build.VLONG { 2442 t.SkipNow() 2443 } 2444 st, err := createServerTester(t.Name()) 2445 if err != nil { 2446 t.Fatal(err) 2447 } 2448 defer st.server.Close() 2449 stH1, err := blankServerTester(t.Name() + " - Host 1") 2450 if err != nil { 2451 t.Fatal(err) 2452 } 2453 defer stH1.server.Close() 2454 stH2, err := blankServerTester(t.Name() + " - Host 2") 2455 if err != nil { 2456 t.Fatal(err) 2457 } 2458 defer stH2.server.Close() 2459 stH3, err := blankServerTester(t.Name() + " - Host 3") 2460 if err != nil { 2461 t.Fatal(err) 2462 } 2463 defer stH3.server.Close() 2464 testGroup := []*serverTester{st, stH1, stH2, stH3} 2465 2466 // Connect the testers to eachother so that they are all on the same 2467 // blockchain. 2468 err = fullyConnectNodes(testGroup) 2469 if err != nil { 2470 t.Fatal(err) 2471 } 2472 // Make sure that every wallet has money in it. 2473 err = fundAllNodes(testGroup) 2474 if err != nil { 2475 t.Fatal(err) 2476 } 2477 2478 // Add storage to every host. 2479 err = addStorageToAllHosts(testGroup) 2480 if err != nil { 2481 t.Fatal(err) 2482 } 2483 err = announceAllHosts(testGroup) 2484 if err != nil { 2485 t.Fatal(err) 2486 } 2487 2488 // Set an allowance with two hosts. 2489 allowanceValues := url.Values{} 2490 allowanceValues.Set("funds", "50000000000000000000000000000") // 50k SC 2491 allowanceValues.Set("hosts", "3") 2492 allowanceValues.Set("period", "20") 2493 err = st.stdPostAPI("/renter", allowanceValues) 2494 if err != nil { 2495 t.Fatal(err) 2496 } 2497 2498 // Block until the allowance has finished forming contracts. 2499 err = build.Retry(50, time.Millisecond*250, func() error { 2500 var rc RenterContracts 2501 err = st.getAPI("/renter/contracts", &rc) 2502 if err != nil { 2503 return errors.New("couldn't get renter stats") 2504 } 2505 if len(rc.Contracts) != 3 { 2506 return errors.New("no contracts") 2507 } 2508 return nil 2509 }) 2510 if err != nil { 2511 t.Fatal("allowance setting failed:", err) 2512 } 2513 2514 // Create a file to upload. 2515 filesize := int(100) 2516 path := filepath.Join(st.dir, "test.dat") 2517 err = createRandFile(path, filesize) 2518 if err != nil { 2519 t.Fatal(err) 2520 } 2521 2522 // upload the file 2523 uploadValues := url.Values{} 2524 uploadValues.Set("source", path) 2525 uploadValues.Set("datapieces", "2") 2526 uploadValues.Set("paritypieces", "1") 2527 err = st.stdPostAPI("/renter/upload/test", uploadValues) 2528 if err != nil { 2529 t.Fatal(err) 2530 } 2531 2532 // redundancy should reach 1.5 2533 var rf RenterFiles 2534 err = retry(20, time.Second, func() error { 2535 st.getAPI("/renter/files", &rf) 2536 if len(rf.Files) >= 1 && rf.Files[0].Redundancy == 1.5 { 2537 return nil 2538 } 2539 return errors.New("file not uploaded") 2540 }) 2541 if err != nil { 2542 t.Fatal(err) 2543 } 2544 2545 // verify we can download 2546 downloadPath := filepath.Join(st.dir, "test-downloaded-verify.dat") 2547 err = st.stdGetAPI("/renter/download/test?destination=" + downloadPath) 2548 if err != nil { 2549 t.Fatal(err) 2550 } 2551 2552 // take down one of the hosts 2553 err = stH1.server.Close() 2554 if err != nil { 2555 t.Fatal(err) 2556 } 2557 2558 // redundancy should not decrement, we have a backup host we can use. 2559 err = retry(60, time.Second, func() error { 2560 st.getAPI("/renter/files", &rf) 2561 if len(rf.Files) >= 1 && rf.Files[0].Redundancy == 1.5 { 2562 return nil 2563 } 2564 return errors.New("file redundancy not decremented: " + fmt.Sprint(rf.Files[0].Redundancy)) 2565 }) 2566 if err != nil { 2567 t.Log(err) 2568 } 2569 2570 // verify we still can download 2571 downloadPath = filepath.Join(st.dir, "test-downloaded-verify2.dat") 2572 err = st.stdGetAPI("/renter/download/test?destination=" + downloadPath) 2573 if err != nil { 2574 t.Fatal(err) 2575 } 2576 2577 // take down another host 2578 err = stH2.server.Close() 2579 if err != nil { 2580 t.Fatal(err) 2581 } 2582 2583 // wait for the redundancy to decrement 2584 err = retry(60, time.Second, func() error { 2585 st.getAPI("/renter/files", &rf) 2586 if len(rf.Files) >= 1 && rf.Files[0].Redundancy == 1 { 2587 return nil 2588 } 2589 return errors.New("file redundancy not decremented: " + fmt.Sprint(rf.Files[0].Redundancy)) 2590 }) 2591 if err != nil { 2592 t.Log(err) 2593 } 2594 2595 // verify we still can download 2596 downloadPath = filepath.Join(st.dir, "test-downloaded-verify2.dat") 2597 err = st.stdGetAPI("/renter/download/test?destination=" + downloadPath) 2598 if err != nil { 2599 t.Fatal(err) 2600 } 2601 2602 // take down another host 2603 err = stH3.server.Close() 2604 if err != nil { 2605 t.Fatal(err) 2606 } 2607 2608 // wait for the redundancy to decrement 2609 err = retry(60, time.Second, func() error { 2610 st.getAPI("/renter/files", &rf) 2611 if len(rf.Files) >= 1 && rf.Files[0].Redundancy == 0 { 2612 return nil 2613 } 2614 return errors.New("file redundancy not decremented: " + fmt.Sprint(rf.Files[0].Redundancy)) 2615 }) 2616 if err != nil { 2617 t.Log(err) 2618 } 2619 2620 // verify that the download fails 2621 downloadPath = filepath.Join(st.dir, "test-downloaded-verify4.dat") 2622 err = st.stdGetAPI("/renter/download/test?destination=" + downloadPath) 2623 if err == nil { 2624 t.Fatal("expected download to fail with redundancy <1") 2625 } 2626 } 2627 2628 // TestRepairLoopBlocking checks if the repair loop blocks operations while a 2629 // non local file is being downloaded for repair. 2630 func TestRepairLoopBlocking(t *testing.T) { 2631 // TODO: Refactor dependency management to block download 2632 t.Skip("Test requires refactoring") 2633 if testing.Short() || !build.VLONG { 2634 t.SkipNow() 2635 } 2636 st, err := createServerTester(t.Name()) 2637 if err != nil { 2638 t.Fatal(err) 2639 } 2640 //st.renter.SetDependencies(renter.BlockRepairUpload{}) 2641 defer st.server.Close() 2642 stH1, err := blankServerTester(t.Name() + " - Host 1") 2643 if err != nil { 2644 t.Fatal(err) 2645 } 2646 defer stH1.server.Close() 2647 testGroup := []*serverTester{st, stH1} 2648 2649 // Connect the testers to eachother so that they are all on the same 2650 // blockchain. 2651 err = fullyConnectNodes(testGroup) 2652 if err != nil { 2653 t.Fatal(err) 2654 } 2655 // Make sure that every wallet has money in it. 2656 err = fundAllNodes(testGroup) 2657 if err != nil { 2658 t.Fatal(err) 2659 } 2660 2661 // Add storage to every host. 2662 err = addStorageToAllHosts(testGroup) 2663 if err != nil { 2664 t.Fatal(err) 2665 } 2666 err = announceAllHosts(testGroup) 2667 if err != nil { 2668 t.Fatal(err) 2669 } 2670 2671 // Set an allowance with two hosts. 2672 allowanceValues := url.Values{} 2673 allowanceValues.Set("funds", "50000000000000000000000000000") // 50k SC 2674 allowanceValues.Set("hosts", "2") 2675 allowanceValues.Set("period", "10") 2676 err = st.stdPostAPI("/renter", allowanceValues) 2677 if err != nil { 2678 t.Fatal(err) 2679 } 2680 2681 // Create a file with 1 chunk to upload. 2682 filesize := int(1) 2683 path := filepath.Join(st.dir, "test.dat") 2684 err = createRandFile(path, filesize) 2685 if err != nil { 2686 t.Fatal(err) 2687 } 2688 2689 // upload the file 2690 uploadValues := url.Values{} 2691 uploadValues.Set("source", path) 2692 err = st.stdPostAPI("/renter/upload/test", uploadValues) 2693 if err != nil { 2694 t.Fatal(err) 2695 } 2696 2697 // redundancy should reach 2 2698 var rf RenterFiles 2699 err = retry(60, time.Second, func() error { 2700 st.getAPI("/renter/files", &rf) 2701 if len(rf.Files) >= 1 && rf.Files[0].Redundancy == 2 { 2702 return nil 2703 } 2704 return errors.New("file not uploaded") 2705 }) 2706 if err != nil { 2707 t.Fatal(err) 2708 } 2709 2710 // verify we can download 2711 downloadPath := filepath.Join(st.dir, "test-downloaded-verify.dat") 2712 err = st.stdGetAPI("/renter/download/test?destination=" + downloadPath) 2713 if err != nil { 2714 t.Fatal(err) 2715 } 2716 2717 // remove the local copy of the file 2718 err = os.Remove(path) 2719 if err != nil { 2720 t.Fatal(err) 2721 } 2722 2723 // take down one of the hosts 2724 err = stH1.server.Close() 2725 if err != nil { 2726 t.Fatal(err) 2727 } 2728 2729 // wait for the redundancy to decrement 2730 err = retry(60, time.Second, func() error { 2731 st.getAPI("/renter/files", &rf) 2732 if len(rf.Files) >= 1 && rf.Files[0].Redundancy == 1 { 2733 return nil 2734 } 2735 return errors.New("file redundancy not decremented") 2736 }) 2737 if err != nil { 2738 t.Fatal(err) 2739 } 2740 2741 // verify we still can download 2742 downloadPath = filepath.Join(st.dir, "test-downloaded-verify2.dat") 2743 err = st.stdGetAPI("/renter/download/test?destination=" + downloadPath) 2744 if err != nil { 2745 t.Fatal(err) 2746 } 2747 2748 // bring up a few new hosts 2749 testGroup = []*serverTester{st} 2750 for i := 0; i < 3; i++ { 2751 stNewHost, err := blankServerTester(t.Name() + fmt.Sprintf("-newhost%d", i)) 2752 if err != nil { 2753 t.Fatal(err) 2754 } 2755 defer stNewHost.server.Close() 2756 testGroup = append(testGroup, stNewHost) 2757 } 2758 2759 // Connect the testers to eachother so that they are all on the same 2760 // blockchain. 2761 err = fullyConnectNodes(testGroup) 2762 if err != nil { 2763 t.Fatal(err) 2764 } 2765 _, err = synchronizationCheck(testGroup) 2766 if err != nil { 2767 t.Fatal(err) 2768 } 2769 2770 // Make sure that every wallet has money in it. 2771 err = fundAllNodes(testGroup) 2772 if err != nil { 2773 t.Fatal(err) 2774 } 2775 2776 for _, stNewHost := range testGroup[1 : len(testGroup)-1] { 2777 err = stNewHost.setHostStorage() 2778 if err != nil { 2779 t.Fatal(err) 2780 } 2781 err = stNewHost.announceHost() 2782 if err != nil { 2783 t.Fatal(err) 2784 } 2785 err = waitForBlock(stNewHost.cs.CurrentBlock().ID(), st) 2786 if err != nil { 2787 t.Fatal(err) 2788 } 2789 2790 // add a few new blocks in order to cause the renter to form contracts with the new host 2791 for i := 0; i < 10; i++ { 2792 b, err := testGroup[0].miner.AddBlock() 2793 if err != nil { 2794 t.Fatal(err) 2795 } 2796 tipID, err := synchronizationCheck(testGroup) 2797 if err != nil { 2798 t.Fatal(err) 2799 } 2800 if b.ID() != tipID { 2801 t.Fatal("test group does not have the tip block") 2802 } 2803 } 2804 } 2805 2806 // wait a few seconds for the the repair to be queued and started 2807 time.Sleep(3 * time.Second) 2808 2809 // redundancy should not increment back to 2 because the renter should be blocked 2810 st.getAPI("/renter/files", &rf) 2811 if len(rf.Files) >= 1 && rf.Files[0].Redundancy >= 2 && rf.Files[0].Available { 2812 t.Error("The file's redundancy incremented back to 2 but shouldn't") 2813 } 2814 2815 // create a second file to upload 2816 filesize = int(1) 2817 path2 := filepath.Join(st.dir, "test2.dat") 2818 err = createRandFile(path2, filesize) 2819 if err != nil { 2820 t.Fatal(err) 2821 } 2822 2823 // upload the second file 2824 uploadValues = url.Values{} 2825 uploadValues.Set("source", path2) 2826 2827 wait := make(chan error) 2828 go func() { 2829 wait <- st.stdPostAPI("/renter/upload/test2", uploadValues) 2830 }() 2831 select { 2832 case <-time.After(time.Minute): 2833 t.Fatal("/renter/upload API call didn't return within 60 seconds") 2834 case err = <-wait: 2835 } 2836 if err != nil { 2837 t.Fatal(err) 2838 } 2839 2840 // redundancy should reach 2 for the second file 2841 err = retry(60, time.Second, func() error { 2842 st.getAPI("/renter/files", &rf) 2843 if len(rf.Files) >= 2 && rf.Files[1].Redundancy >= 2 { 2844 return nil 2845 } 2846 return errors.New("file 2 not uploaded") 2847 }) 2848 if err != nil { 2849 t.Fatal(err) 2850 } 2851 2852 // verify we can download the second file 2853 downloadPath = filepath.Join(st.dir, "test-downloaded-verify2.dat") 2854 err = st.stdGetAPI("/renter/download/test2?destination=" + downloadPath) 2855 if err != nil { 2856 t.Fatal(err) 2857 } 2858 } 2859 2860 // TestRemoteFileRepairMassive is similar to TestRemoteFileRepair but uploads 2861 // more files to find potential deadlocks or crashes 2862 func TestRemoteFileRepairMassive(t *testing.T) { 2863 if testing.Short() || !build.VLONG { 2864 t.SkipNow() 2865 } 2866 st, err := createServerTester(t.Name()) 2867 if err != nil { 2868 t.Fatal(err) 2869 } 2870 defer st.server.Close() 2871 stH1, err := blankServerTester(t.Name() + " - Host 1") 2872 if err != nil { 2873 t.Fatal(err) 2874 } 2875 defer stH1.server.Close() 2876 testGroup := []*serverTester{st, stH1} 2877 2878 // Connect the testers to eachother so that they are all on the same 2879 // blockchain. 2880 err = fullyConnectNodes(testGroup) 2881 if err != nil { 2882 t.Fatal(err) 2883 } 2884 // Make sure that every wallet has money in it. 2885 err = fundAllNodes(testGroup) 2886 if err != nil { 2887 t.Fatal(err) 2888 } 2889 2890 // Add storage to every host. 2891 err = addStorageToAllHosts(testGroup) 2892 if err != nil { 2893 t.Fatal(err) 2894 } 2895 err = announceAllHosts(testGroup) 2896 if err != nil { 2897 t.Fatal(err) 2898 } 2899 2900 // Set an allowance with two hosts. 2901 allowanceValues := url.Values{} 2902 allowanceValues.Set("funds", "50000000000000000000000000000") // 50k SC 2903 allowanceValues.Set("hosts", "2") 2904 allowanceValues.Set("period", "10") 2905 err = st.stdPostAPI("/renter", allowanceValues) 2906 if err != nil { 2907 t.Fatal(err) 2908 } 2909 2910 // Create a file to upload. 2911 filesize := int(4000) 2912 path := filepath.Join(st.dir, "test.dat") 2913 err = createRandFile(path, filesize) 2914 if err != nil { 2915 t.Fatal(err) 2916 } 2917 2918 // upload the file numUploads times 2919 numUploads := 10 2920 uploadValues := url.Values{} 2921 uploadValues.Set("source", path) 2922 2923 for i := 0; i < numUploads; i++ { 2924 err = st.stdPostAPI(fmt.Sprintf("/renter/upload/test%v", i), uploadValues) 2925 if err != nil { 2926 t.Fatal(err) 2927 } 2928 } 2929 2930 // redundancy should reach 2 for all files 2931 var rf RenterFiles 2932 err = retry(600, time.Second, func() error { 2933 st.getAPI("/renter/files", &rf) 2934 if len(rf.Files) != numUploads { 2935 return errors.New("file not uploaded") 2936 } 2937 for i, f := range rf.Files { 2938 if f.Redundancy != 2 { 2939 return fmt.Errorf("file %v only reached %v redundancy", i, f.Redundancy) 2940 } 2941 } 2942 return nil 2943 }) 2944 if err != nil { 2945 t.Fatal(err) 2946 } 2947 2948 // remove the local copy of the file 2949 err = os.Remove(path) 2950 if err != nil { 2951 t.Fatal(err) 2952 } 2953 2954 // take down one of the hosts 2955 err = stH1.server.Close() 2956 if err != nil { 2957 t.Fatal(err) 2958 } 2959 2960 // wait for the redundancy to decrement 2961 err = retry(60, time.Second, func() error { 2962 st.getAPI("/renter/files", &rf) 2963 if len(rf.Files) != numUploads { 2964 return errors.New("file not uploaded") 2965 } 2966 for _, f := range rf.Files { 2967 if f.Redundancy != 1 { 2968 return errors.New("file redudancy didn't decrement to x1") 2969 } 2970 } 2971 return nil 2972 }) 2973 if err != nil { 2974 t.Fatal(err) 2975 } 2976 2977 // bring up a new host 2978 stNewHost, err := blankServerTester(t.Name() + "-newhost") 2979 if err != nil { 2980 t.Fatal(err) 2981 } 2982 defer stNewHost.server.Close() 2983 2984 testGroup = []*serverTester{st, stNewHost} 2985 2986 // Connect the testers to eachother so that they are all on the same 2987 // blockchain. 2988 err = fullyConnectNodes(testGroup) 2989 if err != nil { 2990 t.Fatal(err) 2991 } 2992 _, err = synchronizationCheck(testGroup) 2993 if err != nil { 2994 t.Fatal(err) 2995 } 2996 2997 // Make sure that every wallet has money in it. 2998 err = fundAllNodes(testGroup) 2999 if err != nil { 3000 t.Fatal(err) 3001 } 3002 3003 err = stNewHost.setHostStorage() 3004 if err != nil { 3005 t.Fatal(err) 3006 } 3007 err = stNewHost.announceHost() 3008 if err != nil { 3009 t.Fatal(err) 3010 } 3011 err = waitForBlock(stNewHost.cs.CurrentBlock().ID(), st) 3012 if err != nil { 3013 t.Fatal(err) 3014 } 3015 3016 // add a few new blocks in order to cause the renter to form contracts with the new host 3017 for i := 0; i < 10; i++ { 3018 b, err := testGroup[0].miner.AddBlock() 3019 if err != nil { 3020 t.Fatal(err) 3021 } 3022 tipID, err := synchronizationCheck(testGroup) 3023 if err != nil { 3024 t.Fatal(err) 3025 } 3026 if b.ID() != tipID { 3027 t.Fatal("test group does not have the tip block") 3028 } 3029 } 3030 3031 // redundancy should increment back to 2 as the renter uploads to the new 3032 // host using the download-to-upload strategy 3033 err = retry(300, time.Second, func() error { 3034 st.getAPI("/renter/files", &rf) 3035 if len(rf.Files) != numUploads { 3036 return errors.New("file not uploaded") 3037 } 3038 for i, f := range rf.Files { 3039 if f.Redundancy != 2 { 3040 return fmt.Errorf("file %v only reached %v redundancy", i, f.Redundancy) 3041 } 3042 } 3043 return nil 3044 }) 3045 if err != nil { 3046 t.Fatal(err) 3047 } 3048 }