gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/renter/hostdb/hostweight_test.go (about) 1 package hostdb 2 3 import ( 4 "math" 5 "testing" 6 "time" 7 8 "gitlab.com/NebulousLabs/errors" 9 "gitlab.com/NebulousLabs/fastrand" 10 11 "gitlab.com/SkynetLabs/skyd/skymodules" 12 "go.sia.tech/siad/modules" 13 "go.sia.tech/siad/types" 14 ) 15 16 var ( 17 // Set the default test allowance 18 DefaultTestAllowance = skymodules.Allowance{ 19 Funds: types.SiacoinPrecision.Mul64(500), 20 Hosts: uint64(50), 21 Period: 3 * types.BlocksPerMonth, 22 RenewWindow: types.BlocksPerMonth, 23 24 ExpectedStorage: 1e12, // 1 TB 25 ExpectedUpload: uint64(200e9) / uint64(types.BlocksPerMonth), // 200 GB per month 26 ExpectedDownload: uint64(100e9) / uint64(types.BlocksPerMonth), // 100 GB per month 27 ExpectedRedundancy: 3.0, // default is 10/30 erasure coding 28 } 29 30 // The default entry to use when performing scoring. 31 DefaultHostDBEntry = skymodules.HostDBEntry{ 32 HostExternalSettings: modules.HostExternalSettings{ 33 AcceptingContracts: true, 34 MaxDuration: 26e3, 35 RemainingStorage: 250e9, 36 WindowSize: 144, 37 38 Collateral: types.NewCurrency64(250).Mul(types.SiacoinPrecision).Div(modules.BlockBytesPerMonthTerabyte), 39 MaxCollateral: types.NewCurrency64(750).Mul(types.SiacoinPrecision), 40 41 BaseRPCPrice: types.SiacoinPrecision.Mul64(100).Div64(1e9), 42 ContractPrice: types.NewCurrency64(5).Mul(types.SiacoinPrecision), 43 DownloadBandwidthPrice: types.SiacoinPrecision.Mul64(100).Div64(1e12), 44 SectorAccessPrice: types.SiacoinPrecision.Mul64(2).Div64(1e6), 45 StoragePrice: types.NewCurrency64(100).Mul(types.SiacoinPrecision).Div(modules.BlockBytesPerMonthTerabyte), 46 UploadBandwidthPrice: types.SiacoinPrecision.Mul64(100).Div64(1e12), 47 48 Version: modules.RHPVersion, 49 }, 50 } 51 ) 52 53 // calculateWeightFromUInt64Price will fill out a host entry with a bunch of 54 // defaults, and then grab the weight of that host using a set price. 55 func calculateWeightFromUInt64Price(price, collateral uint64) (weight types.Currency, err error) { 56 hdb := bareHostDB() 57 err = hdb.SetAllowance(DefaultTestAllowance) 58 if err != nil { 59 return 60 } 61 hdb.blockHeight = 0 62 63 entry := DefaultHostDBEntry 64 entry.StoragePrice = types.NewCurrency64(price).Mul(types.SiacoinPrecision).Div(modules.BlockBytesPerMonthTerabyte) 65 entry.Collateral = types.NewCurrency64(collateral).Mul(types.SiacoinPrecision).Div(modules.BlockBytesPerMonthTerabyte) 66 67 return hdb.weightFunc(entry).Score(), nil 68 } 69 70 // TestHostDBBasePriceAdjustment ensures that the basePriceAdjustment is impacted by 71 // changes to BaseRPCPrice, SectorAccessPrice, and MinDownloadBandwidthPrice. 72 func TestHostDBBasePriceAdjustment(t *testing.T) { 73 if testing.Short() { 74 t.SkipNow() 75 } 76 t.Parallel() 77 hdb := bareHostDB() 78 entry := DefaultHostDBEntry 79 80 // Confirm default entry has score of 1 81 bpa := hdb.basePriceAdjustments(entry) 82 if bpa != 1 { 83 t.Error("BasePriceAdjustment for default entry should be 1 but was", bpa) 84 } 85 86 // Confirm a higher BaseRPCPrice results in an almost zero score 87 entry.BaseRPCPrice = entry.MaxBaseRPCPrice().Mul64(2) 88 bpa = hdb.basePriceAdjustments(entry) 89 if bpa != math.SmallestNonzeroFloat64 { 90 t.Errorf("BasePriceAdjustment should have been %v but was %v", math.SmallestNonzeroFloat64, bpa) 91 } 92 entry.BaseRPCPrice = DefaultHostDBEntry.BaseRPCPrice 93 94 // Confirm a higher SectorAccessPrice results in an almost zero score 95 entry.SectorAccessPrice = entry.MaxSectorAccessPrice().Mul64(2) 96 bpa = hdb.basePriceAdjustments(entry) 97 if bpa != math.SmallestNonzeroFloat64 { 98 t.Errorf("BasePriceAdjustment should have been %v but was %v", math.SmallestNonzeroFloat64, bpa) 99 } 100 entry.SectorAccessPrice = DefaultHostDBEntry.SectorAccessPrice 101 102 // Confirm a lower DownloadBandwidthPrice results in an almost zero score. 103 // Check by adjusting the price with both constants 104 entry.DownloadBandwidthPrice = DefaultHostDBEntry.DownloadBandwidthPrice.Div64(modules.MaxBaseRPCPriceVsBandwidth) 105 bpa = hdb.basePriceAdjustments(entry) 106 if bpa != math.SmallestNonzeroFloat64 { 107 t.Errorf("BasePriceAdjustment should have been %v but was %v", math.SmallestNonzeroFloat64, bpa) 108 } 109 entry.DownloadBandwidthPrice = DefaultHostDBEntry.DownloadBandwidthPrice.Div64(modules.MaxSectorAccessPriceVsBandwidth) 110 bpa = hdb.basePriceAdjustments(entry) 111 if bpa != math.SmallestNonzeroFloat64 { 112 t.Errorf("BasePriceAdjustment should have been %v but was %v", math.SmallestNonzeroFloat64, bpa) 113 } 114 } 115 116 // TestHostWeightBasePrice checks that a host with an unacceptable BaseRPCPrice 117 // or SectorAccessPrice has a lower score. 118 func TestHostWeightBasePrice(t *testing.T) { 119 if testing.Short() { 120 t.SkipNow() 121 } 122 t.Parallel() 123 hdb := bareHostDB() 124 125 entry := DefaultHostDBEntry 126 entry2 := DefaultHostDBEntry 127 entry2.BaseRPCPrice = entry.MaxBaseRPCPrice().Mul64(2) 128 entry3 := DefaultHostDBEntry 129 entry3.SectorAccessPrice = entry.MaxSectorAccessPrice().Mul64(2) 130 131 sDefault := hdb.weightFunc(entry).Score() 132 sInsaneBRPCPrice := hdb.weightFunc(entry2).Score() 133 sInsaneSAPrice := hdb.weightFunc(entry3).Score() 134 if sDefault.Cmp(sInsaneBRPCPrice) <= 0 { 135 t.Log("Default Score", sDefault) 136 t.Log("Bad BaseRPCPrice Score", sInsaneBRPCPrice) 137 t.Error("Default host should have higher score") 138 } 139 if sDefault.Cmp(sInsaneSAPrice) <= 0 { 140 t.Log("Default Score", sDefault) 141 t.Log("Bad SectorAccess Score", sInsaneSAPrice) 142 t.Error("Default host should have higher score") 143 } 144 if sInsaneBRPCPrice.Cmp(sInsaneSAPrice) != 0 { 145 t.Log("Bad BaseRPCPrice Score", sInsaneBRPCPrice) 146 t.Log("Bad SectorAccess Score", sInsaneSAPrice) 147 t.Error("Hosts should have the same score") 148 } 149 } 150 151 // TestHostWeightDistinctPrices ensures that the host weight is different if the 152 // prices are different, and that a higher price has a lower score. 153 func TestHostWeightDistinctPrices(t *testing.T) { 154 if testing.Short() { 155 t.SkipNow() 156 } 157 t.Parallel() 158 weight1, err1 := calculateWeightFromUInt64Price(300, 100) 159 weight2, err2 := calculateWeightFromUInt64Price(301, 100) 160 if err := errors.Compose(err1, err2); err != nil { 161 t.Fatal(err) 162 } 163 if weight1.Cmp(weight2) <= 0 { 164 t.Log(weight1) 165 t.Log(weight2) 166 t.Error("Weight of expensive host is not the correct value.") 167 } 168 } 169 170 // TestHostWeightDistinctCollateral ensures that the host weight is different if 171 // the collaterals are different, and that a higher collateral has a higher 172 // score. 173 func TestHostWeightDistinctCollateral(t *testing.T) { 174 if testing.Short() { 175 t.SkipNow() 176 } 177 t.Parallel() 178 weight1, err1 := calculateWeightFromUInt64Price(300, 100) 179 weight2, err2 := calculateWeightFromUInt64Price(300, 99) 180 if err := errors.Compose(err1, err2); err != nil { 181 t.Fatal(err) 182 } 183 if weight1.Cmp(weight2) <= 0 { 184 t.Log(weight1) 185 t.Log(weight2) 186 t.Error("Weight of expensive host is not the correct value.") 187 } 188 } 189 190 // When the collateral is below the cutoff, the collateral should be more 191 // important than the price. 192 func TestHostWeightCollateralBelowCutoff(t *testing.T) { 193 if testing.Short() { 194 t.SkipNow() 195 } 196 t.Parallel() 197 weight1, err1 := calculateWeightFromUInt64Price(300, 10) 198 weight2, err2 := calculateWeightFromUInt64Price(150, 5) 199 if err := errors.Compose(err1, err2); err != nil { 200 t.Fatal(err) 201 } 202 if weight1.Cmp(weight2) <= 0 { 203 t.Log(weight1) 204 t.Log(weight2) 205 t.Error("Weight of expensive host is not the correct value.") 206 } 207 } 208 209 // When the collateral is below the cutoff, the price should be more important 210 // than the collateral. 211 func TestHostWeightCollateralAboveCutoff(t *testing.T) { 212 if testing.Short() { 213 t.SkipNow() 214 } 215 t.Parallel() 216 weight1, err1 := calculateWeightFromUInt64Price(300, 1000) 217 weight2, err2 := calculateWeightFromUInt64Price(150, 500) 218 if err := errors.Compose(err1, err2); err != nil { 219 t.Fatal(err) 220 } 221 if weight1.Cmp(weight2) >= 0 { 222 t.Log(weight1) 223 t.Log(weight2) 224 t.Error("Weight of expensive host is not the correct value.") 225 } 226 } 227 228 // TestHostWeightIdenticalPrices checks that the weight function is 229 // deterministic for two hosts that have identical settings - each should get 230 // the same score. 231 func TestHostWeightIdenticalPrices(t *testing.T) { 232 if testing.Short() { 233 t.SkipNow() 234 } 235 t.Parallel() 236 weight1, err1 := calculateWeightFromUInt64Price(42, 100) 237 weight2, err2 := calculateWeightFromUInt64Price(42, 100) 238 if err := errors.Compose(err1, err2); err != nil { 239 t.Fatal(err) 240 } 241 if weight1.Cmp(weight2) != 0 { 242 t.Error("Weight of identically priced hosts should be equal.") 243 } 244 } 245 246 // TestHostWeightWithOnePricedZero checks that nothing unexpected happens when 247 // there is a zero price, and also checks that the zero priced host scores 248 // higher than the host that charges money. 249 func TestHostWeightWithOnePricedZero(t *testing.T) { 250 if testing.Short() { 251 t.SkipNow() 252 } 253 t.Parallel() 254 weight1, err1 := calculateWeightFromUInt64Price(5, 10) 255 weight2, err2 := calculateWeightFromUInt64Price(0, 10) 256 if err := errors.Compose(err1, err2); err != nil { 257 t.Fatal(err) 258 } 259 if weight1.Cmp(weight2) >= 0 { 260 t.Log(weight1) 261 t.Log(weight2) 262 t.Error("Zero-priced host should have higher weight than nonzero-priced host.") 263 } 264 } 265 266 // TestHostWeightBothPricesZero checks that there is nondeterminism in the 267 // weight function even with zero value prices. 268 func TestHostWeightWithBothPricesZero(t *testing.T) { 269 if testing.Short() { 270 t.SkipNow() 271 } 272 t.Parallel() 273 weight1, err1 := calculateWeightFromUInt64Price(0, 100) 274 weight2, err2 := calculateWeightFromUInt64Price(0, 100) 275 if err := errors.Compose(err1, err2); err != nil { 276 t.Fatal(err) 277 } 278 if weight1.Cmp(weight2) != 0 { 279 t.Error("Weight of two zero-priced hosts should be equal.") 280 } 281 } 282 283 // TestHostWeightWithNoCollateral checks that nothing bad (like a panic) happens 284 // when the collateral is set to zero. 285 func TestHostWeightWithNoCollateral(t *testing.T) { 286 if testing.Short() { 287 t.SkipNow() 288 } 289 t.Parallel() 290 weight1, err1 := calculateWeightFromUInt64Price(300, 1) 291 weight2, err2 := calculateWeightFromUInt64Price(300, 0) 292 if err := errors.Compose(err1, err2); err != nil { 293 t.Fatal(err) 294 } 295 if weight1.Cmp(weight2) <= 0 { 296 t.Log(weight1) 297 t.Log(weight2) 298 t.Error("Weight of lower priced host should be higher") 299 } 300 } 301 302 // TestHostWeightMaxDuration checks that the host with an unacceptable duration 303 // has a lower score. 304 func TestHostWeightMaxDuration(t *testing.T) { 305 if testing.Short() { 306 t.SkipNow() 307 } 308 t.Parallel() 309 hdb := bareHostDB() 310 err := hdb.SetAllowance(DefaultTestAllowance) 311 if err != nil { 312 t.Fatal(err) 313 } 314 315 entry := DefaultHostDBEntry 316 entry2 := DefaultHostDBEntry 317 entry2.MaxDuration = DefaultTestAllowance.Period + DefaultTestAllowance.RenewWindow 318 319 // Entry2 is exactly at the limit. Weights should match. 320 w1 := hdb.weightFunc(entry).Score() 321 w2 := hdb.weightFunc(entry2).Score() 322 if w1.Cmp(w2) != 0 { 323 t.Error("Entries should have same weight", w1, w2) 324 } 325 326 // Entry2 is just below the limit. Should have smallest weight possible. 327 entry2.MaxDuration-- 328 w2 = hdb.weightFunc(entry2).Score() 329 if w1.Cmp(w2) <= 0 { 330 t.Error("Entry2 should have smaller weight", w1, w2) 331 } 332 if w2.Cmp64(1) != 0 { 333 t.Error("Entry2 should have smallest weight") 334 } 335 } 336 337 // TestHostWeightStorageRemainingDifferences checks that the host with more 338 // collateral has more weight. 339 func TestHostWeightCollateralDifferences(t *testing.T) { 340 if testing.Short() { 341 t.SkipNow() 342 } 343 t.Parallel() 344 hdb := bareHostDB() 345 346 entry := DefaultHostDBEntry 347 entry2 := DefaultHostDBEntry 348 entry2.Collateral = entry.Collateral.Mul64(2) 349 entry2.MaxCollateral = entry.MaxCollateral.Mul64(2) 350 351 w1 := hdb.weightFunc(entry).Score() 352 w2 := hdb.weightFunc(entry2).Score() 353 if w1.Cmp(w2) <= 0 { 354 t.Log("w1:", w1) 355 t.Log("w2:", w2) 356 t.Error("Larger collateral should have more weight") 357 } 358 } 359 360 // TestHostWeightStorageRemainingDifferences checks that hosts with less storage 361 // remaining have a lower weight. 362 func TestHostWeightStorageRemainingDifferences(t *testing.T) { 363 if testing.Short() { 364 t.SkipNow() 365 } 366 t.Parallel() 367 hdb := bareHostDB() 368 369 // Create two entries with different host keys. 370 entry := DefaultHostDBEntry 371 entry.PublicKey.Key = fastrand.Bytes(16) 372 entry2 := DefaultHostDBEntry 373 entry2.PublicKey.Key = fastrand.Bytes(16) 374 375 // The first entry has more storage remaining than the second. 376 entry.RemainingStorage = skymodules.DefaultAllowance.ExpectedStorage // 1e12 377 entry2.RemainingStorage = 1e3 378 379 // The entry with more storage should have the higher score. 380 w1 := hdb.weightFunc(entry).Score() 381 w2 := hdb.weightFunc(entry2).Score() 382 if w1.Cmp(w2) <= 0 { 383 t.Log(w1) 384 t.Log(w2) 385 t.Error("Larger storage remaining should have more weight") 386 } 387 388 // Change both entries to have the same remaining storage but add contractInfo 389 // to the HostDB to make it think that we already uploaded some data to one of 390 // the entries. This entry should have the higher score. 391 entry.RemainingStorage = 1e3 392 entry2.RemainingStorage = 1e3 393 hdb.knownContracts[entry.PublicKey.String()] = contractInfo{ 394 HostPublicKey: entry.PublicKey, 395 StoredData: hdb.allowance.ExpectedStorage, 396 } 397 w1 = hdb.weightFunc(entry).Score() 398 w2 = hdb.weightFunc(entry2).Score() 399 if w1.Cmp(w2) <= 0 { 400 t.Log(w1) 401 t.Log(w2) 402 t.Error("Entry with uploaded data should have higher score") 403 } 404 } 405 406 // TestHostWeightVersionDifferences checks that a host with an out of date 407 // version has a lower score than a host with a more recent version. 408 func TestHostWeightVersionDifferences(t *testing.T) { 409 if testing.Short() { 410 t.SkipNow() 411 } 412 t.Parallel() 413 hdb := bareHostDB() 414 415 entry := DefaultHostDBEntry 416 entry2 := DefaultHostDBEntry 417 entry2.Version = "v1.3.2" 418 w1 := hdb.weightFunc(entry) 419 w2 := hdb.weightFunc(entry2) 420 421 if w1.Score().Cmp(w2.Score()) <= 0 { 422 t.Log(w1) 423 t.Log(w2) 424 t.Error("Higher version should have more weight") 425 } 426 } 427 428 // TestHostWeightLifetimeDifferences checks that a host that has been on the 429 // chain for more time has a higher weight than a host that is newer. 430 func TestHostWeightLifetimeDifferences(t *testing.T) { 431 if testing.Short() { 432 t.SkipNow() 433 } 434 t.Parallel() 435 hdb := bareHostDB() 436 hdb.blockHeight = 10000 437 438 entry := DefaultHostDBEntry 439 entry2 := DefaultHostDBEntry 440 entry2.FirstSeen = 8100 441 w1 := hdb.weightFunc(entry).Score() 442 w2 := hdb.weightFunc(entry2).Score() 443 444 if w1.Cmp(w2) <= 0 { 445 t.Log(w1) 446 t.Log(w2) 447 t.Error("Been around longer should have more weight") 448 } 449 } 450 451 // TestHostWeightUptimeDifferences checks that hosts with poorer uptimes have 452 // lower weights. 453 func TestHostWeightUptimeDifferences(t *testing.T) { 454 if testing.Short() { 455 t.SkipNow() 456 } 457 t.Parallel() 458 hdb := bareHostDB() 459 hdb.blockHeight = 10000 460 461 entry := DefaultHostDBEntry 462 entry.ScanHistory = skymodules.HostDBScans{ 463 {Timestamp: time.Now().Add(time.Hour * -100), Success: true}, 464 {Timestamp: time.Now().Add(time.Hour * -80), Success: true}, 465 {Timestamp: time.Now().Add(time.Hour * -60), Success: true}, 466 {Timestamp: time.Now().Add(time.Hour * -40), Success: true}, 467 {Timestamp: time.Now().Add(time.Hour * -20), Success: true}, 468 } 469 470 entry2 := entry 471 entry2.ScanHistory = skymodules.HostDBScans{ 472 {Timestamp: time.Now().Add(time.Hour * -100), Success: true}, 473 {Timestamp: time.Now().Add(time.Hour * -80), Success: true}, 474 {Timestamp: time.Now().Add(time.Hour * -60), Success: true}, 475 {Timestamp: time.Now().Add(time.Hour * -40), Success: true}, 476 {Timestamp: time.Now().Add(time.Hour * -20), Success: false}, 477 } 478 w1 := hdb.weightFunc(entry).Score() 479 w2 := hdb.weightFunc(entry2).Score() 480 481 if w1.Cmp(w2) <= 0 { 482 t.Log(w1) 483 t.Log(w2) 484 t.Error("A host with recorded downtime should have a lower score") 485 } 486 } 487 488 // TestHostWeightUptimeDifferences2 checks that hosts with poorer uptimes have 489 // lower weights. 490 func TestHostWeightUptimeDifferences2(t *testing.T) { 491 t.Skip("Hostdb is not currently doing exponentiation on uptime") 492 if testing.Short() { 493 t.SkipNow() 494 } 495 t.Parallel() 496 hdb := bareHostDB() 497 hdb.blockHeight = 10000 498 499 entry := DefaultHostDBEntry 500 entry.ScanHistory = skymodules.HostDBScans{ 501 {Timestamp: time.Now().Add(time.Hour * -200), Success: true}, 502 {Timestamp: time.Now().Add(time.Hour * -180), Success: true}, 503 {Timestamp: time.Now().Add(time.Hour * -160), Success: true}, 504 {Timestamp: time.Now().Add(time.Hour * -140), Success: true}, 505 {Timestamp: time.Now().Add(time.Hour * -120), Success: true}, 506 {Timestamp: time.Now().Add(time.Hour * -100), Success: true}, 507 {Timestamp: time.Now().Add(time.Hour * -80), Success: false}, 508 {Timestamp: time.Now().Add(time.Hour * -60), Success: true}, 509 {Timestamp: time.Now().Add(time.Hour * -40), Success: true}, 510 {Timestamp: time.Now().Add(time.Hour * -20), Success: true}, 511 } 512 513 entry2 := entry 514 entry2.ScanHistory = skymodules.HostDBScans{ 515 {Timestamp: time.Now().Add(time.Hour * -200), Success: true}, 516 {Timestamp: time.Now().Add(time.Hour * -180), Success: true}, 517 {Timestamp: time.Now().Add(time.Hour * -160), Success: true}, 518 {Timestamp: time.Now().Add(time.Hour * -140), Success: true}, 519 {Timestamp: time.Now().Add(time.Hour * -120), Success: true}, 520 {Timestamp: time.Now().Add(time.Hour * -100), Success: true}, 521 {Timestamp: time.Now().Add(time.Hour * -80), Success: true}, 522 {Timestamp: time.Now().Add(time.Hour * -60), Success: true}, 523 {Timestamp: time.Now().Add(time.Hour * -40), Success: false}, 524 {Timestamp: time.Now().Add(time.Hour * -20), Success: true}, 525 } 526 w1 := hdb.weightFunc(entry).Score() 527 w2 := hdb.weightFunc(entry2).Score() 528 529 if w1.Cmp(w2) <= 0 { 530 t.Log(w1) 531 t.Log(w2) 532 t.Errorf("Downtime that's further in the past should be penalized less") 533 } 534 } 535 536 // TestHostWeightUptimeDifferences3 checks that hosts with poorer uptimes have 537 // lower weights. 538 func TestHostWeightUptimeDifferences3(t *testing.T) { 539 if testing.Short() { 540 t.SkipNow() 541 } 542 t.Parallel() 543 hdb := bareHostDB() 544 hdb.blockHeight = 10000 545 546 entry := DefaultHostDBEntry 547 entry.ScanHistory = skymodules.HostDBScans{ 548 {Timestamp: time.Now().Add(time.Hour * -200), Success: true}, 549 {Timestamp: time.Now().Add(time.Hour * -180), Success: true}, 550 {Timestamp: time.Now().Add(time.Hour * -160), Success: true}, 551 {Timestamp: time.Now().Add(time.Hour * -140), Success: true}, 552 {Timestamp: time.Now().Add(time.Hour * -120), Success: true}, 553 {Timestamp: time.Now().Add(time.Hour * -100), Success: true}, 554 {Timestamp: time.Now().Add(time.Hour * -80), Success: false}, 555 {Timestamp: time.Now().Add(time.Hour * -60), Success: true}, 556 {Timestamp: time.Now().Add(time.Hour * -40), Success: true}, 557 {Timestamp: time.Now().Add(time.Hour * -20), Success: true}, 558 } 559 560 entry2 := entry 561 entry2.ScanHistory = skymodules.HostDBScans{ 562 {Timestamp: time.Now().Add(time.Hour * -200), Success: true}, 563 {Timestamp: time.Now().Add(time.Hour * -180), Success: true}, 564 {Timestamp: time.Now().Add(time.Hour * -160), Success: true}, 565 {Timestamp: time.Now().Add(time.Hour * -140), Success: true}, 566 {Timestamp: time.Now().Add(time.Hour * -120), Success: true}, 567 {Timestamp: time.Now().Add(time.Hour * -100), Success: true}, 568 {Timestamp: time.Now().Add(time.Hour * -80), Success: false}, 569 {Timestamp: time.Now().Add(time.Hour * -60), Success: false}, 570 {Timestamp: time.Now().Add(time.Hour * -40), Success: true}, 571 {Timestamp: time.Now().Add(time.Hour * -20), Success: true}, 572 } 573 w1 := hdb.weightFunc(entry).Score() 574 w2 := hdb.weightFunc(entry2).Score() 575 576 if w1.Cmp(w2) <= 0 { 577 t.Log(w1) 578 t.Log(w2) 579 t.Error("A host with longer downtime should have a lower score") 580 } 581 } 582 583 // TestHostWeightUptimeDifferences4 checks that hosts with poorer uptimes have 584 // lower weights. 585 func TestHostWeightUptimeDifferences4(t *testing.T) { 586 if testing.Short() { 587 t.SkipNow() 588 } 589 t.Parallel() 590 hdb := bareHostDB() 591 hdb.blockHeight = 10000 592 593 entry := DefaultHostDBEntry 594 entry.ScanHistory = skymodules.HostDBScans{ 595 {Timestamp: time.Now().Add(time.Hour * -200), Success: true}, 596 {Timestamp: time.Now().Add(time.Hour * -180), Success: true}, 597 {Timestamp: time.Now().Add(time.Hour * -160), Success: true}, 598 {Timestamp: time.Now().Add(time.Hour * -140), Success: true}, 599 {Timestamp: time.Now().Add(time.Hour * -120), Success: true}, 600 {Timestamp: time.Now().Add(time.Hour * -100), Success: true}, 601 {Timestamp: time.Now().Add(time.Hour * -80), Success: true}, 602 {Timestamp: time.Now().Add(time.Hour * -60), Success: true}, 603 {Timestamp: time.Now().Add(time.Hour * -40), Success: true}, 604 {Timestamp: time.Now().Add(time.Hour * -20), Success: false}, 605 } 606 607 entry2 := entry 608 entry2.ScanHistory = skymodules.HostDBScans{ 609 {Timestamp: time.Now().Add(time.Hour * -200), Success: true}, 610 {Timestamp: time.Now().Add(time.Hour * -180), Success: true}, 611 {Timestamp: time.Now().Add(time.Hour * -160), Success: true}, 612 {Timestamp: time.Now().Add(time.Hour * -140), Success: true}, 613 {Timestamp: time.Now().Add(time.Hour * -120), Success: true}, 614 {Timestamp: time.Now().Add(time.Hour * -100), Success: true}, 615 {Timestamp: time.Now().Add(time.Hour * -80), Success: true}, 616 {Timestamp: time.Now().Add(time.Hour * -60), Success: true}, 617 {Timestamp: time.Now().Add(time.Hour * -40), Success: false}, 618 {Timestamp: time.Now().Add(time.Hour * -20), Success: false}, 619 } 620 w1 := hdb.weightFunc(entry).Score() 621 w2 := hdb.weightFunc(entry2).Score() 622 623 if w1.Cmp(w2) <= 0 { 624 t.Log(w1) 625 t.Log(w2) 626 t.Error("longer tail downtime should have a lower score") 627 } 628 } 629 630 // TestHostWeightConstants checks a few relationships between the constants in 631 // the hostdb. 632 func TestHostWeightConstants(t *testing.T) { 633 // Becaues we no longer use a large base weight, we require that the 634 // collateral floor be higher than the price floor, and also that the 635 // collateralExponentiationSmall be larger than the 636 // priceExponentiationSmall. This protects most hosts from going anywhere 637 // near a 0 score. 638 if collateralFloor < priceFloor { 639 t.Error("Collateral floor should be greater than or equal to price floor") 640 } 641 if collateralExponentiationSmall < priceExponentiationSmall { 642 t.Error("small collateral exponentiation should be larger than small price exponentiation") 643 } 644 645 // Try a few hosts and make sure we always end up with a score that is 646 // greater than 1 million. 647 weight, err := calculateWeightFromUInt64Price(300, 100) 648 if weight.Cmp(types.NewCurrency64(1e9)) < 0 { 649 t.Error("weight is not sufficiently high for hosts") 650 } 651 if err != nil { 652 t.Fatal(err) 653 } 654 weight, err = calculateWeightFromUInt64Price(1000, 1) 655 if weight.Cmp(types.NewCurrency64(1e9)) < 0 { 656 t.Error("weight is not sufficiently high for hosts") 657 } 658 if err != nil { 659 t.Fatal(err) 660 } 661 662 hdb := bareHostDB() 663 err = hdb.SetAllowance(DefaultTestAllowance) 664 if err != nil { 665 t.Fatal(err) 666 } 667 hdb.blockHeight = 0 668 669 entry := DefaultHostDBEntry 670 weight = hdb.weightFunc(entry).Score() 671 if weight.Cmp(types.NewCurrency64(1e9)) < 0 { 672 t.Error("weight is not sufficiently high for hosts") 673 } 674 } 675 676 // TestHostWeightExtraPriceAdjustment tests the affects of changing 677 // BaseRPCPrice and SectorAccessPrice on the score. 678 func TestHostWeightExtraPriceAdjustments(t *testing.T) { 679 if testing.Short() { 680 t.SkipNow() 681 } 682 t.Parallel() 683 hdb := bareHostDB() 684 685 allowance := DefaultTestAllowance 686 err := hdb.SetAllowance(allowance) 687 if err != nil { 688 t.Fatal(err) 689 } 690 entry := DefaultHostDBEntry 691 defaultScore := hdb.weightFunc(entry).Score() 692 693 // Increasing Base RPC Price should decrease the score. 694 entry.BaseRPCPrice = DefaultHostDBEntry.BaseRPCPrice.Mul64(2) 695 higherBasePrice := hdb.weightFunc(entry).Score() 696 if defaultScore.Cmp(higherBasePrice) <= 0 { 697 t.Fatal("Expected score decrease with higher base price.") 698 } 699 700 // Increasing Base RPC Price should decrease the score. 701 entry.BaseRPCPrice = DefaultHostDBEntry.BaseRPCPrice.Mul64(10) 702 highestBasePrice := hdb.weightFunc(entry).Score() 703 if higherBasePrice.Cmp(highestBasePrice) <= 0 { 704 t.Fatal("Expected score decrease with higher base price.") 705 } 706 707 // Increasing SectorAccessPrice should decrease the score. 708 entry = DefaultHostDBEntry // reset entry 709 entry.SectorAccessPrice = DefaultHostDBEntry.SectorAccessPrice.Mul64(2) 710 higherSectorPrice := hdb.weightFunc(entry).Score() 711 if defaultScore.Cmp(higherSectorPrice) <= 0 { 712 t.Fatal("Expected score decrease with higher sector access price") 713 } 714 715 // Increasing SectorAccessPrice should decrease the score. 716 entry.SectorAccessPrice = DefaultHostDBEntry.SectorAccessPrice.Mul64(10) 717 highestSectorPrice := hdb.weightFunc(entry).Score() 718 if higherSectorPrice.Cmp(highestSectorPrice) <= 0 { 719 t.Fatal("Expected score decrease with higher sector access price") 720 } 721 } 722 723 // TestHostWeightAcceptContract checks that the host that doesn't accept 724 // contracts has a worse score than the one that does. 725 func TestHostWeightAcceptContract(t *testing.T) { 726 if testing.Short() { 727 t.SkipNow() 728 } 729 t.Parallel() 730 hdb := bareHostDB() 731 err := hdb.SetAllowance(DefaultTestAllowance) 732 if err != nil { 733 t.Fatal(err) 734 } 735 736 entry := DefaultHostDBEntry 737 entry2 := DefaultHostDBEntry 738 entry2.AcceptingContracts = false 739 740 // Entry2 is not accepting contracts. Should have smallest weight possible. 741 entry2.MaxDuration-- 742 w1 := hdb.weightFunc(entry).Score() 743 w2 := hdb.weightFunc(entry2).Score() 744 if w1.Cmp(w2) <= 0 { 745 t.Error("Entry2 should have smaller weight", w1, w2) 746 } 747 if w2.Cmp64(1) != 0 { 748 t.Error("Entry2 should have smallest weight") 749 } 750 } 751 752 // TestMaxPriceAdjustments tests that hosts which are not below the max prices 753 // specified in the allowance are considered bad. 754 func TestMaxPriceAdjustments(t *testing.T) { 755 t.Parallel() 756 757 hdb := bareHostDB() 758 hdb.allowance = DefaultTestAllowance 759 entry := DefaultHostDBEntry 760 761 // Score should be greater than the smallest score. 762 score := hdb.priceAdjustments(entry, hdb.allowance, types.ZeroCurrency) 763 if score <= math.SmallestNonzeroFloat64 { 764 t.Fatal("false") 765 } 766 767 hdb.allowance.MaxRPCPrice = entry.BaseRPCPrice.Sub64(1) 768 score = hdb.priceAdjustments(entry, hdb.allowance, types.ZeroCurrency) 769 if score != math.SmallestNonzeroFloat64 { 770 t.Fatal("false") 771 } 772 hdb.allowance = DefaultTestAllowance 773 774 hdb.allowance.MaxContractPrice = entry.ContractPrice.Sub64(1) 775 score = hdb.priceAdjustments(entry, hdb.allowance, types.ZeroCurrency) 776 if score != math.SmallestNonzeroFloat64 { 777 t.Fatal("false") 778 } 779 hdb.allowance = DefaultTestAllowance 780 781 hdb.allowance.MaxSectorAccessPrice = entry.SectorAccessPrice.MulTax().Sub64(1) 782 score = hdb.priceAdjustments(entry, hdb.allowance, types.ZeroCurrency) 783 if score != math.SmallestNonzeroFloat64 { 784 t.Fatal("false") 785 } 786 hdb.allowance = DefaultTestAllowance 787 788 hdb.allowance.MaxDownloadBandwidthPrice = entry.DownloadBandwidthPrice.Sub64(1) 789 score = hdb.priceAdjustments(entry, hdb.allowance, types.ZeroCurrency) 790 if score != math.SmallestNonzeroFloat64 { 791 t.Fatal("false") 792 } 793 hdb.allowance = DefaultTestAllowance 794 795 hdb.allowance.MaxUploadBandwidthPrice = entry.UploadBandwidthPrice.Sub64(1) 796 score = hdb.priceAdjustments(entry, hdb.allowance, types.ZeroCurrency) 797 if score != math.SmallestNonzeroFloat64 { 798 t.Fatal("false") 799 } 800 hdb.allowance = DefaultTestAllowance 801 }