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