github.com/fozzysec/SiaPrime@v0.0.0-20190612043147-66c8e8d11fe3/siatest/renter/hostdb_test.go (about) 1 package renter 2 3 import ( 4 "fmt" 5 "net" 6 "path/filepath" 7 "sort" 8 "testing" 9 "time" 10 11 "SiaPrime/build" 12 "SiaPrime/modules" 13 "SiaPrime/node" 14 "SiaPrime/node/api/client" 15 "SiaPrime/siatest" 16 "gitlab.com/NebulousLabs/errors" 17 ) 18 19 // TestInitialScanComplete tests if the initialScanComplete field is set 20 // correctly. 21 func TestInitialScanComplete(t *testing.T) { 22 if testing.Short() { 23 t.SkipNow() 24 } 25 26 // Get a directory for testing. 27 testDir := renterTestDir(t.Name()) 28 29 // Create a group. The renter should block the scanning thread using a 30 // dependency. 31 deps := &dependencyBlockScan{} 32 renterTemplate := node.Renter(filepath.Join(testDir, "renter")) 33 renterTemplate.SkipSetAllowance = true 34 renterTemplate.SkipHostDiscovery = true 35 renterTemplate.HostDBDeps = deps 36 37 tg, err := siatest.NewGroup(testDir, renterTemplate, node.Host(filepath.Join(testDir, "host")), 38 siatest.Miner(filepath.Join(testDir, "miner"))) 39 if err != nil { 40 t.Fatal("Failed to create group: ", err) 41 } 42 defer func() { 43 deps.Scan() 44 if err := tg.Close(); err != nil { 45 t.Fatal(err) 46 } 47 }() 48 49 // The renter should have 1 offline host in its database and 50 // initialScanComplete should be false. 51 renter := tg.Renters()[0] 52 hdag, err := renter.HostDbAllGet() 53 if err != nil { 54 t.Fatal(err) 55 } 56 hdg, err := renter.HostDbGet() 57 if err != nil { 58 t.Fatal(err) 59 } 60 if len(hdag.Hosts) != 1 { 61 t.Fatalf("HostDB should have 1 host but had %v", len(hdag.Hosts)) 62 } 63 if hdag.Hosts[0].ScanHistory.Len() > 0 { 64 t.Fatalf("Host should have 0 scans but had %v", hdag.Hosts[0].ScanHistory.Len()) 65 } 66 if hdg.InitialScanComplete { 67 t.Fatal("Initial scan is complete even though it shouldn't") 68 } 69 70 deps.Scan() 71 err = build.Retry(600, 100*time.Millisecond, func() error { 72 hdag, err := renter.HostDbAllGet() 73 if err != nil { 74 t.Fatal(err) 75 } 76 hdg, err := renter.HostDbGet() 77 if err != nil { 78 t.Fatal(err) 79 } 80 if !hdg.InitialScanComplete { 81 return fmt.Errorf("Initial scan is not complete even though it should be") 82 } 83 if len(hdag.Hosts) != 1 { 84 return fmt.Errorf("HostDB should have 1 host but had %v", len(hdag.Hosts)) 85 } 86 if hdag.Hosts[0].ScanHistory.Len() == 0 { 87 return fmt.Errorf("Host should have >0 scans but had %v", hdag.Hosts[0].ScanHistory.Len()) 88 } 89 return nil 90 }) 91 if err != nil { 92 t.Fatal(err) 93 } 94 } 95 96 // TestPruneRedundantAddressRange checks if the contractor correctly cancels 97 // contracts with redundant IP ranges. 98 func TestPruneRedundantAddressRange(t *testing.T) { 99 if testing.Short() { 100 t.SkipNow() 101 } 102 t.Parallel() 103 104 // Get the testDir for this test. 105 testDir := renterTestDir(t.Name()) 106 107 // Create a group with a few hosts. 108 groupParams := siatest.GroupParams{ 109 Hosts: 3, 110 Miners: 1, 111 } 112 tg, err := siatest.NewGroupFromTemplate(testDir, groupParams) 113 if err != nil { 114 t.Fatal("Failed to create group: ", err) 115 } 116 defer func() { 117 if err := tg.Close(); err != nil { 118 t.Fatal(err) 119 } 120 }() 121 // Get the ports of the hosts. 122 allHosts := tg.Hosts() 123 hg1, err1 := allHosts[0].HostGet() 124 hg2, err2 := allHosts[1].HostGet() 125 hg3, err3 := allHosts[2].HostGet() 126 err = errors.Compose(err1, err2, err3) 127 if err != nil { 128 t.Fatal("Failed to get ports from at least one host", err) 129 } 130 host1Port := hg1.ExternalSettings.NetAddress.Port() 131 host2Port := hg2.ExternalSettings.NetAddress.Port() 132 host3Port := hg3.ExternalSettings.NetAddress.Port() 133 134 // Reannounce the hosts with custom hostnames which match the hostnames 135 // from the custom resolver method. We announce host1 first and host3 last 136 // to make sure host1 is the 'oldest' and host3 the 'youngest'. 137 err1 = allHosts[0].HostAnnounceAddrPost(modules.NetAddress(fmt.Sprintf("host1.com:%s", host1Port))) 138 err2 = tg.Miners()[0].MineBlock() 139 if err := errors.Compose(err1, err2); err != nil { 140 t.Fatal("failed to announce host1") 141 } 142 err1 = allHosts[1].HostAnnounceAddrPost(modules.NetAddress(fmt.Sprintf("host2.com:%s", host2Port))) 143 err2 = tg.Miners()[0].MineBlock() 144 if err := errors.Compose(err1, err2); err != nil { 145 t.Fatal("failed to announce host2") 146 } 147 err1 = allHosts[2].HostAnnounceAddrPost(modules.NetAddress(fmt.Sprintf("host3.com:%s", host3Port))) 148 err2 = tg.Miners()[0].MineBlock() 149 if err := errors.Compose(err1, err2); err != nil { 150 t.Fatal("failed to announce host3") 151 } 152 153 // Mine announcements. 154 if tg.Miners()[0].MineBlock() != nil { 155 t.Fatal(err) 156 } 157 158 // Add a renter with a custom resolver to the group. 159 renterTemplate := node.Renter(testDir + "/renter") 160 renterTemplate.HostDBDeps = siatest.NewDependencyCustomResolver(func(host string) ([]net.IP, error) { 161 switch host { 162 case "host1.com": 163 return []net.IP{{128, 0, 0, 1}}, nil 164 case "host2.com": 165 return []net.IP{{129, 0, 0, 1}}, nil 166 case "host3.com": 167 return []net.IP{{130, 0, 0, 1}}, nil 168 case "host4.com": 169 return []net.IP{{130, 0, 0, 2}}, nil 170 case "localhost": 171 return []net.IP{{127, 0, 0, 1}}, nil 172 default: 173 panic("shouldn't happen") 174 } 175 }) 176 renterTemplate.ContractorDeps = renterTemplate.HostDBDeps 177 178 // Adding a custom RenewWindow will make a contract renewal during the test 179 // unlikely. 180 renterTemplate.Allowance = siatest.DefaultAllowance 181 renterTemplate.Allowance.Period *= 2 182 renterTemplate.Allowance.RenewWindow = 1 183 renterTemplate.Allowance.Hosts = uint64(len(allHosts)) 184 _, err = tg.AddNodes(renterTemplate) 185 if err != nil { 186 t.Fatal(err) 187 } 188 189 // We expect the renter to have 3 active contracts. 190 renter := tg.Renters()[0] 191 contracts, err := renter.RenterContractsGet() 192 if err != nil { 193 t.Fatal(err) 194 } 195 if len(contracts.ActiveContracts) != len(allHosts) { 196 t.Fatalf("Expected %v active contracts but got %v", len(allHosts), len(contracts.ActiveContracts)) 197 } 198 199 // Check that all the hosts have been scanned. 200 hdag, err := renter.HostDbAllGet() 201 if err != nil { 202 t.Fatal(err) 203 } 204 for _, host := range hdag.Hosts { 205 if host.LastIPNetChange.IsZero() { 206 t.Fatal("host's LastIPNetChange is still zero", host.NetAddress.Host()) 207 } 208 if len(host.IPNets) == 0 { 209 t.Fatal("host doesn't have any IPNets associated with it") 210 } 211 } 212 213 // Reannounce host1 as host4 which creates a violation with host3 and 214 // causes host4 to be the 'youngest'. 215 err = allHosts[0].HostAnnounceAddrPost(modules.NetAddress(fmt.Sprintf("host4.com:%s", host1Port))) 216 if err != nil { 217 t.Fatal("Failed to reannonce host 1") 218 } 219 220 // Mine the announcement. 221 if err := tg.Miners()[0].MineBlock(); err != nil { 222 t.Fatal("Failed to mine block", err) 223 } 224 225 // The 'youngest' host should be host4.com. 226 retry := 0 227 err = build.Retry(600, 100*time.Millisecond, func() error { 228 // Mine new blocks periodically. 229 if retry%60 == 0 { 230 if tg.Miners()[0].MineBlock() != nil { 231 return err 232 } 233 } 234 retry++ 235 hdag, err := renter.HostDbAllGet() 236 if err != nil { 237 return (err) 238 } 239 sort.Slice(hdag.Hosts, func(i, j int) bool { 240 return hdag.Hosts[i].LastIPNetChange.Before(hdag.Hosts[j].LastIPNetChange) 241 }) 242 if hdag.Hosts[len(hdag.Hosts)-1].NetAddress.Host() != "host4.com" { 243 return fmt.Errorf("Youngest host should be host4.com but was %v", hdag.Hosts[len(hdag.Hosts)-1].NetAddress.Host()) 244 } 245 return nil 246 }) 247 if err != nil { 248 renter.PrintDebugInfo(t, true, true, false) 249 t.Fatal(err) 250 } 251 252 // host4.com has the most recent change time. It should be canceled and 253 // show up as inactive. 254 retry = 0 255 err = build.Retry(600, 100*time.Millisecond, func() error { 256 // Mine new blocks periodically. 257 if retry%60 == 0 { 258 if tg.Miners()[0].MineBlock() != nil { 259 return err 260 } 261 } 262 retry++ 263 // The renter should now have 2 active contracts and 1 inactive one. 264 // The inactive one should be host4 since it's the 'youngest'. 265 contracts, err = renter.RenterInactiveContractsGet() 266 if err != nil { 267 return err 268 } 269 if len(contracts.InactiveContracts) != 1 { 270 return fmt.Errorf("Expected 1 inactive contract but got %v", len(contracts.InactiveContracts)) 271 } 272 if len(contracts.ActiveContracts) != len(allHosts)-1 { 273 return fmt.Errorf("Expected %v active contracts but got %v", len(allHosts)-1, len(contracts.ActiveContracts)) 274 } 275 canceledHost := contracts.InactiveContracts[0].NetAddress.Host() 276 if canceledHost != "host4.com" { 277 return fmt.Errorf("Expected canceled contract to be host4.com but was %v", canceledHost) 278 } 279 return nil 280 }) 281 if err != nil { 282 renter.PrintDebugInfo(t, true, true, false) 283 t.Fatal(err) 284 } 285 } 286 287 // TestSelectRandomCanceledHost makes sure that we can form a contract with a 288 // hostB even if it has a conflict with a hostA iff hostA is canceled. 289 func TestSelectRandomCanceledHost(t *testing.T) { 290 if testing.Short() { 291 t.SkipNow() 292 } 293 t.Parallel() 294 295 // Get the testDir for this test. 296 testDir := renterTestDir(t.Name()) 297 298 // Create a group with a single host. 299 groupParams := siatest.GroupParams{ 300 Hosts: 1, 301 Miners: 1, 302 } 303 tg, err := siatest.NewGroupFromTemplate(testDir, groupParams) 304 if err != nil { 305 t.Fatal("Failed to create group: ", err) 306 } 307 defer func() { 308 if err := tg.Close(); err != nil { 309 t.Fatal(err) 310 } 311 }() 312 // Get the host's port. 313 hg, err := tg.Hosts()[0].HostGet() 314 if err != nil { 315 t.Fatal("Failed to get port from host", err) 316 } 317 hostPort := hg.ExternalSettings.NetAddress.Port() 318 319 // Reannounce the hosts with custom hostnames which match the hostnames from the custom resolver method. 320 err = tg.Hosts()[0].HostAnnounceAddrPost(modules.NetAddress(fmt.Sprintf("host1.com:%s", hostPort))) 321 if err != nil { 322 t.Fatal("Failed to reannounce at least one of the hosts", err) 323 } 324 325 // Mine the announcements. 326 if err := tg.Miners()[0].MineBlock(); err != nil { 327 t.Fatal("Failed to mine block", err) 328 } 329 330 // Add a renter with a custom resolver to the group. 331 renterTemplate := node.Renter(testDir + "/renter") 332 renterTemplate.HostDBDeps = siatest.NewDependencyCustomResolver(func(host string) ([]net.IP, error) { 333 switch host { 334 case "host1.com": 335 return []net.IP{{128, 0, 0, 1}}, nil 336 case "host2.com": 337 return []net.IP{{128, 1, 0, 1}}, nil 338 case "localhost": 339 return []net.IP{{127, 0, 0, 1}}, nil 340 default: 341 panic("shouldn't happen") 342 } 343 }) 344 renterTemplate.ContractorDeps = renterTemplate.HostDBDeps 345 346 // Create renter. 347 _, err = tg.AddNodes(renterTemplate) 348 if err != nil { 349 t.Fatal(err) 350 } 351 352 // We expect the renter to have 1 active contract. 353 renter := tg.Renters()[0] 354 contracts, err := renter.RenterContractsGet() 355 if err != nil { 356 t.Fatal(err) 357 } 358 if len(contracts.ActiveContracts) != 1 { 359 t.Fatalf("Expected 1 active contract but got %v", len(contracts.Contracts)) 360 } 361 362 // Cancel the active contract. 363 err = renter.RenterContractCancelPost(contracts.ActiveContracts[0].ID) 364 if err != nil { 365 t.Fatal("Failed to cancel contract", err) 366 } 367 368 // We expect the renter to have 1 inactive contract. 369 contracts, err = renter.RenterInactiveContractsGet() 370 if err != nil { 371 t.Fatal(err) 372 } 373 if len(contracts.InactiveContracts) != 1 { 374 t.Fatalf("Expected 1 inactive contract but got %v", len(contracts.InactiveContracts)) 375 } 376 377 // Create a new host which doesn't announce itself right away. 378 newHostTemplate := node.Host(testDir + "/host") 379 newHostTemplate.SkipHostAnnouncement = true 380 newHost, err := tg.AddNodes(newHostTemplate) 381 if err != nil { 382 t.Fatal(err) 383 } 384 385 // Announce the new host as host2.com. That should cause a conflict between 386 // the hosts. That shouldn't be an issue though since one of the hosts has 387 // a canceled contract. 388 hg, err = newHost[0].HostGet() 389 if err != nil { 390 t.Fatal("Failed to get port from host", err) 391 } 392 hostPort = hg.ExternalSettings.NetAddress.Port() 393 err1 := newHost[0].HostModifySettingPost(client.HostParamAcceptingContracts, true) 394 err2 := newHost[0].HostAnnounceAddrPost(modules.NetAddress(fmt.Sprintf("host2.com:%s", hostPort))) 395 err = errors.Compose(err1, err2) 396 if err != nil { 397 t.Fatal("Failed to announce the new host", err) 398 } 399 400 // Mine the announcement. 401 if err := tg.Miners()[0].MineBlock(); err != nil { 402 t.Fatal("Failed to mine block", err) 403 } 404 405 // The renter should have an active contract with the new host and an 406 // inactive contract with the old host now. 407 numRetries := 0 408 err = build.Retry(100, 100*time.Millisecond, func() error { 409 if numRetries%10 == 0 { 410 err := tg.Miners()[0].MineBlock() 411 if err != nil { 412 return err 413 } 414 } 415 numRetries++ 416 // Get the active and inactive contracts. 417 contracts, err := renter.RenterInactiveContractsGet() 418 if err != nil { 419 return err 420 } 421 // Should have 1 active contract and 1 inactive contract. 422 if len(contracts.ActiveContracts) != 1 || len(contracts.InactiveContracts) != 1 { 423 return fmt.Errorf("Expected 1 active contract and 1 inactive contract. (%v/%v)", 424 len(contracts.ActiveContracts), len(contracts.InactiveContracts)) 425 } 426 // The active contract should be with host2. 427 if contracts.ActiveContracts[0].NetAddress.Host() != "host2.com" { 428 return fmt.Errorf("active contract should be with host2.com") 429 } 430 // The inactive contract should be with host1. 431 if contracts.InactiveContracts[0].NetAddress.Host() != "host1.com" { 432 return fmt.Errorf("active contract should be with host1.com") 433 } 434 return nil 435 }) 436 if err != nil { 437 t.Fatal(err) 438 } 439 }