gitlab.com/jokerrs1/Sia@v1.3.2/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 "github.com/NebulousLabs/Sia/build" 18 "github.com/NebulousLabs/Sia/modules" 19 "github.com/NebulousLabs/Sia/modules/renter" 20 "github.com/NebulousLabs/Sia/modules/renter/contractor" 21 "github.com/NebulousLabs/Sia/types" 22 "github.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 = 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 = 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 // Anounce 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.ContractSpending; 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 // Anounce 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 || err.Error() != "download/upload rate limit can't be below 0" { 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 || err.Error() != "download/upload rate limit can't be below 0" { 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 // Anounce 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 // Anounce 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 // Anounce 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 // Anounce 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 // Anounce 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 changed 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 5 to be cheaper than the rest by a substantial amount. This 1479 // should result in a reduction for the price estimation. 1480 vals := url.Values{} 1481 vals.Set("mindownloadbandwidthprice", "100000000000000000000") 1482 vals.Set("mincontractprice", "1000000000000000000000000000") 1483 vals.Set("minstorageprice", "100000000000000000000") 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 aggregate is the same. 1513 if !(rpeMulti.DownloadTerabyte.Cmp(rpeSingle.DownloadTerabyte) > 0) { 1514 t.Error("price did not drop from single to multi") 1515 } 1516 if !(rpeMulti.FormContracts.Cmp(rpeSingle.FormContracts) > 0) { 1517 t.Log(rpeMulti.FormContracts) 1518 t.Log(rpeSingle.FormContracts) 1519 t.Error("price did not drop from single to multi") 1520 } 1521 if !(rpeMulti.StorageTerabyteMonth.Cmp(rpeSingle.StorageTerabyteMonth) > 0) { 1522 t.Error("price did not drop from single to multi") 1523 } 1524 if !(rpeMulti.UploadTerabyte.Cmp(rpeSingle.UploadTerabyte) > 0) { 1525 t.Error("price did not drop from single to multi") 1526 } 1527 } 1528 1529 // TestContractorHostRemoval checks that the contractor properly migrates away 1530 // from low quality hosts when there are higher quality hosts available. 1531 func TestContractorHostRemoval(t *testing.T) { 1532 // Create a renter and 2 hosts. Connect to the hosts and start uploading. 1533 if testing.Short() || !build.VLONG { 1534 t.SkipNow() 1535 } 1536 st, err := createServerTester(t.Name() + "renter") 1537 if err != nil { 1538 t.Fatal(err) 1539 } 1540 defer st.server.panicClose() 1541 stH1, err := blankServerTester(t.Name() + " - Host 1") 1542 if err != nil { 1543 t.Fatal(err) 1544 } 1545 defer stH1.server.Close() 1546 testGroup := []*serverTester{st, stH1} 1547 1548 // Connect the testers to eachother so that they are all on the same 1549 // blockchain. 1550 err = fullyConnectNodes(testGroup) 1551 if err != nil { 1552 t.Fatal(err) 1553 } 1554 // Make sure that every wallet has money in it. 1555 err = fundAllNodes(testGroup) 1556 if err != nil { 1557 t.Fatal(err) 1558 } 1559 1560 // Add storage to every host. 1561 err = addStorageToAllHosts(testGroup) 1562 if err != nil { 1563 t.Fatal(err) 1564 } 1565 // Raise the prices significantly for the two hosts. 1566 raisedPrice := url.Values{} 1567 raisedPrice.Set("mincontractprice", "5000000000000000000000000000") // 5 KS 1568 raisedPrice.Set("period", testPeriod) 1569 err = st.stdPostAPI("/host", raisedPrice) 1570 if err != nil { 1571 t.Fatal(err) 1572 } 1573 err = stH1.stdPostAPI("/host", raisedPrice) 1574 if err != nil { 1575 t.Fatal(err) 1576 } 1577 // Anounce the hosts. 1578 err = announceAllHosts(testGroup) 1579 if err != nil { 1580 t.Fatal(err) 1581 } 1582 1583 // Set an allowance with two hosts. 1584 allowanceValues := url.Values{} 1585 allowanceValues.Set("funds", "500000000000000000000000000000") // 500k SC 1586 allowanceValues.Set("hosts", "2") 1587 allowanceValues.Set("period", "15") 1588 err = st.stdPostAPI("/renter", allowanceValues) 1589 if err != nil { 1590 t.Fatal(err) 1591 } 1592 1593 // Create a file to upload. 1594 filesize := int(100) 1595 path := filepath.Join(st.dir, "test.dat") 1596 err = createRandFile(path, filesize) 1597 if err != nil { 1598 t.Fatal(err) 1599 } 1600 origBytes, err := ioutil.ReadFile(path) 1601 if err != nil { 1602 t.Fatal(err) 1603 } 1604 1605 // upload the file 1606 uploadValues := url.Values{} 1607 uploadValues.Set("source", path) 1608 uploadValues.Set("datapieces", "1") 1609 uploadValues.Set("paritypieces", "1") 1610 err = st.stdPostAPI("/renter/upload/test", uploadValues) 1611 if err != nil { 1612 t.Fatal(err) 1613 } 1614 1615 // redundancy should reach 2 1616 var rf RenterFiles 1617 err = retry(120, 250*time.Millisecond, func() error { 1618 st.getAPI("/renter/files", &rf) 1619 if len(rf.Files) >= 1 && rf.Files[0].Redundancy == 2 { 1620 return nil 1621 } 1622 return errors.New("file not uploaded") 1623 }) 1624 if err != nil { 1625 t.Fatal(err) 1626 } 1627 1628 // verify we can download 1629 downloadPath := filepath.Join(st.dir, "test-downloaded-verify.dat") 1630 err = st.stdGetAPI("/renter/download/test?destination=" + downloadPath) 1631 if err != nil { 1632 t.Fatal(err) 1633 } 1634 downloadBytes, err := ioutil.ReadFile(downloadPath) 1635 if err != nil { 1636 t.Fatal(err) 1637 } 1638 if !bytes.Equal(downloadBytes, origBytes) { 1639 t.Fatal("downloaded file and uploaded file do not match") 1640 } 1641 1642 // Get the values of the first and second contract. 1643 var rc RenterContracts 1644 err = st.getAPI("/renter/contracts", &rc) 1645 if err != nil { 1646 t.Fatal(err) 1647 } 1648 if len(rc.Contracts) != 2 { 1649 t.Fatal("wrong contract count") 1650 } 1651 rc1Host := rc.Contracts[0].HostPublicKey.String() 1652 rc2Host := rc.Contracts[1].HostPublicKey.String() 1653 1654 // Add 3 new hosts that will be competing with the expensive hosts. 1655 stH2, err := blankServerTester(t.Name() + " - Host 2") 1656 if err != nil { 1657 t.Fatal(err) 1658 } 1659 defer stH2.server.Close() 1660 stH3, err := blankServerTester(t.Name() + " - Host 3") 1661 if err != nil { 1662 t.Fatal(err) 1663 } 1664 defer stH3.server.Close() 1665 stH4, err := blankServerTester(t.Name() + " - Host 4") 1666 if err != nil { 1667 t.Fatal(err) 1668 } 1669 defer stH4.server.Close() 1670 testGroup = []*serverTester{st, stH1, stH2, stH3, stH4} 1671 // Connect the testers to eachother so that they are all on the same 1672 // blockchain. 1673 err = fullyConnectNodes(testGroup) 1674 if err != nil { 1675 t.Fatal(err) 1676 } 1677 // Make sure that every wallet has money in it. 1678 err = fundAllNodes(testGroup) 1679 if err != nil { 1680 t.Fatal(err) 1681 } 1682 // Add storage to every host. 1683 err = addStorageToAllHosts([]*serverTester{stH2, stH3, stH4}) 1684 if err != nil { 1685 t.Fatal(err) 1686 } 1687 // Anounce the hosts. 1688 err = announceAllHosts(testGroup) 1689 if err != nil { 1690 t.Fatal(err) 1691 } 1692 1693 // Block until the hostdb reaches five hosts. 1694 err = build.Retry(150, time.Millisecond*250, func() error { 1695 var ah HostdbActiveGET 1696 err = st.getAPI("/hostdb/active", &ah) 1697 if err != nil { 1698 return err 1699 } 1700 if len(ah.Hosts) < 5 { 1701 return errors.New("new hosts never appeared in hostdb") 1702 } 1703 return nil 1704 }) 1705 if err != nil { 1706 t.Fatal(err) 1707 } 1708 1709 // Mine a block to trigger a second run of threadedContractMaintenance. 1710 _, err = st.miner.AddBlock() 1711 if err != nil { 1712 t.Fatal(err) 1713 } 1714 1715 // Verify that st and stH1 are dropped in favor of the newer, better hosts. 1716 err = build.Retry(150, time.Millisecond*250, func() error { 1717 var newContracts int 1718 err = st.getAPI("/renter/contracts", &rc) 1719 if err != nil { 1720 return errors.New("couldn't get renter stats") 1721 } 1722 hostMap := make(map[string]struct{}) 1723 hostMap[rc1Host] = struct{}{} 1724 hostMap[rc2Host] = struct{}{} 1725 for _, contract := range rc.Contracts { 1726 _, exists := hostMap[contract.HostPublicKey.String()] 1727 if !exists { 1728 newContracts++ 1729 hostMap[contract.HostPublicKey.String()] = struct{}{} 1730 } 1731 } 1732 if newContracts != 2 { 1733 return fmt.Errorf("not the right number of new contracts: %v", newContracts) 1734 } 1735 return nil 1736 }) 1737 if err != nil { 1738 t.Fatal(err) 1739 } 1740 1741 // Block until redundancy is restored to 2. 1742 err = retry(120, 250*time.Millisecond, func() error { 1743 st.getAPI("/renter/files", &rf) 1744 if len(rf.Files) == 1 && rf.Files[0].Redundancy == 2 { 1745 return nil 1746 } 1747 return errors.New("file not uploaded to full redundancy") 1748 }) 1749 if err != nil { 1750 t.Fatal(err) 1751 } 1752 1753 // Grab the old contracts, then mine blocks to trigger a renew, and then 1754 // wait until the renew is complete. 1755 err = st.getAPI("/renter/contracts", &rc) 1756 if err != nil { 1757 t.Fatal(err) 1758 } 1759 // Check the amount of data in each contract. 1760 for _, contract := range rc.Contracts { 1761 if contract.Size != modules.SectorSize { 1762 t.Error("Each contrat should have 1 sector:", contract.Size, contract.ID) 1763 } 1764 } 1765 // Mine blocks to force a contract renewal. 1766 for i := 0; i < 11; i++ { 1767 _, err := st.miner.AddBlock() 1768 if err != nil { 1769 t.Fatal(err) 1770 } 1771 _, err = synchronizationCheck(testGroup) 1772 if err != nil { 1773 t.Fatal(err) 1774 } 1775 // Sleep to give the contractor some time to perform the renew. 1776 time.Sleep(time.Millisecond * 100) 1777 } 1778 // Give the renter time to renew. Two of the contracts should renew. 1779 var rc2 RenterContracts 1780 err = build.Retry(50, time.Millisecond*250, func() error { 1781 err = st.getAPI("/renter/contracts", &rc2) 1782 if err != nil { 1783 return errors.New("couldn't get renter stats") 1784 } 1785 1786 // Check that at least 2 contracts are different between rc and rc2. 1787 tracker := make(map[types.FileContractID]struct{}) 1788 // Add all the contracts. 1789 for _, contract := range rc.Contracts { 1790 tracker[contract.ID] = struct{}{} 1791 } 1792 // Count the number of contracts that were not seen in the previous 1793 // batch of contracts, and check that the new contracts are not with the 1794 // expensive hosts. 1795 var unseen int 1796 for _, contract := range rc2.Contracts { 1797 _, exists := tracker[contract.ID] 1798 if !exists { 1799 unseen++ 1800 tracker[contract.ID] = struct{}{} 1801 if contract.HostPublicKey.String() == rc1Host || contract.HostPublicKey.String() == rc2Host { 1802 return errors.New("the wrong contracts are being renewed") 1803 } 1804 } 1805 } 1806 if unseen != 2 { 1807 return fmt.Errorf("the wrong number of contracts seem to be getting renewed") 1808 } 1809 return nil 1810 }) 1811 if err != nil { 1812 t.Fatal(err) 1813 } 1814 // The renewing process should not have resulted in additional data being 1815 // uploaded - it should be the same data in the contracts. 1816 for _, contract := range rc2.Contracts { 1817 if contract.Size != modules.SectorSize { 1818 t.Error("Contract has the wrong size:", contract.Size) 1819 } 1820 } 1821 1822 // Try again to download the file we uploaded. It should still be 1823 // retrievable. 1824 downloadPath2 := filepath.Join(st.dir, "test-downloaded-verify-2.dat") 1825 err = st.stdGetAPI("/renter/download/test?destination=" + downloadPath2) 1826 if err != nil { 1827 t.Fatal(err) 1828 } 1829 downloadBytes2, err := ioutil.ReadFile(downloadPath2) 1830 if err != nil { 1831 t.Fatal(err) 1832 } 1833 if !bytes.Equal(downloadBytes2, origBytes) { 1834 t.Fatal("downloaded file and uploaded file do not match") 1835 } 1836 1837 // Mine out another set of the blocks so that the bad contracts expire. 1838 for i := 0; i < 11; i++ { 1839 _, err := st.miner.AddBlock() 1840 if err != nil { 1841 t.Fatal(err) 1842 } 1843 _, err = synchronizationCheck(testGroup) 1844 if err != nil { 1845 t.Fatal(err) 1846 } 1847 time.Sleep(time.Millisecond * 100) 1848 } 1849 1850 // Should be back down to 2 contracts now, with the new hosts. Verify that 1851 // st and stH1 are dropped in favor of the newer, better hosts. The 1852 // contracts should also have data in them. 1853 err = build.Retry(50, time.Millisecond*250, func() error { 1854 err = st.getAPI("/renter/contracts", &rc) 1855 if err != nil { 1856 return errors.New("couldn't get renter stats") 1857 } 1858 if len(rc.Contracts) != 2 { 1859 return fmt.Errorf("renewing seems to have failed: %v", len(rc.Contracts)) 1860 } 1861 return nil 1862 }) 1863 if err != nil { 1864 t.Fatal(err) 1865 } 1866 if rc.Contracts[0].HostPublicKey.String() == rc1Host || rc.Contracts[0].HostPublicKey.String() == rc2Host { 1867 t.Error("renter is renewing the wrong contracts", rc.Contracts[0].HostPublicKey.String()) 1868 } 1869 if rc.Contracts[1].HostPublicKey.String() == rc1Host || rc.Contracts[1].HostPublicKey.String() == rc2Host { 1870 t.Error("renter is renewing the wrong contracts", rc.Contracts[1].HostPublicKey.String()) 1871 } 1872 // The renewing process should not have resulted in additional data being 1873 // uploaded - it should be the same data in the contracts. 1874 for _, contract := range rc.Contracts { 1875 if contract.Size != modules.SectorSize { 1876 t.Error("Contract has the wrong size:", contract.Size) 1877 } 1878 } 1879 // Redundancy should still be 2. 1880 err = retry(120, 250*time.Millisecond, func() error { 1881 st.getAPI("/renter/files", &rf) 1882 if len(rf.Files) >= 1 && rf.Files[0].Redundancy == 2 { 1883 return nil 1884 } 1885 return errors.New("file not uploaded to full redundancy") 1886 }) 1887 if err != nil { 1888 t.Fatal(err, "::", rf.Files[0].Redundancy) 1889 } 1890 1891 // Try again to download the file we uploaded. It should still be 1892 // retrievable. 1893 downloadPath3 := filepath.Join(st.dir, "test-downloaded-verify-3.dat") 1894 err = st.stdGetAPI("/renter/download/test?destination=" + downloadPath3) 1895 if err != nil { 1896 t.Error("Final download has failed:", err) 1897 } 1898 downloadBytes3, err := ioutil.ReadFile(downloadPath3) 1899 if err != nil { 1900 t.Error(err) 1901 } 1902 if !bytes.Equal(downloadBytes3, origBytes) { 1903 t.Error("downloaded file and uploaded file do not match") 1904 } 1905 } 1906 1907 // TestExhaustedContracts verifies that the contractor renews contracts which 1908 // run out of funds before the period elapses. 1909 func TestExhaustedContracts(t *testing.T) { 1910 if testing.Short() || !build.VLONG { 1911 t.SkipNow() 1912 } 1913 t.Parallel() 1914 st, err := createServerTester(t.Name()) 1915 if err != nil { 1916 t.Fatal(err) 1917 } 1918 defer st.server.panicClose() 1919 1920 // set a very high price for the host so we use up the entire funds of the 1921 // contract 1922 err = st.acceptContracts() 1923 if err != nil { 1924 t.Fatal(err) 1925 } 1926 err = st.setHostStorage() 1927 if err != nil { 1928 t.Fatal(err) 1929 } 1930 // Increase the storage and bandwidth prices to drain the allowance faster. 1931 settings := st.host.InternalSettings() 1932 settings.MinUploadBandwidthPrice = settings.MinUploadBandwidthPrice.Mul64(2) 1933 settings.MinStoragePrice = settings.MinUploadBandwidthPrice.Mul64(2) 1934 settings.MaxDuration = 1e6 // set a high max duration to allow an expensive storage price 1935 err = st.host.SetInternalSettings(settings) 1936 if err != nil { 1937 t.Fatal(err) 1938 } 1939 err = st.announceHost() 1940 if err != nil { 1941 t.Fatal(err) 1942 } 1943 1944 // Set an allowance for the renter, allowing a contract to be formed. 1945 allowanceValues := url.Values{} 1946 testPeriod := "950000" // large period to cause an expensive test, exhausting the allowance faster 1947 allowanceValues.Set("funds", types.SiacoinPrecision.Mul64(10).String()) 1948 allowanceValues.Set("period", testPeriod) 1949 err = st.stdPostAPI("/renter", allowanceValues) 1950 if err != nil { 1951 t.Fatal(err) 1952 } 1953 1954 // wait until we have a contract 1955 err = build.Retry(500, time.Millisecond*50, func() error { 1956 if len(st.renter.Contracts()) >= 1 { 1957 return nil 1958 } 1959 return errors.New("no renter contracts") 1960 }) 1961 if err != nil { 1962 t.Fatal(err) 1963 } 1964 initialContract := st.renter.Contracts()[0] 1965 1966 // upload a file. the high upload cost will cause the underlying contract to 1967 // require premature renewal. If premature renewal never happens, the upload 1968 // will never complete. 1969 path := filepath.Join(st.dir, "randUploadFile") 1970 size := int(modules.SectorSize * 75) 1971 err = createRandFile(path, size) 1972 if err != nil { 1973 t.Fatal(err) 1974 } 1975 uploadValues := url.Values{} 1976 uploadValues.Set("source", path) 1977 uploadValues.Set("renew", "true") 1978 uploadValues.Set("datapieces", "1") 1979 uploadValues.Set("paritypieces", "1") 1980 err = st.stdPostAPI("/renter/upload/"+filepath.Base(path), uploadValues) 1981 if err != nil { 1982 t.Fatal(err) 1983 } 1984 err = build.Retry(100, time.Millisecond*500, func() error { 1985 // mine blocks each iteration to trigger contract maintenance 1986 _, err = st.miner.AddBlock() 1987 if err != nil { 1988 t.Fatal(err) 1989 } 1990 1991 st.miner.AddBlock() 1992 if !st.renter.FileList()[0].Available { 1993 return errors.New("file did not complete uploading") 1994 } 1995 return nil 1996 }) 1997 if err != nil { 1998 t.Fatal(err) 1999 } 2000 2001 // verify that the window did not change and the contract ID did change 2002 newContract := st.renter.Contracts()[0] 2003 endHeight := newContract.EndHeight 2004 if newContract.ID == initialContract.ID { 2005 t.Error("renew did not occur") 2006 } 2007 if endHeight != initialContract.EndHeight { 2008 t.Error("contract end height changed, wanted", initialContract.EndHeight, "got", endHeight) 2009 } 2010 } 2011 2012 // TestAdversarialPriceRenewal verifies that host cannot maliciously raise 2013 // their storage price in order to trigger a premature file contract renewal. 2014 func TestAdversarialPriceRenewal(t *testing.T) { 2015 if testing.Short() || !build.VLONG { 2016 t.SkipNow() 2017 } 2018 t.Parallel() 2019 st, err := createServerTester(t.Name()) 2020 if err != nil { 2021 t.Fatal(err) 2022 } 2023 defer st.server.panicClose() 2024 2025 // announce our host 2026 err = st.acceptContracts() 2027 if err != nil { 2028 t.Fatal(err) 2029 } 2030 err = st.setHostStorage() 2031 if err != nil { 2032 t.Fatal(err) 2033 } 2034 err = st.announceHost() 2035 if err != nil { 2036 t.Fatal(err) 2037 } 2038 2039 // Set an allowance for the renter, allowing a contract to be formed. 2040 allowanceValues := url.Values{} 2041 testPeriod := "10000" 2042 allowanceValues.Set("funds", types.SiacoinPrecision.Mul64(10000).String()) 2043 allowanceValues.Set("period", testPeriod) 2044 err = st.stdPostAPI("/renter", allowanceValues) 2045 if err != nil { 2046 t.Fatal(err) 2047 } 2048 2049 // wait until we have a contract 2050 err = build.Retry(500, time.Millisecond*50, func() error { 2051 if len(st.renter.Contracts()) >= 1 { 2052 return nil 2053 } 2054 return errors.New("no renter contracts") 2055 }) 2056 if err != nil { 2057 t.Fatal(err) 2058 } 2059 2060 // upload a file 2061 path := filepath.Join(st.dir, "randUploadFile") 2062 size := int(modules.SectorSize * 50) 2063 err = createRandFile(path, size) 2064 if err != nil { 2065 t.Fatal(err) 2066 } 2067 uploadValues := url.Values{} 2068 uploadValues.Set("source", path) 2069 uploadValues.Set("renew", "true") 2070 uploadValues.Set("datapieces", "1") 2071 uploadValues.Set("paritypieces", "1") 2072 err = st.stdPostAPI("/renter/upload/"+filepath.Base(path), uploadValues) 2073 if err != nil { 2074 t.Fatal(err) 2075 } 2076 err = build.Retry(100, time.Millisecond*500, func() error { 2077 // mine blocks each iteration to trigger contract maintenance 2078 _, err = st.miner.AddBlock() 2079 if err != nil { 2080 t.Fatal(err) 2081 } 2082 if !st.renter.FileList()[0].Available { 2083 return errors.New("file did not complete uploading") 2084 } 2085 return nil 2086 }) 2087 if err != nil { 2088 t.Fatal(err) 2089 } 2090 initialID := st.renter.Contracts()[0].ID 2091 2092 // jack up the host's storage price to try to trigger a renew 2093 settings := st.host.InternalSettings() 2094 settings.MinStoragePrice = settings.MinStoragePrice.Mul64(10800000000) 2095 err = st.host.SetInternalSettings(settings) 2096 if err != nil { 2097 t.Fatal(err) 2098 } 2099 err = st.announceHost() 2100 if err != nil { 2101 t.Fatal(err) 2102 } 2103 2104 for i := 0; i < 100; i++ { 2105 _, err = st.miner.AddBlock() 2106 if err != nil { 2107 t.Fatal(err) 2108 } 2109 if st.renter.Contracts()[0].ID != initialID { 2110 t.Fatal("changing host price caused renew") 2111 } 2112 time.Sleep(time.Millisecond * 100) 2113 } 2114 }