github.com/fozzysec/SiaPrime@v0.0.0-20190612043147-66c8e8d11fe3/modules/renter/hostdb/hostdb_test.go (about)

     1  package hostdb
     2  
     3  import (
     4  	"bytes"
     5  	"io/ioutil"
     6  	"math"
     7  	"net"
     8  	"os"
     9  	"path/filepath"
    10  	"testing"
    11  	"time"
    12  
    13  	"SiaPrime/build"
    14  	"SiaPrime/crypto"
    15  	"SiaPrime/modules"
    16  	"SiaPrime/modules/consensus"
    17  	"SiaPrime/modules/gateway"
    18  	"SiaPrime/modules/miner"
    19  	"SiaPrime/modules/renter/hostdb/hosttree"
    20  	"SiaPrime/modules/transactionpool"
    21  	"SiaPrime/modules/wallet"
    22  	"SiaPrime/persist"
    23  	"SiaPrime/types"
    24  )
    25  
    26  // hdbTester contains a hostdb and all dependencies.
    27  type hdbTester struct {
    28  	cs        modules.ConsensusSet
    29  	gateway   modules.Gateway
    30  	miner     modules.TestMiner
    31  	tpool     modules.TransactionPool
    32  	wallet    modules.Wallet
    33  	walletKey crypto.TwofishKey
    34  
    35  	hdb *HostDB
    36  
    37  	persistDir string
    38  }
    39  
    40  // bareHostDB returns a HostDB with its fields initialized, but without any
    41  // dependencies or scanning threads. It is only intended for use in unit tests.
    42  func bareHostDB() *HostDB {
    43  	hdb := &HostDB{
    44  		log: persist.NewLogger(ioutil.Discard),
    45  	}
    46  	hdb.weightFunc = hdb.calculateHostWeightFn(modules.DefaultAllowance)
    47  	hdb.hostTree = hosttree.New(hdb.weightFunc, &modules.ProductionResolver{})
    48  	return hdb
    49  }
    50  
    51  // makeHostDBEntry makes a new host entry with a random public key
    52  func makeHostDBEntry() modules.HostDBEntry {
    53  	dbe := modules.HostDBEntry{}
    54  	_, pk := crypto.GenerateKeyPair()
    55  
    56  	dbe.AcceptingContracts = true
    57  	dbe.PublicKey = types.Ed25519PublicKey(pk)
    58  	dbe.ScanHistory = modules.HostDBScans{{
    59  		Timestamp: time.Now(),
    60  		Success:   true,
    61  	}}
    62  	return dbe
    63  }
    64  
    65  // newHDBTester returns a tester object wrapping a HostDB and some extra
    66  // information for testing.
    67  func newHDBTester(name string) (*hdbTester, error) {
    68  	return newHDBTesterDeps(name, modules.ProdDependencies)
    69  }
    70  
    71  // newHDBTesterDeps returns a tester object wrapping a HostDB and some extra
    72  // information for testing, using the provided dependencies for the hostdb.
    73  func newHDBTesterDeps(name string, deps modules.Dependencies) (*hdbTester, error) {
    74  	if testing.Short() {
    75  		panic("should not be calling newHDBTester during short tests")
    76  	}
    77  	testDir := build.TempDir("HostDB", name)
    78  
    79  	g, err := gateway.New("localhost:0", false, filepath.Join(testDir, modules.GatewayDir))
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  	cs, err := consensus.New(g, false, filepath.Join(testDir, modules.ConsensusDir))
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	tp, err := transactionpool.New(cs, g, filepath.Join(testDir, modules.TransactionPoolDir))
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  	w, err := wallet.New(cs, tp, filepath.Join(testDir, modules.WalletDir))
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  	m, err := miner.New(cs, tp, w, filepath.Join(testDir, modules.MinerDir))
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  	hdb, err := NewCustomHostDB(g, cs, filepath.Join(testDir, modules.RenterDir), deps)
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  
   104  	hdbt := &hdbTester{
   105  		cs:      cs,
   106  		gateway: g,
   107  		miner:   m,
   108  		tpool:   tp,
   109  		wallet:  w,
   110  
   111  		hdb: hdb,
   112  
   113  		persistDir: testDir,
   114  	}
   115  
   116  	err = hdbt.initWallet()
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  
   121  	return hdbt, nil
   122  }
   123  
   124  // initWallet creates a wallet key, then initializes and unlocks the wallet.
   125  func (hdbt *hdbTester) initWallet() error {
   126  	hdbt.walletKey = crypto.GenerateTwofishKey()
   127  	_, err := hdbt.wallet.Encrypt(hdbt.walletKey)
   128  	if err != nil {
   129  		return err
   130  	}
   131  	err = hdbt.wallet.Unlock(hdbt.walletKey)
   132  	if err != nil {
   133  		return err
   134  	}
   135  	return nil
   136  }
   137  
   138  // TestAverageContractPrice tests the AverageContractPrice method, which also depends on the
   139  // randomHosts method.
   140  func TestAverageContractPrice(t *testing.T) {
   141  	hdb := bareHostDB()
   142  
   143  	// empty
   144  	if avg := hdb.AverageContractPrice(); !avg.IsZero() {
   145  		t.Error("average of empty hostdb should be zero:", avg)
   146  	}
   147  
   148  	// with one host
   149  	h1 := makeHostDBEntry()
   150  	h1.ContractPrice = types.NewCurrency64(100)
   151  	hdb.hostTree.Insert(h1)
   152  	if avg := hdb.AverageContractPrice(); avg.Cmp(h1.ContractPrice) != 0 {
   153  		t.Error("average of one host should be that host's price:", avg)
   154  	}
   155  
   156  	// with two hosts
   157  	h2 := makeHostDBEntry()
   158  	h2.ContractPrice = types.NewCurrency64(300)
   159  	hdb.hostTree.Insert(h2)
   160  	if avg := hdb.AverageContractPrice(); avg.Cmp64(200) != 0 {
   161  		t.Error("average of two hosts should be their sum/2:", avg)
   162  	}
   163  }
   164  
   165  // TestNew tests the New function.
   166  func TestNew(t *testing.T) {
   167  	if testing.Short() {
   168  		t.SkipNow()
   169  	}
   170  	testDir := build.TempDir("HostDB", t.Name())
   171  	g, err := gateway.New("localhost:0", false, filepath.Join(testDir, modules.GatewayDir))
   172  	if err != nil {
   173  		t.Fatal(err)
   174  	}
   175  	cs, err := consensus.New(g, false, filepath.Join(testDir, modules.ConsensusDir))
   176  	if err != nil {
   177  		t.Fatal(err)
   178  	}
   179  
   180  	// Vanilla HDB, nothing should go wrong.
   181  	hdbName := filepath.Join(testDir, modules.RenterDir)
   182  	_, err = New(g, cs, hdbName+"1")
   183  	if err != nil {
   184  		t.Fatal(err)
   185  	}
   186  
   187  	// Nil gateway.
   188  	_, err = New(nil, cs, hdbName+"2")
   189  	if err != errNilGateway {
   190  		t.Fatalf("expected %v, got %v", errNilGateway, err)
   191  	}
   192  	// Nil consensus set.
   193  	_, err = New(g, nil, hdbName+"3")
   194  	if err != errNilCS {
   195  		t.Fatalf("expected %v, got %v", errNilCS, err)
   196  	}
   197  	// Bad persistDir.
   198  	_, err = New(g, cs, "")
   199  	if !os.IsNotExist(err) {
   200  		t.Fatalf("expected invalid directory, got %v", err)
   201  	}
   202  }
   203  
   204  // quitAfterLoadDeps will quit startup in newHostDB
   205  type disableScanLoopDeps struct {
   206  	modules.ProductionDependencies
   207  }
   208  
   209  // Send a disrupt signal to the quitAfterLoad codebreak.
   210  func (*disableScanLoopDeps) Disrupt(s string) bool {
   211  	if s == "disableScanLoop" {
   212  		return true
   213  	}
   214  	return false
   215  }
   216  
   217  // TestRandomHosts tests the hostdb's exported RandomHosts method.
   218  func TestRandomHosts(t *testing.T) {
   219  	if testing.Short() {
   220  		t.SkipNow()
   221  	}
   222  	hdbt, err := newHDBTesterDeps(t.Name(), &disableScanLoopDeps{})
   223  	if err != nil {
   224  		t.Fatal(err)
   225  	}
   226  
   227  	entries := make(map[string]modules.HostDBEntry)
   228  	nEntries := int(1e3)
   229  	for i := 0; i < nEntries; i++ {
   230  		entry := makeHostDBEntry()
   231  		entries[string(entry.PublicKey.Key)] = entry
   232  		err := hdbt.hdb.hostTree.Insert(entry)
   233  		if err != nil {
   234  			t.Error(err)
   235  		}
   236  	}
   237  
   238  	// Check that all hosts can be queried.
   239  	for i := 0; i < 25; i++ {
   240  		hosts, err := hdbt.hdb.RandomHosts(nEntries, nil, nil)
   241  		if err != nil {
   242  			t.Fatal("Failed to get hosts", err)
   243  		}
   244  		if len(hosts) != nEntries {
   245  			t.Errorf("RandomHosts returned few entries. got %v wanted %v\n", len(hosts), nEntries)
   246  		}
   247  		dupCheck := make(map[string]modules.HostDBEntry)
   248  		for _, host := range hosts {
   249  			_, exists := entries[string(host.PublicKey.Key)]
   250  			if !exists {
   251  				t.Error("hostdb returning host that doesn't exist.")
   252  			}
   253  			_, exists = dupCheck[string(host.PublicKey.Key)]
   254  			if exists {
   255  				t.Error("RandomHosts returning duplicates")
   256  			}
   257  			dupCheck[string(host.PublicKey.Key)] = host
   258  		}
   259  	}
   260  
   261  	// Base case, fill out a map exposing hosts from a single RH query.
   262  	dupCheck1 := make(map[string]modules.HostDBEntry)
   263  	hosts, err := hdbt.hdb.RandomHosts(nEntries/2, nil, nil)
   264  	if err != nil {
   265  		t.Fatal("Failed to get hosts", err)
   266  	}
   267  	if len(hosts) != nEntries/2 {
   268  		t.Fatalf("RandomHosts returned few entries. got %v wanted %v\n", len(hosts), nEntries/2)
   269  	}
   270  	for _, host := range hosts {
   271  		_, exists := entries[string(host.PublicKey.Key)]
   272  		if !exists {
   273  			t.Error("hostdb returning host that doesn't exist.")
   274  		}
   275  		_, exists = dupCheck1[string(host.PublicKey.Key)]
   276  		if exists {
   277  			t.Error("RandomHosts returning duplicates")
   278  		}
   279  		dupCheck1[string(host.PublicKey.Key)] = host
   280  	}
   281  
   282  	// Iterative case. Check that every time you query for random hosts, you
   283  	// get different responses.
   284  	for i := 0; i < 10; i++ {
   285  		dupCheck2 := make(map[string]modules.HostDBEntry)
   286  		var overlap, disjoint bool
   287  		hosts, err = hdbt.hdb.RandomHosts(nEntries/2, nil, nil)
   288  		if err != nil {
   289  			t.Fatal("Failed to get hosts", err)
   290  		}
   291  		if len(hosts) != nEntries/2 {
   292  			t.Fatalf("RandomHosts returned few entries. got %v wanted %v\n", len(hosts), nEntries/2)
   293  		}
   294  		for _, host := range hosts {
   295  			_, exists := entries[string(host.PublicKey.Key)]
   296  			if !exists {
   297  				t.Error("hostdb returning host that doesn't exist.")
   298  			}
   299  			_, exists = dupCheck2[string(host.PublicKey.Key)]
   300  			if exists {
   301  				t.Error("RandomHosts returning duplicates")
   302  			}
   303  			_, exists = dupCheck1[string(host.PublicKey.Key)]
   304  			if exists {
   305  				overlap = true
   306  			} else {
   307  				disjoint = true
   308  			}
   309  			dupCheck2[string(host.PublicKey.Key)] = host
   310  
   311  		}
   312  		if !overlap || !disjoint {
   313  			t.Error("Random hosts does not seem to be random")
   314  		}
   315  		dupCheck1 = dupCheck2
   316  	}
   317  
   318  	// Try exclude list by excluding every host except for the last one, and
   319  	// doing a random select.
   320  	for i := 0; i < 25; i++ {
   321  		hosts, err := hdbt.hdb.RandomHosts(nEntries, nil, nil)
   322  		if err != nil {
   323  			t.Fatal("Failed to get hosts", err)
   324  		}
   325  		var exclude []types.SiaPublicKey
   326  		for j := 1; j < len(hosts); j++ {
   327  			exclude = append(exclude, hosts[j].PublicKey)
   328  		}
   329  		rand, err := hdbt.hdb.RandomHosts(1, exclude, nil)
   330  		if err != nil {
   331  			t.Fatal("Failed to get hosts", err)
   332  		}
   333  		if len(rand) != 1 {
   334  			t.Fatal("wrong number of hosts returned")
   335  		}
   336  		if string(rand[0].PublicKey.Key) != string(hosts[0].PublicKey.Key) {
   337  			t.Error("exclude list seems to be excluding the wrong hosts.")
   338  		}
   339  
   340  		// Try again but request more hosts than are available.
   341  		rand, err = hdbt.hdb.RandomHosts(5, exclude, nil)
   342  		if err != nil {
   343  			t.Fatal("Failed to get hosts", err)
   344  		}
   345  		if len(rand) != 1 {
   346  			t.Fatal("wrong number of hosts returned")
   347  		}
   348  		if string(rand[0].PublicKey.Key) != string(hosts[0].PublicKey.Key) {
   349  			t.Error("exclude list seems to be excluding the wrong hosts.")
   350  		}
   351  
   352  		// Create an include map, and decrease the number of excluded hosts.
   353  		// Make sure all hosts returned by rand function are in the include
   354  		// map.
   355  		includeMap := make(map[string]struct{})
   356  		for j := 0; j < 50; j++ {
   357  			includeMap[string(hosts[j].PublicKey.Key)] = struct{}{}
   358  		}
   359  		exclude = exclude[49:]
   360  
   361  		// Select only 20 hosts.
   362  		dupCheck := make(map[string]struct{})
   363  		rand, err = hdbt.hdb.RandomHosts(20, exclude, nil)
   364  		if err != nil {
   365  			t.Fatal("Failed to get hosts", err)
   366  		}
   367  		if len(rand) != 20 {
   368  			t.Error("random hosts is returning the wrong number of hosts")
   369  		}
   370  		for _, host := range rand {
   371  			_, exists := dupCheck[string(host.PublicKey.Key)]
   372  			if exists {
   373  				t.Error("RandomHosts is seleccting duplicates")
   374  			}
   375  			dupCheck[string(host.PublicKey.Key)] = struct{}{}
   376  			_, exists = includeMap[string(host.PublicKey.Key)]
   377  			if !exists {
   378  				t.Error("RandomHosts returning excluded hosts")
   379  			}
   380  		}
   381  
   382  		// Select exactly 50 hosts.
   383  		dupCheck = make(map[string]struct{})
   384  		rand, err = hdbt.hdb.RandomHosts(50, exclude, nil)
   385  		if err != nil {
   386  			t.Fatal("Failed to get hosts", err)
   387  		}
   388  		if len(rand) != 50 {
   389  			t.Error("random hosts is returning the wrong number of hosts")
   390  		}
   391  		for _, host := range rand {
   392  			_, exists := dupCheck[string(host.PublicKey.Key)]
   393  			if exists {
   394  				t.Error("RandomHosts is seleccting duplicates")
   395  			}
   396  			dupCheck[string(host.PublicKey.Key)] = struct{}{}
   397  			_, exists = includeMap[string(host.PublicKey.Key)]
   398  			if !exists {
   399  				t.Error("RandomHosts returning excluded hosts")
   400  			}
   401  		}
   402  
   403  		// Select 100 hosts.
   404  		dupCheck = make(map[string]struct{})
   405  		rand, err = hdbt.hdb.RandomHosts(100, exclude, nil)
   406  		if err != nil {
   407  			t.Fatal("Failed to get hosts", err)
   408  		}
   409  		if len(rand) != 50 {
   410  			t.Error("random hosts is returning the wrong number of hosts")
   411  		}
   412  		for _, host := range rand {
   413  			_, exists := dupCheck[string(host.PublicKey.Key)]
   414  			if exists {
   415  				t.Error("RandomHosts is seleccting duplicates")
   416  			}
   417  			dupCheck[string(host.PublicKey.Key)] = struct{}{}
   418  			_, exists = includeMap[string(host.PublicKey.Key)]
   419  			if !exists {
   420  				t.Error("RandomHosts returning excluded hosts")
   421  			}
   422  		}
   423  	}
   424  }
   425  
   426  // TestRemoveNonexistingHostFromHostTree checks that the host tree interface
   427  // correctly responds to having a nonexisting host removed from the host tree.
   428  func TestRemoveNonexistingHostFromHostTree(t *testing.T) {
   429  	if testing.Short() {
   430  		t.SkipNow()
   431  	}
   432  	hdbt, err := newHDBTester(t.Name())
   433  	if err != nil {
   434  		t.Fatal(err)
   435  	}
   436  
   437  	// Remove a host that doesn't exist from the tree.
   438  	err = hdbt.hdb.hostTree.Remove(types.SiaPublicKey{})
   439  	if err == nil {
   440  		t.Fatal("There should be an error, but not a panic:", err)
   441  	}
   442  }
   443  
   444  // TestUpdateHistoricInteractions is a simple check to ensure that incrementing
   445  // the recent and historic host interactions works
   446  func TestUpdateHistoricInteractions(t *testing.T) {
   447  	if testing.Short() {
   448  		t.SkipNow()
   449  	}
   450  
   451  	// create a HostDB tester without scanloop to be able to manually increment
   452  	// the interactions without interference.
   453  	hdbt, err := newHDBTesterDeps(t.Name(), &disableScanLoopDeps{})
   454  	if err != nil {
   455  		t.Fatal(err)
   456  	}
   457  
   458  	// create a HostDBEntry and add it to the tree
   459  	host := makeHostDBEntry()
   460  	err = hdbt.hdb.hostTree.Insert(host)
   461  	if err != nil {
   462  		t.Error(err)
   463  	}
   464  
   465  	// increment successful and failed interactions by 100
   466  	interactions := 100.0
   467  	for i := 0.0; i < interactions; i++ {
   468  		hdbt.hdb.IncrementSuccessfulInteractions(host.PublicKey)
   469  		hdbt.hdb.IncrementFailedInteractions(host.PublicKey)
   470  	}
   471  
   472  	// get updated host from hostdb
   473  	host, ok := hdbt.hdb.Host(host.PublicKey)
   474  	if !ok {
   475  		t.Fatal("Modified host not found in hostdb")
   476  	}
   477  
   478  	// check that recent interactions are exactly 100 and historic interactions are 0
   479  	if host.RecentFailedInteractions != interactions || host.RecentSuccessfulInteractions != interactions {
   480  		t.Errorf("Interactions should be %v but were %v and %v", interactions,
   481  			host.RecentFailedInteractions, host.RecentSuccessfulInteractions)
   482  	}
   483  	if host.HistoricFailedInteractions != 0 || host.HistoricSuccessfulInteractions != 0 {
   484  		t.Errorf("Historic Interactions should be %v but were %v and %v", 0,
   485  			host.HistoricFailedInteractions, host.HistoricSuccessfulInteractions)
   486  	}
   487  
   488  	// add single block to consensus
   489  	_, err = hdbt.miner.AddBlock()
   490  	if err != nil {
   491  		t.Fatal(err)
   492  	}
   493  
   494  	// increment interactions again by 100
   495  	for i := 0.0; i < interactions; i++ {
   496  		hdbt.hdb.IncrementSuccessfulInteractions(host.PublicKey)
   497  		hdbt.hdb.IncrementFailedInteractions(host.PublicKey)
   498  	}
   499  
   500  	// get updated host from hostdb
   501  	host, ok = hdbt.hdb.Host(host.PublicKey)
   502  	if !ok {
   503  		t.Fatal("Modified host not found in hostdb")
   504  	}
   505  
   506  	// historic actions should have incremented slightly, due to the clamp the
   507  	// full interactions should not have made it into the historic group.
   508  	if host.RecentFailedInteractions != interactions || host.RecentSuccessfulInteractions != interactions {
   509  		t.Errorf("Interactions should be %v but were %v and %v", interactions,
   510  			host.RecentFailedInteractions, host.RecentSuccessfulInteractions)
   511  	}
   512  	if host.HistoricFailedInteractions == 0 || host.HistoricSuccessfulInteractions == 0 {
   513  		t.Error("historic actions should have updated")
   514  	}
   515  
   516  	// add 200 blocks to consensus, adding large numbers of historic actions
   517  	// each time, so that the clamp does not need to be in effect anymore.
   518  	for i := 0; i < 200; i++ {
   519  		for j := uint64(0); j < 10; j++ {
   520  			hdbt.hdb.IncrementSuccessfulInteractions(host.PublicKey)
   521  			hdbt.hdb.IncrementFailedInteractions(host.PublicKey)
   522  		}
   523  		_, err = hdbt.miner.AddBlock()
   524  		if err != nil {
   525  			t.Fatal(err)
   526  		}
   527  	}
   528  
   529  	// Add five interactions
   530  	for i := 0; i < 5; i++ {
   531  		hdbt.hdb.IncrementSuccessfulInteractions(host.PublicKey)
   532  		hdbt.hdb.IncrementFailedInteractions(host.PublicKey)
   533  	}
   534  
   535  	// get updated host from hostdb
   536  	host, ok = hdbt.hdb.Host(host.PublicKey)
   537  	if !ok {
   538  		t.Fatal("Modified host not found in hostdb")
   539  	}
   540  
   541  	// check that recent interactions are exactly 5. Save the historic actions
   542  	// to check that decay is being handled correctly, and that the recent
   543  	// interactions are moved over correctly.
   544  	if host.RecentFailedInteractions != 5 || host.RecentSuccessfulInteractions != 5 {
   545  		t.Errorf("Interactions should be %v but were %v and %v", interactions,
   546  			host.RecentFailedInteractions, host.RecentSuccessfulInteractions)
   547  	}
   548  	historicFailed := host.HistoricFailedInteractions
   549  	if host.HistoricFailedInteractions != host.HistoricSuccessfulInteractions {
   550  		t.Error("historic failed and successful should have the same values")
   551  	}
   552  
   553  	// Add a single block to apply one round of decay.
   554  	_, err = hdbt.miner.AddBlock()
   555  	if err != nil {
   556  		t.Fatal(err)
   557  	}
   558  	host, ok = hdbt.hdb.Host(host.PublicKey)
   559  	if !ok {
   560  		t.Fatal("Modified host not found in hostdb")
   561  	}
   562  
   563  	// Get the historic successful and failed interactions, and see that they
   564  	// are decaying properly.
   565  	expected := historicFailed*math.Pow(historicInteractionDecay, 1) + 5
   566  	if host.HistoricFailedInteractions != expected || host.HistoricSuccessfulInteractions != expected {
   567  		t.Errorf("Historic Interactions should be %v but were %v and %v", expected,
   568  			host.HistoricFailedInteractions, host.HistoricSuccessfulInteractions)
   569  	}
   570  
   571  	// Add 10 more blocks and check the decay again, make sure it's being
   572  	// applied correctly.
   573  	for i := 0; i < 10; i++ {
   574  		_, err := hdbt.miner.AddBlock()
   575  		if err != nil {
   576  			t.Fatal(err)
   577  		}
   578  	}
   579  	host, ok = hdbt.hdb.Host(host.PublicKey)
   580  	if !ok {
   581  		t.Fatal("Modified host not found in hostdb")
   582  	}
   583  	expected = expected * math.Pow(historicInteractionDecay, 10)
   584  	if host.HistoricFailedInteractions != expected || host.HistoricSuccessfulInteractions != expected {
   585  		t.Errorf("Historic Interactions should be %v but were %v and %v", expected,
   586  			host.HistoricFailedInteractions, host.HistoricSuccessfulInteractions)
   587  	}
   588  }
   589  
   590  // testCheckForIPViolationsResolver is a resolver for the TestTwoAddresses test.
   591  type testCheckForIPViolationsResolver struct{}
   592  
   593  func (testCheckForIPViolationsResolver) LookupIP(host string) ([]net.IP, error) {
   594  	switch host {
   595  	case "host1":
   596  		return []net.IP{{127, 0, 0, 1}}, nil
   597  	case "host2":
   598  		return []net.IP{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, nil
   599  	case "host3":
   600  		return []net.IP{{127, 0, 0, 2}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}}, nil
   601  	default:
   602  		panic("shouldn't happen")
   603  	}
   604  }
   605  
   606  // testCheckForIPViolationsDeps is a custom dependency that overrides the
   607  // Resolver method to return a testCheckForIPViolationsResolver.
   608  type testCheckForIPViolationsDeps struct {
   609  	disableScanLoopDeps
   610  }
   611  
   612  // Resolver returns a testCheckForIPViolationsResolver.
   613  func (*testCheckForIPViolationsDeps) Resolver() modules.Resolver {
   614  	return &testCheckForIPViolationsResolver{}
   615  }
   616  
   617  // TestCheckForIPViolations tests the hostdb's CheckForIPViolations method.
   618  func TestCheckForIPViolations(t *testing.T) {
   619  	if testing.Short() {
   620  		t.SkipNow()
   621  	}
   622  
   623  	// Prepare a few hosts for the test
   624  	entry1 := makeHostDBEntry()
   625  	entry1.NetAddress = "host1:1234"
   626  	entry2 := makeHostDBEntry()
   627  	entry2.NetAddress = "host2:1234"
   628  	entry3 := makeHostDBEntry()
   629  	entry3.NetAddress = "host3:1234"
   630  
   631  	// create a HostDB tester without scanloop to be able to manually increment
   632  	// the interactions without interference.
   633  	hdbt, err := newHDBTesterDeps(t.Name(), &testCheckForIPViolationsDeps{})
   634  	if err != nil {
   635  		t.Fatal(err)
   636  	}
   637  
   638  	// Scan the entries. entry1 should be the 'oldest' and entry3 the
   639  	// 'youngest'. This also inserts the entries into the hosttree.
   640  	hdbt.hdb.managedScanHost(entry1)
   641  	entry1, _ = hdbt.hdb.Host(entry1.PublicKey)
   642  	time.Sleep(time.Millisecond)
   643  
   644  	hdbt.hdb.managedScanHost(entry2)
   645  	entry2, _ = hdbt.hdb.Host(entry2.PublicKey)
   646  	time.Sleep(time.Millisecond)
   647  
   648  	hdbt.hdb.managedScanHost(entry3)
   649  	entry3, _ = hdbt.hdb.Host(entry3.PublicKey)
   650  	time.Sleep(time.Millisecond)
   651  
   652  	// Make sure that the timestamps are not zero and that they entries have
   653  	// subnets associated with them.
   654  	if len(entry1.IPNets) == 0 || entry1.LastIPNetChange.IsZero() {
   655  		t.Fatal("entry1 wasn't updated correctly")
   656  	}
   657  	if len(entry2.IPNets) == 0 || entry2.LastIPNetChange.IsZero() {
   658  		t.Fatal("entry2 wasn't updated correctly")
   659  	}
   660  	if len(entry3.IPNets) == 0 || entry3.LastIPNetChange.IsZero() {
   661  		t.Fatal("entry3 wasn't updated correctly")
   662  	}
   663  
   664  	// Scan all the entries again in reversed order. This is a sanity check. If
   665  	// the code works as expected this shouldn't do anything since the
   666  	// hostnames didn't change. If it doesn't, it will update the timestamps
   667  	// and the following checks will fail.
   668  	time.Sleep(time.Millisecond)
   669  	hdbt.hdb.managedScanHost(entry3)
   670  	entry3, _ = hdbt.hdb.Host(entry3.PublicKey)
   671  
   672  	time.Sleep(time.Millisecond)
   673  	hdbt.hdb.managedScanHost(entry2)
   674  	entry2, _ = hdbt.hdb.Host(entry2.PublicKey)
   675  
   676  	time.Sleep(time.Millisecond)
   677  	hdbt.hdb.managedScanHost(entry1)
   678  	entry1, _ = hdbt.hdb.Host(entry1.PublicKey)
   679  
   680  	// Add entry1 and entry2. There should be no violation.
   681  	badHosts := hdbt.hdb.CheckForIPViolations([]types.SiaPublicKey{entry1.PublicKey, entry2.PublicKey})
   682  	if len(badHosts) != 0 {
   683  		t.Errorf("Got %v violations, should be 0", len(badHosts))
   684  	}
   685  
   686  	// Add entry3. It should cause a violation for entry 3.
   687  	badHosts = hdbt.hdb.CheckForIPViolations([]types.SiaPublicKey{entry1.PublicKey, entry2.PublicKey, entry3.PublicKey})
   688  	if len(badHosts) != 1 {
   689  		t.Errorf("Got %v violations, should be 1", len(badHosts))
   690  	}
   691  	if len(badHosts) > 0 && !bytes.Equal(badHosts[0].Key, entry3.PublicKey.Key) {
   692  		t.Error("Hdb returned violation for wrong host")
   693  	}
   694  
   695  	// Calling CheckForIPViolations with entry 2 as the first argument and
   696  	// entry1 as the second should result in entry3 being the bad host again.
   697  	badHosts = hdbt.hdb.CheckForIPViolations([]types.SiaPublicKey{entry2.PublicKey, entry1.PublicKey, entry3.PublicKey})
   698  	if len(badHosts) != 1 {
   699  		t.Errorf("Got %v violations, should be 1", len(badHosts))
   700  	}
   701  	if len(badHosts) > 0 && !bytes.Equal(badHosts[0].Key, entry3.PublicKey.Key) {
   702  		t.Error("Hdb returned violation for wrong host")
   703  	}
   704  
   705  	// Calling CheckForIPViolations with entry 3 as the first argument should
   706  	// result in 1 bad host, entry3. The reason being that entry3 is the
   707  	// 'youngest' entry.
   708  	badHosts = hdbt.hdb.CheckForIPViolations([]types.SiaPublicKey{entry3.PublicKey, entry1.PublicKey, entry2.PublicKey})
   709  	if len(badHosts) != 1 {
   710  		t.Errorf("Got %v violations, should be 1", len(badHosts))
   711  	}
   712  	if len(badHosts) > 1 || !bytes.Equal(badHosts[0].Key, entry3.PublicKey.Key) {
   713  		t.Error("Hdb returned violation for wrong host")
   714  	}
   715  }