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