gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/renter/contractor/host_integration_test.go (about)

     1  package contractor
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"net"
     7  	"os"
     8  	"path/filepath"
     9  	"testing"
    10  	"time"
    11  
    12  	"gitlab.com/NebulousLabs/fastrand"
    13  	"gitlab.com/SiaPrime/SiaPrime/build"
    14  	"gitlab.com/SiaPrime/SiaPrime/crypto"
    15  	"gitlab.com/SiaPrime/SiaPrime/encoding"
    16  	"gitlab.com/SiaPrime/SiaPrime/modules"
    17  	"gitlab.com/SiaPrime/SiaPrime/modules/consensus"
    18  	"gitlab.com/SiaPrime/SiaPrime/modules/gateway"
    19  	"gitlab.com/SiaPrime/SiaPrime/modules/host"
    20  	"gitlab.com/SiaPrime/SiaPrime/modules/miner"
    21  	"gitlab.com/SiaPrime/SiaPrime/modules/renter/hostdb"
    22  	"gitlab.com/SiaPrime/SiaPrime/modules/transactionpool"
    23  	modWallet "gitlab.com/SiaPrime/SiaPrime/modules/wallet"
    24  	"gitlab.com/SiaPrime/SiaPrime/types"
    25  )
    26  
    27  // newTestingWallet is a helper function that creates a ready-to-use wallet
    28  // and mines some coins into it.
    29  func newTestingWallet(testdir string, cs modules.ConsensusSet, tp modules.TransactionPool) (modules.Wallet, error) {
    30  	w, err := modWallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir))
    31  	if err != nil {
    32  		return nil, err
    33  	}
    34  	key := crypto.GenerateSiaKey(crypto.TypeDefaultWallet)
    35  	encrypted, err := w.Encrypted()
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  	if !encrypted {
    40  		_, err = w.Encrypt(key)
    41  		if err != nil {
    42  			return nil, err
    43  		}
    44  	}
    45  	err = w.Unlock(key)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  	// give it some money
    50  	m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir))
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ {
    55  		_, err := m.AddBlock()
    56  		if err != nil {
    57  			return nil, err
    58  		}
    59  	}
    60  	return w, nil
    61  }
    62  
    63  // newTestingHost is a helper function that creates a ready-to-use host.
    64  func newTestingHost(testdir string, cs modules.ConsensusSet, tp modules.TransactionPool) (modules.Host, error) {
    65  	g, err := gateway.New("localhost:0", false, filepath.Join(testdir, modules.GatewayDir))
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  	w, err := newTestingWallet(testdir, cs, tp)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	h, err := host.New(cs, g, tp, w, "localhost:0", filepath.Join(testdir, modules.HostDir))
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	// configure host to accept contracts
    79  	settings := h.InternalSettings()
    80  	settings.AcceptingContracts = true
    81  	settings.MaxDuration = types.BlockHeight(4 * types.BlocksPerMonth)
    82  	err = h.SetInternalSettings(settings)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	// add storage to host
    88  	storageFolder := filepath.Join(testdir, "storage")
    89  	err = os.MkdirAll(storageFolder, 0700)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  	err = h.AddStorageFolder(storageFolder, modules.SectorSize*64)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  
    98  	return h, nil
    99  }
   100  
   101  // newTestingContractor is a helper function that creates a ready-to-use
   102  // contractor.
   103  func newTestingContractor(testdir string, g modules.Gateway, cs modules.ConsensusSet, tp modules.TransactionPool) (*Contractor, error) {
   104  	w, err := newTestingWallet(testdir, cs, tp)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  	hdb, err := hostdb.New(g, cs, tp, filepath.Join(testdir, "hostdb"))
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	return New(cs, w, tp, hdb, filepath.Join(testdir, "contractor"))
   113  }
   114  
   115  // newTestingTrio creates a Host, Contractor, and TestMiner that can be used
   116  // for testing host/renter interactions.
   117  func newTestingTrio(name string) (modules.Host, *Contractor, modules.TestMiner, error) {
   118  	testdir := build.TempDir("contractor", name)
   119  
   120  	// create miner
   121  	g, err := gateway.New("localhost:0", false, filepath.Join(testdir, modules.GatewayDir))
   122  	if err != nil {
   123  		return nil, nil, nil, err
   124  	}
   125  	cs, err := consensus.New(g, false, filepath.Join(testdir, modules.ConsensusDir))
   126  	if err != nil {
   127  		return nil, nil, nil, err
   128  	}
   129  	tp, err := transactionpool.New(cs, g, filepath.Join(testdir, modules.TransactionPoolDir))
   130  	if err != nil {
   131  		return nil, nil, nil, err
   132  	}
   133  	w, err := modWallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir))
   134  	if err != nil {
   135  		return nil, nil, nil, err
   136  	}
   137  	key := crypto.GenerateSiaKey(crypto.TypeDefaultWallet)
   138  	encrypted, err := w.Encrypted()
   139  	if err != nil {
   140  		return nil, nil, nil, err
   141  	}
   142  	if !encrypted {
   143  		_, err = w.Encrypt(key)
   144  		if err != nil {
   145  			return nil, nil, nil, err
   146  		}
   147  	}
   148  	err = w.Unlock(key)
   149  	if err != nil {
   150  		return nil, nil, nil, err
   151  	}
   152  	m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir))
   153  	if err != nil {
   154  		return nil, nil, nil, err
   155  	}
   156  
   157  	// create host and contractor, using same consensus set and gateway
   158  	h, err := newTestingHost(filepath.Join(testdir, "Host"), cs, tp)
   159  	if err != nil {
   160  		return nil, nil, nil, build.ExtendErr("error creating testing host", err)
   161  	}
   162  	c, err := newTestingContractor(filepath.Join(testdir, "Contractor"), g, cs, tp)
   163  	if err != nil {
   164  		return nil, nil, nil, err
   165  	}
   166  
   167  	// announce the host
   168  	err = h.Announce()
   169  	if err != nil {
   170  		return nil, nil, nil, build.ExtendErr("error announcing host", err)
   171  	}
   172  
   173  	// mine a block, processing the announcement
   174  	_, err = m.AddBlock()
   175  	if err != nil {
   176  		return nil, nil, nil, err
   177  	}
   178  
   179  	// wait for hostdb to scan host
   180  	for i := 0; i < 50 && len(c.hdb.ActiveHosts()) == 0; i++ {
   181  		time.Sleep(time.Millisecond * 100)
   182  	}
   183  	if len(c.hdb.ActiveHosts()) == 0 {
   184  		return nil, nil, nil, errors.New("host did not make it into the contractor hostdb in time")
   185  	}
   186  
   187  	return h, c, m, nil
   188  }
   189  
   190  // TestIntegrationFormContract tests that the contractor can form contracts
   191  // with the host module.
   192  func TestIntegrationFormContract(t *testing.T) {
   193  	if testing.Short() {
   194  		t.SkipNow()
   195  	}
   196  	t.Parallel()
   197  	h, c, _, err := newTestingTrio(t.Name())
   198  	if err != nil {
   199  		t.Fatal(err)
   200  	}
   201  	defer h.Close()
   202  	defer c.Close()
   203  
   204  	// acquire the contract maintenance lock for the duration of the test. This
   205  	// prevents theadedContractMaintenance from running.
   206  	c.maintenanceLock.Lock()
   207  	defer c.maintenanceLock.Unlock()
   208  
   209  	// get the host's entry from the db
   210  	hostEntry, ok := c.hdb.Host(h.PublicKey())
   211  	if !ok {
   212  		t.Fatal("no entry for host in db")
   213  	}
   214  
   215  	// set an allowance but don't use SetAllowance to avoid automatic contract
   216  	// formation.
   217  	c.mu.Lock()
   218  	c.allowance = modules.DefaultAllowance
   219  	c.mu.Unlock()
   220  
   221  	// form a contract with the host
   222  	_, _, err = c.managedNewContract(hostEntry, types.SiacoinPrecision.Mul64(50), c.blockHeight+100)
   223  	if err != nil {
   224  		t.Fatal(err)
   225  	}
   226  }
   227  
   228  // TestFormContractSmallAllowance tests to make sure that a contract doesn't
   229  // form when there are insufficient funds in the allowance
   230  func TestFormContractSmallAllowance(t *testing.T) {
   231  	if testing.Short() {
   232  		t.SkipNow()
   233  	}
   234  	t.Parallel()
   235  	h, c, _, err := newTestingTrio(t.Name())
   236  	if err != nil {
   237  		t.Fatal(err)
   238  	}
   239  	defer h.Close()
   240  	defer c.Close()
   241  
   242  	// get the host's entry from the db
   243  	hostEntry, ok := c.hdb.Host(h.PublicKey())
   244  	if !ok {
   245  		t.Fatal("no entry for host in db")
   246  	}
   247  
   248  	// set an allowance but don't use SetAllowance to avoid automatic contract
   249  	// formation. Setting funds to 1SC to mimic bug report found in production.
   250  	// Using production number of hosts as well
   251  	c.mu.Lock()
   252  	c.allowance = modules.DefaultAllowance
   253  	c.allowance.Funds = types.SiacoinPrecision.Mul64(1)
   254  	c.allowance.Hosts = uint64(50)
   255  	initialContractFunds := c.allowance.Funds.Div64(c.allowance.Hosts).Div64(3)
   256  	c.mu.Unlock()
   257  
   258  	// try to form a contract with the host
   259  	_, _, err = c.managedNewContract(hostEntry, initialContractFunds, c.blockHeight+100)
   260  	if err == nil {
   261  		t.Fatal("Expected underflow error for insufficient funds")
   262  	}
   263  }
   264  
   265  // TestIntegrationReviseContract tests that the contractor can revise a
   266  // contract previously formed with a host.
   267  func TestIntegrationReviseContract(t *testing.T) {
   268  	if testing.Short() {
   269  		t.SkipNow()
   270  	}
   271  	t.Parallel()
   272  	// create testing trio
   273  	h, c, _, err := newTestingTrio(t.Name())
   274  	if err != nil {
   275  		t.Fatal(err)
   276  	}
   277  	defer h.Close()
   278  	defer c.Close()
   279  
   280  	// acquire the contract maintenance lock for the duration of the test. This
   281  	// prevents theadedContractMaintenance from running.
   282  	c.maintenanceLock.Lock()
   283  	defer c.maintenanceLock.Unlock()
   284  
   285  	// get the host's entry from the db
   286  	hostEntry, ok := c.hdb.Host(h.PublicKey())
   287  	if !ok {
   288  		t.Fatal("no entry for host in db")
   289  	}
   290  
   291  	// set an allowance but don't use SetAllowance to avoid automatic contract
   292  	// formation.
   293  	c.mu.Lock()
   294  	c.allowance = modules.DefaultAllowance
   295  	c.mu.Unlock()
   296  
   297  	// form a contract with the host
   298  	_, contract, err := c.managedNewContract(hostEntry, types.SiacoinPrecision.Mul64(50), c.blockHeight+100)
   299  	if err != nil {
   300  		t.Fatal(err)
   301  	}
   302  
   303  	// revise the contract
   304  	editor, err := c.Editor(contract.HostPublicKey, nil)
   305  	if err != nil {
   306  		t.Fatal(err)
   307  	}
   308  	data := fastrand.Bytes(int(modules.SectorSize))
   309  	_, err = editor.Upload(data)
   310  	if err != nil {
   311  		t.Fatal(err)
   312  	}
   313  	err = editor.Close()
   314  	if err != nil {
   315  		t.Fatal(err)
   316  	}
   317  }
   318  
   319  // TestIntegrationUploadDownload tests that the contractor can upload data to
   320  // a host and download it intact.
   321  func TestIntegrationUploadDownload(t *testing.T) {
   322  	if testing.Short() {
   323  		t.SkipNow()
   324  	}
   325  	t.Parallel()
   326  	// create testing trio
   327  	h, c, _, err := newTestingTrio(t.Name())
   328  	if err != nil {
   329  		t.Fatal(err)
   330  	}
   331  	defer h.Close()
   332  	defer c.Close()
   333  
   334  	// get the host's entry from the db
   335  	hostEntry, ok := c.hdb.Host(h.PublicKey())
   336  	if !ok {
   337  		t.Fatal("no entry for host in db")
   338  	}
   339  
   340  	// set an allowance but don't use SetAllowance to avoid automatic contract
   341  	// formation.
   342  	c.mu.Lock()
   343  	c.allowance = modules.DefaultAllowance
   344  	c.mu.Unlock()
   345  
   346  	// form a contract with the host
   347  	_, contract, err := c.managedNewContract(hostEntry, types.SiacoinPrecision.Mul64(50), c.blockHeight+100)
   348  	if err != nil {
   349  		t.Fatal(err)
   350  	}
   351  
   352  	// revise the contract
   353  	editor, err := c.Editor(contract.HostPublicKey, nil)
   354  	if err != nil {
   355  		t.Fatal(err)
   356  	}
   357  	data := fastrand.Bytes(int(modules.SectorSize))
   358  	root, err := editor.Upload(data)
   359  	if err != nil {
   360  		t.Fatal(err)
   361  	}
   362  	err = editor.Close()
   363  	if err != nil {
   364  		t.Fatal(err)
   365  	}
   366  
   367  	// download the data
   368  	downloader, err := c.Downloader(contract.HostPublicKey, nil)
   369  	if err != nil {
   370  		t.Fatal(err)
   371  	}
   372  	retrieved, err := downloader.Download(root, 0, uint32(modules.SectorSize))
   373  	if err != nil {
   374  		t.Fatal(err)
   375  	}
   376  	if !bytes.Equal(data, retrieved) {
   377  		t.Fatal("downloaded data does not match original")
   378  	}
   379  	err = downloader.Close()
   380  	if err != nil {
   381  		t.Fatal(err)
   382  	}
   383  }
   384  
   385  // TestIntegrationRenew tests that the contractor can renew a previously-
   386  // formed file contract.
   387  func TestIntegrationRenew(t *testing.T) {
   388  	if testing.Short() {
   389  		t.SkipNow()
   390  	}
   391  	t.Parallel()
   392  	// create testing trio
   393  	h, c, _, err := newTestingTrio(t.Name())
   394  	if err != nil {
   395  		t.Fatal(err)
   396  	}
   397  	defer h.Close()
   398  	defer c.Close()
   399  
   400  	// set an allowance and wait for a contract to be formed.
   401  	if err := c.SetAllowance(modules.DefaultAllowance); err != nil {
   402  		t.Fatal(err)
   403  	}
   404  	if err := build.Retry(10, time.Second, func() error {
   405  		if len(c.Contracts()) == 0 {
   406  			return errors.New("no contracts were formed")
   407  		}
   408  		return nil
   409  	}); err != nil {
   410  		t.Fatal(err)
   411  	}
   412  	// get the contract
   413  	contract := c.Contracts()[0]
   414  
   415  	// revise the contract
   416  	editor, err := c.Editor(contract.HostPublicKey, nil)
   417  	if err != nil {
   418  		t.Fatal(err)
   419  	}
   420  	data := fastrand.Bytes(int(modules.SectorSize))
   421  	// insert the sector
   422  	root, err := editor.Upload(data)
   423  	if err != nil {
   424  		t.Fatal(err)
   425  	}
   426  	err = editor.Close()
   427  	if err != nil {
   428  		t.Fatal(err)
   429  	}
   430  
   431  	// renew the contract
   432  	err = c.managedUpdateContractUtility(contract.ID, modules.ContractUtility{GoodForRenew: true})
   433  	if err != nil {
   434  		t.Fatal(err)
   435  	}
   436  	oldContract, ok := c.staticContracts.Acquire(contract.ID)
   437  	if !ok {
   438  		t.Fatal("failed to acquire contract")
   439  	}
   440  	contract, err = c.managedRenew(oldContract, types.SiacoinPrecision.Mul64(50), c.blockHeight+200)
   441  	if err != nil {
   442  		t.Fatal(err)
   443  	}
   444  	c.staticContracts.Return(oldContract)
   445  
   446  	// check renewed contract
   447  	if contract.EndHeight != c.blockHeight+200 {
   448  		t.Fatal(contract.EndHeight)
   449  	}
   450  
   451  	// download the renewed contract
   452  	downloader, err := c.Downloader(contract.HostPublicKey, nil)
   453  	if err != nil {
   454  		t.Fatal(err)
   455  	}
   456  	retrieved, err := downloader.Download(root, 0, uint32(modules.SectorSize))
   457  	if err != nil {
   458  		t.Fatal(err)
   459  	}
   460  	if !bytes.Equal(data, retrieved) {
   461  		t.Fatal("downloaded data does not match original")
   462  	}
   463  	err = downloader.Close()
   464  	if err != nil {
   465  		t.Fatal(err)
   466  	}
   467  
   468  	// renew to a lower height
   469  	err = c.managedUpdateContractUtility(contract.ID, modules.ContractUtility{GoodForRenew: true})
   470  	if err != nil {
   471  		t.Fatal(err)
   472  	}
   473  	oldContract, _ = c.staticContracts.Acquire(contract.ID)
   474  	contract, err = c.managedRenew(oldContract, types.SiacoinPrecision.Mul64(50), c.blockHeight+100)
   475  	if err != nil {
   476  		t.Fatal(err)
   477  	}
   478  	c.staticContracts.Return(oldContract)
   479  	if contract.EndHeight != c.blockHeight+100 {
   480  		t.Fatal(contract.EndHeight)
   481  	}
   482  
   483  	// revise the contract
   484  	editor, err = c.Editor(contract.HostPublicKey, nil)
   485  	if err != nil {
   486  		t.Fatal(err)
   487  	}
   488  	data = fastrand.Bytes(int(modules.SectorSize))
   489  	// insert the sector
   490  	_, err = editor.Upload(data)
   491  	if err != nil {
   492  		t.Fatal(err)
   493  	}
   494  	err = editor.Close()
   495  	if err != nil {
   496  		t.Fatal(err)
   497  	}
   498  }
   499  
   500  // TestIntegrationDownloaderCaching tests that downloaders are properly cached
   501  // by the contractor. When two downloaders are requested for the same
   502  // contract, only one underlying downloader should be created.
   503  func TestIntegrationDownloaderCaching(t *testing.T) {
   504  	if testing.Short() {
   505  		t.SkipNow()
   506  	}
   507  	t.Parallel()
   508  	// create testing trio
   509  	h, c, _, err := newTestingTrio(t.Name())
   510  	if err != nil {
   511  		t.Fatal(err)
   512  	}
   513  	defer h.Close()
   514  	defer c.Close()
   515  
   516  	// set an allowance and wait for a contract to be formed.
   517  	if err := c.SetAllowance(modules.DefaultAllowance); err != nil {
   518  		t.Fatal(err)
   519  	}
   520  	if err := build.Retry(10, time.Second, func() error {
   521  		if len(c.Contracts()) == 0 {
   522  			return errors.New("no contracts were formed")
   523  		}
   524  		return nil
   525  	}); err != nil {
   526  		t.Fatal(err)
   527  	}
   528  	// get the contract
   529  	contract := c.Contracts()[0]
   530  
   531  	// create a downloader
   532  	d1, err := c.Downloader(contract.HostPublicKey, nil)
   533  	if err != nil {
   534  		t.Fatal(err)
   535  	}
   536  
   537  	// create another downloader
   538  	d2, err := c.Downloader(contract.HostPublicKey, nil)
   539  	if err != nil {
   540  		t.Fatal(err)
   541  	}
   542  
   543  	// downloaders should match
   544  	if d1 != d2 {
   545  		t.Fatal("downloader was not cached")
   546  	}
   547  
   548  	// close one of the downloaders; it should not fully close, since d1 is
   549  	// still using it
   550  	d2.Close()
   551  
   552  	c.mu.RLock()
   553  	_, ok := c.downloaders[contract.ID]
   554  	_, sok := c.sessions[contract.ID]
   555  	c.mu.RUnlock()
   556  	if !ok && !sok {
   557  		t.Fatal("expected downloader to still be present")
   558  	}
   559  
   560  	// create another downloader
   561  	d3, err := c.Downloader(contract.HostPublicKey, nil)
   562  	if err != nil {
   563  		t.Fatal(err)
   564  	}
   565  
   566  	// downloaders should match
   567  	if d3 != d1 {
   568  		t.Fatal("closing one client should not fully close the downloader")
   569  	}
   570  
   571  	// close both downloaders
   572  	d1.Close()
   573  	d2.Close()
   574  
   575  	c.mu.RLock()
   576  	_, ok = c.downloaders[contract.ID]
   577  	_, sok = c.sessions[contract.ID]
   578  	c.mu.RUnlock()
   579  	if ok || sok {
   580  		t.Fatal("did not expect downloader to still be present")
   581  	}
   582  
   583  	// create another downloader
   584  	d4, err := c.Downloader(contract.HostPublicKey, nil)
   585  	if err != nil {
   586  		t.Fatal(err)
   587  	}
   588  
   589  	// downloaders should match
   590  	if d4 == d1 {
   591  		t.Fatal("downloader should not have been cached after all clients were closed")
   592  	}
   593  	d4.Close()
   594  }
   595  
   596  // TestIntegrationEditorCaching tests that editors are properly cached
   597  // by the contractor. When two editors are requested for the same
   598  // contract, only one underlying editor should be created.
   599  func TestIntegrationEditorCaching(t *testing.T) {
   600  	if testing.Short() {
   601  		t.SkipNow()
   602  	}
   603  	t.Parallel()
   604  	// create testing trio
   605  	h, c, _, err := newTestingTrio(t.Name())
   606  	if err != nil {
   607  		t.Fatal(err)
   608  	}
   609  	defer h.Close()
   610  	defer c.Close()
   611  
   612  	// set an allowance and wait for a contract to be formed.
   613  	if err := c.SetAllowance(modules.DefaultAllowance); err != nil {
   614  		t.Fatal(err)
   615  	}
   616  	if err := build.Retry(10, time.Second, func() error {
   617  		if len(c.Contracts()) == 0 {
   618  			return errors.New("no contracts were formed")
   619  		}
   620  		return nil
   621  	}); err != nil {
   622  		t.Fatal(err)
   623  	}
   624  	// get the contract
   625  	contract := c.Contracts()[0]
   626  
   627  	// create an editor
   628  	d1, err := c.Editor(contract.HostPublicKey, nil)
   629  	if err != nil {
   630  		t.Fatal(err)
   631  	}
   632  
   633  	// create another editor
   634  	d2, err := c.Editor(contract.HostPublicKey, nil)
   635  	if err != nil {
   636  		t.Fatal(err)
   637  	}
   638  
   639  	// editors should match
   640  	if d1 != d2 {
   641  		t.Fatal("editor was not cached")
   642  	}
   643  
   644  	// close one of the editors; it should not fully close, since d1 is
   645  	// still using it
   646  	d2.Close()
   647  
   648  	c.mu.RLock()
   649  	_, ok := c.editors[contract.ID]
   650  	_, sok := c.sessions[contract.ID]
   651  	c.mu.RUnlock()
   652  	if !ok && !sok {
   653  		t.Fatal("expected editor to still be present")
   654  	}
   655  
   656  	// create another editor
   657  	d3, err := c.Editor(contract.HostPublicKey, nil)
   658  	if err != nil {
   659  		t.Fatal(err)
   660  	}
   661  
   662  	// editors should match
   663  	if d3 != d1 {
   664  		t.Fatal("closing one client should not fully close the editor")
   665  	}
   666  
   667  	// close both editors
   668  	d1.Close()
   669  	d2.Close()
   670  
   671  	c.mu.RLock()
   672  	_, ok = c.editors[contract.ID]
   673  	_, sok = c.sessions[contract.ID]
   674  	c.mu.RUnlock()
   675  	if ok || sok {
   676  		t.Fatal("did not expect editor to still be present")
   677  	}
   678  
   679  	// create another editor
   680  	d4, err := c.Editor(contract.HostPublicKey, nil)
   681  	if err != nil {
   682  		t.Fatal(err)
   683  	}
   684  
   685  	// editors should match
   686  	if d4 == d1 {
   687  		t.Fatal("editor should not have been cached after all clients were closed")
   688  	}
   689  	d4.Close()
   690  }
   691  
   692  // TestContractPresenceLeak tests that a renter can not tell from the response
   693  // of the host to RPCs if the host has the contract if the renter doesn't
   694  // own this contract. See https://gitlab.com/SiaPrime/SiaPrime/issues/2327.
   695  func TestContractPresenceLeak(t *testing.T) {
   696  	if testing.Short() {
   697  		t.SkipNow()
   698  	}
   699  	t.Parallel()
   700  	// create testing trio
   701  	h, c, _, err := newTestingTrio(t.Name())
   702  	if err != nil {
   703  		t.Fatal(err)
   704  	}
   705  	defer h.Close()
   706  	defer c.Close()
   707  
   708  	// get the host's entry from the db
   709  	hostEntry, ok := c.hdb.Host(h.PublicKey())
   710  	if !ok {
   711  		t.Fatal("no entry for host in db")
   712  	}
   713  
   714  	// set an allowance but don't use SetAllowance to avoid automatic contract
   715  	// formation.
   716  	c.mu.Lock()
   717  	c.allowance = modules.DefaultAllowance
   718  	c.mu.Unlock()
   719  
   720  	// form a contract with the host
   721  	_, contract, err := c.managedNewContract(hostEntry, types.SiacoinPrecision.Mul64(10), c.blockHeight+100)
   722  	if err != nil {
   723  		t.Fatal(err)
   724  	}
   725  
   726  	// Connect with bad challenge response. Try correct
   727  	// and incorrect contract IDs. Compare errors.
   728  	wrongID := contract.ID
   729  	wrongID[0] ^= 0x01
   730  	fcids := []types.FileContractID{contract.ID, wrongID}
   731  	var errors []error
   732  
   733  	for _, fcid := range fcids {
   734  		var challenge crypto.Hash
   735  		var signature crypto.Signature
   736  		conn, err := net.Dial("tcp", string(hostEntry.NetAddress))
   737  		if err != nil {
   738  			t.Fatalf("Couldn't dial tpc connection with host @ %v: %v.", string(hostEntry.NetAddress), err)
   739  		}
   740  		if err := encoding.WriteObject(conn, modules.RPCDownload); err != nil {
   741  			t.Fatalf("Couldn't initiate RPC: %v.", err)
   742  		}
   743  		if err := encoding.WriteObject(conn, fcid); err != nil {
   744  			t.Fatalf("Couldn't send fcid: %v.", err)
   745  		}
   746  		if err := encoding.ReadObject(conn, &challenge, 32); err != nil {
   747  			t.Fatalf("Couldn't read challenge: %v.", err)
   748  		}
   749  		if err := encoding.WriteObject(conn, signature); err != nil {
   750  			t.Fatalf("Couldn't send signature: %v.", err)
   751  		}
   752  		err = modules.ReadNegotiationAcceptance(conn)
   753  		if err == nil {
   754  			t.Fatal("Expected an error, got success.")
   755  		}
   756  		errors = append(errors, err)
   757  	}
   758  	if errors[0].Error() != errors[1].Error() {
   759  		t.Fatalf("Expected to get equal errors, got %q and %q.", errors[0], errors[1])
   760  	}
   761  }