gitlab.com/SiaPrime/SiaPrime@v1.4.1/node/api/renterhost_test.go (about)

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