gitlab.com/jokerrs1/Sia@v1.3.2/modules/host/host_test.go (about) 1 package host 2 3 import ( 4 // "errors" 5 "os" 6 "path/filepath" 7 "testing" 8 9 "github.com/NebulousLabs/Sia/build" 10 "github.com/NebulousLabs/Sia/crypto" 11 "github.com/NebulousLabs/Sia/modules" 12 "github.com/NebulousLabs/Sia/modules/consensus" 13 "github.com/NebulousLabs/Sia/modules/gateway" 14 "github.com/NebulousLabs/Sia/modules/miner" 15 // "github.com/NebulousLabs/Sia/modules/renter" 16 "github.com/NebulousLabs/Sia/modules/transactionpool" 17 "github.com/NebulousLabs/Sia/modules/wallet" 18 siasync "github.com/NebulousLabs/Sia/sync" 19 "github.com/NebulousLabs/Sia/types" 20 ) 21 22 // A hostTester is the helper object for host testing, including helper modules 23 // and methods for controlling synchronization. 24 type hostTester struct { 25 cs modules.ConsensusSet 26 gateway modules.Gateway 27 miner modules.TestMiner 28 // renter modules.Renter 29 renting bool 30 tpool modules.TransactionPool 31 wallet modules.Wallet 32 walletKey crypto.TwofishKey 33 34 host *Host 35 36 persistDir string 37 } 38 39 /* 40 // initRenting prepares the host tester for uploads and downloads by announcing 41 // the host to the network and performing other preparational tasks. 42 // initRenting takes a while because the renter needs to process the host 43 // announcement, requiring asynchronous network communication between the 44 // renter and host. 45 func (ht *hostTester) initRenting() error { 46 if ht.renting { 47 return nil 48 } 49 50 // Because the renting test takes a long time, it will fail if 51 // testing.Short. 52 if testing.Short() { 53 return errors.New("cannot call initRenting in short tests") 54 } 55 56 // Announce the host. 57 err := ht.host.Announce() 58 if err != nil { 59 return err 60 } 61 62 // Mine a block to get the announcement into the blockchain. 63 _, err = ht.miner.AddBlock() 64 if err != nil { 65 return err 66 } 67 68 // Wait for the renter to see the host announcement. 69 for i := 0; i < 50; i++ { 70 time.Sleep(time.Millisecond * 100) 71 if len(ht.renter.ActiveHosts()) != 0 { 72 break 73 } 74 } 75 if len(ht.renter.ActiveHosts()) == 0 { 76 return errors.New("could not start renting in the host tester") 77 } 78 ht.renting = true 79 return nil 80 } 81 */ 82 83 // initWallet creates a wallet key, initializes the host wallet, unlocks it, 84 // and then stores the key in the host tester. 85 func (ht *hostTester) initWallet() error { 86 // Create the keys for the wallet and unlock it. 87 key := crypto.GenerateTwofishKey() 88 ht.walletKey = key 89 _, err := ht.wallet.Encrypt(key) 90 if err != nil { 91 return err 92 } 93 err = ht.wallet.Unlock(key) 94 if err != nil { 95 return err 96 } 97 return nil 98 } 99 100 // blankHostTester creates a host tester where the modules are created but no 101 // extra initialization has been done, for example no blocks have been mined 102 // and the wallet keys have not been created. 103 func blankHostTester(name string) (*hostTester, error) { 104 return blankMockHostTester(&modules.ProductionDependencies{}, name) 105 } 106 107 // blankMockHostTester creates a host tester where the modules are created but no 108 // extra initialization has been done, for example no blocks have been mined 109 // and the wallet keys have not been created. 110 func blankMockHostTester(d modules.Dependencies, name string) (*hostTester, error) { 111 testdir := build.TempDir(modules.HostDir, name) 112 113 // Create the modules. 114 g, err := gateway.New("localhost:0", false, filepath.Join(testdir, modules.GatewayDir)) 115 if err != nil { 116 return nil, err 117 } 118 cs, err := consensus.New(g, false, filepath.Join(testdir, modules.ConsensusDir)) 119 if err != nil { 120 return nil, err 121 } 122 tp, err := transactionpool.New(cs, g, filepath.Join(testdir, modules.TransactionPoolDir)) 123 if err != nil { 124 return nil, err 125 } 126 w, err := wallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir)) 127 if err != nil { 128 return nil, err 129 } 130 m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir)) 131 if err != nil { 132 return nil, err 133 } 134 h, err := newHost(d, cs, tp, w, "localhost:0", filepath.Join(testdir, modules.HostDir)) 135 if err != nil { 136 return nil, err 137 } 138 /* 139 r, err := renter.New(cs, w, tp, filepath.Join(testdir, modules.RenterDir)) 140 if err != nil { 141 return nil, err 142 } 143 */ 144 145 // Assemble all objects into a hostTester 146 ht := &hostTester{ 147 cs: cs, 148 gateway: g, 149 miner: m, 150 // renter: r, 151 tpool: tp, 152 wallet: w, 153 154 host: h, 155 156 persistDir: testdir, 157 } 158 159 return ht, nil 160 } 161 162 // newHostTester creates a host tester with an initialized wallet and money in 163 // that wallet. 164 func newHostTester(name string) (*hostTester, error) { 165 return newMockHostTester(&modules.ProductionDependencies{}, name) 166 } 167 168 // newMockHostTester creates a host tester with an initialized wallet and money 169 // in that wallet, using the dependencies provided. 170 func newMockHostTester(d modules.Dependencies, name string) (*hostTester, error) { 171 // Create a blank host tester. 172 ht, err := blankMockHostTester(d, name) 173 if err != nil { 174 return nil, err 175 } 176 177 // Initialize the wallet and mine blocks until the wallet has money. 178 err = ht.initWallet() 179 if err != nil { 180 return nil, err 181 } 182 for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ { 183 _, err = ht.miner.AddBlock() 184 if err != nil { 185 return nil, err 186 } 187 } 188 189 // Create two storage folder for the host, one the minimum size and one 190 // twice the minimum size. 191 storageFolderOne := filepath.Join(ht.persistDir, "hostTesterStorageFolderOne") 192 err = os.Mkdir(storageFolderOne, 0700) 193 if err != nil { 194 return nil, err 195 } 196 err = ht.host.AddStorageFolder(storageFolderOne, modules.SectorSize*64) 197 if err != nil { 198 return nil, err 199 } 200 storageFolderTwo := filepath.Join(ht.persistDir, "hostTesterStorageFolderTwo") 201 err = os.Mkdir(storageFolderTwo, 0700) 202 if err != nil { 203 return nil, err 204 } 205 err = ht.host.AddStorageFolder(storageFolderTwo, modules.SectorSize*64*2) 206 if err != nil { 207 return nil, err 208 } 209 return ht, nil 210 } 211 212 // Close safely closes the hostTester. It panics if err != nil because there 213 // isn't a good way to errcheck when deferring a close. 214 func (ht *hostTester) Close() error { 215 errs := []error{ 216 ht.host.Close(), 217 ht.miner.Close(), 218 ht.tpool.Close(), 219 ht.cs.Close(), 220 ht.gateway.Close(), 221 } 222 if err := build.JoinErrors(errs, "; "); err != nil { 223 panic(err) 224 } 225 return nil 226 } 227 228 // TestHostInitialization checks that the host initializes to sensible default 229 // values. 230 func TestHostInitialization(t *testing.T) { 231 if testing.Short() { 232 t.SkipNow() 233 } 234 t.Parallel() 235 // Create a blank host tester and check that the height is zero. 236 bht, err := blankHostTester("TestHostInitialization") 237 if err != nil { 238 t.Fatal(err) 239 } 240 defer bht.Close() 241 if bht.host.blockHeight != 0 { 242 t.Error("host initialized to the wrong block height") 243 } 244 245 // Initialize the wallet so that a block can be mined, then mine a block 246 // and check that it sets the host height to 1. 247 err = bht.initWallet() 248 if err != nil { 249 t.Fatal(err) 250 } 251 _, err = bht.miner.AddBlock() 252 if err != nil { 253 t.Fatal(err) 254 } 255 if bht.host.blockHeight != 1 { 256 t.Fatal("block height did not increase correctly after first block mined:", bht.host.blockHeight, 1) 257 } 258 } 259 260 // TestHostMultiClose checks that the host returns an error if Close is called 261 // multiple times on the host. 262 func TestHostMultiClose(t *testing.T) { 263 if testing.Short() { 264 t.SkipNow() 265 } 266 t.Parallel() 267 ht, err := newHostTester("TestHostMultiClose") 268 if err != nil { 269 t.Fatal(err) 270 } 271 defer ht.Close() 272 273 err = ht.host.Close() 274 if err != nil { 275 t.Fatal(err) 276 } 277 err = ht.host.Close() 278 if err != siasync.ErrStopped { 279 t.Fatal(err) 280 } 281 err = ht.host.Close() 282 if err != siasync.ErrStopped { 283 t.Fatal(err) 284 } 285 // Set ht.host to something non-nil - nil was returned because startup was 286 // incomplete. If ht.host is nil at the end of the function, the ht.Close() 287 // operation will fail. 288 ht.host, err = newHost(&modules.ProductionDependencies{}, ht.cs, ht.tpool, ht.wallet, "localhost:0", filepath.Join(ht.persistDir, modules.HostDir)) 289 if err != nil { 290 t.Fatal(err) 291 } 292 } 293 294 // TestNilValues tries initializing the host with nil values. 295 func TestNilValues(t *testing.T) { 296 if testing.Short() { 297 t.SkipNow() 298 } 299 t.Parallel() 300 ht, err := blankHostTester("TestStartupRescan") 301 if err != nil { 302 t.Fatal(err) 303 } 304 defer ht.Close() 305 306 hostDir := filepath.Join(ht.persistDir, modules.HostDir) 307 _, err = New(nil, ht.tpool, ht.wallet, "localhost:0", hostDir) 308 if err != errNilCS { 309 t.Fatal("could not trigger errNilCS") 310 } 311 _, err = New(ht.cs, nil, ht.wallet, "localhost:0", hostDir) 312 if err != errNilTpool { 313 t.Fatal("could not trigger errNilTpool") 314 } 315 _, err = New(ht.cs, ht.tpool, nil, "localhost:0", hostDir) 316 if err != errNilWallet { 317 t.Fatal("Could not trigger errNilWallet") 318 } 319 } 320 321 // TestSetAndGetInternalSettings checks that the functions for interacting with 322 // the host's internal settings object are working as expected. 323 func TestSetAndGetInternalSettings(t *testing.T) { 324 if testing.Short() { 325 t.SkipNow() 326 } 327 t.Parallel() 328 329 ht, err := newHostTester("TestSetAndGetInternalSettings") 330 if err != nil { 331 t.Fatal(err) 332 } 333 defer ht.Close() 334 335 // Check the default settings get returned at first call. 336 settings := ht.host.InternalSettings() 337 if settings.AcceptingContracts != false { 338 t.Error("settings retrieval did not return default value") 339 } 340 if settings.MaxDuration != defaultMaxDuration { 341 t.Error("settings retrieval did not return default value") 342 } 343 if settings.MaxDownloadBatchSize != uint64(defaultMaxDownloadBatchSize) { 344 t.Error("settings retrieval did not return default value") 345 } 346 if settings.MaxReviseBatchSize != uint64(defaultMaxReviseBatchSize) { 347 t.Error("settings retrieval did not return default value") 348 } 349 if settings.NetAddress != "" { 350 t.Error("settings retrieval did not return default value") 351 } 352 if settings.WindowSize != defaultWindowSize { 353 t.Error("settings retrieval did not return default value") 354 } 355 if !settings.Collateral.Equals(defaultCollateral) { 356 t.Error("settings retrieval did not return default value") 357 } 358 if !settings.CollateralBudget.Equals(defaultCollateralBudget) { 359 t.Error("settings retrieval did not return default value") 360 } 361 if !settings.MaxCollateral.Equals(defaultMaxCollateral) { 362 t.Error("settings retrieval did not return default value") 363 } 364 if !settings.MinContractPrice.Equals(defaultContractPrice) { 365 t.Error("settings retrieval did not return default value") 366 } 367 if !settings.MinDownloadBandwidthPrice.Equals(defaultDownloadBandwidthPrice) { 368 t.Error("settings retrieval did not return default value") 369 } 370 if !settings.MinStoragePrice.Equals(defaultStoragePrice) { 371 t.Error("settings retrieval did not return default value") 372 } 373 if !settings.MinUploadBandwidthPrice.Equals(defaultUploadBandwidthPrice) { 374 t.Error("settings retrieval did not return default value") 375 } 376 377 // Check that calling SetInternalSettings with valid settings updates the settings. 378 settings.AcceptingContracts = true 379 settings.NetAddress = "foo.com:123" 380 err = ht.host.SetInternalSettings(settings) 381 if err != nil { 382 t.Fatal(err) 383 } 384 settings = ht.host.InternalSettings() 385 if settings.AcceptingContracts != true { 386 t.Fatal("SetInternalSettings failed to update settings") 387 } 388 if settings.NetAddress != "foo.com:123" { 389 t.Fatal("SetInternalSettings failed to update settings") 390 } 391 392 // Check that calling SetInternalSettings with invalid settings does not update the settings. 393 settings.NetAddress = "invalid" 394 err = ht.host.SetInternalSettings(settings) 395 if err == nil { 396 t.Fatal("expected SetInternalSettings to error with invalid settings") 397 } 398 settings = ht.host.InternalSettings() 399 if settings.NetAddress != "foo.com:123" { 400 t.Fatal("SetInternalSettings should not modify the settings if the new settings are invalid") 401 } 402 403 // Reload the host and verify that the altered settings persisted. 404 err = ht.host.Close() 405 if err != nil { 406 t.Fatal(err) 407 } 408 rebootHost, err := New(ht.cs, ht.tpool, ht.wallet, "localhost:0", filepath.Join(ht.persistDir, modules.HostDir)) 409 if err != nil { 410 t.Fatal(err) 411 } 412 rebootSettings := rebootHost.InternalSettings() 413 if rebootSettings.AcceptingContracts != settings.AcceptingContracts { 414 t.Error("settings retrieval did not return updated value") 415 } 416 if rebootSettings.NetAddress != settings.NetAddress { 417 t.Error("settings retrieval did not return updated value") 418 } 419 420 // Set ht.host to 'rebootHost' so that the 'ht.Close()' method will close 421 // everything cleanly. 422 ht.host = rebootHost 423 } 424 425 /* 426 // TestSetAndGetSettings checks that the functions for interacting with the 427 // hosts settings object are working as expected. 428 func TestSetAndGetSettings(t *testing.T) { 429 if testing.Short() { 430 t.SkipNow() 431 } 432 ht, err := newHostTester("TestSetAndGetSettings") 433 if err != nil { 434 t.Fatal(err) 435 } 436 defer ht.Close() 437 438 // Check the default settings get returned at first call. 439 settings := ht.host.Settings() 440 if settings.MaxDuration != defaultMaxDuration { 441 t.Error("settings retrieval did not return default value") 442 } 443 if settings.WindowSize != defaultWindowSize { 444 t.Error("settings retrieval did not return default value") 445 } 446 if settings.Price.Cmp(defaultPrice) != 0 { 447 t.Error("settings retrieval did not return default value") 448 } 449 if settings.Collateral.Cmp(defaultCollateral) != 0 { 450 t.Error("settings retrieval did not return default value") 451 } 452 453 // Submit updated settings and check that the changes stuck. 454 settings.TotalStorage += 15 455 settings.MaxDuration += 16 456 settings.WindowSize += 17 457 settings.Price = settings.Price.Add(types.NewCurrency64(18)) 458 settings.Collateral = settings.Collateral.Add(types.NewCurrency64(19)) 459 err = ht.host.SetSettings(settings) 460 if err != nil { 461 t.Fatal(err) 462 } 463 newSettings := ht.host.Settings() 464 if settings.MaxDuration != newSettings.MaxDuration { 465 t.Error("settings retrieval did not return updated value") 466 } 467 if settings.WindowSize != newSettings.WindowSize { 468 t.Error("settings retrieval did not return updated value") 469 } 470 if settings.Price.Cmp(newSettings.Price) != 0 { 471 t.Error("settings retrieval did not return updated value") 472 } 473 if settings.Collateral.Cmp(newSettings.Collateral) != 0 { 474 t.Error("settings retrieval did not return updated value") 475 } 476 477 // Reload the host and verify that the altered settings persisted. 478 err = ht.host.Close() 479 if err != nil { 480 t.Fatal(err) 481 } 482 rebootHost, err := New(ht.cs, ht.tpool, ht.wallet, "localhost:0", filepath.Join(ht.persistDir, modules.HostDir)) 483 if err != nil { 484 t.Fatal(err) 485 } 486 rebootSettings := rebootHost.Settings() 487 if settings.TotalStorage != rebootSettings.TotalStorage { 488 t.Error("settings retrieval did not return updated value") 489 } 490 if settings.MaxDuration != rebootSettings.MaxDuration { 491 t.Error("settings retrieval did not return updated value") 492 } 493 if settings.WindowSize != rebootSettings.WindowSize { 494 t.Error("settings retrieval did not return updated value") 495 } 496 if settings.Price.Cmp(rebootSettings.Price) != 0 { 497 t.Error("settings retrieval did not return updated value") 498 } 499 if settings.Collateral.Cmp(rebootSettings.Collateral) != 0 { 500 t.Error("settings retrieval did not return updated value") 501 } 502 } 503 504 // TestPersistentSettings checks that settings persist between instances of the 505 // host. 506 func TestPersistentSettings(t *testing.T) { 507 if testing.Short() { 508 t.SkipNow() 509 } 510 ht, err := newHostTester("TestSetPersistentSettings") 511 if err != nil { 512 t.Fatal(err) 513 } 514 defer ht.Close() 515 516 // Submit updated settings. 517 settings := ht.host.Settings() 518 settings.TotalStorage += 25 519 settings.MaxDuration += 36 520 settings.WindowSize += 47 521 settings.Price = settings.Price.Add(types.NewCurrency64(38)) 522 settings.Collateral = settings.Collateral.Add(types.NewCurrency64(99)) 523 err = ht.host.SetSettings(settings) 524 if err != nil { 525 t.Fatal(err) 526 } 527 528 // Reboot the host and verify that the new settings stuck. 529 err = ht.host.Close() // host saves upon closing 530 if err != nil { 531 t.Fatal(err) 532 } 533 h, err := New(ht.cs, ht.tpool, ht.wallet, "localhost:0", filepath.Join(ht.persistDir, modules.HostDir)) 534 if err != nil { 535 t.Fatal(err) 536 } 537 newSettings := h.Settings() 538 if settings.TotalStorage != newSettings.TotalStorage { 539 t.Error("settings retrieval did not return updated value:", settings.TotalStorage, "vs", newSettings.TotalStorage) 540 } 541 if settings.MaxDuration != newSettings.MaxDuration { 542 t.Error("settings retrieval did not return updated value") 543 } 544 if settings.WindowSize != newSettings.WindowSize { 545 t.Error("settings retrieval did not return updated value") 546 } 547 if settings.Price.Cmp(newSettings.Price) != 0 { 548 t.Error("settings retrieval did not return updated value") 549 } 550 if settings.Collateral.Cmp(newSettings.Collateral) != 0 { 551 t.Error("settings retrieval did not return updated value") 552 } 553 } 554 */