github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/renter/contractor/host_integration_test.go (about)

     1  package contractor
     2  
     3  import (
     4  	"bytes"
     5  	"os"
     6  	"path/filepath"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/NebulousLabs/Sia/build"
    11  	"github.com/NebulousLabs/Sia/crypto"
    12  	"github.com/NebulousLabs/Sia/modules"
    13  	"github.com/NebulousLabs/Sia/modules/consensus"
    14  	"github.com/NebulousLabs/Sia/modules/gateway"
    15  	"github.com/NebulousLabs/Sia/modules/host"
    16  	"github.com/NebulousLabs/Sia/modules/miner"
    17  	"github.com/NebulousLabs/Sia/modules/renter/hostdb"
    18  	"github.com/NebulousLabs/Sia/modules/transactionpool"
    19  	modWallet "github.com/NebulousLabs/Sia/modules/wallet"
    20  	"github.com/NebulousLabs/Sia/types"
    21  )
    22  
    23  // newTestingWallet is a helper function that creates a ready-to-use wallet
    24  // and mines some coins into it.
    25  func newTestingWallet(testdir string, cs modules.ConsensusSet, tp modules.TransactionPool) (modules.Wallet, error) {
    26  	w, err := modWallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir))
    27  	if err != nil {
    28  		return nil, err
    29  	}
    30  	key, err := crypto.GenerateTwofishKey()
    31  	if err != nil {
    32  		return nil, err
    33  	}
    34  	if !w.Encrypted() {
    35  		_, err = w.Encrypt(key)
    36  		if err != nil {
    37  			return nil, err
    38  		}
    39  	}
    40  	err = w.Unlock(key)
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  	// give it some money
    45  	m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir))
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  	for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ {
    50  		_, err := m.AddBlock()
    51  		if err != nil {
    52  			return nil, err
    53  		}
    54  	}
    55  	return w, nil
    56  }
    57  
    58  // newTestingHost is a helper function that creates a ready-to-use host.
    59  func newTestingHost(testdir string, cs modules.ConsensusSet, tp modules.TransactionPool) (modules.Host, error) {
    60  	w, err := newTestingWallet(testdir, cs, tp)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  	return host.New(cs, tp, w, "localhost:0", filepath.Join(testdir, modules.HostDir))
    65  }
    66  
    67  // newTestingContractor is a helper function that creates a ready-to-use
    68  // contractor.
    69  func newTestingContractor(testdir string, cs modules.ConsensusSet, tp modules.TransactionPool) (*Contractor, error) {
    70  	w, err := newTestingWallet(testdir, cs, tp)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	hdb, err := hostdb.New(cs, filepath.Join(testdir, "hostdb"))
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	return New(cs, w, tp, hdb, filepath.Join(testdir, "contractor"))
    79  }
    80  
    81  // newTestingTrio creates a Host, Contractor, and TestMiner that can be used
    82  // for testing host/renter interactions.
    83  func newTestingTrio(name string) (modules.Host, *Contractor, modules.TestMiner, error) {
    84  	testdir := build.TempDir("contractor", name)
    85  
    86  	// create miner
    87  	g, err := gateway.New("localhost:0", filepath.Join(testdir, modules.GatewayDir))
    88  	if err != nil {
    89  		return nil, nil, nil, err
    90  	}
    91  	cs, err := consensus.New(g, filepath.Join(testdir, modules.ConsensusDir))
    92  	if err != nil {
    93  		return nil, nil, nil, err
    94  	}
    95  	tp, err := transactionpool.New(cs, g, filepath.Join(testdir, modules.TransactionPoolDir))
    96  	if err != nil {
    97  		return nil, nil, nil, err
    98  	}
    99  	w, err := modWallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir))
   100  	if err != nil {
   101  		return nil, nil, nil, err
   102  	}
   103  	key, err := crypto.GenerateTwofishKey()
   104  	if err != nil {
   105  		return nil, nil, nil, err
   106  	}
   107  	if !w.Encrypted() {
   108  		_, err = w.Encrypt(key)
   109  		if err != nil {
   110  			return nil, nil, nil, err
   111  		}
   112  	}
   113  	err = w.Unlock(key)
   114  	if err != nil {
   115  		return nil, nil, nil, err
   116  	}
   117  	m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir))
   118  	if err != nil {
   119  		return nil, nil, nil, err
   120  	}
   121  
   122  	// create host and contractor, using same consensus set and gateway
   123  	h, err := newTestingHost(filepath.Join(testdir, "Host"), cs, tp)
   124  	if err != nil {
   125  		return nil, nil, nil, err
   126  	}
   127  	c, err := newTestingContractor(filepath.Join(testdir, "Contractor"), cs, tp)
   128  	if err != nil {
   129  		return nil, nil, nil, err
   130  	}
   131  
   132  	// Configure host to accept contracts
   133  	settings := h.InternalSettings()
   134  	settings.AcceptingContracts = true
   135  	err = h.SetInternalSettings(settings)
   136  	if err != nil {
   137  		return nil, nil, nil, err
   138  	}
   139  
   140  	// add storage to host
   141  	storageFolder := filepath.Join(build.SiaTestingDir, "contractor", "TestIntegrationReviseContract", "storage")
   142  	err = os.MkdirAll(storageFolder, 0700)
   143  	if err != nil {
   144  		return nil, nil, nil, err
   145  	}
   146  	err = h.AddStorageFolder(storageFolder, 1e6)
   147  	if err != nil {
   148  		return nil, nil, nil, err
   149  	}
   150  
   151  	// announce the host
   152  	err = h.Announce()
   153  	if err != nil {
   154  		return nil, nil, nil, err
   155  	}
   156  
   157  	// mine a block, processing the announcement
   158  	m.AddBlock()
   159  
   160  	// wait for hostdb to scan host
   161  	for i := 0; i < 500 && len(c.hdb.RandomHosts(1, nil)) == 0; i++ {
   162  		time.Sleep(time.Millisecond)
   163  	}
   164  
   165  	return h, c, m, nil
   166  }
   167  
   168  // TestIntegrationFormContract tests that the contractor can form contracts
   169  // with the host module.
   170  func TestIntegrationFormContract(t *testing.T) {
   171  	if testing.Short() {
   172  		t.SkipNow()
   173  	}
   174  	h, c, _, err := newTestingTrio("TestIntegrationFormContract")
   175  	if err != nil {
   176  		t.Fatal(err)
   177  	}
   178  
   179  	// get the host's entry from the db
   180  	hostEntry, ok := c.hdb.Host(h.ExternalSettings().NetAddress)
   181  	if !ok {
   182  		t.Fatal("no entry for host in db")
   183  	}
   184  
   185  	// form a contract with the host
   186  	contract, err := c.managedNewContract(hostEntry, 64000, c.blockHeight+100)
   187  	if err != nil {
   188  		t.Fatal(err)
   189  	}
   190  
   191  	if contract.NetAddress != h.ExternalSettings().NetAddress {
   192  		t.Fatal("bad contract")
   193  	}
   194  }
   195  
   196  // TestIntegrationReviseContract tests that the contractor can revise a
   197  // contract previously formed with a host.
   198  func TestIntegrationReviseContract(t *testing.T) {
   199  	if testing.Short() {
   200  		t.SkipNow()
   201  	}
   202  	// create testing trio
   203  	h, c, _, err := newTestingTrio("TestIntegrationReviseContract")
   204  	if err != nil {
   205  		t.Fatal(err)
   206  	}
   207  
   208  	// get the host's entry from the db
   209  	hostEntry, ok := c.hdb.Host(h.ExternalSettings().NetAddress)
   210  	if !ok {
   211  		t.Fatal("no entry for host in db")
   212  	}
   213  
   214  	// form a contract with the host
   215  	contract, err := c.managedNewContract(hostEntry, 64000, c.blockHeight+100)
   216  	if err != nil {
   217  		t.Fatal(err)
   218  	}
   219  
   220  	// revise the contract
   221  	editor, err := c.Editor(contract)
   222  	if err != nil {
   223  		t.Fatal(err)
   224  	}
   225  	data, err := crypto.RandBytes(int(modules.SectorSize))
   226  	if err != nil {
   227  		t.Fatal(err)
   228  	}
   229  	_, err = editor.Upload(data)
   230  	if err != nil {
   231  		t.Fatal(err)
   232  	}
   233  	err = editor.Close()
   234  	if err != nil {
   235  		t.Fatal(err)
   236  	}
   237  }
   238  
   239  // TestIntegrationUploadDownload tests that the contractor can upload data to
   240  // a host and download it intact.
   241  func TestIntegrationUploadDownload(t *testing.T) {
   242  	if testing.Short() {
   243  		t.SkipNow()
   244  	}
   245  	// create testing trio
   246  	h, c, _, err := newTestingTrio("TestIntegrationUploadDownload")
   247  	if err != nil {
   248  		t.Fatal(err)
   249  	}
   250  
   251  	// get the host's entry from the db
   252  	hostEntry, ok := c.hdb.Host(h.ExternalSettings().NetAddress)
   253  	if !ok {
   254  		t.Fatal("no entry for host in db")
   255  	}
   256  
   257  	// form a contract with the host
   258  	contract, err := c.managedNewContract(hostEntry, modules.SectorSize*10, c.blockHeight+100)
   259  	if err != nil {
   260  		t.Fatal(err)
   261  	}
   262  
   263  	// revise the contract
   264  	editor, err := c.Editor(contract)
   265  	if err != nil {
   266  		t.Fatal(err)
   267  	}
   268  	data, err := crypto.RandBytes(int(modules.SectorSize))
   269  	if err != nil {
   270  		t.Fatal(err)
   271  	}
   272  	root, err := editor.Upload(data)
   273  	if err != nil {
   274  		t.Fatal(err)
   275  	}
   276  	err = editor.Close()
   277  	if err != nil {
   278  		t.Fatal(err)
   279  	}
   280  
   281  	// download the data
   282  	contract = c.contracts[contract.ID]
   283  	downloader, err := c.Downloader(contract)
   284  	if err != nil {
   285  		t.Fatal(err)
   286  	}
   287  	retrieved, err := downloader.Sector(root)
   288  	if err != nil {
   289  		t.Fatal(err)
   290  	}
   291  	if !bytes.Equal(data, retrieved) {
   292  		t.Fatal("downloaded data does not match original")
   293  	}
   294  	err = downloader.Close()
   295  	if err != nil {
   296  		t.Fatal(err)
   297  	}
   298  }
   299  
   300  // TestIntegrationDelete tests that the contractor can delete a sector from a
   301  // contract previously formed with a host.
   302  func TestIntegrationDelete(t *testing.T) {
   303  	if testing.Short() {
   304  		t.SkipNow()
   305  	}
   306  	// create testing trio
   307  	h, c, _, err := newTestingTrio("TestIntegrationDelete")
   308  	if err != nil {
   309  		t.Fatal(err)
   310  	}
   311  
   312  	// get the host's entry from the db
   313  	hostEntry, ok := c.hdb.Host(h.ExternalSettings().NetAddress)
   314  	if !ok {
   315  		t.Fatal("no entry for host in db")
   316  	}
   317  
   318  	// form a contract with the host
   319  	contract, err := c.managedNewContract(hostEntry, modules.SectorSize*10, c.blockHeight+100)
   320  	if err != nil {
   321  		t.Fatal(err)
   322  	}
   323  
   324  	// revise the contract
   325  	editor, err := c.Editor(contract)
   326  	if err != nil {
   327  		t.Fatal(err)
   328  	}
   329  	data, err := crypto.RandBytes(int(modules.SectorSize))
   330  	if err != nil {
   331  		t.Fatal(err)
   332  	}
   333  	_, err = editor.Upload(data)
   334  	if err != nil {
   335  		t.Fatal(err)
   336  	}
   337  	err = editor.Close()
   338  	if err != nil {
   339  		t.Fatal(err)
   340  	}
   341  
   342  	// delete the sector
   343  	contract = c.contracts[contract.ID]
   344  	editor, err = c.Editor(contract)
   345  	if err != nil {
   346  		t.Fatal(err)
   347  	}
   348  	err = editor.Delete(contract.MerkleRoots[0])
   349  	if err != nil {
   350  		t.Fatal(err)
   351  	}
   352  	err = editor.Close()
   353  	if err != nil {
   354  		t.Fatal(err)
   355  	}
   356  }
   357  
   358  // TestIntegrationInsertDelete tests that the contractor can insert and delete
   359  // a sector during the same revision.
   360  func TestIntegrationInsertDelete(t *testing.T) {
   361  	if testing.Short() {
   362  		t.SkipNow()
   363  	}
   364  	// create testing trio
   365  	h, c, _, err := newTestingTrio("TestIntegrationInsertDelete")
   366  	if err != nil {
   367  		t.Fatal(err)
   368  	}
   369  
   370  	// get the host's entry from the db
   371  	hostEntry, ok := c.hdb.Host(h.ExternalSettings().NetAddress)
   372  	if !ok {
   373  		t.Fatal("no entry for host in db")
   374  	}
   375  
   376  	// form a contract with the host
   377  	contract, err := c.managedNewContract(hostEntry, modules.SectorSize*10, c.blockHeight+100)
   378  	if err != nil {
   379  		t.Fatal(err)
   380  	}
   381  
   382  	// revise the contract
   383  	editor, err := c.Editor(contract)
   384  	if err != nil {
   385  		t.Fatal(err)
   386  	}
   387  	data, err := crypto.RandBytes(int(modules.SectorSize))
   388  	if err != nil {
   389  		t.Fatal(err)
   390  	}
   391  	// insert the sector
   392  	_, err = editor.Upload(data)
   393  	if err != nil {
   394  		t.Fatal(err)
   395  	}
   396  	// delete the sector
   397  	err = editor.Delete(crypto.MerkleRoot(data))
   398  	if err != nil {
   399  		t.Fatal(err)
   400  	}
   401  	err = editor.Close()
   402  	if err != nil {
   403  		t.Fatal(err)
   404  	}
   405  
   406  	// contract should have no sectors
   407  	contract = c.contracts[contract.ID]
   408  	if len(contract.MerkleRoots) != 0 {
   409  		t.Fatal("contract should have no sectors:", contract.MerkleRoots)
   410  	}
   411  }
   412  
   413  // TestIntegrationModify tests that the contractor can modify a previously-
   414  // uploaded sector.
   415  func TestIntegrationModify(t *testing.T) {
   416  	if testing.Short() {
   417  		t.SkipNow()
   418  	}
   419  	// create testing trio
   420  	h, c, _, err := newTestingTrio("TestIntegrationModify")
   421  	if err != nil {
   422  		t.Fatal(err)
   423  	}
   424  
   425  	// get the host's entry from the db
   426  	hostEntry, ok := c.hdb.Host(h.ExternalSettings().NetAddress)
   427  	if !ok {
   428  		t.Fatal("no entry for host in db")
   429  	}
   430  
   431  	// form a contract with the host
   432  	contract, err := c.managedNewContract(hostEntry, modules.SectorSize*10, c.blockHeight+100)
   433  	if err != nil {
   434  		t.Fatal(err)
   435  	}
   436  
   437  	// revise the contract
   438  	editor, err := c.Editor(contract)
   439  	if err != nil {
   440  		t.Fatal(err)
   441  	}
   442  	data, err := crypto.RandBytes(int(modules.SectorSize))
   443  	if err != nil {
   444  		t.Fatal(err)
   445  	}
   446  	// insert the sector
   447  	_, err = editor.Upload(data)
   448  	if err != nil {
   449  		t.Fatal(err)
   450  	}
   451  	err = editor.Close()
   452  	if err != nil {
   453  		t.Fatal(err)
   454  	}
   455  
   456  	// modify the sector
   457  	oldRoot := crypto.MerkleRoot(data)
   458  	offset, newData := uint64(10), []byte{1, 2, 3, 4, 5}
   459  	copy(data[offset:], newData)
   460  	newRoot := crypto.MerkleRoot(data)
   461  	contract = c.contracts[contract.ID]
   462  	editor, err = c.Editor(contract)
   463  	if err != nil {
   464  		t.Fatal(err)
   465  	}
   466  	err = editor.Modify(oldRoot, newRoot, offset, newData)
   467  	if err != nil {
   468  		t.Fatal(err)
   469  	}
   470  	err = editor.Close()
   471  	if err != nil {
   472  		t.Fatal(err)
   473  	}
   474  }
   475  
   476  // TestIntegrationRenew tests that the contractor can renew a previously-
   477  // formed file contract.
   478  func TestIntegrationRenew(t *testing.T) {
   479  	if testing.Short() {
   480  		t.SkipNow()
   481  	}
   482  	// create testing trio
   483  	h, c, _, err := newTestingTrio("TestIntegrationRenew")
   484  	if err != nil {
   485  		t.Fatal(err)
   486  	}
   487  
   488  	// get the host's entry from the db
   489  	hostEntry, ok := c.hdb.Host(h.ExternalSettings().NetAddress)
   490  	if !ok {
   491  		t.Fatal("no entry for host in db")
   492  	}
   493  
   494  	// form a contract with the host
   495  	contract, err := c.managedNewContract(hostEntry, modules.SectorSize*10, c.blockHeight+100)
   496  	if err != nil {
   497  		t.Fatal(err)
   498  	}
   499  
   500  	// revise the contract
   501  	editor, err := c.Editor(contract)
   502  	if err != nil {
   503  		t.Fatal(err)
   504  	}
   505  	data, err := crypto.RandBytes(int(modules.SectorSize))
   506  	if err != nil {
   507  		t.Fatal(err)
   508  	}
   509  	// insert the sector
   510  	root, err := editor.Upload(data)
   511  	if err != nil {
   512  		t.Fatal(err)
   513  	}
   514  	err = editor.Close()
   515  	if err != nil {
   516  		t.Fatal(err)
   517  	}
   518  
   519  	// renew the contract
   520  	contract = c.contracts[contract.ID]
   521  	newID, err := c.managedRenew(contract, modules.SectorSize*10, c.blockHeight+200)
   522  	if err != nil {
   523  		t.Fatal(err)
   524  	}
   525  
   526  	// check renewed contract
   527  	contract = c.contracts[newID]
   528  	if contract.FileContract.FileMerkleRoot != root {
   529  		t.Fatal(contract.FileContract.FileMerkleRoot)
   530  	} else if contract.FileContract.FileSize != modules.SectorSize {
   531  		t.Fatal(contract.FileContract.FileSize)
   532  	} else if contract.FileContract.RevisionNumber != 0 {
   533  		t.Fatal(contract.FileContract.RevisionNumber)
   534  	} else if contract.FileContract.WindowStart != c.blockHeight+200 {
   535  		t.Fatal(contract.FileContract.WindowStart)
   536  	}
   537  
   538  	// download the renewed contract
   539  	downloader, err := c.Downloader(contract)
   540  	if err != nil {
   541  		t.Fatal(err)
   542  	}
   543  	retrieved, err := downloader.Sector(root)
   544  	if err != nil {
   545  		t.Fatal(err)
   546  	}
   547  	if !bytes.Equal(data, retrieved) {
   548  		t.Fatal("downloaded data does not match original")
   549  	}
   550  	err = downloader.Close()
   551  	if err != nil {
   552  		t.Fatal(err)
   553  	}
   554  }