github.com/johnathanhowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/host/host_test.go (about)

     1  package host
     2  
     3  import (
     4  	// "errors"
     5  	"os"
     6  	"path/filepath"
     7  	"testing"
     8  
     9  	"github.com/NebulousLabs/Sia/build"
    10  	"github.com/NebulousLabs/Sia/crypto"
    11  	"github.com/NebulousLabs/Sia/modules"
    12  	"github.com/NebulousLabs/Sia/modules/consensus"
    13  	"github.com/NebulousLabs/Sia/modules/gateway"
    14  	"github.com/NebulousLabs/Sia/modules/miner"
    15  	// "github.com/NebulousLabs/Sia/modules/renter"
    16  	"github.com/NebulousLabs/Sia/modules/transactionpool"
    17  	"github.com/NebulousLabs/Sia/modules/wallet"
    18  	"github.com/NebulousLabs/Sia/types"
    19  )
    20  
    21  // A hostTester is the helper object for host testing, including helper modules
    22  // and methods for controlling synchronization.
    23  type hostTester struct {
    24  	cs      modules.ConsensusSet
    25  	gateway modules.Gateway
    26  	miner   modules.TestMiner
    27  	// renter    modules.Renter
    28  	renting   bool
    29  	tpool     modules.TransactionPool
    30  	wallet    modules.Wallet
    31  	walletKey crypto.TwofishKey
    32  
    33  	host *Host
    34  
    35  	persistDir string
    36  }
    37  
    38  /*
    39  // initRenting prepares the host tester for uploads and downloads by announcing
    40  // the host to the network and performing other preparational tasks.
    41  // initRenting takes a while because the renter needs to process the host
    42  // announcement, requiring asynchronous network communication between the
    43  // renter and host.
    44  func (ht *hostTester) initRenting() error {
    45  	if ht.renting {
    46  		return nil
    47  	}
    48  
    49  	// Because the renting test takes a long time, it will fail if
    50  	// testing.Short.
    51  	if testing.Short() {
    52  		return errors.New("cannot call initRenting in short tests")
    53  	}
    54  
    55  	// Announce the host.
    56  	err := ht.host.Announce()
    57  	if err != nil {
    58  		return err
    59  	}
    60  
    61  	// Mine a block to get the announcement into the blockchain.
    62  	_, err = ht.miner.AddBlock()
    63  	if err != nil {
    64  		return err
    65  	}
    66  
    67  	// Wait for the renter to see the host announcement.
    68  	for i := 0; i < 50; i++ {
    69  		time.Sleep(time.Millisecond * 100)
    70  		if len(ht.renter.ActiveHosts()) != 0 {
    71  			break
    72  		}
    73  	}
    74  	if len(ht.renter.ActiveHosts()) == 0 {
    75  		return errors.New("could not start renting in the host tester")
    76  	}
    77  	ht.renting = true
    78  	return nil
    79  }
    80  */
    81  
    82  // initWallet creates a wallet key, initializes the host wallet, unlocks it,
    83  // and then stores the key in the host tester.
    84  func (ht *hostTester) initWallet() error {
    85  	// Create the keys for the wallet and unlock it.
    86  	key, err := crypto.GenerateTwofishKey()
    87  	if err != nil {
    88  		return err
    89  	}
    90  	ht.walletKey = key
    91  	_, err = ht.wallet.Encrypt(key)
    92  	if err != nil {
    93  		return err
    94  	}
    95  	err = ht.wallet.Unlock(key)
    96  	if err != nil {
    97  		return err
    98  	}
    99  	return nil
   100  }
   101  
   102  // blankHostTester creates a host tester where the modules are created but no
   103  // extra initialization has been done, for example no blocks have been mined
   104  // and the wallet keys have not been created.
   105  func blankHostTester(name string) (*hostTester, error) {
   106  	testdir := build.TempDir(modules.HostDir, name)
   107  
   108  	// Create the modules.
   109  	g, err := gateway.New("localhost:0", filepath.Join(testdir, modules.GatewayDir))
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	cs, err := consensus.New(g, filepath.Join(testdir, modules.ConsensusDir))
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  	tp, err := transactionpool.New(cs, g, filepath.Join(testdir, modules.TransactionPoolDir))
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  	w, err := wallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir))
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  	m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir))
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  	h, err := New(cs, tp, w, "localhost:0", filepath.Join(testdir, modules.HostDir))
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	/*
   134  		r, err := renter.New(cs, w, tp, filepath.Join(testdir, modules.RenterDir))
   135  		if err != nil {
   136  			return nil, err
   137  		}
   138  	*/
   139  
   140  	// Assemble all objects into a hostTester
   141  	ht := &hostTester{
   142  		cs:      cs,
   143  		gateway: g,
   144  		miner:   m,
   145  		// renter:  r,
   146  		tpool:  tp,
   147  		wallet: w,
   148  
   149  		host: h,
   150  
   151  		persistDir: testdir,
   152  	}
   153  
   154  	return ht, nil
   155  }
   156  
   157  // newHostTester creates a host tester with an initialized wallet and money in
   158  // that wallet.
   159  func newHostTester(name string) (*hostTester, error) {
   160  	// Create a blank host tester.
   161  	ht, err := blankHostTester(name)
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  
   166  	// Initialize the wallet and mine blocks until the wallet has money.
   167  	err = ht.initWallet()
   168  	if err != nil {
   169  		return nil, err
   170  	}
   171  	for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ {
   172  		_, err = ht.miner.AddBlock()
   173  		if err != nil {
   174  			return nil, err
   175  		}
   176  	}
   177  
   178  	// Create two storage folder for the host, one the minimum size and one
   179  	// twice the minimum size.
   180  	storageFolderOne := filepath.Join(ht.persistDir, "hostTesterStorageFolderOne")
   181  	err = os.Mkdir(storageFolderOne, 0700)
   182  	if err != nil {
   183  		return nil, err
   184  	}
   185  	err = ht.host.AddStorageFolder(storageFolderOne, modules.SectorSize*8)
   186  	if err != nil {
   187  		return nil, err
   188  	}
   189  	storageFolderTwo := filepath.Join(ht.persistDir, "hostTesterStorageFolderTwo")
   190  	err = os.Mkdir(storageFolderTwo, 0700)
   191  	if err != nil {
   192  		return nil, err
   193  	}
   194  	err = ht.host.AddStorageFolder(storageFolderTwo, modules.SectorSize*8*2)
   195  	if err != nil {
   196  		return nil, err
   197  	}
   198  	return ht, nil
   199  }
   200  
   201  // TestHostInitialization checks that the host initializes to sensible default
   202  // values.
   203  func TestHostInitialization(t *testing.T) {
   204  	if testing.Short() {
   205  		t.SkipNow()
   206  	}
   207  	t.Parallel()
   208  	// Create a blank host tester and check that the height is zero.
   209  	bht, err := blankHostTester("TestHostInitialization")
   210  	if err != nil {
   211  		t.Fatal(err)
   212  	}
   213  	if bht.host.blockHeight != 0 {
   214  		t.Error("host initialized to the wrong block height")
   215  	}
   216  
   217  	// Initialize the wallet so that a block can be mined, then mine a block
   218  	// and check that it sets the host height to 1.
   219  	err = bht.initWallet()
   220  	if err != nil {
   221  		t.Fatal(err)
   222  	}
   223  	_, err = bht.miner.AddBlock()
   224  	if err != nil {
   225  		t.Fatal(err)
   226  	}
   227  	if bht.host.blockHeight != 1 {
   228  		t.Fatal("block height did not increase correctly after first block mined:", bht.host.blockHeight, 1)
   229  	}
   230  }
   231  
   232  // TestNilValues tries initializing the host with nil values.
   233  func TestNilValues(t *testing.T) {
   234  	if testing.Short() {
   235  		t.SkipNow()
   236  	}
   237  	t.Parallel()
   238  	ht, err := blankHostTester("TestStartupRescan")
   239  	if err != nil {
   240  		t.Fatal(err)
   241  	}
   242  
   243  	hostDir := filepath.Join(ht.persistDir, modules.HostDir)
   244  	_, err = New(nil, ht.tpool, ht.wallet, "localhost:0", hostDir)
   245  	if err != errNilCS {
   246  		t.Fatal("could not trigger errNilCS")
   247  	}
   248  	_, err = New(ht.cs, nil, ht.wallet, "localhost:0", hostDir)
   249  	if err != errNilTpool {
   250  		t.Fatal("could not trigger errNilTpool")
   251  	}
   252  	_, err = New(ht.cs, ht.tpool, nil, "localhost:0", hostDir)
   253  	if err != errNilWallet {
   254  		t.Fatal("Could not trigger errNilWallet")
   255  	}
   256  }
   257  
   258  // TestSetAndGetInternalSettings checks that the functions for interacting with
   259  // the host's internal settings object are working as expected.
   260  func TestSetAndGetInternalSettings(t *testing.T) {
   261  	if testing.Short() {
   262  		t.SkipNow()
   263  	}
   264  	t.Parallel()
   265  
   266  	ht, err := newHostTester("TestSetAndGetInternalSettings")
   267  	if err != nil {
   268  		t.Fatal(err)
   269  	}
   270  
   271  	// Check the default settings get returned at first call.
   272  	settings := ht.host.InternalSettings()
   273  	if settings.AcceptingContracts != false {
   274  		t.Error("settings retrieval did not return default value")
   275  	}
   276  	if settings.MaxDuration != defaultMaxDuration {
   277  		t.Error("settings retrieval did not return default value")
   278  	}
   279  	if settings.MaxDownloadBatchSize != uint64(defaultMaxDownloadBatchSize) {
   280  		t.Error("settings retrieval did not return default value")
   281  	}
   282  	if settings.MaxReviseBatchSize != uint64(defaultMaxReviseBatchSize) {
   283  		t.Error("settings retrieval did not return default value")
   284  	}
   285  	if settings.NetAddress != "" {
   286  		t.Error("settings retrieval did not return default value")
   287  	}
   288  	if settings.WindowSize != defaultWindowSize {
   289  		t.Error("settings retrieval did not return default value")
   290  	}
   291  	if settings.Collateral.Cmp(defaultCollateral) != 0 {
   292  		t.Error("settings retrieval did not return default value")
   293  	}
   294  	if settings.CollateralBudget.Cmp(defaultCollateralBudget) != 0 {
   295  		t.Error("settings retrieval did not return default value")
   296  	}
   297  	if settings.MaxCollateral.Cmp(defaultMaxCollateral) != 0 {
   298  		t.Error("settings retrieval did not return default value")
   299  	}
   300  	if settings.DownloadLimitGrowth != 0 {
   301  		t.Error("settings retrieval did not return default value")
   302  	}
   303  	if settings.DownloadLimitCap != 0 {
   304  		t.Error("settings retrieval did not return default value")
   305  	}
   306  	if settings.DownloadSpeedLimit != 0 {
   307  		t.Error("settings retrieval did not return default value")
   308  	}
   309  	if settings.UploadLimitGrowth != 0 {
   310  		t.Error("settings retrieval did not return default value")
   311  	}
   312  	if settings.UploadLimitCap != 0 {
   313  		t.Error("settings retrieval did not return default value")
   314  	}
   315  	if settings.UploadSpeedLimit != 0 {
   316  		t.Error("settings retrieval did not return default value")
   317  	}
   318  	if settings.MinimumContractPrice.Cmp(defaultContractPrice) != 0 {
   319  		t.Error("settings retrieval did not return default value")
   320  	}
   321  	if settings.MinimumDownloadBandwidthPrice.Cmp(defaultDownloadBandwidthPrice) != 0 {
   322  		t.Error("settings retrieval did not return default value")
   323  	}
   324  	if settings.MinimumStoragePrice.Cmp(defaultStoragePrice) != 0 {
   325  		t.Error("settings retrieval did not return default value")
   326  	}
   327  	if settings.MinimumUploadBandwidthPrice.Cmp(defaultUploadBandwidthPrice) != 0 {
   328  		t.Error("settings retrieval did not return default value")
   329  	}
   330  
   331  	// Check that calling SetInternalSettings with valid settings updates the settings.
   332  	settings.AcceptingContracts = true
   333  	settings.NetAddress = "foo.com:123"
   334  	err = ht.host.SetInternalSettings(settings)
   335  	if err != nil {
   336  		t.Fatal(err)
   337  	}
   338  	settings = ht.host.InternalSettings()
   339  	if settings.AcceptingContracts != true {
   340  		t.Fatal("SetInternalSettings failed to update settings")
   341  	}
   342  	if settings.NetAddress != "foo.com:123" {
   343  		t.Fatal("SetInternalSettings failed to update settings")
   344  	}
   345  
   346  	// Check that calling SetInternalSettings with invalid settings does not update the settings.
   347  	settings.NetAddress = "invalid"
   348  	err = ht.host.SetInternalSettings(settings)
   349  	if err == nil {
   350  		t.Fatal("expected SetInternalSettings to error with invalid settings")
   351  	}
   352  	settings = ht.host.InternalSettings()
   353  	if settings.NetAddress != "foo.com:123" {
   354  		t.Fatal("SetInternalSettings should not modify the settings if the new settings are invalid")
   355  	}
   356  
   357  	// Reload the host and verify that the altered settings persisted.
   358  	err = ht.host.Close()
   359  	if err != nil {
   360  		t.Fatal(err)
   361  	}
   362  	rebootHost, err := New(ht.cs, ht.tpool, ht.wallet, "localhost:0", filepath.Join(ht.persistDir, modules.HostDir))
   363  	if err != nil {
   364  		t.Fatal(err)
   365  	}
   366  	rebootSettings := rebootHost.InternalSettings()
   367  	if rebootSettings.AcceptingContracts != settings.AcceptingContracts {
   368  		t.Error("settings retrieval did not return updated value")
   369  	}
   370  	if rebootSettings.NetAddress != settings.NetAddress {
   371  		t.Error("settings retrieval did not return updated value")
   372  	}
   373  }
   374  
   375  /*
   376  // TestSetAndGetSettings checks that the functions for interacting with the
   377  // hosts settings object are working as expected.
   378  func TestSetAndGetSettings(t *testing.T) {
   379  	if testing.Short() {
   380  		t.SkipNow()
   381  	}
   382  	ht, err := newHostTester("TestSetAndGetSettings")
   383  	if err != nil {
   384  		t.Fatal(err)
   385  	}
   386  
   387  	// Check the default settings get returned at first call.
   388  	settings := ht.host.Settings()
   389  	if settings.MaxDuration != defaultMaxDuration {
   390  		t.Error("settings retrieval did not return default value")
   391  	}
   392  	if settings.WindowSize != defaultWindowSize {
   393  		t.Error("settings retrieval did not return default value")
   394  	}
   395  	if settings.Price.Cmp(defaultPrice) != 0 {
   396  		t.Error("settings retrieval did not return default value")
   397  	}
   398  	if settings.Collateral.Cmp(defaultCollateral) != 0 {
   399  		t.Error("settings retrieval did not return default value")
   400  	}
   401  
   402  	// Submit updated settings and check that the changes stuck.
   403  	settings.TotalStorage += 15
   404  	settings.MaxDuration += 16
   405  	settings.WindowSize += 17
   406  	settings.Price = settings.Price.Add(types.NewCurrency64(18))
   407  	settings.Collateral = settings.Collateral.Add(types.NewCurrency64(19))
   408  	err = ht.host.SetSettings(settings)
   409  	if err != nil {
   410  		t.Fatal(err)
   411  	}
   412  	newSettings := ht.host.Settings()
   413  	if settings.MaxDuration != newSettings.MaxDuration {
   414  		t.Error("settings retrieval did not return updated value")
   415  	}
   416  	if settings.WindowSize != newSettings.WindowSize {
   417  		t.Error("settings retrieval did not return updated value")
   418  	}
   419  	if settings.Price.Cmp(newSettings.Price) != 0 {
   420  		t.Error("settings retrieval did not return updated value")
   421  	}
   422  	if settings.Collateral.Cmp(newSettings.Collateral) != 0 {
   423  		t.Error("settings retrieval did not return updated value")
   424  	}
   425  
   426  	// Reload the host and verify that the altered settings persisted.
   427  	err = ht.host.Close()
   428  	if err != nil {
   429  		t.Fatal(err)
   430  	}
   431  	rebootHost, err := New(ht.cs, ht.tpool, ht.wallet, "localhost:0", filepath.Join(ht.persistDir, modules.HostDir))
   432  	if err != nil {
   433  		t.Fatal(err)
   434  	}
   435  	rebootSettings := rebootHost.Settings()
   436  	if settings.TotalStorage != rebootSettings.TotalStorage {
   437  		t.Error("settings retrieval did not return updated value")
   438  	}
   439  	if settings.MaxDuration != rebootSettings.MaxDuration {
   440  		t.Error("settings retrieval did not return updated value")
   441  	}
   442  	if settings.WindowSize != rebootSettings.WindowSize {
   443  		t.Error("settings retrieval did not return updated value")
   444  	}
   445  	if settings.Price.Cmp(rebootSettings.Price) != 0 {
   446  		t.Error("settings retrieval did not return updated value")
   447  	}
   448  	if settings.Collateral.Cmp(rebootSettings.Collateral) != 0 {
   449  		t.Error("settings retrieval did not return updated value")
   450  	}
   451  }
   452  
   453  // TestPersistentSettings checks that settings persist between instances of the
   454  // host.
   455  func TestPersistentSettings(t *testing.T) {
   456  	if testing.Short() {
   457  		t.SkipNow()
   458  	}
   459  	ht, err := newHostTester("TestSetPersistentSettings")
   460  	if err != nil {
   461  		t.Fatal(err)
   462  	}
   463  
   464  	// Submit updated settings.
   465  	settings := ht.host.Settings()
   466  	settings.TotalStorage += 25
   467  	settings.MaxDuration += 36
   468  	settings.WindowSize += 47
   469  	settings.Price = settings.Price.Add(types.NewCurrency64(38))
   470  	settings.Collateral = settings.Collateral.Add(types.NewCurrency64(99))
   471  	err = ht.host.SetSettings(settings)
   472  	if err != nil {
   473  		t.Fatal(err)
   474  	}
   475  
   476  	// Reboot the host and verify that the new settings stuck.
   477  	err = ht.host.Close() // host saves upon closing
   478  	if err != nil {
   479  		t.Fatal(err)
   480  	}
   481  	h, err := New(ht.cs, ht.tpool, ht.wallet, "localhost:0", filepath.Join(ht.persistDir, modules.HostDir))
   482  	if err != nil {
   483  		t.Fatal(err)
   484  	}
   485  	newSettings := h.Settings()
   486  	if settings.TotalStorage != newSettings.TotalStorage {
   487  		t.Error("settings retrieval did not return updated value:", settings.TotalStorage, "vs", newSettings.TotalStorage)
   488  	}
   489  	if settings.MaxDuration != newSettings.MaxDuration {
   490  		t.Error("settings retrieval did not return updated value")
   491  	}
   492  	if settings.WindowSize != newSettings.WindowSize {
   493  		t.Error("settings retrieval did not return updated value")
   494  	}
   495  	if settings.Price.Cmp(newSettings.Price) != 0 {
   496  		t.Error("settings retrieval did not return updated value")
   497  	}
   498  	if settings.Collateral.Cmp(newSettings.Collateral) != 0 {
   499  		t.Error("settings retrieval did not return updated value")
   500  	}
   501  }
   502  */