github.com/johnathanhowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/host/storageobligations_smoke_test.go (about) 1 package host 2 3 // storageobligations_smoke_test.go performs smoke testing on the the storage 4 // obligation management. This includes adding valid storage obligations, and 5 // waiting until they expire, to see if the failure modes are all handled 6 // correctly. 7 8 import ( 9 "testing" 10 11 "github.com/NebulousLabs/Sia/crypto" 12 "github.com/NebulousLabs/Sia/modules" 13 "github.com/NebulousLabs/Sia/types" 14 15 "github.com/NebulousLabs/bolt" 16 ) 17 18 // randSector creates a random sector, returning the sector along with the 19 // Merkle root of the sector. 20 func randSector() (crypto.Hash, []byte, error) { 21 sectorData, err := crypto.RandBytes(int(modules.SectorSize)) 22 if err != nil { 23 return crypto.Hash{}, nil, err 24 } 25 sectorRoot := crypto.MerkleRoot(sectorData) 26 return sectorRoot, sectorData, nil 27 } 28 29 // newTesterStorageObligation uses the wallet to create and fund a file 30 // contract that will form the foundation of a storage obligation. 31 func (ht *hostTester) newTesterStorageObligation() (*storageObligation, error) { 32 // Create the file contract that will be used in the obligation. 33 builder := ht.wallet.StartTransaction() 34 // Fund the file contract with a payout. The payout needs to be big enough 35 // that the expected revenue is larger than the fee that the host may end 36 // up paying. 37 payout := types.SiacoinPrecision.Mul64(1e3) 38 err := builder.FundSiacoins(payout) 39 if err != nil { 40 return nil, err 41 } 42 // Add the file contract that consumes the funds. 43 _ = builder.AddFileContract(types.FileContract{ 44 // Because this file contract needs to be able to accept file contract 45 // revisions, the expiration is put more than 46 // 'revisionSubmissionBuffer' blocks into the future. 47 WindowStart: ht.host.blockHeight + revisionSubmissionBuffer + 2, 48 WindowEnd: ht.host.blockHeight + revisionSubmissionBuffer + defaultWindowSize + 2, 49 50 Payout: payout, 51 ValidProofOutputs: []types.SiacoinOutput{ 52 { 53 Value: types.PostTax(ht.host.blockHeight, payout), 54 }, 55 { 56 Value: types.ZeroCurrency, 57 }, 58 }, 59 MissedProofOutputs: []types.SiacoinOutput{ 60 { 61 Value: types.PostTax(ht.host.blockHeight, payout), 62 }, 63 { 64 Value: types.ZeroCurrency, 65 }, 66 }, 67 UnlockHash: (types.UnlockConditions{}).UnlockHash(), 68 RevisionNumber: 0, 69 }) 70 // Sign the transaction. 71 tSet, err := builder.Sign(true) 72 if err != nil { 73 return nil, err 74 } 75 76 // Assemble and return the storage obligation. 77 so := &storageObligation{ 78 OriginTransactionSet: tSet, 79 80 // TODO: There are no tracking values, because no fees were added. 81 } 82 return so, nil 83 } 84 85 // TestBlankStorageObligation checks that the host correctly manages a blank 86 // storage obligation. 87 func TestBlankStorageObligation(t *testing.T) { 88 if testing.Short() { 89 t.SkipNow() 90 } 91 t.Parallel() 92 ht, err := newHostTester("TestBlankStorageObligation") 93 if err != nil { 94 t.Fatal(err) 95 } 96 97 // Start by adding a storage obligation to the host. To emulate conditions 98 // of a renter creating the first contract, the storage obligation has no 99 // data, but does have money. 100 so, err := ht.newTesterStorageObligation() 101 if err != nil { 102 t.Fatal(err) 103 } 104 err = ht.host.lockStorageObligation(so) 105 if err != nil { 106 t.Fatal(err) 107 } 108 err = ht.host.addStorageObligation(so) 109 if err != nil { 110 t.Fatal(err) 111 } 112 err = ht.host.unlockStorageObligation(so) 113 if err != nil { 114 t.Fatal(err) 115 } 116 // Storage obligation should not be marked as having the transaction 117 // confirmed on the blockchain. 118 if so.OriginConfirmed { 119 t.Fatal("storage obligation should not yet be marked as confirmed, confirmation is on the way") 120 } 121 122 // Mine a block to confirm the transaction containing the storage 123 // obligation. 124 _, err = ht.miner.AddBlock() 125 if err != nil { 126 t.Fatal(err) 127 } 128 // Load the storage obligation from the database, see if it updated 129 // correctly. 130 err = ht.host.db.View(func(tx *bolt.Tx) error { 131 *so, err = getStorageObligation(tx, so.id()) 132 if err != nil { 133 return err 134 } 135 return nil 136 }) 137 if err != nil { 138 t.Fatal(err) 139 } 140 if !so.OriginConfirmed { 141 t.Fatal("origin transaction for storage obligation was not confirmed after a block was mined") 142 } 143 144 // Mine until the host would be submitting a storage proof. Check that the 145 // host has cleared out the storage proof - the consensus code makes it 146 // impossible to submit a storage proof for an empty file contract, so the 147 // host should fail and give up by deleting the storage obligation. 148 for i := types.BlockHeight(0); i <= revisionSubmissionBuffer*2+1; i++ { 149 _, err := ht.miner.AddBlock() 150 if err != nil { 151 t.Fatal(err) 152 } 153 } 154 err = ht.host.db.View(func(tx *bolt.Tx) error { 155 *so, err = getStorageObligation(tx, so.id()) 156 if err != nil { 157 return err 158 } 159 return nil 160 }) 161 if err != errNoStorageObligation { 162 t.Fatal(err) 163 } 164 } 165 166 // TestSingleSectorObligationStack checks that the host correctly manages a 167 // storage obligation with a single sector, the revision is created the same 168 // block as the file contract. 169 func TestSingleSectorStorageObligationStack(t *testing.T) { 170 if testing.Short() { 171 t.SkipNow() 172 } 173 t.Parallel() 174 ht, err := newHostTester("TestSingleSectorStorageObligationStack") 175 if err != nil { 176 t.Fatal(err) 177 } 178 179 // Start by adding a storage obligation to the host. To emulate conditions 180 // of a renter creating the first contract, the storage obligation has no 181 // data, but does have money. 182 so, err := ht.newTesterStorageObligation() 183 if err != nil { 184 t.Fatal(err) 185 } 186 err = ht.host.lockStorageObligation(so) 187 if err != nil { 188 t.Fatal(err) 189 } 190 err = ht.host.addStorageObligation(so) 191 if err != nil { 192 t.Fatal(err) 193 } 194 err = ht.host.unlockStorageObligation(so) 195 if err != nil { 196 t.Fatal(err) 197 } 198 // Storage obligation should not be marked as having the transaction 199 // confirmed on the blockchain. 200 if so.OriginConfirmed { 201 t.Fatal("storage obligation should not yet be marked as confirmed, confirmation is on the way") 202 } 203 204 // Add a file contract revision, moving over a small amount of money to pay 205 // for the file contract. 206 sectorRoot, sectorData, err := randSector() 207 if err != nil { 208 t.Fatal(err) 209 } 210 so.SectorRoots = []crypto.Hash{sectorRoot} 211 sectorCost := types.SiacoinPrecision.Mul64(550) 212 so.PotentialStorageRevenue = so.PotentialStorageRevenue.Add(sectorCost) 213 ht.host.financialMetrics.PotentialStorageRevenue = ht.host.financialMetrics.PotentialStorageRevenue.Add(sectorCost) 214 validPayouts, missedPayouts := so.payouts() 215 validPayouts[0].Value = validPayouts[0].Value.Sub(sectorCost) 216 validPayouts[1].Value = validPayouts[1].Value.Add(sectorCost) 217 missedPayouts[0].Value = missedPayouts[0].Value.Sub(sectorCost) 218 missedPayouts[1].Value = missedPayouts[1].Value.Add(sectorCost) 219 revisionSet := []types.Transaction{{ 220 FileContractRevisions: []types.FileContractRevision{{ 221 ParentID: so.id(), 222 UnlockConditions: types.UnlockConditions{}, 223 NewRevisionNumber: 1, 224 225 NewFileSize: uint64(len(sectorData)), 226 NewFileMerkleRoot: sectorRoot, 227 NewWindowStart: so.expiration(), 228 NewWindowEnd: so.proofDeadline(), 229 NewValidProofOutputs: validPayouts, 230 NewMissedProofOutputs: missedPayouts, 231 NewUnlockHash: types.UnlockConditions{}.UnlockHash(), 232 }}, 233 }} 234 err = ht.host.lockStorageObligation(so) 235 if err != nil { 236 t.Fatal(err) 237 } 238 err = ht.host.modifyStorageObligation(so, nil, []crypto.Hash{sectorRoot}, [][]byte{sectorData}) 239 if err != nil { 240 t.Fatal(err) 241 } 242 err = ht.host.unlockStorageObligation(so) 243 if err != nil { 244 t.Fatal(err) 245 } 246 // Submit the revision set to the transaction pool. 247 err = ht.tpool.AcceptTransactionSet(revisionSet) 248 if err != nil { 249 t.Fatal(err) 250 } 251 252 // Mine a block to confirm the transactions containing the file contract 253 // and the file contract revision. 254 _, err = ht.miner.AddBlock() 255 if err != nil { 256 t.Fatal(err) 257 } 258 // Load the storage obligation from the database, see if it updated 259 // correctly. 260 err = ht.host.db.View(func(tx *bolt.Tx) error { 261 *so, err = getStorageObligation(tx, so.id()) 262 if err != nil { 263 return err 264 } 265 return nil 266 }) 267 if err != nil { 268 t.Fatal(err) 269 } 270 if !so.OriginConfirmed { 271 t.Fatal("origin transaction for storage obligation was not confirmed after a block was mined") 272 } 273 if !so.RevisionConfirmed { 274 t.Fatal("revision transaction for storage obligation was not confirmed after a block was mined") 275 } 276 277 // Mine until the host submits a storage proof. 278 for i := ht.host.blockHeight; i <= so.expiration()+resubmissionTimeout; i++ { 279 _, err := ht.miner.AddBlock() 280 if err != nil { 281 t.Fatal(err) 282 } 283 } 284 err = ht.host.db.View(func(tx *bolt.Tx) error { 285 *so, err = getStorageObligation(tx, so.id()) 286 if err != nil { 287 return err 288 } 289 return nil 290 }) 291 if err != nil { 292 t.Fatal(err) 293 } 294 if !so.OriginConfirmed { 295 t.Fatal("origin transaction for storage obligation was not confirmed after a block was mined") 296 } 297 if !so.RevisionConfirmed { 298 t.Fatal("revision transaction for storage obligation was not confirmed after a block was mined") 299 } 300 if !so.ProofConfirmed { 301 t.Fatal("storage obligation is not saying that the storage proof was confirmed on the blockchain") 302 } 303 304 // Mine blocks until the storage proof has enough confirmations that the 305 // host will delete the file entirely. 306 for i := 0; i <= int(defaultWindowSize); i++ { 307 _, err := ht.miner.AddBlock() 308 if err != nil { 309 t.Fatal(err) 310 } 311 } 312 err = ht.host.db.View(func(tx *bolt.Tx) error { 313 *so, err = getStorageObligation(tx, so.id()) 314 if err != nil { 315 return err 316 } 317 return nil 318 }) 319 if err != errNoStorageObligation { 320 t.Fatal(err) 321 } 322 if ht.host.financialMetrics.StorageRevenue.Cmp(sectorCost) != 0 { 323 t.Fatal("the host should be reporting revenue after a successful storage proof") 324 } 325 } 326 327 // TestMultiSectorObligationStack checks that the host correctly manages a 328 // storage obligation with a single sector, the revision is created the same 329 // block as the file contract. 330 // 331 // Unlike the SingleSector test, the multi sector test attempts to spread file 332 // contract revisions over multiple blocks. 333 func TestMultiSectorStorageObligationStack(t *testing.T) { 334 if testing.Short() { 335 t.SkipNow() 336 } 337 t.Parallel() 338 ht, err := newHostTester("TestMultiSectorStorageObligationStack") 339 if err != nil { 340 t.Fatal(err) 341 } 342 343 // Start by adding a storage obligation to the host. To emulate conditions 344 // of a renter creating the first contract, the storage obligation has no 345 // data, but does have money. 346 so, err := ht.newTesterStorageObligation() 347 if err != nil { 348 t.Fatal(err) 349 } 350 err = ht.host.lockStorageObligation(so) 351 if err != nil { 352 t.Fatal(err) 353 } 354 err = ht.host.addStorageObligation(so) 355 if err != nil { 356 t.Fatal(err) 357 } 358 err = ht.host.unlockStorageObligation(so) 359 if err != nil { 360 t.Fatal(err) 361 } 362 // Storage obligation should not be marked as having the transaction 363 // confirmed on the blockchain. 364 if so.OriginConfirmed { 365 t.Fatal("storage obligation should not yet be marked as confirmed, confirmation is on the way") 366 } 367 // Deviation from SingleSector test - mine a block here to confirm the 368 // storage obligation before a file contract revision is created. 369 _, err = ht.miner.AddBlock() 370 if err != nil { 371 t.Fatal(err) 372 } 373 // Load the storage obligation from the database, see if it updated 374 // correctly. 375 err = ht.host.db.View(func(tx *bolt.Tx) error { 376 *so, err = getStorageObligation(tx, so.id()) 377 if err != nil { 378 return err 379 } 380 return nil 381 }) 382 if err != nil { 383 t.Fatal(err) 384 } 385 if !so.OriginConfirmed { 386 t.Fatal("origin transaction for storage obligation was not confirmed after a block was mined") 387 } 388 389 // Add a file contract revision, moving over a small amount of money to pay 390 // for the file contract. 391 sectorRoot, sectorData, err := randSector() 392 if err != nil { 393 t.Fatal(err) 394 } 395 so.SectorRoots = []crypto.Hash{sectorRoot} 396 sectorCost := types.SiacoinPrecision.Mul64(550) 397 so.PotentialStorageRevenue = so.PotentialStorageRevenue.Add(sectorCost) 398 ht.host.financialMetrics.PotentialStorageRevenue = ht.host.financialMetrics.PotentialStorageRevenue.Add(sectorCost) 399 validPayouts, missedPayouts := so.payouts() 400 validPayouts[0].Value = validPayouts[0].Value.Sub(sectorCost) 401 validPayouts[1].Value = validPayouts[1].Value.Add(sectorCost) 402 missedPayouts[0].Value = missedPayouts[0].Value.Sub(sectorCost) 403 missedPayouts[1].Value = missedPayouts[1].Value.Add(sectorCost) 404 revisionSet := []types.Transaction{{ 405 FileContractRevisions: []types.FileContractRevision{{ 406 ParentID: so.id(), 407 UnlockConditions: types.UnlockConditions{}, 408 NewRevisionNumber: 1, 409 410 NewFileSize: uint64(len(sectorData)), 411 NewFileMerkleRoot: sectorRoot, 412 NewWindowStart: so.expiration(), 413 NewWindowEnd: so.proofDeadline(), 414 NewValidProofOutputs: validPayouts, 415 NewMissedProofOutputs: missedPayouts, 416 NewUnlockHash: types.UnlockConditions{}.UnlockHash(), 417 }}, 418 }} 419 err = ht.host.lockStorageObligation(so) 420 if err != nil { 421 t.Fatal(err) 422 } 423 err = ht.host.modifyStorageObligation(so, nil, []crypto.Hash{sectorRoot}, [][]byte{sectorData}) 424 if err != nil { 425 t.Fatal(err) 426 } 427 err = ht.host.unlockStorageObligation(so) 428 if err != nil { 429 t.Fatal(err) 430 } 431 // Submit the revision set to the transaction pool. 432 err = ht.tpool.AcceptTransactionSet(revisionSet) 433 if err != nil { 434 t.Fatal(err) 435 } 436 437 // Create a second file contract revision, which is going to be submitted 438 // to the transaction pool after the first revision. Though, in practice 439 // this should never happen, we want to check that the transaction pool is 440 // correctly handling multiple file contract revisions being submitted in 441 // the same block cycle. This test will additionally tell us whether or not 442 // the host can correctly handle buildling storage proofs for files with 443 // multiple sectors. 444 sectorRoot2, sectorData2, err := randSector() 445 if err != nil { 446 t.Fatal(err) 447 } 448 so.SectorRoots = []crypto.Hash{sectorRoot, sectorRoot2} 449 sectorCost2 := types.SiacoinPrecision.Mul64(650) 450 so.PotentialStorageRevenue = so.PotentialStorageRevenue.Add(sectorCost2) 451 ht.host.financialMetrics.PotentialStorageRevenue = ht.host.financialMetrics.PotentialStorageRevenue.Add(sectorCost2) 452 validPayouts, missedPayouts = so.payouts() 453 validPayouts[0].Value = validPayouts[0].Value.Sub(sectorCost2) 454 validPayouts[1].Value = validPayouts[1].Value.Add(sectorCost2) 455 missedPayouts[0].Value = missedPayouts[0].Value.Sub(sectorCost2) 456 missedPayouts[1].Value = missedPayouts[1].Value.Add(sectorCost2) 457 combinedSectors := append(sectorData, sectorData2...) 458 combinedRoot := crypto.MerkleRoot(combinedSectors) 459 revisionSet2 := []types.Transaction{{ 460 FileContractRevisions: []types.FileContractRevision{{ 461 ParentID: so.id(), 462 UnlockConditions: types.UnlockConditions{}, 463 NewRevisionNumber: 2, 464 465 NewFileSize: uint64(len(sectorData) + len(sectorData2)), 466 NewFileMerkleRoot: combinedRoot, 467 NewWindowStart: so.expiration(), 468 NewWindowEnd: so.proofDeadline(), 469 NewValidProofOutputs: validPayouts, 470 NewMissedProofOutputs: missedPayouts, 471 NewUnlockHash: types.UnlockConditions{}.UnlockHash(), 472 }}, 473 }} 474 err = ht.host.lockStorageObligation(so) 475 if err != nil { 476 t.Fatal(err) 477 } 478 err = ht.host.modifyStorageObligation(so, nil, []crypto.Hash{sectorRoot2}, [][]byte{sectorData2}) 479 if err != nil { 480 t.Fatal(err) 481 } 482 err = ht.host.unlockStorageObligation(so) 483 if err != nil { 484 t.Fatal(err) 485 } 486 // Submit the revision set to the transaction pool. 487 err = ht.tpool.AcceptTransactionSet(revisionSet2) 488 if err != nil { 489 t.Fatal(err) 490 } 491 492 // Mine a block to confirm the transactions containing the file contract 493 // and the file contract revision. 494 _, err = ht.miner.AddBlock() 495 if err != nil { 496 t.Fatal(err) 497 } 498 // Load the storage obligation from the database, see if it updated 499 // correctly. 500 err = ht.host.db.View(func(tx *bolt.Tx) error { 501 *so, err = getStorageObligation(tx, so.id()) 502 if err != nil { 503 return err 504 } 505 return nil 506 }) 507 if err != nil { 508 t.Fatal(err) 509 } 510 if !so.OriginConfirmed { 511 t.Fatal("origin transaction for storage obligation was not confirmed after a block was mined") 512 } 513 if !so.RevisionConfirmed { 514 t.Fatal("revision transaction for storage obligation was not confirmed after a block was mined") 515 } 516 517 // Mine until the host submits a storage proof. 518 for i := ht.host.blockHeight; i <= so.expiration()+resubmissionTimeout; i++ { 519 _, err := ht.miner.AddBlock() 520 if err != nil { 521 t.Fatal(err) 522 } 523 } 524 err = ht.host.db.View(func(tx *bolt.Tx) error { 525 *so, err = getStorageObligation(tx, so.id()) 526 if err != nil { 527 return err 528 } 529 return nil 530 }) 531 if err != nil { 532 t.Fatal(err) 533 } 534 if !so.OriginConfirmed { 535 t.Fatal("origin transaction for storage obligation was not confirmed after a block was mined") 536 } 537 if !so.RevisionConfirmed { 538 t.Fatal("revision transaction for storage obligation was not confirmed after a block was mined") 539 } 540 if !so.ProofConfirmed { 541 t.Fatal("storage obligation is not saying that the storage proof was confirmed on the blockchain") 542 } 543 544 // Mine blocks until the storage proof has enough confirmations that the 545 // host will delete the file entirely. 546 for i := 0; i <= int(defaultWindowSize); i++ { 547 _, err := ht.miner.AddBlock() 548 if err != nil { 549 t.Fatal(err) 550 } 551 } 552 err = ht.host.db.View(func(tx *bolt.Tx) error { 553 *so, err = getStorageObligation(tx, so.id()) 554 if err != nil { 555 return err 556 } 557 return nil 558 }) 559 if err != errNoStorageObligation { 560 t.Fatal(err) 561 } 562 if ht.host.financialMetrics.StorageRevenue.Cmp(sectorCost.Add(sectorCost2)) != 0 { 563 t.Fatal("the host should be reporting revenue after a successful storage proof") 564 } 565 } 566 567 // TestAutoRevisionSubmission checks that the host correctly submits a file 568 // contract revision to the consensus set. 569 func TestAutoRevisionSubmission(t *testing.T) { 570 if testing.Short() { 571 t.SkipNow() 572 } 573 t.Parallel() 574 ht, err := newHostTester("TestAutoRevisionSubmission") 575 if err != nil { 576 t.Fatal(err) 577 } 578 579 // Start by adding a storage obligation to the host. To emulate conditions 580 // of a renter creating the first contract, the storage obligation has no 581 // data, but does have money. 582 so, err := ht.newTesterStorageObligation() 583 if err != nil { 584 t.Fatal(err) 585 } 586 err = ht.host.lockStorageObligation(so) 587 if err != nil { 588 t.Fatal(err) 589 } 590 err = ht.host.addStorageObligation(so) 591 if err != nil { 592 t.Fatal(err) 593 } 594 err = ht.host.unlockStorageObligation(so) 595 if err != nil { 596 t.Fatal(err) 597 } 598 // Storage obligation should not be marked as having the transaction 599 // confirmed on the blockchain. 600 if so.OriginConfirmed { 601 t.Fatal("storage obligation should not yet be marked as confirmed, confirmation is on the way") 602 } 603 604 // Add a file contract revision, moving over a small amount of money to pay 605 // for the file contract. 606 sectorRoot, sectorData, err := randSector() 607 if err != nil { 608 t.Fatal(err) 609 } 610 so.SectorRoots = []crypto.Hash{sectorRoot} 611 sectorCost := types.SiacoinPrecision.Mul64(550) 612 so.PotentialStorageRevenue = so.PotentialStorageRevenue.Add(sectorCost) 613 ht.host.financialMetrics.PotentialStorageRevenue = ht.host.financialMetrics.PotentialStorageRevenue.Add(sectorCost) 614 validPayouts, missedPayouts := so.payouts() 615 validPayouts[0].Value = validPayouts[0].Value.Sub(sectorCost) 616 validPayouts[1].Value = validPayouts[1].Value.Add(sectorCost) 617 missedPayouts[0].Value = missedPayouts[0].Value.Sub(sectorCost) 618 missedPayouts[1].Value = missedPayouts[1].Value.Add(sectorCost) 619 revisionSet := []types.Transaction{{ 620 FileContractRevisions: []types.FileContractRevision{{ 621 ParentID: so.id(), 622 UnlockConditions: types.UnlockConditions{}, 623 NewRevisionNumber: 1, 624 625 NewFileSize: uint64(len(sectorData)), 626 NewFileMerkleRoot: sectorRoot, 627 NewWindowStart: so.expiration(), 628 NewWindowEnd: so.proofDeadline(), 629 NewValidProofOutputs: validPayouts, 630 NewMissedProofOutputs: missedPayouts, 631 NewUnlockHash: types.UnlockConditions{}.UnlockHash(), 632 }}, 633 }} 634 so.RevisionTransactionSet = revisionSet 635 err = ht.host.lockStorageObligation(so) 636 if err != nil { 637 t.Fatal(err) 638 } 639 err = ht.host.modifyStorageObligation(so, nil, []crypto.Hash{sectorRoot}, [][]byte{sectorData}) 640 if err != nil { 641 t.Fatal(err) 642 } 643 err = ht.host.unlockStorageObligation(so) 644 if err != nil { 645 t.Fatal(err) 646 } 647 // Unlike the other tests, this test does not submit the file contract 648 // revision to the transaction pool for the host, the host is expected to 649 // do it automatically. 650 651 // Mine until the host submits a storage proof. 652 for i := types.BlockHeight(0); i <= revisionSubmissionBuffer+2+resubmissionTimeout; i++ { 653 _, err := ht.miner.AddBlock() 654 if err != nil { 655 t.Fatal(err) 656 } 657 } 658 err = ht.host.db.View(func(tx *bolt.Tx) error { 659 *so, err = getStorageObligation(tx, so.id()) 660 if err != nil { 661 return err 662 } 663 return nil 664 }) 665 if err != nil { 666 t.Fatal(err) 667 } 668 if !so.OriginConfirmed { 669 t.Fatal("origin transaction for storage obligation was not confirmed after blocks were mined") 670 } 671 if !so.RevisionConfirmed { 672 t.Fatal("revision transaction for storage obligation was not confirmed after blocks were mined") 673 } 674 if !so.ProofConfirmed { 675 t.Fatal("storage obligation is not saying that the storage proof was confirmed on the blockchain") 676 } 677 678 // Mine blocks until the storage proof has enough confirmations that the 679 // host will delete the file entirely. 680 for i := 0; i <= int(defaultWindowSize); i++ { 681 _, err := ht.miner.AddBlock() 682 if err != nil { 683 t.Fatal(err) 684 } 685 } 686 err = ht.host.db.View(func(tx *bolt.Tx) error { 687 *so, err = getStorageObligation(tx, so.id()) 688 if err != nil { 689 return err 690 } 691 return nil 692 }) 693 if err != errNoStorageObligation { 694 t.Error(so.OriginConfirmed) 695 t.Error(so.RevisionConfirmed) 696 t.Error(so.ProofConfirmed) 697 t.Fatal(err) 698 } 699 if ht.host.financialMetrics.StorageRevenue.Cmp(sectorCost) != 0 { 700 t.Fatal("the host should be reporting revenue after a successful storage proof") 701 } 702 }