gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/renter/hostdb/hostdb_test.go (about) 1 package hostdb 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "math" 7 "net" 8 "os" 9 "path/filepath" 10 "testing" 11 "time" 12 13 "gitlab.com/NebulousLabs/errors" 14 "gitlab.com/SkynetLabs/skyd/build" 15 "gitlab.com/SkynetLabs/skyd/siatest/dependencies" 16 "gitlab.com/SkynetLabs/skyd/skymodules" 17 "gitlab.com/SkynetLabs/skyd/skymodules/renter/hostdb/hosttree" 18 "go.sia.tech/siad/crypto" 19 "go.sia.tech/siad/modules" 20 "go.sia.tech/siad/modules/consensus" 21 "go.sia.tech/siad/modules/gateway" 22 "go.sia.tech/siad/modules/miner" 23 "go.sia.tech/siad/modules/transactionpool" 24 "go.sia.tech/siad/modules/wallet" 25 "go.sia.tech/siad/persist" 26 "go.sia.tech/siad/types" 27 28 "gitlab.com/NebulousLabs/siamux" 29 ) 30 31 // hdbTester contains a hostdb and all dependencies. 32 type hdbTester struct { 33 cs modules.ConsensusSet 34 gateway modules.Gateway 35 miner modules.TestMiner 36 tpool modules.TransactionPool 37 mux *siamux.SiaMux 38 wallet modules.Wallet 39 walletKey crypto.CipherKey 40 41 hdb *HostDB 42 43 persistDir string 44 } 45 46 // bareHostDB returns a HostDB with its fields initialized, but without any 47 // dependencies or scanning threads. It is only intended for use in unit tests. 48 func bareHostDB() *HostDB { 49 logger, err := persist.NewLogger(ioutil.Discard) 50 if err != nil { 51 panic(err) 52 } 53 hdb := &HostDB{ 54 allowance: skymodules.DefaultAllowance, 55 staticLog: logger, 56 knownContracts: make(map[string]contractInfo), 57 priceTables: make(map[string]modules.RPCPriceTable), 58 } 59 hdb.weightFunc = hdb.managedCalculateHostWeightFn(hdb.allowance) 60 hdb.staticHostTree = hosttree.New(hdb.weightFunc, &modules.ProductionResolver{}) 61 hdb.staticFilteredTree = hosttree.New(hdb.weightFunc, &modules.ProductionResolver{}) 62 return hdb 63 } 64 65 // makeHostDBEntry makes a new host entry with a random public key 66 func makeHostDBEntry() skymodules.HostDBEntry { 67 dbe := DefaultHostDBEntry 68 _, pk := crypto.GenerateKeyPair() 69 70 dbe.PublicKey = types.Ed25519PublicKey(pk) 71 dbe.ScanHistory = skymodules.HostDBScans{{ 72 Timestamp: time.Now(), 73 Success: true, 74 }} 75 return dbe 76 } 77 78 // newHDBTester returns a tester object wrapping a HostDB and some extra 79 // information for testing. 80 func newHDBTester(name string) (*hdbTester, error) { 81 return newHDBTesterDeps(name, modules.ProdDependencies) 82 } 83 84 // newHDBTesterDeps returns a tester object wrapping a HostDB and some extra 85 // information for testing, using the provided dependencies for the hostdb. 86 func newHDBTesterDeps(name string, deps modules.Dependencies) (*hdbTester, error) { 87 if testing.Short() { 88 panic("should not be calling newHDBTester during short tests") 89 } 90 testDir := build.TempDir("HostDB", name) 91 92 g, err := gateway.New("localhost:0", false, filepath.Join(testDir, modules.GatewayDir)) 93 if err != nil { 94 return nil, err 95 } 96 cs, errChan := consensus.New(g, false, filepath.Join(testDir, modules.ConsensusDir)) 97 if err := <-errChan; err != nil { 98 return nil, err 99 } 100 tp, err := transactionpool.New(cs, g, filepath.Join(testDir, modules.TransactionPoolDir)) 101 if err != nil { 102 return nil, err 103 } 104 mux, err := modules.NewSiaMux(filepath.Join(testDir, modules.SiaMuxDir), testDir, "localhost:0", "localhost:0") 105 if err != nil { 106 return nil, err 107 } 108 w, err := wallet.New(cs, tp, filepath.Join(testDir, modules.WalletDir)) 109 if err != nil { 110 return nil, err 111 } 112 m, err := miner.New(cs, tp, w, filepath.Join(testDir, modules.MinerDir)) 113 if err != nil { 114 return nil, err 115 } 116 hdb, errChan := NewCustomHostDB(g, cs, tp, mux, filepath.Join(testDir, skymodules.RenterDir), deps) 117 if err := <-errChan; err != nil { 118 return nil, err 119 } 120 121 hdbt := &hdbTester{ 122 cs: cs, 123 gateway: g, 124 miner: m, 125 tpool: tp, 126 wallet: w, 127 mux: mux, 128 129 hdb: hdb, 130 131 persistDir: testDir, 132 } 133 134 err = hdbt.initWallet() 135 if err != nil { 136 return nil, err 137 } 138 139 return hdbt, nil 140 } 141 142 // initWallet creates a wallet key, then initializes and unlocks the wallet. 143 func (hdbt *hdbTester) initWallet() error { 144 hdbt.walletKey = crypto.GenerateSiaKey(crypto.TypeDefaultWallet) 145 _, err := hdbt.wallet.Encrypt(hdbt.walletKey) 146 if err != nil { 147 return err 148 } 149 err = hdbt.wallet.Unlock(hdbt.walletKey) 150 if err != nil { 151 return err 152 } 153 return nil 154 } 155 156 // TestNew tests the New function. 157 func TestNew(t *testing.T) { 158 if testing.Short() { 159 t.SkipNow() 160 } 161 t.Parallel() 162 testDir := build.TempDir("HostDB", t.Name()) 163 g, err := gateway.New("localhost:0", false, filepath.Join(testDir, modules.GatewayDir)) 164 if err != nil { 165 t.Fatal(err) 166 } 167 cs, errChan := consensus.New(g, false, filepath.Join(testDir, modules.ConsensusDir)) 168 if err := <-errChan; err != nil { 169 t.Fatal(err) 170 } 171 tp, err := transactionpool.New(cs, g, filepath.Join(testDir, modules.TransactionPoolDir)) 172 if err != nil { 173 t.Fatal(err) 174 } 175 mux, err := modules.NewSiaMux(filepath.Join(testDir, modules.SiaMuxDir), testDir, "localhost:0", "localhost:0") 176 if err != nil { 177 t.Fatal(err) 178 } 179 180 // Vanilla HDB, nothing should go wrong. 181 hdbName := filepath.Join(testDir, skymodules.RenterDir) 182 _, errChan = New(g, cs, tp, mux, hdbName+"1") 183 if err := <-errChan; err != nil { 184 t.Fatal(err) 185 } 186 187 // Nil gateway. 188 _, errChan = New(nil, cs, tp, mux, hdbName+"2") 189 if err := <-errChan; !errors.Contains(err, errNilGateway) { 190 t.Fatalf("expected %v, got %v", errNilGateway, err) 191 } 192 // Nil consensus set. 193 _, errChan = New(g, nil, tp, mux, hdbName+"3") 194 if err := <-errChan; !errors.Contains(err, errNilCS) { 195 t.Fatalf("expected %v, got %v", errNilCS, err) 196 } 197 // Nil tpool. 198 _, errChan = New(g, cs, nil, mux, hdbName+"3") 199 if err := <-errChan; !errors.Contains(err, errNilTPool) { 200 t.Fatalf("expected %v, got %v", errNilTPool, err) 201 } 202 // TODO: Nil siamux? 203 // Bad persistDir. 204 _, errChan = New(g, cs, tp, mux, "") 205 if err := <-errChan; !os.IsNotExist(err) { 206 t.Fatalf("expected invalid directory, got %v", err) 207 } 208 } 209 210 // TestRandomHosts tests the hostdb's exported RandomHosts method. 211 func TestRandomHosts(t *testing.T) { 212 if testing.Short() { 213 t.SkipNow() 214 } 215 t.Parallel() 216 hdbt, err := newHDBTesterDeps(t.Name(), &dependencies.DependencyDisableScanLoopDeps{}) 217 if err != nil { 218 t.Fatal(err) 219 } 220 221 entries := make(map[string]skymodules.HostDBEntry) 222 nEntries := int(1e3) 223 for i := 0; i < nEntries; i++ { 224 entry := makeHostDBEntry() 225 entries[entry.PublicKey.String()] = entry 226 err := hdbt.hdb.staticFilteredTree.Insert(entry) 227 if err != nil { 228 t.Error(err) 229 } 230 } 231 232 // Check that all hosts can be queried. 233 for i := 0; i < 25; i++ { 234 hosts, err := hdbt.hdb.RandomHosts(nEntries, nil, nil) 235 if err != nil { 236 t.Fatal("Failed to get hosts", err) 237 } 238 if len(hosts) != nEntries { 239 t.Errorf("RandomHosts returned few entries. got %v wanted %v\n", len(hosts), nEntries) 240 } 241 dupCheck := make(map[string]skymodules.HostDBEntry) 242 for _, host := range hosts { 243 _, exists := entries[host.PublicKey.String()] 244 if !exists { 245 t.Error("hostdb returning host that doesn't exist.") 246 } 247 _, exists = dupCheck[host.PublicKey.String()] 248 if exists { 249 t.Error("RandomHosts returning duplicates") 250 } 251 dupCheck[host.PublicKey.String()] = host 252 } 253 } 254 255 // Base case, fill out a map exposing hosts from a single RH query. 256 dupCheck1 := make(map[string]skymodules.HostDBEntry) 257 hosts, err := hdbt.hdb.RandomHosts(nEntries/2, nil, nil) 258 if err != nil { 259 t.Fatal("Failed to get hosts", err) 260 } 261 if len(hosts) != nEntries/2 { 262 t.Fatalf("RandomHosts returned few entries. got %v wanted %v\n", len(hosts), nEntries/2) 263 } 264 for _, host := range hosts { 265 _, exists := entries[host.PublicKey.String()] 266 if !exists { 267 t.Error("hostdb returning host that doesn't exist.") 268 } 269 _, exists = dupCheck1[host.PublicKey.String()] 270 if exists { 271 t.Error("RandomHosts returning duplicates") 272 } 273 dupCheck1[host.PublicKey.String()] = host 274 } 275 276 // Iterative case. Check that every time you query for random hosts, you 277 // get different responses. 278 for i := 0; i < 10; i++ { 279 dupCheck2 := make(map[string]skymodules.HostDBEntry) 280 var overlap, disjoint bool 281 hosts, err = hdbt.hdb.RandomHosts(nEntries/2, nil, nil) 282 if err != nil { 283 t.Fatal("Failed to get hosts", err) 284 } 285 if len(hosts) != nEntries/2 { 286 t.Fatalf("RandomHosts returned few entries. got %v wanted %v\n", len(hosts), nEntries/2) 287 } 288 for _, host := range hosts { 289 _, exists := entries[host.PublicKey.String()] 290 if !exists { 291 t.Error("hostdb returning host that doesn't exist.") 292 } 293 _, exists = dupCheck2[host.PublicKey.String()] 294 if exists { 295 t.Error("RandomHosts returning duplicates") 296 } 297 _, exists = dupCheck1[host.PublicKey.String()] 298 if exists { 299 overlap = true 300 } else { 301 disjoint = true 302 } 303 dupCheck2[host.PublicKey.String()] = host 304 } 305 if !overlap || !disjoint { 306 t.Error("Random hosts does not seem to be random") 307 } 308 dupCheck1 = dupCheck2 309 } 310 311 // Try exclude list by excluding every host except for the last one, and 312 // doing a random select. 313 for i := 0; i < 25; i++ { 314 hosts, err := hdbt.hdb.RandomHosts(nEntries, nil, nil) 315 if err != nil { 316 t.Fatal("Failed to get hosts", err) 317 } 318 var exclude []types.SiaPublicKey 319 for j := 1; j < len(hosts); j++ { 320 exclude = append(exclude, hosts[j].PublicKey) 321 } 322 rand, err := hdbt.hdb.RandomHosts(1, exclude, nil) 323 if err != nil { 324 t.Fatal("Failed to get hosts", err) 325 } 326 if len(rand) != 1 { 327 t.Fatal("wrong number of hosts returned") 328 } 329 if !rand[0].PublicKey.Equals(hosts[0].PublicKey) { 330 t.Error("exclude list seems to be excluding the wrong hosts.") 331 } 332 333 // Try again but request more hosts than are available. 334 rand, err = hdbt.hdb.RandomHosts(5, exclude, nil) 335 if err != nil { 336 t.Fatal("Failed to get hosts", err) 337 } 338 if len(rand) != 1 { 339 t.Fatal("wrong number of hosts returned") 340 } 341 if !rand[0].PublicKey.Equals(hosts[0].PublicKey) { 342 t.Error("exclude list seems to be excluding the wrong hosts.") 343 } 344 345 // Create an include map, and decrease the number of excluded hosts. 346 // Make sure all hosts returned by rand function are in the include 347 // map. 348 includeMap := make(map[string]struct{}) 349 for j := 0; j < 50; j++ { 350 includeMap[hosts[j].PublicKey.String()] = struct{}{} 351 } 352 exclude = exclude[49:] 353 354 // Select only 20 hosts. 355 dupCheck := make(map[string]struct{}) 356 rand, err = hdbt.hdb.RandomHosts(20, exclude, nil) 357 if err != nil { 358 t.Fatal("Failed to get hosts", err) 359 } 360 if len(rand) != 20 { 361 t.Error("random hosts is returning the wrong number of hosts") 362 } 363 for _, host := range rand { 364 _, exists := dupCheck[host.PublicKey.String()] 365 if exists { 366 t.Error("RandomHosts is selecting duplicates") 367 } 368 dupCheck[host.PublicKey.String()] = struct{}{} 369 _, exists = includeMap[host.PublicKey.String()] 370 if !exists { 371 t.Error("RandomHosts returning excluded hosts") 372 } 373 } 374 375 // Select exactly 50 hosts. 376 dupCheck = make(map[string]struct{}) 377 rand, err = hdbt.hdb.RandomHosts(50, exclude, nil) 378 if err != nil { 379 t.Fatal("Failed to get hosts", err) 380 } 381 if len(rand) != 50 { 382 t.Error("random hosts is returning the wrong number of hosts") 383 } 384 for _, host := range rand { 385 _, exists := dupCheck[host.PublicKey.String()] 386 if exists { 387 t.Error("RandomHosts is selecting duplicates") 388 } 389 dupCheck[host.PublicKey.String()] = struct{}{} 390 _, exists = includeMap[host.PublicKey.String()] 391 if !exists { 392 t.Error("RandomHosts returning excluded hosts") 393 } 394 } 395 396 // Select 100 hosts. 397 dupCheck = make(map[string]struct{}) 398 rand, err = hdbt.hdb.RandomHosts(100, exclude, nil) 399 if err != nil { 400 t.Fatal("Failed to get hosts", err) 401 } 402 if len(rand) != 50 { 403 t.Error("random hosts is returning the wrong number of hosts") 404 } 405 for _, host := range rand { 406 _, exists := dupCheck[host.PublicKey.String()] 407 if exists { 408 t.Error("RandomHosts is selecting duplicates") 409 } 410 dupCheck[host.PublicKey.String()] = struct{}{} 411 _, exists = includeMap[host.PublicKey.String()] 412 if !exists { 413 t.Error("RandomHosts returning excluded hosts") 414 } 415 } 416 } 417 } 418 419 // TestRemoveNonexistingHostFromHostTree checks that the host tree interface 420 // correctly responds to having a nonexisting host removed from the host tree. 421 func TestRemoveNonexistingHostFromHostTree(t *testing.T) { 422 if testing.Short() { 423 t.SkipNow() 424 } 425 t.Parallel() 426 hdbt, err := newHDBTester(t.Name()) 427 if err != nil { 428 t.Fatal(err) 429 } 430 431 // Remove a host that doesn't exist from the tree. 432 err = hdbt.hdb.staticHostTree.Remove(types.SiaPublicKey{}) 433 if err == nil { 434 t.Fatal("There should be an error, but not a panic:", err) 435 } 436 } 437 438 // TestUpdateHistoricInteractions is a simple check to ensure that incrementing 439 // the recent and historic host interactions works 440 func TestUpdateHistoricInteractions(t *testing.T) { 441 if testing.Short() { 442 t.SkipNow() 443 } 444 t.Parallel() 445 446 // create a HostDB tester without scanloop to be able to manually increment 447 // the interactions without interference. 448 hdbt, err := newHDBTesterDeps(t.Name(), &dependencies.DependencyDisableScanLoopDeps{}) 449 if err != nil { 450 t.Fatal(err) 451 } 452 453 // create a HostDBEntry and add it to the tree 454 host := makeHostDBEntry() 455 err = hdbt.hdb.staticHostTree.Insert(host) 456 if err != nil { 457 t.Error(err) 458 } 459 460 // increment successful and failed interactions by 100 461 interactions := 100.0 462 for i := 0.0; i < interactions; i++ { 463 hdbt.hdb.IncrementSuccessfulInteractions(host.PublicKey) 464 hdbt.hdb.IncrementFailedInteractions(host.PublicKey) 465 } 466 467 // get updated host from hostdb 468 host, ok, err := hdbt.hdb.Host(host.PublicKey) 469 if err != nil { 470 t.Fatal(err) 471 } 472 if !ok { 473 t.Fatal("Modified host not found in hostdb") 474 } 475 476 // check that recent interactions are exactly 100 and historic interactions are 0 477 if host.RecentFailedInteractions != interactions || host.RecentSuccessfulInteractions != interactions { 478 t.Errorf("Interactions should be %v but were %v and %v", interactions, 479 host.RecentFailedInteractions, host.RecentSuccessfulInteractions) 480 } 481 if host.HistoricFailedInteractions != 0 || host.HistoricSuccessfulInteractions != 0 { 482 t.Errorf("Historic Interactions should be %v but were %v and %v", 0, 483 host.HistoricFailedInteractions, host.HistoricSuccessfulInteractions) 484 } 485 486 // add single block to consensus 487 _, err = hdbt.miner.AddBlock() 488 if err != nil { 489 t.Fatal(err) 490 } 491 492 // increment interactions again by 100 493 for i := 0.0; i < interactions; i++ { 494 hdbt.hdb.IncrementSuccessfulInteractions(host.PublicKey) 495 hdbt.hdb.IncrementFailedInteractions(host.PublicKey) 496 } 497 498 // get updated host from hostdb 499 host, ok, err = hdbt.hdb.Host(host.PublicKey) 500 if err != nil { 501 t.Fatal(err) 502 } 503 if !ok { 504 t.Fatal("Modified host not found in hostdb") 505 } 506 507 // historic actions should have incremented slightly, due to the clamp the 508 // full interactions should not have made it into the historic group. 509 if host.RecentFailedInteractions != interactions || host.RecentSuccessfulInteractions != interactions { 510 t.Errorf("Interactions should be %v but were %v and %v", interactions, 511 host.RecentFailedInteractions, host.RecentSuccessfulInteractions) 512 } 513 if host.HistoricFailedInteractions == 0 || host.HistoricSuccessfulInteractions == 0 { 514 t.Error("historic actions should have updated") 515 } 516 517 // add 200 blocks to consensus, adding large numbers of historic actions 518 // each time, so that the clamp does not need to be in effect anymore. 519 for i := 0; i < 200; i++ { 520 for j := uint64(0); j < 10; j++ { 521 hdbt.hdb.IncrementSuccessfulInteractions(host.PublicKey) 522 hdbt.hdb.IncrementFailedInteractions(host.PublicKey) 523 } 524 _, err = hdbt.miner.AddBlock() 525 if err != nil { 526 t.Fatal(err) 527 } 528 } 529 530 // Add five interactions 531 for i := 0; i < 5; i++ { 532 hdbt.hdb.IncrementSuccessfulInteractions(host.PublicKey) 533 hdbt.hdb.IncrementFailedInteractions(host.PublicKey) 534 } 535 536 // get updated host from hostdb 537 host, ok, err = hdbt.hdb.Host(host.PublicKey) 538 if err != nil { 539 t.Fatal(err) 540 } 541 if !ok { 542 t.Fatal("Modified host not found in hostdb") 543 } 544 545 // check that recent interactions are exactly 5. Save the historic actions 546 // to check that decay is being handled correctly, and that the recent 547 // interactions are moved over correctly. 548 if host.RecentFailedInteractions != 5 || host.RecentSuccessfulInteractions != 5 { 549 t.Errorf("Interactions should be %v but were %v and %v", interactions, 550 host.RecentFailedInteractions, host.RecentSuccessfulInteractions) 551 } 552 historicFailed := host.HistoricFailedInteractions 553 if host.HistoricFailedInteractions != host.HistoricSuccessfulInteractions { 554 t.Error("historic failed and successful should have the same values") 555 } 556 557 // Add a single block to apply one round of decay. 558 _, err = hdbt.miner.AddBlock() 559 if err != nil { 560 t.Fatal(err) 561 } 562 host, ok, err = hdbt.hdb.Host(host.PublicKey) 563 if err != nil { 564 t.Fatal(err) 565 } 566 if !ok { 567 t.Fatal("Modified host not found in hostdb") 568 } 569 570 // Get the historic successful and failed interactions, and see that they 571 // are decaying properly. 572 expected := historicFailed*math.Pow(historicInteractionDecay, 1) + 5 573 if host.HistoricFailedInteractions != expected || host.HistoricSuccessfulInteractions != expected { 574 t.Errorf("Historic Interactions should be %v but were %v and %v", expected, 575 host.HistoricFailedInteractions, host.HistoricSuccessfulInteractions) 576 } 577 578 // Add 10 more blocks and check the decay again, make sure it's being 579 // applied correctly. 580 for i := 0; i < 10; i++ { 581 _, err := hdbt.miner.AddBlock() 582 if err != nil { 583 t.Fatal(err) 584 } 585 } 586 host, ok, err = hdbt.hdb.Host(host.PublicKey) 587 if err != nil { 588 t.Fatal(err) 589 } 590 if !ok { 591 t.Fatal("Modified host not found in hostdb") 592 } 593 expected = expected * math.Pow(historicInteractionDecay, 10) 594 if host.HistoricFailedInteractions != expected || host.HistoricSuccessfulInteractions != expected { 595 t.Errorf("Historic Interactions should be %v but were %v and %v", expected, 596 host.HistoricFailedInteractions, host.HistoricSuccessfulInteractions) 597 } 598 } 599 600 // testCheckForIPViolationsResolver is a resolver for the TestTwoAddresses test. 601 type testCheckForIPViolationsResolver struct{} 602 603 func (testCheckForIPViolationsResolver) LookupIP(host string) ([]net.IP, error) { 604 switch host { 605 case "host1": 606 return []net.IP{{127, 0, 0, 1}}, nil 607 case "host2": 608 return []net.IP{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, nil 609 case "host3": 610 return []net.IP{{127, 0, 0, 2}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}}, nil 611 default: 612 panic("shouldn't happen") 613 } 614 } 615 616 // testCheckForIPViolationsDeps is a custom dependency that overrides the 617 // Resolver method to return a testCheckForIPViolationsResolver. 618 type testCheckForIPViolationsDeps struct { 619 dependencies.DependencyDisableScanLoopDeps 620 } 621 622 // Resolver returns a testCheckForIPViolationsResolver. 623 func (*testCheckForIPViolationsDeps) Resolver() modules.Resolver { 624 return &testCheckForIPViolationsResolver{} 625 } 626 627 // TestCheckForIPViolations tests the hostdb's CheckForIPViolations method. 628 func TestCheckForIPViolations(t *testing.T) { 629 if testing.Short() { 630 t.SkipNow() 631 } 632 t.Parallel() 633 634 // Prepare a few hosts for the test 635 entry1 := makeHostDBEntry() 636 entry1.NetAddress = "host1:1234" 637 entry2 := makeHostDBEntry() 638 entry2.NetAddress = "host2:1234" 639 entry3 := makeHostDBEntry() 640 entry3.NetAddress = "host3:1234" 641 642 // create a HostDB tester without scanloop to be able to manually increment 643 // the interactions without interference. 644 hdbt, err := newHDBTesterDeps(t.Name(), &testCheckForIPViolationsDeps{}) 645 if err != nil { 646 t.Fatal(err) 647 } 648 649 // Scan the entries. entry1 should be the 'oldest' and entry3 the 650 // 'youngest'. This also inserts the entries into the hosttree. 651 hdbt.hdb.managedScanHost(entry1) 652 entry1, _, err = hdbt.hdb.Host(entry1.PublicKey) 653 if err != nil { 654 t.Fatal(err) 655 } 656 time.Sleep(time.Millisecond) 657 658 hdbt.hdb.managedScanHost(entry2) 659 entry2, _, err = hdbt.hdb.Host(entry2.PublicKey) 660 if err != nil { 661 t.Fatal(err) 662 } 663 time.Sleep(time.Millisecond) 664 665 hdbt.hdb.managedScanHost(entry3) 666 entry3, _, err = hdbt.hdb.Host(entry3.PublicKey) 667 if err != nil { 668 t.Fatal(err) 669 } 670 time.Sleep(time.Millisecond) 671 672 // Make sure that the timestamps are not zero and that they entries have 673 // subnets associated with them. 674 if len(entry1.IPNets) == 0 || entry1.LastIPNetChange.IsZero() { 675 t.Fatal("entry1 wasn't updated correctly") 676 } 677 if len(entry2.IPNets) == 0 || entry2.LastIPNetChange.IsZero() { 678 t.Fatal("entry2 wasn't updated correctly") 679 } 680 if len(entry3.IPNets) == 0 || entry3.LastIPNetChange.IsZero() { 681 t.Fatal("entry3 wasn't updated correctly") 682 } 683 684 // Scan all the entries again in reversed order. This is a sanity check. If 685 // the code works as expected this shouldn't do anything since the 686 // hostnames didn't change. If it doesn't, it will update the timestamps 687 // and the following checks will fail. 688 time.Sleep(time.Millisecond) 689 hdbt.hdb.managedScanHost(entry3) 690 entry3, _, err = hdbt.hdb.Host(entry3.PublicKey) 691 if err != nil { 692 t.Fatal(err) 693 } 694 695 time.Sleep(time.Millisecond) 696 hdbt.hdb.managedScanHost(entry2) 697 entry2, _, err = hdbt.hdb.Host(entry2.PublicKey) 698 if err != nil { 699 t.Fatal(err) 700 } 701 702 time.Sleep(time.Millisecond) 703 hdbt.hdb.managedScanHost(entry1) 704 entry1, _, err = hdbt.hdb.Host(entry1.PublicKey) 705 if err != nil { 706 t.Fatal(err) 707 } 708 709 // Add entry1 and entry2. There should be no violation. 710 badHosts, err := hdbt.hdb.CheckForIPViolations([]types.SiaPublicKey{entry1.PublicKey, entry2.PublicKey}) 711 if err != nil { 712 t.Fatal(err) 713 } 714 if len(badHosts) != 0 { 715 t.Errorf("Got %v violations, should be 0", len(badHosts)) 716 } 717 718 // Add entry3. It should cause a violation for entry 3. 719 badHosts, err = hdbt.hdb.CheckForIPViolations([]types.SiaPublicKey{entry1.PublicKey, entry2.PublicKey, entry3.PublicKey}) 720 if err != nil { 721 t.Fatal(err) 722 } 723 if len(badHosts) != 1 { 724 t.Errorf("Got %v violations, should be 1", len(badHosts)) 725 } 726 if len(badHosts) > 0 && !badHosts[0].Equals(entry3.PublicKey) { 727 t.Error("Hdb returned violation for wrong host") 728 } 729 730 // Calling CheckForIPViolations with entry 2 as the first argument and 731 // entry1 as the second should result in entry3 being the bad host again. 732 badHosts, err = hdbt.hdb.CheckForIPViolations([]types.SiaPublicKey{entry2.PublicKey, entry1.PublicKey, entry3.PublicKey}) 733 if err != nil { 734 t.Fatal(err) 735 } 736 if len(badHosts) != 1 { 737 t.Errorf("Got %v violations, should be 1", len(badHosts)) 738 } 739 if len(badHosts) > 0 && !badHosts[0].Equals(entry3.PublicKey) { 740 t.Error("Hdb returned violation for wrong host") 741 } 742 743 // Calling CheckForIPViolations with entry 3 as the first argument should 744 // result in 1 bad host, entry3. The reason being that entry3 is the 745 // 'youngest' entry. 746 badHosts, err = hdbt.hdb.CheckForIPViolations([]types.SiaPublicKey{entry3.PublicKey, entry1.PublicKey, entry2.PublicKey}) 747 if err != nil { 748 t.Fatal(err) 749 } 750 if len(badHosts) != 1 { 751 t.Errorf("Got %v violations, should be 1", len(badHosts)) 752 } 753 if len(badHosts) > 1 || !badHosts[0].Equals(entry3.PublicKey) { 754 t.Error("Hdb returned violation for wrong host") 755 } 756 } 757 758 // TestBlockedDomains probes the blockedDomains struct and methods 759 func TestBlockedDomains(t *testing.T) { 760 // Create a blocked domain 761 bd := newBlockedDomains(nil) 762 763 // There should be no blocked domains 764 if len(bd.domains) != 0 { 765 t.Fatalf("Expected %v domains but found %v", 0, len(bd.domains)) 766 } 767 768 // Add blocked domains 769 badDomain := "mooo.com" 770 badSubDomain := "bad.mooo.com" 771 localhost := "localhost" 772 localhostIP := "127.0.0.1" 773 ipv6 := "1234:1243::1234:1234" 774 ipv6Zero := "::" 775 tld := "com" 776 domains := []string{badDomain, badSubDomain, localhost, localhostIP, ipv6, ipv6Zero, tld} 777 bd.managedAddDomains(domains) 778 779 // Check expected number of blocked domains 780 expected := len(domains) 781 actual := len(bd.domains) 782 if expected != actual { 783 t.Fatalf("Expected %v domains but found %v", expected, actual) 784 } 785 786 // Re-add one to verify it isn't duplicated 787 bd.managedAddDomains([]string{badDomain}) 788 actual = len(bd.domains) 789 if expected != actual { 790 t.Fatalf("Expected %v domains but found %v", expected, actual) 791 } 792 793 // Probe the managedIsBlocked method 794 var domaintests = []struct { 795 addr modules.NetAddress 796 blocked bool 797 }{ 798 // badDomain Tests 799 {modules.NetAddress(fmt.Sprintf("%s:00", badDomain)), true}, 800 {modules.NetAddress(fmt.Sprintf("sub.%s:00", badDomain)), true}, 801 {modules.NetAddress(fmt.Sprintf("sub.sub.%s:00", badDomain)), true}, 802 {modules.NetAddress(fmt.Sprintf("sub.%s:00", badSubDomain)), true}, 803 {modules.NetAddress(fmt.Sprintf("sub.sub.%s:00", badSubDomain)), true}, 804 805 // localhost test 806 {modules.NetAddress(fmt.Sprintf("%s:00", localhost)), true}, 807 {modules.NetAddress(fmt.Sprintf("%s:00", localhostIP)), true}, 808 809 // ipv6 test 810 // NOTE: ipv6 addresses need to be enclosed in square brackets 811 {modules.NetAddress(fmt.Sprintf("[%s]:00", ipv6)), true}, 812 {modules.NetAddress(fmt.Sprintf("[%s]:00", ipv6Zero)), true}, 813 814 // Not supporting blocking localhost subdomains 815 {modules.NetAddress(fmt.Sprintf("sub.%s:00", localhost)), false}, 816 817 // Not blocked 818 {modules.NetAddress(fmt.Sprintf("%s.weird:00", badDomain)), false}, 819 {modules.NetAddress("somedomain.com:00"), false}, 820 {modules.NetAddress("10.10.10.10:00"), false}, 821 {modules.NetAddress("[123::123]:00"), false}, 822 } 823 for _, domaintest := range domaintests { 824 blocked := bd.managedIsBlocked(domaintest.addr) 825 if blocked != domaintest.blocked { 826 t.Errorf("Incorrect block result for %v; %v != %v", domaintest.addr, domaintest.blocked, blocked) 827 } 828 } 829 830 // Remove all domains 831 bd.managedRemoveDomains(domains) 832 833 // There should be no blocked domains 834 actual = len(bd.domains) 835 if actual != 0 { 836 t.Fatalf("Expected %v domains but found %v", 0, actual) 837 } 838 } 839 840 // TestHostDBBlockDomain tests blocking a domain for a host in the hostdb 841 func TestHostDBBlockDomain(t *testing.T) { 842 if testing.Short() { 843 t.SkipNow() 844 } 845 t.Parallel() 846 847 // Bad domain 848 badDomain := "badhost.com" 849 850 // Prepare two bad hosts with the same root domain 851 entry1 := makeHostDBEntry() 852 entry1.NetAddress = modules.NetAddress(fmt.Sprintf("%v:1234", badDomain)) 853 entry2 := makeHostDBEntry() 854 entry2.NetAddress = modules.NetAddress(fmt.Sprintf("another.%v:1234", badDomain)) 855 entry3 := makeHostDBEntry() 856 entry3.NetAddress = "goodhost.com:1234" 857 858 // create a HostDB tester. 859 hdbt, err := newHDBTester(t.Name()) 860 if err != nil { 861 t.Fatal(err) 862 } 863 864 // Add the three hosts 865 err1 := hdbt.hdb.staticHostTree.Insert(entry1) 866 err2 := hdbt.hdb.staticHostTree.Insert(entry2) 867 err3 := hdbt.hdb.staticHostTree.Insert(entry3) 868 err = errors.Compose(err1, err2, err3) 869 if err != nil { 870 t.Fatal(err) 871 } 872 873 // Verify all hosts are in the hosttree 874 allHosts, err := hdbt.hdb.AllHosts() 875 if err != nil { 876 t.Fatal(err) 877 } 878 if len(allHosts) != 3 { 879 t.Fatalf("Expected %v hosts but got %v", 3, len(allHosts)) 880 } 881 882 // Block the badDomain 883 err = hdbt.hdb.BlockDomains([]string{badDomain}) 884 if err != nil { 885 t.Fatal(err) 886 } 887 888 // Verify there is one domain being blocked 889 blockedDomains, err := hdbt.hdb.BlockedDomains() 890 if err != nil { 891 t.Fatal(err) 892 } 893 if len(blockedDomains) != 1 { 894 t.Fatalf("Expected %v blocked domains, got %v", 1, len(blockedDomains)) 895 } 896 if blockedDomains[0] != badDomain { 897 t.Fatalf("Expected %v to be blocked domain, got %v", badDomain, blockedDomains[0]) 898 } 899 900 // Verify they are blocked 901 if !hdbt.hdb.staticBlockedDomains.managedIsBlocked(entry1.NetAddress) { 902 t.Fatal("host not blocked") 903 } 904 if !hdbt.hdb.staticBlockedDomains.managedIsBlocked(entry2.NetAddress) { 905 t.Fatal("host not blocked") 906 } 907 if hdbt.hdb.staticBlockedDomains.managedIsBlocked(entry3.NetAddress) { 908 t.Fatal("host should not be blocked") 909 } 910 911 // Verify all bad hosts are removed 912 allHosts, err = hdbt.hdb.AllHosts() 913 if err != nil { 914 t.Fatal(err) 915 } 916 if len(allHosts) != 1 { 917 t.Fatalf("Expected %v hosts but got %v", 1, len(allHosts)) 918 } 919 if allHosts[0].NetAddress != entry3.NetAddress { 920 t.Fatal("remaining host is not the good host") 921 } 922 923 // Unblock the badDomain 924 err = hdbt.hdb.UnblockDomains([]string{badDomain}) 925 if err != nil { 926 t.Fatal(err) 927 } 928 929 // Verify there are no domains being blocked 930 blockedDomains, err = hdbt.hdb.BlockedDomains() 931 if err != nil { 932 t.Fatal(err) 933 } 934 if len(blockedDomains) != 0 { 935 t.Fatalf("Expected %v blocked domains, got %v", 0, len(blockedDomains)) 936 } 937 938 // Verify they are not blocked 939 if hdbt.hdb.staticBlockedDomains.managedIsBlocked(entry1.NetAddress) { 940 t.Fatal("host should not be blocked") 941 } 942 if hdbt.hdb.staticBlockedDomains.managedIsBlocked(entry2.NetAddress) { 943 t.Fatal("host should not be blocked") 944 } 945 if hdbt.hdb.staticBlockedDomains.managedIsBlocked(entry3.NetAddress) { 946 t.Fatal("host should not be blocked") 947 } 948 }