github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/renter/contractor/host_integration_test.go (about) 1 package contractor 2 3 import ( 4 "bytes" 5 "os" 6 "path/filepath" 7 "testing" 8 "time" 9 10 "github.com/NebulousLabs/Sia/build" 11 "github.com/NebulousLabs/Sia/crypto" 12 "github.com/NebulousLabs/Sia/modules" 13 "github.com/NebulousLabs/Sia/modules/consensus" 14 "github.com/NebulousLabs/Sia/modules/gateway" 15 "github.com/NebulousLabs/Sia/modules/host" 16 "github.com/NebulousLabs/Sia/modules/miner" 17 "github.com/NebulousLabs/Sia/modules/renter/hostdb" 18 "github.com/NebulousLabs/Sia/modules/transactionpool" 19 modWallet "github.com/NebulousLabs/Sia/modules/wallet" 20 "github.com/NebulousLabs/Sia/types" 21 ) 22 23 // newTestingWallet is a helper function that creates a ready-to-use wallet 24 // and mines some coins into it. 25 func newTestingWallet(testdir string, cs modules.ConsensusSet, tp modules.TransactionPool) (modules.Wallet, error) { 26 w, err := modWallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir)) 27 if err != nil { 28 return nil, err 29 } 30 key, err := crypto.GenerateTwofishKey() 31 if err != nil { 32 return nil, err 33 } 34 if !w.Encrypted() { 35 _, err = w.Encrypt(key) 36 if err != nil { 37 return nil, err 38 } 39 } 40 err = w.Unlock(key) 41 if err != nil { 42 return nil, err 43 } 44 // give it some money 45 m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir)) 46 if err != nil { 47 return nil, err 48 } 49 for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ { 50 _, err := m.AddBlock() 51 if err != nil { 52 return nil, err 53 } 54 } 55 return w, nil 56 } 57 58 // newTestingHost is a helper function that creates a ready-to-use host. 59 func newTestingHost(testdir string, cs modules.ConsensusSet, tp modules.TransactionPool) (modules.Host, error) { 60 w, err := newTestingWallet(testdir, cs, tp) 61 if err != nil { 62 return nil, err 63 } 64 return host.New(cs, tp, w, "localhost:0", filepath.Join(testdir, modules.HostDir)) 65 } 66 67 // newTestingContractor is a helper function that creates a ready-to-use 68 // contractor. 69 func newTestingContractor(testdir string, cs modules.ConsensusSet, tp modules.TransactionPool) (*Contractor, error) { 70 w, err := newTestingWallet(testdir, cs, tp) 71 if err != nil { 72 return nil, err 73 } 74 hdb, err := hostdb.New(cs, filepath.Join(testdir, "hostdb")) 75 if err != nil { 76 return nil, err 77 } 78 return New(cs, w, tp, hdb, filepath.Join(testdir, "contractor")) 79 } 80 81 // newTestingTrio creates a Host, Contractor, and TestMiner that can be used 82 // for testing host/renter interactions. 83 func newTestingTrio(name string) (modules.Host, *Contractor, modules.TestMiner, error) { 84 testdir := build.TempDir("contractor", name) 85 86 // create miner 87 g, err := gateway.New("localhost:0", filepath.Join(testdir, modules.GatewayDir)) 88 if err != nil { 89 return nil, nil, nil, err 90 } 91 cs, err := consensus.New(g, filepath.Join(testdir, modules.ConsensusDir)) 92 if err != nil { 93 return nil, nil, nil, err 94 } 95 tp, err := transactionpool.New(cs, g, filepath.Join(testdir, modules.TransactionPoolDir)) 96 if err != nil { 97 return nil, nil, nil, err 98 } 99 w, err := modWallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir)) 100 if err != nil { 101 return nil, nil, nil, err 102 } 103 key, err := crypto.GenerateTwofishKey() 104 if err != nil { 105 return nil, nil, nil, err 106 } 107 if !w.Encrypted() { 108 _, err = w.Encrypt(key) 109 if err != nil { 110 return nil, nil, nil, err 111 } 112 } 113 err = w.Unlock(key) 114 if err != nil { 115 return nil, nil, nil, err 116 } 117 m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir)) 118 if err != nil { 119 return nil, nil, nil, err 120 } 121 122 // create host and contractor, using same consensus set and gateway 123 h, err := newTestingHost(filepath.Join(testdir, "Host"), cs, tp) 124 if err != nil { 125 return nil, nil, nil, err 126 } 127 c, err := newTestingContractor(filepath.Join(testdir, "Contractor"), cs, tp) 128 if err != nil { 129 return nil, nil, nil, err 130 } 131 132 // Configure host to accept contracts 133 settings := h.InternalSettings() 134 settings.AcceptingContracts = true 135 err = h.SetInternalSettings(settings) 136 if err != nil { 137 return nil, nil, nil, err 138 } 139 140 // add storage to host 141 storageFolder := filepath.Join(build.SiaTestingDir, "contractor", "TestIntegrationReviseContract", "storage") 142 err = os.MkdirAll(storageFolder, 0700) 143 if err != nil { 144 return nil, nil, nil, err 145 } 146 err = h.AddStorageFolder(storageFolder, 1e6) 147 if err != nil { 148 return nil, nil, nil, err 149 } 150 151 // announce the host 152 err = h.Announce() 153 if err != nil { 154 return nil, nil, nil, err 155 } 156 157 // mine a block, processing the announcement 158 m.AddBlock() 159 160 // wait for hostdb to scan host 161 for i := 0; i < 500 && len(c.hdb.RandomHosts(1, nil)) == 0; i++ { 162 time.Sleep(time.Millisecond) 163 } 164 165 return h, c, m, nil 166 } 167 168 // TestIntegrationFormContract tests that the contractor can form contracts 169 // with the host module. 170 func TestIntegrationFormContract(t *testing.T) { 171 if testing.Short() { 172 t.SkipNow() 173 } 174 h, c, _, err := newTestingTrio("TestIntegrationFormContract") 175 if err != nil { 176 t.Fatal(err) 177 } 178 179 // get the host's entry from the db 180 hostEntry, ok := c.hdb.Host(h.ExternalSettings().NetAddress) 181 if !ok { 182 t.Fatal("no entry for host in db") 183 } 184 185 // form a contract with the host 186 contract, err := c.managedNewContract(hostEntry, 64000, c.blockHeight+100) 187 if err != nil { 188 t.Fatal(err) 189 } 190 191 if contract.NetAddress != h.ExternalSettings().NetAddress { 192 t.Fatal("bad contract") 193 } 194 } 195 196 // TestIntegrationReviseContract tests that the contractor can revise a 197 // contract previously formed with a host. 198 func TestIntegrationReviseContract(t *testing.T) { 199 if testing.Short() { 200 t.SkipNow() 201 } 202 // create testing trio 203 h, c, _, err := newTestingTrio("TestIntegrationReviseContract") 204 if err != nil { 205 t.Fatal(err) 206 } 207 208 // get the host's entry from the db 209 hostEntry, ok := c.hdb.Host(h.ExternalSettings().NetAddress) 210 if !ok { 211 t.Fatal("no entry for host in db") 212 } 213 214 // form a contract with the host 215 contract, err := c.managedNewContract(hostEntry, 64000, c.blockHeight+100) 216 if err != nil { 217 t.Fatal(err) 218 } 219 220 // revise the contract 221 editor, err := c.Editor(contract) 222 if err != nil { 223 t.Fatal(err) 224 } 225 data, err := crypto.RandBytes(int(modules.SectorSize)) 226 if err != nil { 227 t.Fatal(err) 228 } 229 _, err = editor.Upload(data) 230 if err != nil { 231 t.Fatal(err) 232 } 233 err = editor.Close() 234 if err != nil { 235 t.Fatal(err) 236 } 237 } 238 239 // TestIntegrationUploadDownload tests that the contractor can upload data to 240 // a host and download it intact. 241 func TestIntegrationUploadDownload(t *testing.T) { 242 if testing.Short() { 243 t.SkipNow() 244 } 245 // create testing trio 246 h, c, _, err := newTestingTrio("TestIntegrationUploadDownload") 247 if err != nil { 248 t.Fatal(err) 249 } 250 251 // get the host's entry from the db 252 hostEntry, ok := c.hdb.Host(h.ExternalSettings().NetAddress) 253 if !ok { 254 t.Fatal("no entry for host in db") 255 } 256 257 // form a contract with the host 258 contract, err := c.managedNewContract(hostEntry, modules.SectorSize*10, c.blockHeight+100) 259 if err != nil { 260 t.Fatal(err) 261 } 262 263 // revise the contract 264 editor, err := c.Editor(contract) 265 if err != nil { 266 t.Fatal(err) 267 } 268 data, err := crypto.RandBytes(int(modules.SectorSize)) 269 if err != nil { 270 t.Fatal(err) 271 } 272 root, err := editor.Upload(data) 273 if err != nil { 274 t.Fatal(err) 275 } 276 err = editor.Close() 277 if err != nil { 278 t.Fatal(err) 279 } 280 281 // download the data 282 contract = c.contracts[contract.ID] 283 downloader, err := c.Downloader(contract) 284 if err != nil { 285 t.Fatal(err) 286 } 287 retrieved, err := downloader.Sector(root) 288 if err != nil { 289 t.Fatal(err) 290 } 291 if !bytes.Equal(data, retrieved) { 292 t.Fatal("downloaded data does not match original") 293 } 294 err = downloader.Close() 295 if err != nil { 296 t.Fatal(err) 297 } 298 } 299 300 // TestIntegrationDelete tests that the contractor can delete a sector from a 301 // contract previously formed with a host. 302 func TestIntegrationDelete(t *testing.T) { 303 if testing.Short() { 304 t.SkipNow() 305 } 306 // create testing trio 307 h, c, _, err := newTestingTrio("TestIntegrationDelete") 308 if err != nil { 309 t.Fatal(err) 310 } 311 312 // get the host's entry from the db 313 hostEntry, ok := c.hdb.Host(h.ExternalSettings().NetAddress) 314 if !ok { 315 t.Fatal("no entry for host in db") 316 } 317 318 // form a contract with the host 319 contract, err := c.managedNewContract(hostEntry, modules.SectorSize*10, c.blockHeight+100) 320 if err != nil { 321 t.Fatal(err) 322 } 323 324 // revise the contract 325 editor, err := c.Editor(contract) 326 if err != nil { 327 t.Fatal(err) 328 } 329 data, err := crypto.RandBytes(int(modules.SectorSize)) 330 if err != nil { 331 t.Fatal(err) 332 } 333 _, err = editor.Upload(data) 334 if err != nil { 335 t.Fatal(err) 336 } 337 err = editor.Close() 338 if err != nil { 339 t.Fatal(err) 340 } 341 342 // delete the sector 343 contract = c.contracts[contract.ID] 344 editor, err = c.Editor(contract) 345 if err != nil { 346 t.Fatal(err) 347 } 348 err = editor.Delete(contract.MerkleRoots[0]) 349 if err != nil { 350 t.Fatal(err) 351 } 352 err = editor.Close() 353 if err != nil { 354 t.Fatal(err) 355 } 356 } 357 358 // TestIntegrationInsertDelete tests that the contractor can insert and delete 359 // a sector during the same revision. 360 func TestIntegrationInsertDelete(t *testing.T) { 361 if testing.Short() { 362 t.SkipNow() 363 } 364 // create testing trio 365 h, c, _, err := newTestingTrio("TestIntegrationInsertDelete") 366 if err != nil { 367 t.Fatal(err) 368 } 369 370 // get the host's entry from the db 371 hostEntry, ok := c.hdb.Host(h.ExternalSettings().NetAddress) 372 if !ok { 373 t.Fatal("no entry for host in db") 374 } 375 376 // form a contract with the host 377 contract, err := c.managedNewContract(hostEntry, modules.SectorSize*10, c.blockHeight+100) 378 if err != nil { 379 t.Fatal(err) 380 } 381 382 // revise the contract 383 editor, err := c.Editor(contract) 384 if err != nil { 385 t.Fatal(err) 386 } 387 data, err := crypto.RandBytes(int(modules.SectorSize)) 388 if err != nil { 389 t.Fatal(err) 390 } 391 // insert the sector 392 _, err = editor.Upload(data) 393 if err != nil { 394 t.Fatal(err) 395 } 396 // delete the sector 397 err = editor.Delete(crypto.MerkleRoot(data)) 398 if err != nil { 399 t.Fatal(err) 400 } 401 err = editor.Close() 402 if err != nil { 403 t.Fatal(err) 404 } 405 406 // contract should have no sectors 407 contract = c.contracts[contract.ID] 408 if len(contract.MerkleRoots) != 0 { 409 t.Fatal("contract should have no sectors:", contract.MerkleRoots) 410 } 411 } 412 413 // TestIntegrationModify tests that the contractor can modify a previously- 414 // uploaded sector. 415 func TestIntegrationModify(t *testing.T) { 416 if testing.Short() { 417 t.SkipNow() 418 } 419 // create testing trio 420 h, c, _, err := newTestingTrio("TestIntegrationModify") 421 if err != nil { 422 t.Fatal(err) 423 } 424 425 // get the host's entry from the db 426 hostEntry, ok := c.hdb.Host(h.ExternalSettings().NetAddress) 427 if !ok { 428 t.Fatal("no entry for host in db") 429 } 430 431 // form a contract with the host 432 contract, err := c.managedNewContract(hostEntry, modules.SectorSize*10, c.blockHeight+100) 433 if err != nil { 434 t.Fatal(err) 435 } 436 437 // revise the contract 438 editor, err := c.Editor(contract) 439 if err != nil { 440 t.Fatal(err) 441 } 442 data, err := crypto.RandBytes(int(modules.SectorSize)) 443 if err != nil { 444 t.Fatal(err) 445 } 446 // insert the sector 447 _, err = editor.Upload(data) 448 if err != nil { 449 t.Fatal(err) 450 } 451 err = editor.Close() 452 if err != nil { 453 t.Fatal(err) 454 } 455 456 // modify the sector 457 oldRoot := crypto.MerkleRoot(data) 458 offset, newData := uint64(10), []byte{1, 2, 3, 4, 5} 459 copy(data[offset:], newData) 460 newRoot := crypto.MerkleRoot(data) 461 contract = c.contracts[contract.ID] 462 editor, err = c.Editor(contract) 463 if err != nil { 464 t.Fatal(err) 465 } 466 err = editor.Modify(oldRoot, newRoot, offset, newData) 467 if err != nil { 468 t.Fatal(err) 469 } 470 err = editor.Close() 471 if err != nil { 472 t.Fatal(err) 473 } 474 } 475 476 // TestIntegrationRenew tests that the contractor can renew a previously- 477 // formed file contract. 478 func TestIntegrationRenew(t *testing.T) { 479 if testing.Short() { 480 t.SkipNow() 481 } 482 // create testing trio 483 h, c, _, err := newTestingTrio("TestIntegrationRenew") 484 if err != nil { 485 t.Fatal(err) 486 } 487 488 // get the host's entry from the db 489 hostEntry, ok := c.hdb.Host(h.ExternalSettings().NetAddress) 490 if !ok { 491 t.Fatal("no entry for host in db") 492 } 493 494 // form a contract with the host 495 contract, err := c.managedNewContract(hostEntry, modules.SectorSize*10, c.blockHeight+100) 496 if err != nil { 497 t.Fatal(err) 498 } 499 500 // revise the contract 501 editor, err := c.Editor(contract) 502 if err != nil { 503 t.Fatal(err) 504 } 505 data, err := crypto.RandBytes(int(modules.SectorSize)) 506 if err != nil { 507 t.Fatal(err) 508 } 509 // insert the sector 510 root, err := editor.Upload(data) 511 if err != nil { 512 t.Fatal(err) 513 } 514 err = editor.Close() 515 if err != nil { 516 t.Fatal(err) 517 } 518 519 // renew the contract 520 contract = c.contracts[contract.ID] 521 newID, err := c.managedRenew(contract, modules.SectorSize*10, c.blockHeight+200) 522 if err != nil { 523 t.Fatal(err) 524 } 525 526 // check renewed contract 527 contract = c.contracts[newID] 528 if contract.FileContract.FileMerkleRoot != root { 529 t.Fatal(contract.FileContract.FileMerkleRoot) 530 } else if contract.FileContract.FileSize != modules.SectorSize { 531 t.Fatal(contract.FileContract.FileSize) 532 } else if contract.FileContract.RevisionNumber != 0 { 533 t.Fatal(contract.FileContract.RevisionNumber) 534 } else if contract.FileContract.WindowStart != c.blockHeight+200 { 535 t.Fatal(contract.FileContract.WindowStart) 536 } 537 538 // download the renewed contract 539 downloader, err := c.Downloader(contract) 540 if err != nil { 541 t.Fatal(err) 542 } 543 retrieved, err := downloader.Sector(root) 544 if err != nil { 545 t.Fatal(err) 546 } 547 if !bytes.Equal(data, retrieved) { 548 t.Fatal("downloaded data does not match original") 549 } 550 err = downloader.Close() 551 if err != nil { 552 t.Fatal(err) 553 } 554 }