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