github.com/NebulousLabs/Sia@v1.3.7/modules/consensus/validtransaction_test.go (about) 1 package consensus 2 3 import ( 4 "testing" 5 6 "github.com/NebulousLabs/Sia/build" 7 "github.com/NebulousLabs/Sia/crypto" 8 "github.com/NebulousLabs/Sia/types" 9 "github.com/NebulousLabs/fastrand" 10 11 "github.com/coreos/bbolt" 12 ) 13 14 // TestTryValidTransactionSet submits a valid transaction set to the 15 // TryTransactionSet method. 16 func TestTryValidTransactionSet(t *testing.T) { 17 if testing.Short() { 18 t.SkipNow() 19 } 20 t.Parallel() 21 cst, err := createConsensusSetTester(t.Name()) 22 if err != nil { 23 t.Fatal(err) 24 } 25 defer cst.Close() 26 initialHash := cst.cs.dbConsensusChecksum() 27 28 // Try a valid transaction. 29 _, err = cst.wallet.SendSiacoins(types.NewCurrency64(1), types.UnlockHash{}) 30 if err != nil { 31 t.Fatal(err) 32 } 33 txns := cst.tpool.TransactionList() 34 cc, err := cst.cs.TryTransactionSet(txns) 35 if err != nil { 36 t.Error(err) 37 } 38 if cst.cs.dbConsensusChecksum() != initialHash { 39 t.Error("TryTransactionSet did not resotre order") 40 } 41 if len(cc.SiacoinOutputDiffs) == 0 { 42 t.Error("consensus change is missing diffs after verifying a transction clump") 43 } 44 } 45 46 // TestTryInvalidTransactionSet submits an invalid transaction set to the 47 // TryTransaction method. 48 func TestTryInvalidTransactionSet(t *testing.T) { 49 if testing.Short() { 50 t.SkipNow() 51 } 52 t.Parallel() 53 cst, err := createConsensusSetTester(t.Name()) 54 if err != nil { 55 t.Fatal(err) 56 } 57 defer cst.Close() 58 initialHash := cst.cs.dbConsensusChecksum() 59 60 // Try a valid transaction followed by an invalid transaction. 61 _, err = cst.wallet.SendSiacoins(types.NewCurrency64(1), types.UnlockHash{}) 62 if err != nil { 63 t.Fatal(err) 64 } 65 txns := cst.tpool.TransactionList() 66 txn := types.Transaction{ 67 SiacoinInputs: []types.SiacoinInput{{}}, 68 } 69 txns = append(txns, txn) 70 cc, err := cst.cs.TryTransactionSet(txns) 71 if err == nil { 72 t.Error("bad transaction survived filter") 73 } 74 if cst.cs.dbConsensusChecksum() != initialHash { 75 t.Error("TryTransactionSet did not restore order") 76 } 77 if len(cc.SiacoinOutputDiffs) != 0 { 78 t.Error("consensus change was not empty despite an error being returned") 79 } 80 } 81 82 // TestStorageProofBoundaries creates file contracts and submits storage proofs 83 // for them, probing segment boundaries (first segment, last segment, 84 // incomplete segment, etc.). 85 func TestStorageProofBoundaries(t *testing.T) { 86 if testing.Short() || !build.VLONG { 87 t.SkipNow() 88 } 89 t.Parallel() 90 cst, err := createConsensusSetTester(t.Name()) 91 if err != nil { 92 t.Fatal(err) 93 } 94 defer cst.Close() 95 96 // Mine enough blocks to put us beyond the testing hardfork. 97 for i := 0; i < 10; i++ { 98 _, err = cst.miner.AddBlock() 99 if err != nil { 100 t.Fatal(err) 101 } 102 } 103 104 // Try storage proofs on data between 0 bytes and 128 bytes (0 segments and 105 // 1 segment). Perform the operation five times because we can't control 106 // which segment gets selected - it is randomly decided by the block. 107 segmentRange := []int{0, 1, 2, 3, 4, 5, 15, 25, 30, 32, 62, 63, 64, 65, 66, 70, 81, 89, 90, 126, 127, 128, 129} 108 for i := 0; i < 3; i++ { 109 randData := fastrand.Bytes(140) 110 111 // Create a file contract for all sizes of the data between 0 and 2 112 // segments and put them in the transaction pool. 113 var fcids []types.FileContractID 114 for _, k := range segmentRange { 115 // Create the data and the file contract around it. 116 truncatedData := randData[:k] 117 fc := types.FileContract{ 118 FileSize: uint64(k), 119 FileMerkleRoot: crypto.MerkleRoot(truncatedData), 120 WindowStart: cst.cs.dbBlockHeight() + 2, 121 WindowEnd: cst.cs.dbBlockHeight() + 4, 122 Payout: types.NewCurrency64(500), // Too small to be subject to siafund fee. 123 ValidProofOutputs: []types.SiacoinOutput{{Value: types.NewCurrency64(500)}}, 124 MissedProofOutputs: []types.SiacoinOutput{{Value: types.NewCurrency64(500)}}, 125 } 126 127 // Create a transaction around the file contract and add it to the 128 // transaction pool. 129 b, err := cst.wallet.StartTransaction() 130 if err != nil { 131 t.Fatal(err) 132 } 133 err = b.FundSiacoins(types.NewCurrency64(500)) 134 if err != nil { 135 t.Fatal(err) 136 } 137 b.AddFileContract(fc) 138 txnSet, err := b.Sign(true) 139 if err != nil { 140 t.Fatal(err) 141 } 142 err = cst.tpool.AcceptTransactionSet(txnSet) 143 if err != nil { 144 t.Fatal(err) 145 } 146 147 // Store the file contract id for later when building the storage 148 // proof. 149 fcids = append(fcids, txnSet[len(txnSet)-1].FileContractID(0)) 150 } 151 152 // Mine blocks to get the file contracts into the blockchain and 153 // confirming. 154 for j := 0; j < 2; j++ { 155 _, err = cst.miner.AddBlock() 156 if err != nil { 157 t.Fatal(err) 158 } 159 } 160 161 // Create storage proofs for the file contracts and submit the proofs 162 // to the blockchain. 163 for j, k := range segmentRange { 164 // Build the storage proof. 165 truncatedData := randData[:k] 166 proofIndex, err := cst.cs.StorageProofSegment(fcids[j]) 167 if err != nil { 168 t.Fatal(err) 169 } 170 base, hashSet := crypto.MerkleProof(truncatedData, proofIndex) 171 sp := types.StorageProof{ 172 ParentID: fcids[j], 173 HashSet: hashSet, 174 } 175 copy(sp.Segment[:], base) 176 177 if k > 0 { 178 // Try submitting an empty storage proof, to make sure that the 179 // hardfork code didn't accidentally allow empty storage proofs 180 // in situations other than file sizes with 0 bytes. 181 badSP := types.StorageProof{ParentID: fcids[j]} 182 badTxn := types.Transaction{ 183 StorageProofs: []types.StorageProof{badSP}, 184 } 185 if sp.Segment == badSP.Segment { 186 continue 187 } 188 err = cst.tpool.AcceptTransactionSet([]types.Transaction{badTxn}) 189 if err == nil { 190 t.Fatal("An empty storage proof got into the transaction pool with non-empty data") 191 } 192 } 193 194 // Submit the storage proof to the blockchain in a transaction. 195 txn := types.Transaction{ 196 StorageProofs: []types.StorageProof{sp}, 197 } 198 err = cst.tpool.AcceptTransactionSet([]types.Transaction{txn}) 199 if err != nil { 200 t.Fatal(err, "-", j, k) 201 } 202 } 203 204 // Mine blocks to get the storage proofs on the blockchain. 205 for j := 0; j < 2; j++ { 206 _, err = cst.miner.AddBlock() 207 if err != nil { 208 t.Fatal(err) 209 } 210 } 211 } 212 } 213 214 // TestEmptyStorageProof creates file contracts and submits storage proofs for 215 // them, probing segment boundaries (first segment, last segment, incomplete 216 // segment, etc.). 217 func TestEmptyStorageProof(t *testing.T) { 218 if testing.Short() || !build.VLONG { 219 t.SkipNow() 220 } 221 t.Parallel() 222 cst, err := createConsensusSetTester(t.Name()) 223 if err != nil { 224 t.Fatal(err) 225 } 226 defer cst.Close() 227 228 // Mine enough blocks to put us beyond the testing hardfork. 229 for i := 0; i < 10; i++ { 230 _, err = cst.miner.AddBlock() 231 if err != nil { 232 t.Fatal(err) 233 } 234 } 235 236 // Try storage proofs on data between 0 bytes and 128 bytes (0 segments and 237 // 1 segment). Perform the operation five times because we can't control 238 // which segment gets selected - it is randomly decided by the block. 239 segmentRange := []int{0, 1, 2, 3, 4, 5, 15, 25, 30, 32, 62, 63, 64, 65, 66, 70, 81, 89, 90, 126, 127, 128, 129} 240 for i := 0; i < 3; i++ { 241 randData := fastrand.Bytes(140) 242 243 // Create a file contract for all sizes of the data between 0 and 2 244 // segments and put them in the transaction pool. 245 var fcids []types.FileContractID 246 for _, k := range segmentRange { 247 // Create the data and the file contract around it. 248 truncatedData := randData[:k] 249 fc := types.FileContract{ 250 FileSize: uint64(k), 251 FileMerkleRoot: crypto.MerkleRoot(truncatedData), 252 WindowStart: cst.cs.dbBlockHeight() + 2, 253 WindowEnd: cst.cs.dbBlockHeight() + 4, 254 Payout: types.NewCurrency64(500), // Too small to be subject to siafund fee. 255 ValidProofOutputs: []types.SiacoinOutput{{Value: types.NewCurrency64(500)}}, 256 MissedProofOutputs: []types.SiacoinOutput{{Value: types.NewCurrency64(500)}}, 257 } 258 259 // Create a transaction around the file contract and add it to the 260 // transaction pool. 261 b, err := cst.wallet.StartTransaction() 262 if err != nil { 263 t.Fatal(err) 264 } 265 err = b.FundSiacoins(types.NewCurrency64(500)) 266 if err != nil { 267 t.Fatal(err) 268 } 269 b.AddFileContract(fc) 270 txnSet, err := b.Sign(true) 271 if err != nil { 272 t.Fatal(err) 273 } 274 err = cst.tpool.AcceptTransactionSet(txnSet) 275 if err != nil { 276 t.Fatal(err) 277 } 278 279 // Store the file contract id for later when building the storage 280 // proof. 281 fcids = append(fcids, txnSet[len(txnSet)-1].FileContractID(0)) 282 } 283 284 // Mine blocks to get the file contracts into the blockchain and 285 // confirming. 286 for j := 0; j < 2; j++ { 287 _, err = cst.miner.AddBlock() 288 if err != nil { 289 t.Fatal(err) 290 } 291 } 292 293 // Create storage proofs for the file contracts and submit the proofs 294 // to the blockchain. 295 for j, k := range segmentRange { 296 // Build the storage proof. 297 truncatedData := randData[:k] 298 proofIndex, err := cst.cs.StorageProofSegment(fcids[j]) 299 if err != nil { 300 t.Fatal(err) 301 } 302 base, hashSet := crypto.MerkleProof(truncatedData, proofIndex) 303 sp := types.StorageProof{ 304 ParentID: fcids[j], 305 HashSet: hashSet, 306 } 307 copy(sp.Segment[:], base) 308 309 // Submit the storage proof to the blockchain in a transaction. 310 txn := types.Transaction{ 311 StorageProofs: []types.StorageProof{sp}, 312 } 313 err = cst.tpool.AcceptTransactionSet([]types.Transaction{txn}) 314 if err != nil { 315 t.Fatal(err, "-", j, k) 316 } 317 } 318 319 // Mine blocks to get the storage proofs on the blockchain. 320 for j := 0; j < 2; j++ { 321 _, err = cst.miner.AddBlock() 322 if err != nil { 323 t.Fatal(err) 324 } 325 } 326 } 327 } 328 329 // TestValidSiacoins probes the validSiacoins method of the consensus set. 330 func TestValidSiacoins(t *testing.T) { 331 if testing.Short() { 332 t.SkipNow() 333 } 334 t.Parallel() 335 cst, err := createConsensusSetTester(t.Name()) 336 if err != nil { 337 t.Fatal(err) 338 } 339 defer cst.Close() 340 341 // Create a transaction pointing to a nonexistent siacoin output. 342 txn := types.Transaction{ 343 SiacoinInputs: []types.SiacoinInput{{}}, 344 } 345 err = cst.cs.db.View(func(tx *bolt.Tx) error { 346 err := validSiacoins(tx, txn) 347 if err != errMissingSiacoinOutput { 348 t.Fatal(err) 349 } 350 return nil 351 }) 352 if err != nil { 353 t.Fatal(err) 354 } 355 356 // Create a transaction with invalid unlock conditions. 357 scoid, _, err := cst.cs.getArbSiacoinOutput() 358 if err != nil { 359 t.Fatal(err) 360 } 361 txn = types.Transaction{ 362 SiacoinInputs: []types.SiacoinInput{{ 363 ParentID: scoid, 364 }}, 365 } 366 err = cst.cs.db.View(func(tx *bolt.Tx) error { 367 err := validSiacoins(tx, txn) 368 if err != errWrongUnlockConditions { 369 t.Fatal(err) 370 } 371 return nil 372 }) 373 if err != nil { 374 t.Fatal(err) 375 } 376 377 // Create a txn with more outputs than inputs. 378 txn = types.Transaction{ 379 SiacoinOutputs: []types.SiacoinOutput{{ 380 Value: types.NewCurrency64(1), 381 }}, 382 } 383 err = cst.cs.db.View(func(tx *bolt.Tx) error { 384 err := validSiacoins(tx, txn) 385 if err != errSiacoinInputOutputMismatch { 386 t.Fatal(err) 387 } 388 return nil 389 }) 390 if err != nil { 391 t.Fatal(err) 392 } 393 } 394 395 // TestStorageProofSegment probes the storageProofSegment method of the 396 // consensus set. 397 func TestStorageProofSegment(t *testing.T) { 398 if testing.Short() { 399 t.SkipNow() 400 } 401 t.Parallel() 402 cst, err := createConsensusSetTester(t.Name()) 403 if err != nil { 404 t.Fatal(err) 405 } 406 defer cst.Close() 407 408 // Submit a file contract that is unrecognized. 409 _, err = cst.cs.dbStorageProofSegment(types.FileContractID{}) 410 if err != errUnrecognizedFileContractID { 411 t.Error(err) 412 } 413 414 // Try to get the segment of an unfinished file contract. 415 cst.cs.dbAddFileContract(types.FileContractID{}, types.FileContract{ 416 Payout: types.NewCurrency64(1), 417 WindowStart: 100000, 418 }) 419 _, err = cst.cs.dbStorageProofSegment(types.FileContractID{}) 420 if err != errUnfinishedFileContract { 421 t.Error(err) 422 } 423 } 424 425 // TestValidStorageProofs probes the validStorageProofs method of the consensus 426 // set. 427 func TestValidStorageProofs(t *testing.T) { 428 if testing.Short() { 429 t.SkipNow() 430 } 431 t.Parallel() 432 cst, err := createConsensusSetTester(t.Name()) 433 if err != nil { 434 t.Fatal(err) 435 } 436 defer cst.Close() 437 438 // COMPATv0.4.0 439 // 440 // Mine 10 blocks so that the post-hardfork rules are in effect. 441 for i := 0; i < 10; i++ { 442 block, _ := cst.miner.FindBlock() 443 err = cst.cs.AcceptBlock(block) 444 if err != nil { 445 t.Fatal(err) 446 } 447 } 448 449 // Create a file contract for which a storage proof can be created. 450 var fcid types.FileContractID 451 fcid[0] = 12 452 simFile := fastrand.Bytes(64 * 1024) 453 root := crypto.MerkleRoot(simFile) 454 fc := types.FileContract{ 455 FileSize: 64 * 1024, 456 FileMerkleRoot: root, 457 Payout: types.NewCurrency64(1), 458 WindowStart: 2, 459 WindowEnd: 1200, 460 } 461 cst.cs.dbAddFileContract(fcid, fc) 462 463 // Create a transaction with a storage proof. 464 proofIndex, err := cst.cs.dbStorageProofSegment(fcid) 465 if err != nil { 466 t.Fatal(err) 467 } 468 base, proofSet := crypto.MerkleProof(simFile, proofIndex) 469 txn := types.Transaction{ 470 StorageProofs: []types.StorageProof{{ 471 ParentID: fcid, 472 HashSet: proofSet, 473 }}, 474 } 475 copy(txn.StorageProofs[0].Segment[:], base) 476 err = cst.cs.dbValidStorageProofs(txn) 477 if err != nil { 478 t.Error(err) 479 } 480 481 // Corrupt the proof set. 482 proofSet[0][0]++ 483 txn = types.Transaction{ 484 StorageProofs: []types.StorageProof{{ 485 ParentID: fcid, 486 HashSet: proofSet, 487 }}, 488 } 489 copy(txn.StorageProofs[0].Segment[:], base) 490 err = cst.cs.dbValidStorageProofs(txn) 491 if err != errInvalidStorageProof { 492 t.Error(err) 493 } 494 495 // Try to validate a proof for a file contract that doesn't exist. 496 txn.StorageProofs[0].ParentID = types.FileContractID{} 497 err = cst.cs.dbValidStorageProofs(txn) 498 if err != errUnrecognizedFileContractID { 499 t.Error(err) 500 } 501 502 // Try a proof set where there is padding on the last segment in the file. 503 file := fastrand.Bytes(100) 504 root = crypto.MerkleRoot(file) 505 fc = types.FileContract{ 506 FileSize: 100, 507 FileMerkleRoot: root, 508 Payout: types.NewCurrency64(1), 509 WindowStart: 2, 510 WindowEnd: 1200, 511 } 512 513 // Find a proofIndex that has the value '1'. 514 for { 515 fcid[0]++ 516 cst.cs.dbAddFileContract(fcid, fc) 517 proofIndex, err = cst.cs.dbStorageProofSegment(fcid) 518 if err != nil { 519 t.Fatal(err) 520 } 521 if proofIndex == 1 { 522 break 523 } 524 } 525 base, proofSet = crypto.MerkleProof(file, proofIndex) 526 txn = types.Transaction{ 527 StorageProofs: []types.StorageProof{{ 528 ParentID: fcid, 529 HashSet: proofSet, 530 }}, 531 } 532 copy(txn.StorageProofs[0].Segment[:], base) 533 err = cst.cs.dbValidStorageProofs(txn) 534 if err != nil { 535 t.Fatal(err) 536 } 537 } 538 539 // HARDFORK 21,000 540 // 541 // TestPreForkValidStorageProofs checks that storage proofs which are invalid 542 // before the hardfork (but valid afterwards) are still rejected before the 543 // hardfork). 544 func TestPreForkValidStorageProofs(t *testing.T) { 545 if testing.Short() { 546 t.SkipNow() 547 } 548 t.Parallel() 549 cst, err := createConsensusSetTester(t.Name()) 550 if err != nil { 551 t.Fatal(err) 552 } 553 defer cst.Close() 554 555 // Try a proof set where there is padding on the last segment in the file. 556 file := fastrand.Bytes(100) 557 root := crypto.MerkleRoot(file) 558 fc := types.FileContract{ 559 FileSize: 100, 560 FileMerkleRoot: root, 561 Payout: types.NewCurrency64(1), 562 WindowStart: 2, 563 WindowEnd: 1200, 564 } 565 566 // Find a proofIndex that has the value '1'. 567 var fcid types.FileContractID 568 var proofIndex uint64 569 for { 570 fcid[0]++ 571 cst.cs.dbAddFileContract(fcid, fc) 572 proofIndex, err = cst.cs.dbStorageProofSegment(fcid) 573 if err != nil { 574 t.Fatal(err) 575 } 576 if proofIndex == 1 { 577 break 578 } 579 } 580 base, proofSet := crypto.MerkleProof(file, proofIndex) 581 txn := types.Transaction{ 582 StorageProofs: []types.StorageProof{{ 583 ParentID: fcid, 584 HashSet: proofSet, 585 }}, 586 } 587 copy(txn.StorageProofs[0].Segment[:], base) 588 err = cst.cs.dbValidStorageProofs(txn) 589 if err != errInvalidStorageProof { 590 t.Log(cst.cs.dbBlockHeight()) 591 t.Fatal(err) 592 } 593 } 594 595 // TestValidFileContractRevisions probes the validFileContractRevisions method 596 // of the consensus set. 597 func TestValidFileContractRevisions(t *testing.T) { 598 if testing.Short() { 599 t.SkipNow() 600 } 601 t.Parallel() 602 cst, err := createConsensusSetTester(t.Name()) 603 if err != nil { 604 t.Fatal(err) 605 } 606 defer cst.Close() 607 608 // Grab an address + unlock conditions for the transaction. 609 unlockConditions, err := cst.wallet.NextAddress() 610 if err != nil { 611 t.Fatal(err) 612 } 613 614 // Create a file contract for which a storage proof can be created. 615 var fcid types.FileContractID 616 fcid[0] = 12 617 simFile := fastrand.Bytes(64 * 1024) 618 root := crypto.MerkleRoot(simFile) 619 fc := types.FileContract{ 620 FileSize: 64 * 1024, 621 FileMerkleRoot: root, 622 WindowStart: 102, 623 WindowEnd: 1200, 624 Payout: types.NewCurrency64(1), 625 UnlockHash: unlockConditions.UnlockHash(), 626 RevisionNumber: 1, 627 } 628 cst.cs.dbAddFileContract(fcid, fc) 629 630 // Try a working file contract revision. 631 txn := types.Transaction{ 632 FileContractRevisions: []types.FileContractRevision{ 633 { 634 ParentID: fcid, 635 UnlockConditions: unlockConditions, 636 NewRevisionNumber: 2, 637 }, 638 }, 639 } 640 err = cst.cs.dbValidFileContractRevisions(txn) 641 if err != nil { 642 t.Error(err) 643 } 644 645 // Try a transaction with an insufficient revision number. 646 txn = types.Transaction{ 647 FileContractRevisions: []types.FileContractRevision{ 648 { 649 ParentID: fcid, 650 UnlockConditions: unlockConditions, 651 NewRevisionNumber: 1, 652 }, 653 }, 654 } 655 err = cst.cs.dbValidFileContractRevisions(txn) 656 if err != errLowRevisionNumber { 657 t.Error(err) 658 } 659 txn = types.Transaction{ 660 FileContractRevisions: []types.FileContractRevision{ 661 { 662 ParentID: fcid, 663 UnlockConditions: unlockConditions, 664 NewRevisionNumber: 0, 665 }, 666 }, 667 } 668 err = cst.cs.dbValidFileContractRevisions(txn) 669 if err != errLowRevisionNumber { 670 t.Error(err) 671 } 672 673 // Submit a file contract revision pointing to an invalid parent. 674 txn.FileContractRevisions[0].ParentID[0]-- 675 err = cst.cs.dbValidFileContractRevisions(txn) 676 if err != errNilItem { 677 t.Error(err) 678 } 679 txn.FileContractRevisions[0].ParentID[0]++ 680 681 // Submit a file contract revision for a file contract whose window has 682 // already opened. 683 fc, err = cst.cs.dbGetFileContract(fcid) 684 if err != nil { 685 t.Fatal(err) 686 } 687 fc.WindowStart = 0 688 cst.cs.dbRemoveFileContract(fcid) 689 cst.cs.dbAddFileContract(fcid, fc) 690 txn.FileContractRevisions[0].NewRevisionNumber = 3 691 err = cst.cs.dbValidFileContractRevisions(txn) 692 if err != errLateRevision { 693 t.Error(err) 694 } 695 696 // Submit a file contract revision with incorrect unlock conditions. 697 fc.WindowStart = 100 698 cst.cs.dbRemoveFileContract(fcid) 699 cst.cs.dbAddFileContract(fcid, fc) 700 txn.FileContractRevisions[0].UnlockConditions.Timelock++ 701 err = cst.cs.dbValidFileContractRevisions(txn) 702 if err != errWrongUnlockConditions { 703 t.Error(err) 704 } 705 txn.FileContractRevisions[0].UnlockConditions.Timelock-- 706 707 // Submit file contract revisions for file contracts with altered payouts. 708 txn.FileContractRevisions[0].NewValidProofOutputs = []types.SiacoinOutput{{ 709 Value: types.NewCurrency64(1), 710 }} 711 txn.FileContractRevisions[0].NewMissedProofOutputs = []types.SiacoinOutput{{ 712 Value: types.NewCurrency64(1), 713 }} 714 err = cst.cs.dbValidFileContractRevisions(txn) 715 if err != errAlteredRevisionPayouts { 716 t.Error(err) 717 } 718 txn.FileContractRevisions[0].NewValidProofOutputs = nil 719 err = cst.cs.dbValidFileContractRevisions(txn) 720 if err != errAlteredRevisionPayouts { 721 t.Error(err) 722 } 723 txn.FileContractRevisions[0].NewValidProofOutputs = []types.SiacoinOutput{{ 724 Value: types.NewCurrency64(1), 725 }} 726 txn.FileContractRevisions[0].NewMissedProofOutputs = nil 727 err = cst.cs.dbValidFileContractRevisions(txn) 728 if err != errAlteredRevisionPayouts { 729 t.Error(err) 730 } 731 } 732 733 /* 734 // TestValidSiafunds probes the validSiafunds mthod of the consensus set. 735 func TestValidSiafunds(t *testing.T) { 736 if testing.Short() { 737 t.SkipNow() 738 } 739 cst, err := createConsensusSetTester(t.Name()) 740 if err != nil { 741 t.Fatal(err) 742 } 743 defer cst.closeCst() 744 745 // Create a transaction pointing to a nonexistent siafund output. 746 txn := types.Transaction{ 747 SiafundInputs: []types.SiafundInput{{}}, 748 } 749 err = cst.cs.validSiafunds(txn) 750 if err != ErrMissingSiafundOutput { 751 t.Error(err) 752 } 753 754 // Create a transaction with invalid unlock conditions. 755 var sfoid types.SiafundOutputID 756 cst.cs.db.forEachSiafundOutputs(func(mapSfoid types.SiafundOutputID, sfo types.SiafundOutput) { 757 sfoid = mapSfoid 758 // pointless to do this but I can't think of a better way. 759 }) 760 txn = types.Transaction{ 761 SiafundInputs: []types.SiafundInput{{ 762 ParentID: sfoid, 763 UnlockConditions: types.UnlockConditions{Timelock: 12345}, // avoid collisions with existing outputs 764 }}, 765 } 766 err = cst.cs.validSiafunds(txn) 767 if err != ErrWrongUnlockConditions { 768 t.Error(err) 769 } 770 771 // Create a transaction with more outputs than inputs. 772 txn = types.Transaction{ 773 SiafundOutputs: []types.SiafundOutput{{ 774 Value: types.NewCurrency64(1), 775 }}, 776 } 777 err = cst.cs.validSiafunds(txn) 778 if err != ErrSiafundInputOutputMismatch { 779 t.Error(err) 780 } 781 } 782 783 // TestValidTransaction probes the validTransaction method of the consensus 784 // set. 785 func TestValidTransaction(t *testing.T) { 786 if testing.Short() { 787 t.SkipNow() 788 } 789 cst, err := createConsensusSetTester(t.Name()) 790 if err != nil { 791 t.Fatal(err) 792 } 793 defer cst.closeCst() 794 795 // Create a transaction that is not standalone valid. 796 txn := types.Transaction{ 797 FileContracts: []types.FileContract{{ 798 WindowStart: 0, 799 }}, 800 } 801 err = cst.cs.validTransaction(txn) 802 if err == nil { 803 t.Error("transaction is valid") 804 } 805 806 // Create a transaction with invalid siacoins. 807 txn = types.Transaction{ 808 SiacoinInputs: []types.SiacoinInput{{}}, 809 } 810 err = cst.cs.validTransaction(txn) 811 if err == nil { 812 t.Error("transaction is valid") 813 } 814 815 // Create a transaction with invalid storage proofs. 816 txn = types.Transaction{ 817 StorageProofs: []types.StorageProof{{}}, 818 } 819 err = cst.cs.validTransaction(txn) 820 if err == nil { 821 t.Error("transaction is valid") 822 } 823 824 // Create a transaction with invalid file contract revisions. 825 txn = types.Transaction{ 826 FileContractRevisions: []types.FileContractRevision{{ 827 NewWindowStart: 5000, 828 NewWindowEnd: 5005, 829 ParentID: types.FileContractID{}, 830 }}, 831 } 832 err = cst.cs.validTransaction(txn) 833 if err == nil { 834 t.Error("transaction is valid") 835 } 836 837 // Create a transaction with invalid siafunds. 838 txn = types.Transaction{ 839 SiafundInputs: []types.SiafundInput{{}}, 840 } 841 err = cst.cs.validTransaction(txn) 842 if err == nil { 843 t.Error("transaction is valid") 844 } 845 } 846 */