github.com/fozzysec/SiaPrime@v0.0.0-20190612043147-66c8e8d11fe3/node/api/renter_test.go (about) 1 package api 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "net/url" 10 "os" 11 "path/filepath" 12 "strconv" 13 "strings" 14 "testing" 15 "time" 16 17 "SiaPrime/build" 18 "SiaPrime/modules" 19 "SiaPrime/modules/renter" 20 "SiaPrime/modules/renter/contractor" 21 "SiaPrime/types" 22 "gitlab.com/NebulousLabs/fastrand" 23 ) 24 25 const ( 26 testFunds = "10000000000000000000000000000" // 10k SC 27 testPeriod = "5" 28 testRenewWindow = "2" 29 ) 30 31 // createRandFile creates a file on disk and fills it with random bytes. 32 func createRandFile(path string, size int) error { 33 return ioutil.WriteFile(path, fastrand.Bytes(size), 0600) 34 } 35 36 // setupTestDownload creates a server tester with an uploaded file of size 37 // `size` and name `name`. 38 func setupTestDownload(t *testing.T, size int, name string, waitOnAvailability bool) (*serverTester, string) { 39 st, err := createServerTester(t.Name()) 40 if err != nil { 41 t.Fatal(err) 42 } 43 44 // Announce the host and start accepting contracts. 45 err = st.announceHost() 46 if err != nil { 47 t.Fatal(err) 48 } 49 err = st.acceptContracts() 50 if err != nil { 51 t.Fatal(err) 52 } 53 err = st.setHostStorage() 54 if err != nil { 55 t.Fatal(err) 56 } 57 58 // Set an allowance for the renter, allowing a contract to be formed. 59 allowanceValues := url.Values{} 60 testFunds := testFunds 61 testPeriod := "10" 62 renewWindow := "5" 63 allowanceValues.Set("funds", testFunds) 64 allowanceValues.Set("period", testPeriod) 65 allowanceValues.Set("renewwindow", renewWindow) 66 allowanceValues.Set("hosts", fmt.Sprint(recommendedHosts)) 67 err = st.stdPostAPI("/renter", allowanceValues) 68 if err != nil { 69 t.Fatal(err) 70 } 71 72 // Create a file. 73 path := filepath.Join(build.SiaTestingDir, "api", t.Name(), name) 74 err = createRandFile(path, size) 75 if err != nil { 76 t.Fatal(err) 77 } 78 79 // Upload to host. 80 uploadValues := url.Values{} 81 uploadValues.Set("source", path) 82 uploadValues.Set("renew", "true") 83 uploadValues.Set("datapieces", "1") 84 uploadValues.Set("paritypieces", "1") 85 err = st.stdPostAPI("/renter/upload/"+name, uploadValues) 86 if err != nil { 87 t.Fatal(err) 88 } 89 90 if waitOnAvailability { 91 // wait for the file to become available 92 err = build.Retry(200, time.Second, func() error { 93 var rf RenterFiles 94 st.getAPI("/renter/files", &rf) 95 if len(rf.Files) != 1 || !rf.Files[0].Available { 96 return fmt.Errorf("the uploading is not succeeding for some reason: %v\n", rf.Files[0]) 97 } 98 return nil 99 }) 100 if err != nil { 101 t.Fatal(err) 102 } 103 } 104 105 return st, path 106 } 107 108 // runDownloadTest uploads a file and downloads it using the specified 109 // parameters, verifying that the parameters are applied correctly and the file 110 // is downloaded successfully. 111 func runDownloadTest(t *testing.T, filesize, offset, length int64, useHttpResp bool, testName string) error { 112 ulSiaPath := testName + ".dat" 113 st, path := setupTestDownload(t, int(filesize), ulSiaPath, true) 114 defer func() { 115 st.server.panicClose() 116 os.Remove(path) 117 }() 118 119 // Read the section to be downloaded from the original file. 120 uf, err := os.Open(path) // Uploaded file. 121 if err != nil { 122 return err 123 } 124 var originalBytes bytes.Buffer 125 _, err = uf.Seek(offset, 0) 126 if err != nil { 127 return err 128 } 129 _, err = io.CopyN(&originalBytes, uf, length) 130 if err != nil { 131 return err 132 } 133 134 // Download the original file from the passed offsets. 135 fname := testName + "-download.dat" 136 downpath := filepath.Join(st.dir, fname) 137 defer os.Remove(downpath) 138 139 dlURL := fmt.Sprintf("/renter/download/%s?offset=%d&length=%d", ulSiaPath, offset, length) 140 141 var downbytes bytes.Buffer 142 143 if useHttpResp { 144 dlURL += "&httpresp=true" 145 // Make request. 146 resp, err := HttpGET("http://" + st.server.listener.Addr().String() + dlURL) 147 if err != nil { 148 return err 149 } 150 defer resp.Body.Close() 151 152 _, err = io.Copy(&downbytes, resp.Body) 153 if err != nil { 154 return err 155 } 156 } else { 157 dlURL += "&destination=" + downpath 158 err := st.getAPI(dlURL, nil) 159 if err != nil { 160 return err 161 } 162 // wait for the download to complete 163 err = build.Retry(30, time.Second, func() error { 164 var rdq RenterDownloadQueue 165 err = st.getAPI("/renter/downloads", &rdq) 166 if err != nil { 167 return err 168 } 169 for _, download := range rdq.Downloads { 170 if download.Received == download.Filesize && download.SiaPath == ulSiaPath { 171 return nil 172 } 173 } 174 return errors.New("file not downloaded") 175 }) 176 if err != nil { 177 t.Fatal(err) 178 } 179 180 // open the downloaded file 181 df, err := os.Open(downpath) 182 if err != nil { 183 return err 184 } 185 defer df.Close() 186 187 _, err = io.Copy(&downbytes, df) 188 if err != nil { 189 return err 190 } 191 } 192 193 // should have correct length 194 if int64(downbytes.Len()) != length { 195 return fmt.Errorf("downloaded file has incorrect size: %d, %d expected", downbytes.Len(), length) 196 } 197 198 // should be byte-for-byte equal to the original uploaded file 199 if !bytes.Equal(originalBytes.Bytes(), downbytes.Bytes()) { 200 return fmt.Errorf("downloaded content differs from original content") 201 } 202 203 return nil 204 } 205 206 // TestRenterDownloadError tests that the /renter/download route sets the 207 // download's error field if it fails. 208 func TestRenterDownloadError(t *testing.T) { 209 if testing.Short() { 210 t.SkipNow() 211 } 212 t.Parallel() 213 214 st, _ := setupTestDownload(t, 1e4, "test.dat", false) 215 defer st.server.Close() 216 217 // don't wait for the upload to complete, try to download immediately to 218 // intentionally cause a download error 219 downpath := filepath.Join(st.dir, "down.dat") 220 expectedErr := st.getAPI("/renter/download/test.dat?destination="+downpath, nil) 221 if expectedErr == nil { 222 t.Fatal("download unexpectedly succeeded") 223 } 224 225 // verify the file has the expected error 226 var rdq RenterDownloadQueue 227 err := st.getAPI("/renter/downloads", &rdq) 228 if err != nil { 229 t.Fatal(err) 230 } 231 for _, download := range rdq.Downloads { 232 if download.SiaPath == "test.dat" && download.Received == download.Filesize && download.Error == expectedErr.Error() { 233 t.Fatal("download had unexpected error: ", download.Error) 234 } 235 } 236 } 237 238 // TestValidDownloads tests valid and boundary parameter combinations. 239 func TestValidDownloads(t *testing.T) { 240 if testing.Short() { 241 t.SkipNow() 242 } 243 t.Parallel() 244 245 sectorSize := int64(modules.SectorSize) 246 247 testParams := []struct { 248 filesize, 249 offset, 250 length int64 251 useHttpResp bool 252 testName string 253 }{ 254 // file-backed tests. 255 {sectorSize, 40, sectorSize - 40, false, "OffsetSingleChunk"}, 256 {sectorSize * 2, 20, sectorSize*2 - 20, false, "OffsetTwoChunk"}, 257 {int64(float64(sectorSize) * 2.4), 20, int64(float64(sectorSize)*2.4) - 20, false, "OffsetThreeChunk"}, 258 {sectorSize, 0, sectorSize / 2, false, "ShortLengthSingleChunk"}, 259 {sectorSize, sectorSize / 4, sectorSize / 2, false, "ShortLengthAndOffsetSingleChunk"}, 260 {sectorSize * 2, 0, int64(float64(sectorSize) * 2 * 0.75), false, "ShortLengthTwoChunk"}, 261 {int64(float64(sectorSize) * 2.7), 0, int64(2.2 * float64(sectorSize)), false, "ShortLengthThreeChunkInThirdChunk"}, 262 {int64(float64(sectorSize) * 2.7), 0, int64(1.6 * float64(sectorSize)), false, "ShortLengthThreeChunkInSecondChunk"}, 263 {sectorSize * 5, 0, int64(float64(sectorSize*5) * 0.75), false, "ShortLengthMultiChunk"}, 264 {sectorSize * 2, 50, int64(float64(sectorSize*2) * 0.75), false, "ShortLengthAndOffsetTwoChunk"}, 265 {sectorSize * 3, 50, int64(float64(sectorSize*3) * 0.5), false, "ShortLengthAndOffsetThreeChunkInSecondChunk"}, 266 {sectorSize * 3, 50, int64(float64(sectorSize*3) * 0.75), false, "ShortLengthAndOffsetThreeChunkInThirdChunk"}, 267 268 // http response tests. 269 {sectorSize, 40, sectorSize - 40, true, "HttpRespOffsetSingleChunk"}, 270 {sectorSize * 2, 40, sectorSize*2 - 40, true, "HttpRespOffsetTwoChunk"}, 271 {sectorSize * 5, 40, sectorSize*5 - 40, true, "HttpRespOffsetManyChunks"}, 272 {sectorSize, 40, 4 * sectorSize / 5, true, "RespOffsetAndLengthSingleChunk"}, 273 {sectorSize * 2, 80, 3 * (sectorSize * 2) / 4, true, "RespOffsetAndLengthTwoChunk"}, 274 {sectorSize * 5, 150, 3 * (sectorSize * 5) / 4, true, "HttpRespOffsetAndLengthManyChunks"}, 275 {sectorSize * 5, 150, sectorSize * 5 / 4, true, "HttpRespOffsetAndLengthManyChunksSubsetOfChunks"}, 276 } 277 for i, params := range testParams { 278 params := params 279 t.Run(fmt.Sprintf("%v-%v", t.Name(), i), func(st *testing.T) { 280 st.Parallel() 281 err := runDownloadTest(st, params.filesize, params.offset, params.length, params.useHttpResp, params.testName) 282 if err != nil { 283 st.Fatal(err) 284 } 285 }) 286 } 287 } 288 289 func runDownloadParamTest(t *testing.T, length, offset, filesize int) error { 290 ulSiaPath := "test.dat" 291 292 st, _ := setupTestDownload(t, int(filesize), ulSiaPath, true) 293 defer st.server.Close() 294 295 // Download the original file from offset 40 and length 10. 296 fname := "offsetsinglechunk.dat" 297 downpath := filepath.Join(st.dir, fname) 298 dlURL := fmt.Sprintf("/renter/download/%s?destination=%s", ulSiaPath, downpath) 299 dlURL += fmt.Sprintf("&length=%d", length) 300 dlURL += fmt.Sprintf("&offset=%d", offset) 301 return st.getAPI(dlURL, nil) 302 } 303 304 func TestInvalidDownloadParameters(t *testing.T) { 305 if testing.Short() || !build.VLONG { 306 t.SkipNow() 307 } 308 t.Parallel() 309 310 testParams := []struct { 311 length int 312 offset int 313 filesize int 314 errorMsg string 315 }{ 316 {0, -10, 1e4, "/download not prompting error when passing negative offset."}, 317 {0, 1e4, 1e4, "/download not prompting error when passing offset equal to filesize."}, 318 {1e4 + 1, 0, 1e4, "/download not prompting error when passing length exceeding filesize."}, 319 {1e4 + 11, 10, 1e4, "/download not prompting error when passing length exceeding filesize with non-zero offset."}, 320 {-1, 0, 1e4, "/download not prompting error when passing negative length."}, 321 } 322 323 for _, params := range testParams { 324 err := runDownloadParamTest(t, params.length, params.offset, params.filesize) 325 if err == nil { 326 t.Fatal(params.errorMsg) 327 } 328 } 329 } 330 331 func TestRenterDownloadAsyncAndHttpRespError(t *testing.T) { 332 if testing.Short() { 333 t.SkipNow() 334 } 335 t.Parallel() 336 337 filesize := 1e4 338 ulSiaPath := "test.dat" 339 340 st, _ := setupTestDownload(t, int(filesize), ulSiaPath, true) 341 defer st.server.Close() 342 343 // Download the original file from offset 40 and length 10. 344 fname := "offsetsinglechunk.dat" 345 dlURL := fmt.Sprintf("/renter/download/%s?destination=%s&async=true&httpresp=true", ulSiaPath, fname) 346 err := st.getAPI(dlURL, nil) 347 if err == nil { 348 t.Fatalf("/download not prompting error when only passing both async and httpresp fields.") 349 } 350 } 351 352 func TestRenterDownloadAsyncNonexistentFile(t *testing.T) { 353 if testing.Short() { 354 t.SkipNow() 355 } 356 t.Parallel() 357 358 st, err := createServerTester(t.Name()) 359 if err != nil { 360 t.Fatal(err) 361 } 362 defer st.server.Close() 363 364 downpath := filepath.Join(st.dir, "testfile") 365 err = st.getAPI(fmt.Sprintf("/renter/downloadasync/doesntexist?destination=%v", downpath), nil) 366 if err == nil || err.Error() != fmt.Sprintf("download failed: no file with that path: doesntexist") { 367 t.Fatal("downloadasync did not return error on nonexistent file") 368 } 369 } 370 371 func TestRenterDownloadAsyncAndNotDestinationError(t *testing.T) { 372 if testing.Short() { 373 t.SkipNow() 374 } 375 t.Parallel() 376 377 filesize := 1e4 378 ulSiaPath := "test.dat" 379 380 st, _ := setupTestDownload(t, int(filesize), ulSiaPath, true) 381 defer st.server.Close() 382 383 // Download the original file from offset 40 and length 10. 384 dlURL := fmt.Sprintf("/renter/download/%s?async=true", ulSiaPath) 385 err := st.getAPI(dlURL, nil) 386 if err == nil { 387 t.Fatal("/download not prompting error when async is specified but destination is empty.") 388 } 389 } 390 391 func TestRenterDownloadHttpRespAndDestinationError(t *testing.T) { 392 if testing.Short() { 393 t.SkipNow() 394 } 395 t.Parallel() 396 397 filesize := 1e4 398 ulSiaPath := "test.dat" 399 400 st, _ := setupTestDownload(t, int(filesize), ulSiaPath, true) 401 defer st.server.Close() 402 403 // Download the original file from offset 40 and length 10. 404 fname := "test.dat" 405 dlURL := fmt.Sprintf("/renter/download/%s?destination=%shttpresp=true", ulSiaPath, fname) 406 err := st.getAPI(dlURL, nil) 407 if err == nil { 408 t.Fatal("/download not prompting error when httpresp is specified and destination is non-empty.") 409 } 410 } 411 412 // TestRenterAsyncDownloadError tests that the /renter/asyncdownload route sets 413 // the download's error field if it fails. 414 func TestRenterAsyncDownloadError(t *testing.T) { 415 if testing.Short() { 416 t.SkipNow() 417 } 418 t.Parallel() 419 420 st, _ := setupTestDownload(t, 1e4, "test.dat", false) 421 defer st.server.panicClose() 422 423 // don't wait for the upload to complete, try to download immediately to 424 // intentionally cause a download error 425 downpath := filepath.Join(st.dir, "asyncdown.dat") 426 st.getAPI("/renter/downloadasync/test.dat?destination="+downpath, nil) 427 428 // verify the file has an error 429 var rdq RenterDownloadQueue 430 err := st.getAPI("/renter/downloads", &rdq) 431 if err != nil { 432 t.Fatal(err) 433 } 434 for _, download := range rdq.Downloads { 435 if download.SiaPath == "test.dat" && download.Received == download.Filesize && download.Error == "" { 436 t.Fatal("download had nil error") 437 } 438 } 439 } 440 441 // TestRenterAsyncDownload tests that the /renter/downloadasync route works 442 // correctly. 443 func TestRenterAsyncDownload(t *testing.T) { 444 if testing.Short() { 445 t.SkipNow() 446 } 447 t.Parallel() 448 449 st, _ := setupTestDownload(t, 1e4, "test.dat", true) 450 defer st.server.panicClose() 451 452 // Download the file asynchronously. 453 downpath := filepath.Join(st.dir, "asyncdown.dat") 454 err := st.getAPI("/renter/downloadasync/test.dat?destination="+downpath, nil) 455 if err != nil { 456 t.Fatal(err) 457 } 458 459 // download should eventually complete 460 var rdq RenterDownloadQueue 461 success := false 462 for start := time.Now(); time.Since(start) < 30*time.Second; time.Sleep(time.Millisecond * 10) { 463 err = st.getAPI("/renter/downloads", &rdq) 464 if err != nil { 465 t.Fatal(err) 466 } 467 for _, download := range rdq.Downloads { 468 if download.Received == download.Filesize && download.SiaPath == "test.dat" { 469 success = true 470 } 471 } 472 if success { 473 break 474 } 475 } 476 if !success { 477 t.Fatal("/renter/downloadasync did not download our test file") 478 } 479 } 480 481 // TestRenterPaths tests that the /renter routes handle path parameters 482 // properly. 483 func TestRenterPaths(t *testing.T) { 484 if testing.Short() { 485 t.SkipNow() 486 } 487 t.Parallel() 488 st, err := createServerTester(t.Name()) 489 if err != nil { 490 t.Fatal(err) 491 } 492 defer st.server.panicClose() 493 494 // Announce the host. 495 err = st.announceHost() 496 if err != nil { 497 t.Fatal(err) 498 } 499 500 // Create a file. 501 path := filepath.Join(build.SiaTestingDir, "api", t.Name(), "test.dat") 502 err = createRandFile(path, 1024) 503 if err != nil { 504 t.Fatal(err) 505 } 506 507 // Upload to host. 508 uploadValues := url.Values{} 509 uploadValues.Set("source", path) 510 uploadValues.Set("renew", "true") 511 err = st.stdPostAPI("/renter/upload/foo/bar/test", uploadValues) 512 if err != nil { 513 t.Fatal(err) 514 } 515 516 // File should be listed by the renter. 517 var rf RenterFiles 518 err = st.getAPI("/renter/files", &rf) 519 if err != nil { 520 t.Fatal(err) 521 } 522 if len(rf.Files) != 1 || rf.Files[0].SiaPath != "foo/bar/test" { 523 t.Fatal("/renter/files did not return correct file:", rf) 524 } 525 } 526 527 // TestRenterConflicts tests that the renter handles naming conflicts properly. 528 func TestRenterConflicts(t *testing.T) { 529 if testing.Short() { 530 t.SkipNow() 531 } 532 t.Parallel() 533 st, err := createServerTester(t.Name()) 534 if err != nil { 535 t.Fatal(err) 536 } 537 defer st.server.panicClose() 538 539 // Announce the host. 540 err = st.announceHost() 541 if err != nil { 542 t.Fatal(err) 543 } 544 545 // Create a file. 546 path := filepath.Join(build.SiaTestingDir, "api", t.Name(), "test.dat") 547 err = createRandFile(path, 1024) 548 if err != nil { 549 t.Fatal(err) 550 } 551 552 // Upload to host, using a path designed to cause conflicts. The renter 553 // should automatically create a folder called foo/bar.sia. Later, we'll 554 // exploit this by uploading a file called foo/bar. 555 uploadValues := url.Values{} 556 uploadValues.Set("source", path) 557 uploadValues.Set("renew", "true") 558 err = st.stdPostAPI("/renter/upload/foo/bar.sia/test", uploadValues) 559 if err != nil { 560 t.Fatal(err) 561 } 562 563 // File should be listed by the renter. 564 var rf RenterFiles 565 err = st.getAPI("/renter/files", &rf) 566 if err != nil { 567 t.Fatal(err) 568 } 569 if len(rf.Files) != 1 || rf.Files[0].SiaPath != "foo/bar.sia/test" { 570 t.Fatal("/renter/files did not return correct file:", rf) 571 } 572 573 // Upload using the same nickname. 574 err = st.stdPostAPI("/renter/upload/foo/bar.sia/test", uploadValues) 575 expectedErr := Error{"upload failed: " + renter.ErrPathOverload.Error()} 576 if err != expectedErr { 577 t.Fatalf("expected %v, got %v", Error{"upload failed: " + renter.ErrPathOverload.Error()}, err) 578 } 579 580 // Upload using nickname that conflicts with folder. 581 err = st.stdPostAPI("/renter/upload/foo/bar", uploadValues) 582 if err == nil { 583 t.Fatal("expecting conflict error, got nil") 584 } 585 } 586 587 // TestRenterHandlerContracts checks that contract formation between a host and 588 // renter behaves as expected, and that contract spending is the right amount. 589 func TestRenterHandlerContracts(t *testing.T) { 590 if testing.Short() { 591 t.SkipNow() 592 } 593 t.Parallel() 594 st, err := createServerTester(t.Name()) 595 if err != nil { 596 t.Fatal(err) 597 } 598 defer st.server.panicClose() 599 600 // Announce the host and start accepting contracts. 601 if err := st.announceHost(); err != nil { 602 t.Fatal(err) 603 } 604 if err = st.acceptContracts(); err != nil { 605 t.Fatal(err) 606 } 607 if err = st.setHostStorage(); err != nil { 608 t.Fatal(err) 609 } 610 611 // The renter should not have any contracts yet. 612 var contracts RenterContracts 613 if err = st.getAPI("/renter/contracts", &contracts); err != nil { 614 t.Fatal(err) 615 } 616 if len(contracts.Contracts) != 0 { 617 t.Fatalf("expected renter to have 0 contracts; got %v", len(contracts.Contracts)) 618 } 619 620 // Set an allowance for the renter, allowing a contract to be formed. 621 allowanceValues := url.Values{} 622 allowanceValues.Set("funds", testFunds) 623 allowanceValues.Set("period", testPeriod) 624 allowanceValues.Set("renewwindow", testRenewWindow) 625 allowanceValues.Set("hosts", fmt.Sprint(recommendedHosts)) 626 if err = st.stdPostAPI("/renter", allowanceValues); err != nil { 627 t.Fatal(err) 628 } 629 630 // Block until the allowance has finished forming contracts. 631 err = build.Retry(50, time.Millisecond*250, func() error { 632 var rc RenterContracts 633 err = st.getAPI("/renter/contracts", &rc) 634 if err != nil { 635 return errors.New("couldn't get renter stats") 636 } 637 if len(rc.Contracts) != 1 { 638 return errors.New("no contracts") 639 } 640 return nil 641 }) 642 if err != nil { 643 t.Fatal("allowance setting failed") 644 } 645 646 // The renter should now have 1 contract. 647 if err = st.getAPI("/renter/contracts", &contracts); err != nil { 648 t.Fatal(err) 649 } 650 if len(contracts.Contracts) != 1 { 651 t.Fatalf("expected renter to have 1 contract; got %v", len(contracts.Contracts)) 652 } 653 if !contracts.Contracts[0].GoodForUpload || !contracts.Contracts[0].GoodForRenew { 654 t.Errorf("expected contract to be good for upload and renew") 655 } 656 657 // Check the renter's contract spending. 658 var get RenterGET 659 if err = st.getAPI("/renter", &get); err != nil { 660 t.Fatal(err) 661 } 662 expectedContractSpending := types.ZeroCurrency 663 for _, contract := range contracts.Contracts { 664 expectedContractSpending = expectedContractSpending.Add(contract.TotalCost) 665 } 666 if got := get.FinancialMetrics.TotalAllocated; got.Cmp(expectedContractSpending) != 0 { 667 t.Fatalf("expected contract spending to be %v; got %v", expectedContractSpending, got) 668 } 669 } 670 671 // TestRenterHandlerGetAndPost checks that valid /renter calls successfully set 672 // allowance values, while /renter calls with invalid allowance values are 673 // correctly handled. 674 func TestRenterHandlerGetAndPost(t *testing.T) { 675 if testing.Short() { 676 t.SkipNow() 677 } 678 t.Parallel() 679 st, err := createServerTester(t.Name()) 680 if err != nil { 681 t.Fatal(err) 682 } 683 defer st.server.panicClose() 684 685 // Announce the host and start accepting contracts. 686 if err := st.announceHost(); err != nil { 687 t.Fatal(err) 688 } 689 if err = st.acceptContracts(); err != nil { 690 t.Fatal(err) 691 } 692 if err = st.setHostStorage(); err != nil { 693 t.Fatal(err) 694 } 695 696 // Set an allowance for the renter, allowing a contract to be formed. 697 allowanceValues := url.Values{} 698 allowanceValues.Set("funds", testFunds) 699 allowanceValues.Set("period", testPeriod) 700 allowanceValues.Set("renewwindow", testRenewWindow) 701 allowanceValues.Set("hosts", fmt.Sprint(recommendedHosts)) 702 if err = st.stdPostAPI("/renter", allowanceValues); err != nil { 703 t.Fatal(err) 704 } 705 706 // Check that a call to /renter returns the expected values. 707 var get RenterGET 708 if err = st.getAPI("/renter", &get); err != nil { 709 t.Fatal(err) 710 } 711 // Check the renter's funds. 712 expectedFunds, ok := scanAmount(testFunds) 713 if !ok { 714 t.Fatal("scanAmount failed") 715 } 716 if got := get.Settings.Allowance.Funds; got.Cmp(expectedFunds) != 0 { 717 t.Fatalf("expected funds to be %v; got %v", expectedFunds, got) 718 } 719 // Check the renter's period. 720 intPeriod, err := strconv.Atoi(testPeriod) 721 if err != nil { 722 t.Fatal(err) 723 } 724 expectedPeriod := types.BlockHeight(intPeriod) 725 if got := get.Settings.Allowance.Period; got != expectedPeriod { 726 t.Fatalf("expected period to be %v; got %v", expectedPeriod, got) 727 } 728 // Check the renter's renew window. 729 expectedRenewWindow := expectedPeriod / 2 730 if got := get.Settings.Allowance.RenewWindow; got != expectedRenewWindow { 731 t.Fatalf("expected renew window to be %v; got %v", expectedRenewWindow, got) 732 } 733 // Try an invalid period string. 734 allowanceValues.Set("period", "-1") 735 err = st.stdPostAPI("/renter", allowanceValues) 736 if err == nil || !strings.Contains(err.Error(), "unable to parse period") { 737 t.Errorf("expected error to begin with 'unable to parse period'; got %v", err) 738 } 739 // Try to set a zero renew window 740 allowanceValues.Set("period", "2") 741 allowanceValues.Set("renewwindow", "0") 742 err = st.stdPostAPI("/renter", allowanceValues) 743 if err == nil || err.Error() != contractor.ErrAllowanceZeroWindow.Error() { 744 t.Errorf("expected error to be %v, got %v", contractor.ErrAllowanceZeroWindow, err) 745 } 746 // Try to set a negative bandwidth limit 747 allowanceValues.Set("maxdownloadspeed", "-1") 748 allowanceValues.Set("renewwindow", "1") 749 err = st.stdPostAPI("/renter", allowanceValues) 750 if err == nil { 751 t.Errorf("expected error to be 'download/upload rate limit...'; got %v", err) 752 } 753 allowanceValues.Set("maxuploadspeed", "-1") 754 err = st.stdPostAPI("/renter", allowanceValues) 755 if err == nil { 756 t.Errorf("expected error to be 'download/upload rate limit...'; got %v", err) 757 } 758 } 759 760 // TestRenterLoadNonexistent checks that attempting to upload or download a 761 // nonexistent file triggers the appropriate error. 762 func TestRenterLoadNonexistent(t *testing.T) { 763 if testing.Short() { 764 t.SkipNow() 765 } 766 t.Parallel() 767 st, err := createServerTester(t.Name()) 768 if err != nil { 769 t.Fatal(err) 770 } 771 defer st.server.panicClose() 772 773 // Announce the host and start accepting contracts. 774 if err := st.announceHost(); err != nil { 775 t.Fatal(err) 776 } 777 if err = st.acceptContracts(); err != nil { 778 t.Fatal(err) 779 } 780 if err = st.setHostStorage(); err != nil { 781 t.Fatal(err) 782 } 783 784 // Set an allowance for the renter, allowing a contract to be formed. 785 allowanceValues := url.Values{} 786 allowanceValues.Set("funds", testFunds) 787 allowanceValues.Set("period", testPeriod) 788 allowanceValues.Set("renewwindow", testRenewWindow) 789 allowanceValues.Set("hosts", fmt.Sprint(recommendedHosts)) 790 if err = st.stdPostAPI("/renter", allowanceValues); err != nil { 791 t.Fatal(err) 792 } 793 794 // Try uploading a nonexistent file. 795 fakepath := filepath.Join(st.dir, "dne.dat") 796 uploadValues := url.Values{} 797 uploadValues.Set("source", fakepath) 798 err = st.stdPostAPI("/renter/upload/dne", uploadValues) 799 if err == nil { 800 t.Errorf("expected error when uploading nonexistent file") 801 } 802 803 // Try downloading a nonexistent file. 804 downpath := filepath.Join(st.dir, "dnedown.dat") 805 err = st.stdGetAPI("/renter/download/dne?destination=" + downpath) 806 hasPrefix := strings.HasPrefix(err.Error(), "download failed: no file with that path") 807 if err == nil || !hasPrefix { 808 t.Errorf("expected error to be 'download failed: no file with that path'; got %v instead", err) 809 } 810 811 // The renter's downloads queue should be empty. 812 var queue RenterDownloadQueue 813 if err = st.getAPI("/renter/downloads", &queue); err != nil { 814 t.Fatal(err) 815 } 816 if len(queue.Downloads) != 0 { 817 t.Fatalf("expected renter to have 0 downloads in the queue; got %v", len(queue.Downloads)) 818 } 819 } 820 821 // TestRenterHandlerRename checks that valid /renter/rename calls are 822 // successful, and that invalid calls fail with the appropriate error. 823 func TestRenterHandlerRename(t *testing.T) { 824 if testing.Short() { 825 t.SkipNow() 826 } 827 t.Parallel() 828 st, err := createServerTester(t.Name()) 829 if err != nil { 830 t.Fatal(err) 831 } 832 defer st.server.panicClose() 833 834 // Announce the host and start accepting contracts. 835 if err := st.announceHost(); err != nil { 836 t.Fatal(err) 837 } 838 if err = st.acceptContracts(); err != nil { 839 t.Fatal(err) 840 } 841 if err = st.setHostStorage(); err != nil { 842 t.Fatal(err) 843 } 844 845 // Try renaming a nonexistent file. 846 renameValues := url.Values{} 847 renameValues.Set("newsiapath", "newdne") 848 err = st.stdPostAPI("/renter/rename/dne", renameValues) 849 if err == nil || err.Error() != renter.ErrUnknownPath.Error() { 850 t.Errorf("expected error to be %v; got %v", renter.ErrUnknownPath, err) 851 } 852 853 // Set an allowance for the renter, allowing a contract to be formed. 854 allowanceValues := url.Values{} 855 allowanceValues.Set("funds", testFunds) 856 allowanceValues.Set("period", testPeriod) 857 allowanceValues.Set("renewwindow", testRenewWindow) 858 allowanceValues.Set("hosts", fmt.Sprint(recommendedHosts)) 859 if err = st.stdPostAPI("/renter", allowanceValues); err != nil { 860 t.Fatal(err) 861 } 862 863 // Block until the allowance has finished forming contracts. 864 err = build.Retry(50, time.Millisecond*250, func() error { 865 var rc RenterContracts 866 err = st.getAPI("/renter/contracts", &rc) 867 if err != nil { 868 return errors.New("couldn't get renter stats") 869 } 870 if len(rc.Contracts) != 1 { 871 return errors.New("no contracts") 872 } 873 return nil 874 }) 875 if err != nil { 876 t.Fatal("allowance setting failed") 877 } 878 879 // Create a file. 880 path1 := filepath.Join(st.dir, "test1.dat") 881 if err = createRandFile(path1, 512); err != nil { 882 t.Fatal(err) 883 } 884 885 // Upload to host. 886 uploadValues := url.Values{} 887 uploadValues.Set("source", path1) 888 if err = st.stdPostAPI("/renter/upload/test1", uploadValues); err != nil { 889 t.Fatal(err) 890 } 891 892 // Try renaming to an empty string. 893 renameValues.Set("newsiapath", "") 894 err = st.stdPostAPI("/renter/rename/test1", renameValues) 895 if err == nil || err.Error() != renter.ErrEmptyFilename.Error() { 896 t.Fatalf("expected error to be %v; got %v", renter.ErrEmptyFilename, err) 897 } 898 899 // Rename the file. 900 renameValues.Set("newsiapath", "newtest1") 901 if err = st.stdPostAPI("/renter/rename/test1", renameValues); err != nil { 902 t.Fatal(err) 903 } 904 905 // Should be able to continue uploading and downloading using the new name. 906 var rf RenterFiles 907 for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ { 908 st.getAPI("/renter/files", &rf) 909 time.Sleep(100 * time.Millisecond) 910 } 911 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 { 912 t.Fatal("upload is not succeeding:", rf.Files[0]) 913 } 914 err = st.stdGetAPI("/renter/download/newtest1?destination=" + filepath.Join(st.dir, "testdown2.dat")) 915 if err != nil { 916 t.Fatal(err) 917 } 918 919 // Create and upload another file. 920 path2 := filepath.Join(st.dir, "test2.dat") 921 if err = createRandFile(path2, 512); err != nil { 922 t.Fatal(err) 923 } 924 uploadValues.Set("source", path2) 925 if err = st.stdPostAPI("/renter/upload/test2", uploadValues); err != nil { 926 t.Fatal(err) 927 } 928 // Try renaming to a name that's already taken. 929 renameValues.Set("newsiapath", "newtest1") 930 err = st.stdPostAPI("/renter/rename/test2", renameValues) 931 if err == nil || err.Error() != renter.ErrPathOverload.Error() { 932 t.Errorf("expected error to be %v; got %v", renter.ErrPathOverload, err) 933 } 934 } 935 936 // TestRenterHandlerDelete checks that deleting a valid file from the renter 937 // goes as planned and that attempting to delete a nonexistent file fails with 938 // the appropriate error. 939 func TestRenterHandlerDelete(t *testing.T) { 940 if testing.Short() { 941 t.SkipNow() 942 } 943 t.Parallel() 944 st, err := createServerTester(t.Name()) 945 if err != nil { 946 t.Fatal(err) 947 } 948 defer st.server.panicClose() 949 950 // Announce the host and start accepting contracts. 951 if err := st.announceHost(); err != nil { 952 t.Fatal(err) 953 } 954 if err = st.acceptContracts(); err != nil { 955 t.Fatal(err) 956 } 957 if err = st.setHostStorage(); err != nil { 958 t.Fatal(err) 959 } 960 961 // Set an allowance for the renter, allowing a contract to be formed. 962 allowanceValues := url.Values{} 963 allowanceValues.Set("funds", testFunds) 964 allowanceValues.Set("period", testPeriod) 965 allowanceValues.Set("renewwindow", testRenewWindow) 966 allowanceValues.Set("hosts", fmt.Sprint(recommendedHosts)) 967 if err = st.stdPostAPI("/renter", allowanceValues); err != nil { 968 t.Fatal(err) 969 } 970 971 // Create a file. 972 path := filepath.Join(st.dir, "test.dat") 973 if err = createRandFile(path, 1024); err != nil { 974 t.Fatal(err) 975 } 976 977 // Upload to host. 978 uploadValues := url.Values{} 979 uploadValues.Set("source", path) 980 if err = st.stdPostAPI("/renter/upload/test", uploadValues); err != nil { 981 t.Fatal(err) 982 } 983 984 // Delete the file. 985 if err = st.stdPostAPI("/renter/delete/test", url.Values{}); err != nil { 986 t.Fatal(err) 987 } 988 989 // The renter's list of files should now be empty. 990 var files RenterFiles 991 if err = st.getAPI("/renter/files", &files); err != nil { 992 t.Fatal(err) 993 } 994 if len(files.Files) != 0 { 995 t.Fatalf("renter's list of files should be empty; got %v instead", files) 996 } 997 998 // Try deleting a nonexistent file. 999 err = st.stdPostAPI("/renter/delete/dne", url.Values{}) 1000 if err == nil || err.Error() != renter.ErrUnknownPath.Error() { 1001 t.Errorf("expected error to be %v, got %v", renter.ErrUnknownPath, err) 1002 } 1003 } 1004 1005 // Tests that the /renter/upload call checks for relative paths. 1006 func TestRenterRelativePathErrorUpload(t *testing.T) { 1007 if testing.Short() { 1008 t.SkipNow() 1009 } 1010 t.Parallel() 1011 st, err := createServerTester(t.Name()) 1012 if err != nil { 1013 t.Fatal(err) 1014 } 1015 defer st.server.panicClose() 1016 1017 // Announce the host and start accepting contracts. 1018 if err := st.announceHost(); err != nil { 1019 t.Fatal(err) 1020 } 1021 if err = st.acceptContracts(); err != nil { 1022 t.Fatal(err) 1023 } 1024 if err = st.setHostStorage(); err != nil { 1025 t.Fatal(err) 1026 } 1027 1028 // Set an allowance for the renter, allowing a contract to be formed. 1029 allowanceValues := url.Values{} 1030 allowanceValues.Set("funds", testFunds) 1031 allowanceValues.Set("period", testPeriod) 1032 allowanceValues.Set("renewwindow", testRenewWindow) 1033 allowanceValues.Set("hosts", fmt.Sprint(recommendedHosts)) 1034 if err = st.stdPostAPI("/renter", allowanceValues); err != nil { 1035 t.Fatal(err) 1036 } 1037 1038 renterUploadAbsoluteError := "source must be an absolute path" 1039 1040 // Create a file. 1041 path := filepath.Join(st.dir, "test.dat") 1042 if err = createRandFile(path, 1024); err != nil { 1043 t.Fatal(err) 1044 } 1045 1046 // This should fail. 1047 uploadValues := url.Values{} 1048 uploadValues.Set("source", "test.dat") 1049 if err = st.stdPostAPI("/renter/upload/test", uploadValues); err.Error() != renterUploadAbsoluteError { 1050 t.Fatal(err) 1051 } 1052 1053 // As should this. 1054 uploadValues = url.Values{} 1055 uploadValues.Set("source", "../test.dat") 1056 if err = st.stdPostAPI("/renter/upload/test", uploadValues); err.Error() != renterUploadAbsoluteError { 1057 t.Fatal(err) 1058 } 1059 1060 // This should succeed. 1061 uploadValues = url.Values{} 1062 uploadValues.Set("source", path) 1063 if err = st.stdPostAPI("/renter/upload/test", uploadValues); err != nil { 1064 t.Fatal(err) 1065 } 1066 } 1067 1068 // Tests that the /renter/download call checks for relative paths. 1069 func TestRenterRelativePathErrorDownload(t *testing.T) { 1070 if testing.Short() { 1071 t.SkipNow() 1072 } 1073 t.Parallel() 1074 st, err := createServerTester(t.Name()) 1075 if err != nil { 1076 t.Fatal(err) 1077 } 1078 defer st.server.panicClose() 1079 1080 // Announce the host and start accepting contracts. 1081 if err := st.announceHost(); err != nil { 1082 t.Fatal(err) 1083 } 1084 if err = st.acceptContracts(); err != nil { 1085 t.Fatal(err) 1086 } 1087 if err = st.setHostStorage(); err != nil { 1088 t.Fatal(err) 1089 } 1090 1091 // Set an allowance for the renter, allowing a contract to be formed. 1092 allowanceValues := url.Values{} 1093 allowanceValues.Set("funds", testFunds) 1094 allowanceValues.Set("period", testPeriod) 1095 allowanceValues.Set("renewwindow", testRenewWindow) 1096 allowanceValues.Set("hosts", fmt.Sprint(recommendedHosts)) 1097 if err = st.stdPostAPI("/renter", allowanceValues); err != nil { 1098 t.Fatal(err) 1099 } 1100 1101 renterDownloadAbsoluteError := "download failed: destination must be an absolute path" 1102 1103 // Create a file, and upload it. 1104 path := filepath.Join(st.dir, "test.dat") 1105 if err = createRandFile(path, 1024); err != nil { 1106 t.Fatal(err) 1107 } 1108 uploadValues := url.Values{} 1109 uploadValues.Set("source", path) 1110 if err = st.stdPostAPI("/renter/upload/test", uploadValues); err != nil { 1111 t.Fatal(err) 1112 } 1113 var rf RenterFiles 1114 for i := 0; i < 200 && (len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10); i++ { 1115 st.getAPI("/renter/files", &rf) 1116 time.Sleep(200 * time.Millisecond) 1117 } 1118 if len(rf.Files) != 1 || rf.Files[0].UploadProgress < 10 { 1119 t.Fatal("the uploading is not succeeding for some reason:", rf.Files[0]) 1120 } 1121 1122 // Use a relative destination, which should fail. 1123 downloadPath := "test1.dat" 1124 if err = st.stdGetAPI("/renter/download/test?destination=" + downloadPath); err.Error() != renterDownloadAbsoluteError { 1125 t.Fatal(err) 1126 } 1127 1128 // Relative destination stepping backwards should also fail. 1129 downloadPath = "../test1.dat" 1130 if err = st.stdGetAPI("/renter/download/test?destination=" + downloadPath); err.Error() != renterDownloadAbsoluteError { 1131 t.Fatal(err) 1132 } 1133 1134 // Long relative destination should also fail (just missing leading slash). 1135 downloadPath = filepath.Join(st.dir[1:], "test1.dat") 1136 err = st.stdGetAPI("/renter/download/test?destination=" + downloadPath) 1137 if err == nil { 1138 t.Fatal("expecting an error") 1139 } 1140 1141 // Full destination should succeed. 1142 downloadPath = filepath.Join(st.dir, "test1.dat") 1143 err = st.stdGetAPI("/renter/download/test?destination=" + downloadPath) 1144 if err != nil { 1145 t.Fatal("expecting an error") 1146 } 1147 } 1148 1149 // TestRenterPricesHandler checks that the prices command returns reasonable 1150 // values given the settings of the hosts. 1151 func TestRenterPricesHandler(t *testing.T) { 1152 if testing.Short() { 1153 t.SkipNow() 1154 } 1155 t.Parallel() 1156 st, err := createServerTester(t.Name()) 1157 if err != nil { 1158 t.Fatal(err) 1159 } 1160 defer st.server.panicClose() 1161 1162 // Announce the host and then get the calculated prices for when there is a 1163 // single host. 1164 var rpeSingle modules.RenterPriceEstimation 1165 if err = st.announceHost(); err != nil { 1166 t.Fatal(err) 1167 } 1168 if err = st.getAPI("/renter/prices", &rpeSingle); err != nil { 1169 t.Fatal(err) 1170 } 1171 1172 // Create several more hosts all using the default settings. 1173 stHost1, err := blankServerTester(t.Name() + " - Host 1") 1174 if err != nil { 1175 t.Fatal(err) 1176 } 1177 defer stHost1.panicClose() 1178 stHost2, err := blankServerTester(t.Name() + " - Host 2") 1179 if err != nil { 1180 t.Fatal(err) 1181 } 1182 defer stHost2.panicClose() 1183 1184 // Connect all the nodes and announce all of the hosts. 1185 sts := []*serverTester{st, stHost1, stHost2} 1186 err = fullyConnectNodes(sts) 1187 if err != nil { 1188 t.Fatal(err) 1189 } 1190 err = fundAllNodes(sts) 1191 if err != nil { 1192 t.Fatal(err) 1193 } 1194 err = announceAllHosts(sts) 1195 if err != nil { 1196 t.Fatal(err) 1197 } 1198 1199 // Grab the price estimates for when there are a bunch of hosts with the 1200 // same stats. 1201 var rpeMulti modules.RenterPriceEstimation 1202 if err = st.getAPI("/renter/prices", &rpeMulti); err != nil { 1203 t.Fatal(err) 1204 } 1205 1206 // Verify that the aggregate is the same. 1207 if !rpeMulti.DownloadTerabyte.Equals(rpeSingle.DownloadTerabyte) { 1208 t.Log(rpeMulti.DownloadTerabyte) 1209 t.Log(rpeSingle.DownloadTerabyte) 1210 t.Error("price changed from single to multi") 1211 } 1212 if rpeMulti.FormContracts.Equals(rpeSingle.FormContracts) { 1213 t.Error("price of forming contracts should have increased from single to multi") 1214 } 1215 if !rpeMulti.StorageTerabyteMonth.Equals(rpeSingle.StorageTerabyteMonth) { 1216 t.Error("price changed from single to multi") 1217 } 1218 if !rpeMulti.UploadTerabyte.Equals(rpeSingle.UploadTerabyte) { 1219 t.Error("price changed from single to multi") 1220 } 1221 } 1222 1223 // TestRenterPricesHandlerCheap checks that the prices command returns 1224 // reasonable values given the settings of the hosts. 1225 func TestRenterPricesHandlerCheap(t *testing.T) { 1226 if testing.Short() || !build.VLONG { 1227 t.SkipNow() 1228 } 1229 t.Parallel() 1230 st, err := createServerTester(t.Name()) 1231 if err != nil { 1232 t.Fatal(err) 1233 } 1234 defer st.server.panicClose() 1235 1236 // Announce the host and then get the calculated prices for when there is a 1237 // single host. 1238 var rpeSingle modules.RenterPriceEstimation 1239 if err = st.announceHost(); err != nil { 1240 t.Fatal(err) 1241 } 1242 if err = st.getAPI("/renter/prices", &rpeSingle); err != nil { 1243 t.Fatal(err) 1244 } 1245 1246 // Create several more hosts all using the default settings. 1247 stHost1, err := blankServerTester(t.Name() + " - Host 1") 1248 if err != nil { 1249 t.Fatal(err) 1250 } 1251 defer stHost1.panicClose() 1252 stHost2, err := blankServerTester(t.Name() + " - Host 2") 1253 if err != nil { 1254 t.Fatal(err) 1255 } 1256 defer stHost2.panicClose() 1257 1258 var hg HostGET 1259 err = st.getAPI("/host", &hg) 1260 if err != nil { 1261 t.Fatal(err) 1262 } 1263 err = stHost1.getAPI("/host", &hg) 1264 if err != nil { 1265 t.Fatal(err) 1266 } 1267 err = stHost2.getAPI("/host", &hg) 1268 if err != nil { 1269 t.Fatal(err) 1270 } 1271 1272 // Set host 5 to be cheaper than the rest by a substantial amount. This 1273 // should result in a reduction for the price estimation. 1274 vals := url.Values{} 1275 vals.Set("mincontractprice", "1") 1276 vals.Set("mindownloadbandwidthprice", "1") 1277 vals.Set("minstorageprice", "1") 1278 vals.Set("minuploadbandwidthprice", "1") 1279 err = stHost2.stdPostAPI("/host", vals) 1280 if err != nil { 1281 t.Fatal(err) 1282 } 1283 1284 // Connect all the nodes and announce all of the hosts. 1285 sts := []*serverTester{st, stHost1, stHost2} 1286 err = fullyConnectNodes(sts) 1287 if err != nil { 1288 t.Fatal(err) 1289 } 1290 err = fundAllNodes(sts) 1291 if err != nil { 1292 t.Fatal(err) 1293 } 1294 err = announceAllHosts(sts) 1295 if err != nil { 1296 t.Fatal(err) 1297 } 1298 1299 // Grab the price estimates for when there are a bunch of hosts with the 1300 // same stats. 1301 var rpeMulti modules.RenterPriceEstimation 1302 if err = st.announceHost(); err != nil { 1303 t.Fatal(err) 1304 } 1305 if err = st.getAPI("/renter/prices", &rpeMulti); err != nil { 1306 t.Fatal(err) 1307 } 1308 1309 // Verify that the aggregate is the same. 1310 if !(rpeMulti.DownloadTerabyte.Cmp(rpeSingle.DownloadTerabyte) < 0) { 1311 t.Log(rpeMulti.DownloadTerabyte) 1312 t.Log(rpeSingle.DownloadTerabyte) 1313 t.Error("price did not drop from single to multi") 1314 } 1315 if !(rpeMulti.FormContracts.Cmp(rpeSingle.FormContracts) < 0) { 1316 t.Error("price did not drop from single to multi") 1317 } 1318 if !(rpeMulti.StorageTerabyteMonth.Cmp(rpeSingle.StorageTerabyteMonth) < 0) { 1319 t.Error("price did not drop from single to multi") 1320 } 1321 if !(rpeMulti.UploadTerabyte.Cmp(rpeSingle.UploadTerabyte) < 0) { 1322 t.Error("price did not drop from single to multi") 1323 } 1324 } 1325 1326 // TestRenterPricesHandlerIgnorePricey checks that the prices command returns 1327 // reasonable values given the settings of the hosts. 1328 func TestRenterPricesHandlerIgnorePricey(t *testing.T) { 1329 if testing.Short() || !build.VLONG { 1330 t.SkipNow() 1331 } 1332 t.Parallel() 1333 st, err := createServerTester(t.Name()) 1334 if err != nil { 1335 t.Fatal(err) 1336 } 1337 defer st.server.panicClose() 1338 1339 // Announce the host and then get the calculated prices for when there is a 1340 // single host. 1341 var rpeSingle modules.RenterPriceEstimation 1342 if err = st.announceHost(); err != nil { 1343 t.Fatal(err) 1344 } 1345 if err = st.getAPI("/renter/prices", &rpeSingle); err != nil { 1346 t.Fatal(err) 1347 } 1348 1349 // Create several more hosts all using the default settings. 1350 stHost1, err := blankServerTester(t.Name() + " - Host 1") 1351 if err != nil { 1352 t.Fatal(err) 1353 } 1354 defer stHost1.panicClose() 1355 stHost2, err := blankServerTester(t.Name() + " - Host 2") 1356 if err != nil { 1357 t.Fatal(err) 1358 } 1359 defer stHost2.panicClose() 1360 stHost3, err := blankServerTester(t.Name() + " - Host 3") 1361 if err != nil { 1362 t.Fatal(err) 1363 } 1364 defer stHost3.panicClose() 1365 stHost4, err := blankServerTester(t.Name() + " - Host 4") 1366 if err != nil { 1367 t.Fatal(err) 1368 } 1369 defer stHost4.panicClose() 1370 stHost5, err := blankServerTester(t.Name() + " - Host 5") 1371 if err != nil { 1372 t.Fatal(err) 1373 } 1374 defer stHost5.panicClose() 1375 1376 // Set host 5 to be cheaper than the rest by a substantial amount. This 1377 // should result in a reduction for the price estimation. 1378 vals := url.Values{} 1379 vals.Set("mindownloadbandwidthprice", "100000000000000000000") 1380 vals.Set("mincontractprice", "1000000000000000000000000000") 1381 vals.Set("minstorageprice", "100000000000000000000") 1382 vals.Set("minuploadbandwidthprice", "100000000000000000000") 1383 err = stHost5.stdPostAPI("/host", vals) 1384 if err != nil { 1385 t.Fatal(err) 1386 } 1387 1388 // Connect all the nodes and announce all of the hosts. 1389 sts := []*serverTester{st, stHost1, stHost2, stHost3, stHost4, stHost5} 1390 err = fullyConnectNodes(sts) 1391 if err != nil { 1392 t.Fatal(err) 1393 } 1394 err = fundAllNodes(sts) 1395 if err != nil { 1396 t.Fatal(err) 1397 } 1398 err = announceAllHosts(sts) 1399 if err != nil { 1400 t.Fatal(err) 1401 } 1402 1403 // Grab the price estimates for when there are a bunch of hosts with the 1404 // same stats. 1405 var rpeMulti modules.RenterPriceEstimation 1406 if err = st.announceHost(); err != nil { 1407 t.Fatal(err) 1408 } 1409 if err = st.getAPI("/renter/prices", &rpeMulti); err != nil { 1410 t.Fatal(err) 1411 } 1412 1413 // Verify that the aggregate is the same - price should not have moved 1414 // because the expensive host will be ignored as there is only one. 1415 if !rpeMulti.DownloadTerabyte.Equals(rpeSingle.DownloadTerabyte) { 1416 t.Log(rpeMulti.DownloadTerabyte) 1417 t.Log(rpeSingle.DownloadTerabyte) 1418 t.Error("price changed from single to multi") 1419 } 1420 if !rpeMulti.FormContracts.Equals(rpeSingle.FormContracts) { 1421 t.Error("price changed from single to multi") 1422 } 1423 if !rpeMulti.StorageTerabyteMonth.Equals(rpeSingle.StorageTerabyteMonth) { 1424 t.Error("price changed from single to multi") 1425 } 1426 if !rpeMulti.UploadTerabyte.Equals(rpeSingle.UploadTerabyte) { 1427 t.Error("price changed from single to multi") 1428 } 1429 } 1430 1431 // TestRenterPricesHandlerPricey checks that the prices command returns 1432 // reasonable values given the settings of the hosts. 1433 func TestRenterPricesHandlerPricey(t *testing.T) { 1434 if testing.Short() { 1435 t.SkipNow() 1436 } 1437 t.Parallel() 1438 st, err := createServerTester(t.Name()) 1439 if err != nil { 1440 t.Fatal(err) 1441 } 1442 defer st.server.panicClose() 1443 1444 // Announce the host and then get the calculated prices for when there is a 1445 // single host. 1446 var rpeSingle modules.RenterPriceEstimation 1447 if err = st.announceHost(); err != nil { 1448 t.Fatal(err) 1449 } 1450 if err = st.getAPI("/renter/prices", &rpeSingle); err != nil { 1451 t.Fatal(err) 1452 } 1453 1454 // Create several more hosts all using the default settings. 1455 stHost1, err := blankServerTester(t.Name() + " - Host 1") 1456 if err != nil { 1457 t.Fatal(err) 1458 } 1459 stHost2, err := blankServerTester(t.Name() + " - Host 2") 1460 if err != nil { 1461 t.Fatal(err) 1462 } 1463 1464 var hg HostGET 1465 err = st.getAPI("/host", &hg) 1466 if err != nil { 1467 t.Fatal(err) 1468 } 1469 err = stHost1.getAPI("/host", &hg) 1470 if err != nil { 1471 t.Fatal(err) 1472 } 1473 err = stHost2.getAPI("/host", &hg) 1474 if err != nil { 1475 t.Fatal(err) 1476 } 1477 1478 // Set host 2 to be more expensive than the rest by a substantial amount. This 1479 // should result in an increase for the price estimation. 1480 vals := url.Values{} 1481 vals.Set("mindownloadbandwidthprice", "100000000000000000000") 1482 vals.Set("mincontractprice", "1000000000000000000000000") 1483 vals.Set("minstorageprice", "1000000000000000000000") 1484 vals.Set("minuploadbandwidthprice", "100000000000000000000") 1485 err = stHost2.stdPostAPI("/host", vals) 1486 if err != nil { 1487 t.Fatal(err) 1488 } 1489 1490 // Connect all the nodes and announce all of the hosts. 1491 sts := []*serverTester{st, stHost1, stHost2} 1492 err = fullyConnectNodes(sts) 1493 if err != nil { 1494 t.Fatal(err) 1495 } 1496 err = fundAllNodes(sts) 1497 if err != nil { 1498 t.Fatal(err) 1499 } 1500 err = announceAllHosts(sts) 1501 if err != nil { 1502 t.Fatal(err) 1503 } 1504 1505 // Grab the price estimates for when there are a bunch of hosts with the 1506 // same stats. 1507 var rpeMulti modules.RenterPriceEstimation 1508 if err = st.getAPI("/renter/prices", &rpeMulti); err != nil { 1509 t.Fatal(err) 1510 } 1511 1512 // Verify that the estimate for downloading, uploading, and storing 1513 // increased but the estimate for formaing contracts decreased. Forming 1514 // contracts decreases because with a more expensive host you are not able 1515 // to store as much data therefore reducing the amount of host collateral 1516 // you have to pay the siafund fee on 1517 if !(rpeMulti.DownloadTerabyte.Cmp(rpeSingle.DownloadTerabyte) > 0) { 1518 t.Log("Multi DownloadTerabyte cost:", rpeMulti.DownloadTerabyte.HumanString()) 1519 t.Log("Single DownloadTerabyte cost:", rpeSingle.DownloadTerabyte.HumanString()) 1520 t.Error("price did not increase from single to multi") 1521 } 1522 if rpeMulti.FormContracts.Cmp(rpeSingle.FormContracts) > 0 { 1523 t.Log("Multi FormContracts cost:", rpeMulti.FormContracts.HumanString()) 1524 t.Log("Single FormContracts cost:", rpeSingle.FormContracts.HumanString()) 1525 t.Error("price did not drop from single to multi") 1526 } 1527 if !(rpeMulti.StorageTerabyteMonth.Cmp(rpeSingle.StorageTerabyteMonth) > 0) { 1528 t.Log("Multi StorageTerabyteMonth cost:", rpeMulti.StorageTerabyteMonth.HumanString()) 1529 t.Log("Single StorageTerabyteMonth cost:", rpeSingle.StorageTerabyteMonth.HumanString()) 1530 t.Error("price did not increase from single to multi") 1531 } 1532 if !(rpeMulti.UploadTerabyte.Cmp(rpeSingle.UploadTerabyte) > 0) { 1533 t.Log("Multi UploadTerabyte cost:", rpeMulti.UploadTerabyte.HumanString()) 1534 t.Log("Single UploadTerabyte cost:", rpeSingle.UploadTerabyte.HumanString()) 1535 t.Error("price did not increase from single to multi") 1536 } 1537 } 1538 1539 // TestContractorHostRemoval checks that the contractor properly migrates away 1540 // from low quality hosts when there are higher quality hosts available. 1541 func TestContractorHostRemoval(t *testing.T) { 1542 // Create a renter and 2 hosts. Connect to the hosts and start uploading. 1543 if testing.Short() || !build.VLONG { 1544 t.SkipNow() 1545 } 1546 st, err := createServerTester(t.Name() + "renter") 1547 if err != nil { 1548 t.Fatal(err) 1549 } 1550 defer st.server.panicClose() 1551 stH1, err := blankServerTester(t.Name() + " - Host 1") 1552 if err != nil { 1553 t.Fatal(err) 1554 } 1555 defer stH1.server.Close() 1556 testGroup := []*serverTester{st, stH1} 1557 1558 // Connect the testers to eachother so that they are all on the same 1559 // blockchain. 1560 err = fullyConnectNodes(testGroup) 1561 if err != nil { 1562 t.Fatal(err) 1563 } 1564 // Make sure that every wallet has money in it. 1565 err = fundAllNodes(testGroup) 1566 if err != nil { 1567 t.Fatal(err) 1568 } 1569 1570 // Add storage to every host. 1571 err = addStorageToAllHosts(testGroup) 1572 if err != nil { 1573 t.Fatal(err) 1574 } 1575 // Raise the prices significantly for the two hosts. 1576 raisedPrice := url.Values{} 1577 raisedPrice.Set("mincontractprice", "5000000000000000000000000000") // 5 KS 1578 raisedPrice.Set("period", testPeriod) 1579 err = st.stdPostAPI("/host", raisedPrice) 1580 if err != nil { 1581 t.Fatal(err) 1582 } 1583 err = stH1.stdPostAPI("/host", raisedPrice) 1584 if err != nil { 1585 t.Fatal(err) 1586 } 1587 // Announce the hosts. 1588 err = announceAllHosts(testGroup) 1589 if err != nil { 1590 t.Fatal(err) 1591 } 1592 1593 // Set an allowance with two hosts. 1594 allowanceValues := url.Values{} 1595 allowanceValues.Set("funds", "500000000000000000000000000000") // 500k SC 1596 allowanceValues.Set("hosts", "2") 1597 allowanceValues.Set("period", "15") 1598 err = st.stdPostAPI("/renter", allowanceValues) 1599 if err != nil { 1600 t.Fatal(err) 1601 } 1602 1603 // Create a file to upload. 1604 filesize := int(100) 1605 path := filepath.Join(st.dir, "test.dat") 1606 err = createRandFile(path, filesize) 1607 if err != nil { 1608 t.Fatal(err) 1609 } 1610 origBytes, err := ioutil.ReadFile(path) 1611 if err != nil { 1612 t.Fatal(err) 1613 } 1614 1615 // upload the file 1616 uploadValues := url.Values{} 1617 uploadValues.Set("source", path) 1618 uploadValues.Set("datapieces", "1") 1619 uploadValues.Set("paritypieces", "1") 1620 err = st.stdPostAPI("/renter/upload/test", uploadValues) 1621 if err != nil { 1622 t.Fatal(err) 1623 } 1624 1625 // redundancy should reach 2 1626 var rf RenterFiles 1627 err = build.Retry(120, 250*time.Millisecond, func() error { 1628 st.getAPI("/renter/files", &rf) 1629 if len(rf.Files) >= 1 && rf.Files[0].Redundancy == 2 { 1630 return nil 1631 } 1632 return errors.New("file not uploaded") 1633 }) 1634 if err != nil { 1635 t.Fatal(err) 1636 } 1637 1638 // verify we can download 1639 downloadPath := filepath.Join(st.dir, "test-downloaded-verify.dat") 1640 err = st.stdGetAPI("/renter/download/test?destination=" + downloadPath) 1641 if err != nil { 1642 t.Fatal(err) 1643 } 1644 downloadBytes, err := ioutil.ReadFile(downloadPath) 1645 if err != nil { 1646 t.Fatal(err) 1647 } 1648 if !bytes.Equal(downloadBytes, origBytes) { 1649 t.Fatal("downloaded file and uploaded file do not match") 1650 } 1651 1652 // Get the values of the first and second contract. 1653 var rc RenterContracts 1654 err = st.getAPI("/renter/contracts", &rc) 1655 if err != nil { 1656 t.Fatal(err) 1657 } 1658 if len(rc.Contracts) != 2 { 1659 t.Fatal("wrong contract count") 1660 } 1661 rc1Host := rc.Contracts[0].HostPublicKey.String() 1662 rc2Host := rc.Contracts[1].HostPublicKey.String() 1663 1664 // Add 3 new hosts that will be competing with the expensive hosts. 1665 stH2, err := blankServerTester(t.Name() + " - Host 2") 1666 if err != nil { 1667 t.Fatal(err) 1668 } 1669 defer stH2.server.Close() 1670 stH3, err := blankServerTester(t.Name() + " - Host 3") 1671 if err != nil { 1672 t.Fatal(err) 1673 } 1674 defer stH3.server.Close() 1675 stH4, err := blankServerTester(t.Name() + " - Host 4") 1676 if err != nil { 1677 t.Fatal(err) 1678 } 1679 defer stH4.server.Close() 1680 testGroup = []*serverTester{st, stH1, stH2, stH3, stH4} 1681 // Connect the testers to eachother so that they are all on the same 1682 // blockchain. 1683 err = fullyConnectNodes(testGroup) 1684 if err != nil { 1685 t.Fatal(err) 1686 } 1687 // Make sure that every wallet has money in it. 1688 err = fundAllNodes(testGroup) 1689 if err != nil { 1690 t.Fatal(err) 1691 } 1692 // Add storage to every host. 1693 err = addStorageToAllHosts([]*serverTester{stH2, stH3, stH4}) 1694 if err != nil { 1695 t.Fatal(err) 1696 } 1697 // Announce the hosts. 1698 err = announceAllHosts(testGroup) 1699 if err != nil { 1700 t.Fatal(err) 1701 } 1702 1703 // Block until the hostdb reaches five hosts. 1704 err = build.Retry(150, time.Millisecond*250, func() error { 1705 var ah HostdbActiveGET 1706 err = st.getAPI("/hostdb/active", &ah) 1707 if err != nil { 1708 return err 1709 } 1710 if len(ah.Hosts) < 5 { 1711 return errors.New("new hosts never appeared in hostdb") 1712 } 1713 return nil 1714 }) 1715 if err != nil { 1716 t.Fatal(err) 1717 } 1718 1719 // Mine a block to trigger a second run of threadedContractMaintenance. 1720 _, err = st.miner.AddBlock() 1721 if err != nil { 1722 t.Fatal(err) 1723 } 1724 1725 // Verify that st and stH1 are dropped in favor of the newer, better hosts. 1726 err = build.Retry(150, time.Millisecond*250, func() error { 1727 var newContracts int 1728 err = st.getAPI("/renter/contracts", &rc) 1729 if err != nil { 1730 return errors.New("couldn't get renter stats") 1731 } 1732 hostMap := make(map[string]struct{}) 1733 hostMap[rc1Host] = struct{}{} 1734 hostMap[rc2Host] = struct{}{} 1735 for _, contract := range rc.Contracts { 1736 _, exists := hostMap[contract.HostPublicKey.String()] 1737 if !exists { 1738 newContracts++ 1739 hostMap[contract.HostPublicKey.String()] = struct{}{} 1740 } 1741 } 1742 if newContracts != 2 { 1743 return fmt.Errorf("not the right number of new contracts: %v", newContracts) 1744 } 1745 return nil 1746 }) 1747 if err != nil { 1748 t.Fatal(err) 1749 } 1750 1751 // Block until redundancy is restored to 2. 1752 err = build.Retry(120, 250*time.Millisecond, func() error { 1753 st.getAPI("/renter/files", &rf) 1754 if len(rf.Files) == 1 && rf.Files[0].Redundancy == 2 { 1755 return nil 1756 } 1757 return errors.New("file not uploaded to full redundancy") 1758 }) 1759 if err != nil { 1760 t.Fatal(err) 1761 } 1762 1763 // Grab the old contracts, then mine blocks to trigger a renew, and then 1764 // wait until the renew is complete. 1765 err = st.getAPI("/renter/contracts", &rc) 1766 if err != nil { 1767 t.Fatal(err) 1768 } 1769 // Check the amount of data in each contract. 1770 for _, contract := range rc.Contracts { 1771 if contract.Size != modules.SectorSize { 1772 t.Error("Each contrat should have 1 sector:", contract.Size, contract.ID) 1773 } 1774 } 1775 // Mine blocks to force a contract renewal. 1776 for i := 0; i < 11; i++ { 1777 _, err := st.miner.AddBlock() 1778 if err != nil { 1779 t.Fatal(err) 1780 } 1781 _, err = synchronizationCheck(testGroup) 1782 if err != nil { 1783 t.Fatal(err) 1784 } 1785 // Sleep to give the contractor some time to perform the renew. 1786 time.Sleep(time.Millisecond * 100) 1787 } 1788 // Give the renter time to renew. Two of the contracts should renew. 1789 var rc2 RenterContracts 1790 err = build.Retry(50, time.Millisecond*250, func() error { 1791 err = st.getAPI("/renter/contracts", &rc2) 1792 if err != nil { 1793 return errors.New("couldn't get renter stats") 1794 } 1795 1796 // Check that at least 2 contracts are different between rc and rc2. 1797 tracker := make(map[types.FileContractID]struct{}) 1798 // Add all the contracts. 1799 for _, contract := range rc.Contracts { 1800 tracker[contract.ID] = struct{}{} 1801 } 1802 // Count the number of contracts that were not seen in the previous 1803 // batch of contracts, and check that the new contracts are not with the 1804 // expensive hosts. 1805 var unseen int 1806 for _, contract := range rc2.Contracts { 1807 _, exists := tracker[contract.ID] 1808 if !exists { 1809 unseen++ 1810 tracker[contract.ID] = struct{}{} 1811 if contract.HostPublicKey.String() == rc1Host || contract.HostPublicKey.String() == rc2Host { 1812 return errors.New("the wrong contracts are being renewed") 1813 } 1814 } 1815 } 1816 if unseen != 2 { 1817 return fmt.Errorf("the wrong number of contracts seem to be getting renewed") 1818 } 1819 return nil 1820 }) 1821 if err != nil { 1822 t.Fatal(err) 1823 } 1824 // The renewing process should not have resulted in additional data being 1825 // uploaded - it should be the same data in the contracts. 1826 for _, contract := range rc2.Contracts { 1827 if contract.Size != modules.SectorSize { 1828 t.Error("Contract has the wrong size:", contract.Size) 1829 } 1830 } 1831 1832 // Try again to download the file we uploaded. It should still be 1833 // retrievable. 1834 downloadPath2 := filepath.Join(st.dir, "test-downloaded-verify-2.dat") 1835 err = st.stdGetAPI("/renter/download/test?destination=" + downloadPath2) 1836 if err != nil { 1837 t.Fatal(err) 1838 } 1839 downloadBytes2, err := ioutil.ReadFile(downloadPath2) 1840 if err != nil { 1841 t.Fatal(err) 1842 } 1843 if !bytes.Equal(downloadBytes2, origBytes) { 1844 t.Fatal("downloaded file and uploaded file do not match") 1845 } 1846 1847 // Mine out another set of the blocks so that the bad contracts expire. 1848 for i := 0; i < 11; i++ { 1849 _, err := st.miner.AddBlock() 1850 if err != nil { 1851 t.Fatal(err) 1852 } 1853 _, err = synchronizationCheck(testGroup) 1854 if err != nil { 1855 t.Fatal(err) 1856 } 1857 time.Sleep(time.Millisecond * 100) 1858 } 1859 1860 // Should be back down to 2 contracts now, with the new hosts. Verify that 1861 // st and stH1 are dropped in favor of the newer, better hosts. The 1862 // contracts should also have data in them. 1863 err = build.Retry(50, time.Millisecond*250, func() error { 1864 err = st.getAPI("/renter/contracts", &rc) 1865 if err != nil { 1866 return errors.New("couldn't get renter stats") 1867 } 1868 if len(rc.Contracts) != 2 { 1869 return fmt.Errorf("renewing seems to have failed: %v", len(rc.Contracts)) 1870 } 1871 return nil 1872 }) 1873 if err != nil { 1874 t.Fatal(err) 1875 } 1876 if rc.Contracts[0].HostPublicKey.String() == rc1Host || rc.Contracts[0].HostPublicKey.String() == rc2Host { 1877 t.Error("renter is renewing the wrong contracts", rc.Contracts[0].HostPublicKey.String()) 1878 } 1879 if rc.Contracts[1].HostPublicKey.String() == rc1Host || rc.Contracts[1].HostPublicKey.String() == rc2Host { 1880 t.Error("renter is renewing the wrong contracts", rc.Contracts[1].HostPublicKey.String()) 1881 } 1882 // The renewing process should not have resulted in additional data being 1883 // uploaded - it should be the same data in the contracts. 1884 for _, contract := range rc.Contracts { 1885 if contract.Size != modules.SectorSize { 1886 t.Error("Contract has the wrong size:", contract.Size) 1887 } 1888 } 1889 // Redundancy should still be 2. 1890 err = build.Retry(120, 250*time.Millisecond, func() error { 1891 st.getAPI("/renter/files", &rf) 1892 if len(rf.Files) >= 1 && rf.Files[0].Redundancy == 2 { 1893 return nil 1894 } 1895 return errors.New("file not uploaded to full redundancy") 1896 }) 1897 if err != nil { 1898 t.Fatal(err, "::", rf.Files[0].Redundancy) 1899 } 1900 1901 // Try again to download the file we uploaded. It should still be 1902 // retrievable. 1903 downloadPath3 := filepath.Join(st.dir, "test-downloaded-verify-3.dat") 1904 err = st.stdGetAPI("/renter/download/test?destination=" + downloadPath3) 1905 if err != nil { 1906 t.Error("Final download has failed:", err) 1907 } 1908 downloadBytes3, err := ioutil.ReadFile(downloadPath3) 1909 if err != nil { 1910 t.Error(err) 1911 } 1912 if !bytes.Equal(downloadBytes3, origBytes) { 1913 t.Error("downloaded file and uploaded file do not match") 1914 } 1915 } 1916 1917 // TestExhaustedContracts verifies that the contractor renews contracts which 1918 // run out of funds before the period elapses. 1919 func TestExhaustedContracts(t *testing.T) { 1920 if testing.Short() || !build.VLONG { 1921 t.SkipNow() 1922 } 1923 t.Parallel() 1924 st, err := createServerTester(t.Name()) 1925 if err != nil { 1926 t.Fatal(err) 1927 } 1928 defer st.server.panicClose() 1929 1930 // set a very high price for the host so we use up the entire funds of the 1931 // contract 1932 err = st.acceptContracts() 1933 if err != nil { 1934 t.Fatal(err) 1935 } 1936 err = st.setHostStorage() 1937 if err != nil { 1938 t.Fatal(err) 1939 } 1940 // Increase the storage and bandwidth prices to drain the allowance faster. 1941 settings := st.host.InternalSettings() 1942 settings.MinUploadBandwidthPrice = settings.MinUploadBandwidthPrice.Mul64(2) 1943 settings.MinStoragePrice = settings.MinUploadBandwidthPrice.Mul64(2) 1944 settings.MaxDuration = 1e6 // set a high max duration to allow an expensive storage price 1945 err = st.host.SetInternalSettings(settings) 1946 if err != nil { 1947 t.Fatal(err) 1948 } 1949 err = st.announceHost() 1950 if err != nil { 1951 t.Fatal(err) 1952 } 1953 1954 // Set an allowance for the renter, allowing a contract to be formed. 1955 allowanceValues := url.Values{} 1956 testPeriod := "950000" // large period to cause an expensive test, exhausting the allowance faster 1957 allowanceValues.Set("funds", types.SiacoinPrecision.Mul64(10).String()) 1958 allowanceValues.Set("period", testPeriod) 1959 err = st.stdPostAPI("/renter", allowanceValues) 1960 if err != nil { 1961 t.Fatal(err) 1962 } 1963 1964 // wait until we have a contract 1965 err = build.Retry(500, time.Millisecond*50, func() error { 1966 if len(st.renter.Contracts()) >= 1 { 1967 return nil 1968 } 1969 return errors.New("no renter contracts") 1970 }) 1971 if err != nil { 1972 t.Fatal(err) 1973 } 1974 initialContract := st.renter.Contracts()[0] 1975 1976 // upload a file. the high upload cost will cause the underlying contract to 1977 // require premature renewal. If premature renewal never happens, the upload 1978 // will never complete. 1979 path := filepath.Join(st.dir, "randUploadFile") 1980 size := int(modules.SectorSize * 75) 1981 err = createRandFile(path, size) 1982 if err != nil { 1983 t.Fatal(err) 1984 } 1985 uploadValues := url.Values{} 1986 uploadValues.Set("source", path) 1987 uploadValues.Set("renew", "true") 1988 uploadValues.Set("datapieces", "1") 1989 uploadValues.Set("paritypieces", "1") 1990 err = st.stdPostAPI("/renter/upload/"+filepath.Base(path), uploadValues) 1991 if err != nil { 1992 t.Fatal(err) 1993 } 1994 err = build.Retry(100, time.Millisecond*500, func() error { 1995 // mine blocks each iteration to trigger contract maintenance 1996 _, err = st.miner.AddBlock() 1997 if err != nil { 1998 t.Fatal(err) 1999 } 2000 2001 st.miner.AddBlock() 2002 if !st.renter.FileList()[0].Available { 2003 return errors.New("file did not complete uploading") 2004 } 2005 return nil 2006 }) 2007 if err != nil { 2008 t.Fatal(err) 2009 } 2010 2011 // verify that the window did not change and the contract ID did change 2012 newContract := st.renter.Contracts()[0] 2013 endHeight := newContract.EndHeight 2014 if newContract.ID == initialContract.ID { 2015 t.Error("renew did not occur") 2016 } 2017 if endHeight != initialContract.EndHeight { 2018 t.Error("contract end height changed, wanted", initialContract.EndHeight, "got", endHeight) 2019 } 2020 } 2021 2022 // TestAdversarialPriceRenewal verifies that host cannot maliciously raise 2023 // their storage price in order to trigger a premature file contract renewal. 2024 func TestAdversarialPriceRenewal(t *testing.T) { 2025 if testing.Short() || !build.VLONG { 2026 t.SkipNow() 2027 } 2028 t.Parallel() 2029 st, err := createServerTester(t.Name()) 2030 if err != nil { 2031 t.Fatal(err) 2032 } 2033 defer st.server.panicClose() 2034 2035 // announce our host 2036 err = st.acceptContracts() 2037 if err != nil { 2038 t.Fatal(err) 2039 } 2040 err = st.setHostStorage() 2041 if err != nil { 2042 t.Fatal(err) 2043 } 2044 err = st.announceHost() 2045 if err != nil { 2046 t.Fatal(err) 2047 } 2048 2049 // Set an allowance for the renter, allowing a contract to be formed. 2050 allowanceValues := url.Values{} 2051 testPeriod := "10000" 2052 allowanceValues.Set("funds", types.SiacoinPrecision.Mul64(10000).String()) 2053 allowanceValues.Set("period", testPeriod) 2054 err = st.stdPostAPI("/renter", allowanceValues) 2055 if err != nil { 2056 t.Fatal(err) 2057 } 2058 2059 // wait until we have a contract 2060 err = build.Retry(500, time.Millisecond*50, func() error { 2061 if len(st.renter.Contracts()) >= 1 { 2062 return nil 2063 } 2064 return errors.New("no renter contracts") 2065 }) 2066 if err != nil { 2067 t.Fatal(err) 2068 } 2069 2070 // upload a file 2071 path := filepath.Join(st.dir, "randUploadFile") 2072 size := int(modules.SectorSize * 50) 2073 err = createRandFile(path, size) 2074 if err != nil { 2075 t.Fatal(err) 2076 } 2077 uploadValues := url.Values{} 2078 uploadValues.Set("source", path) 2079 uploadValues.Set("renew", "true") 2080 uploadValues.Set("datapieces", "1") 2081 uploadValues.Set("paritypieces", "1") 2082 err = st.stdPostAPI("/renter/upload/"+filepath.Base(path), uploadValues) 2083 if err != nil { 2084 t.Fatal(err) 2085 } 2086 err = build.Retry(100, time.Millisecond*500, func() error { 2087 // mine blocks each iteration to trigger contract maintenance 2088 _, err = st.miner.AddBlock() 2089 if err != nil { 2090 t.Fatal(err) 2091 } 2092 if !st.renter.FileList()[0].Available { 2093 return errors.New("file did not complete uploading") 2094 } 2095 return nil 2096 }) 2097 if err != nil { 2098 t.Fatal(err) 2099 } 2100 initialID := st.renter.Contracts()[0].ID 2101 2102 // jack up the host's storage price to try to trigger a renew 2103 settings := st.host.InternalSettings() 2104 settings.MinStoragePrice = settings.MinStoragePrice.Mul64(10800000000) 2105 err = st.host.SetInternalSettings(settings) 2106 if err != nil { 2107 t.Fatal(err) 2108 } 2109 err = st.announceHost() 2110 if err != nil { 2111 t.Fatal(err) 2112 } 2113 2114 for i := 0; i < 100; i++ { 2115 _, err = st.miner.AddBlock() 2116 if err != nil { 2117 t.Fatal(err) 2118 } 2119 if st.renter.Contracts()[0].ID != initialID { 2120 t.Fatal("changing host price caused renew") 2121 } 2122 time.Sleep(time.Millisecond * 100) 2123 } 2124 }