github.com/NebulousLabs/Sia@v1.3.7/modules/consensus/applytransaction_test.go (about) 1 package consensus 2 3 /* 4 import ( 5 "testing" 6 7 "github.com/NebulousLabs/Sia/modules" 8 "github.com/NebulousLabs/Sia/types" 9 ) 10 11 // TestApplySiacoinInputs probes the applySiacoinInputs method of the consensus 12 // set. 13 func TestApplySiacoinInputs(t *testing.T) { 14 if testing.Short() { 15 t.SkipNow() 16 } 17 18 // Create a consensus set and get it to 3 siacoin outputs. The consensus 19 // set starts with 2 siacoin outputs, mining a block will add another. 20 cst, err := createConsensusSetTester(t.Name()) 21 if err != nil { 22 t.Fatal(err) 23 } 24 defer cst.closeCst() 25 b, _ := cst.miner.FindBlock() 26 err = cst.cs.AcceptBlock(b) 27 if err != nil { 28 t.Fatal(err) 29 } 30 31 // Create a block node to use with application. 32 pb := new(processedBlock) 33 34 // Fetch the output id's of each siacoin output in the consensus set. 35 var ids []types.SiacoinOutputID 36 cst.cs.db.forEachSiacoinOutputs(func(id types.SiacoinOutputID, sco types.SiacoinOutput) { 37 ids = append(ids, id) 38 }) 39 40 // Apply a transaction with a single siacoin input. 41 txn := types.Transaction{ 42 SiacoinInputs: []types.SiacoinInput{ 43 {ParentID: ids[0]}, 44 }, 45 } 46 cst.cs.applySiacoinInputs(pb, txn) 47 exists := cst.cs.db.inSiacoinOutputs(ids[0]) 48 if exists { 49 t.Error("Failed to conusme a siacoin output") 50 } 51 if cst.cs.db.lenSiacoinOutputs() != 2 { 52 t.Error("siacoin outputs not correctly updated") 53 } 54 if len(pb.SiacoinOutputDiffs) != 1 { 55 t.Error("block node was not updated for single transaction") 56 } 57 if pb.SiacoinOutputDiffs[0].Direction != modules.DiffRevert { 58 t.Error("wrong diff direction applied when consuming a siacoin output") 59 } 60 if pb.SiacoinOutputDiffs[0].ID != ids[0] { 61 t.Error("wrong id used when consuming a siacoin output") 62 } 63 64 // Apply a transaction with two siacoin inputs. 65 txn = types.Transaction{ 66 SiacoinInputs: []types.SiacoinInput{ 67 {ParentID: ids[1]}, 68 {ParentID: ids[2]}, 69 }, 70 } 71 cst.cs.applySiacoinInputs(pb, txn) 72 if cst.cs.db.lenSiacoinOutputs() != 0 { 73 t.Error("failed to consume all siacoin outputs in the consensus set") 74 } 75 if len(pb.SiacoinOutputDiffs) != 3 { 76 t.Error("processed block was not updated for single transaction") 77 } 78 } 79 80 // TestMisuseApplySiacoinInputs misuses applySiacoinInput and checks that a 81 // panic was triggered. 82 func TestMisuseApplySiacoinInputs(t *testing.T) { 83 if testing.Short() { 84 t.SkipNow() 85 } 86 cst, err := createConsensusSetTester(t.Name()) 87 if err != nil { 88 t.Fatal(err) 89 } 90 defer cst.closeCst() 91 92 // Create a block node to use with application. 93 pb := new(processedBlock) 94 95 // Fetch the output id's of each siacoin output in the consensus set. 96 var ids []types.SiacoinOutputID 97 cst.cs.db.forEachSiacoinOutputs(func(id types.SiacoinOutputID, sco types.SiacoinOutput) { 98 ids = append(ids, id) 99 }) 100 101 // Apply a transaction with a single siacoin input. 102 txn := types.Transaction{ 103 SiacoinInputs: []types.SiacoinInput{ 104 {ParentID: ids[0]}, 105 }, 106 } 107 cst.cs.applySiacoinInputs(pb, txn) 108 109 // Trigger the panic that occurs when an output is applied incorrectly, and 110 // perform a catch to read the error that is created. 111 defer func() { 112 r := recover() 113 if r == nil { 114 t.Error("expecting error after corrupting database") 115 } 116 }() 117 cst.cs.applySiacoinInputs(pb, txn) 118 } 119 120 // TestApplySiacoinOutputs probes the applySiacoinOutput method of the 121 // consensus set. 122 func TestApplySiacoinOutputs(t *testing.T) { 123 if testing.Short() { 124 t.SkipNow() 125 } 126 cst, err := createConsensusSetTester(t.Name()) 127 if err != nil { 128 t.Fatal(err) 129 } 130 defer cst.closeCst() 131 132 // Create a block node to use with application. 133 pb := new(processedBlock) 134 135 // Apply a transaction with a single siacoin output. 136 txn := types.Transaction{ 137 SiacoinOutputs: []types.SiacoinOutput{{}}, 138 } 139 cst.cs.applySiacoinOutputs(pb, txn) 140 scoid := txn.SiacoinOutputID(0) 141 exists := cst.cs.db.inSiacoinOutputs(scoid) 142 if !exists { 143 t.Error("Failed to create siacoin output") 144 } 145 if cst.cs.db.lenSiacoinOutputs() != 3 { // 3 because createConsensusSetTester has 2 initially. 146 t.Error("siacoin outputs not correctly updated") 147 } 148 if len(pb.SiacoinOutputDiffs) != 1 { 149 t.Error("block node was not updated for single element transaction") 150 } 151 if pb.SiacoinOutputDiffs[0].Direction != modules.DiffApply { 152 t.Error("wrong diff direction applied when creating a siacoin output") 153 } 154 if pb.SiacoinOutputDiffs[0].ID != scoid { 155 t.Error("wrong id used when creating a siacoin output") 156 } 157 158 // Apply a transaction with 2 siacoin outputs. 159 txn = types.Transaction{ 160 SiacoinOutputs: []types.SiacoinOutput{ 161 {Value: types.NewCurrency64(1)}, 162 {Value: types.NewCurrency64(2)}, 163 }, 164 } 165 cst.cs.applySiacoinOutputs(pb, txn) 166 scoid0 := txn.SiacoinOutputID(0) 167 scoid1 := txn.SiacoinOutputID(1) 168 exists = cst.cs.db.inSiacoinOutputs(scoid0) 169 if !exists { 170 t.Error("Failed to create siacoin output") 171 } 172 exists = cst.cs.db.inSiacoinOutputs(scoid1) 173 if !exists { 174 t.Error("Failed to create siacoin output") 175 } 176 if cst.cs.db.lenSiacoinOutputs() != 5 { // 5 because createConsensusSetTester has 2 initially. 177 t.Error("siacoin outputs not correctly updated") 178 } 179 if len(pb.SiacoinOutputDiffs) != 3 { 180 t.Error("block node was not updated correctly") 181 } 182 } 183 184 // TestMisuseApplySiacoinOutputs misuses applySiacoinOutputs and checks that a 185 // panic was triggered. 186 func TestMisuseApplySiacoinOutputs(t *testing.T) { 187 if testing.Short() { 188 t.SkipNow() 189 } 190 cst, err := createConsensusSetTester(t.Name()) 191 if err != nil { 192 t.Fatal(err) 193 } 194 defer cst.closeCst() 195 196 // Create a block node to use with application. 197 pb := new(processedBlock) 198 199 // Apply a transaction with a single siacoin output. 200 txn := types.Transaction{ 201 SiacoinOutputs: []types.SiacoinOutput{{}}, 202 } 203 cst.cs.applySiacoinOutputs(pb, txn) 204 205 // Trigger the panic that occurs when an output is applied incorrectly, and 206 // perform a catch to read the error that is created. 207 defer func() { 208 r := recover() 209 if r == nil { 210 t.Error("no panic occurred when misusing applySiacoinInput") 211 } 212 }() 213 cst.cs.applySiacoinOutputs(pb, txn) 214 } 215 216 // TestApplyFileContracts probes the applyFileContracts method of the 217 // consensus set. 218 func TestApplyFileContracts(t *testing.T) { 219 if testing.Short() { 220 t.SkipNow() 221 } 222 cst, err := createConsensusSetTester(t.Name()) 223 if err != nil { 224 t.Fatal(err) 225 } 226 defer cst.closeCst() 227 228 // Create a block node to use with application. 229 pb := new(processedBlock) 230 231 // Apply a transaction with a single file contract. 232 txn := types.Transaction{ 233 FileContracts: []types.FileContract{{}}, 234 } 235 cst.cs.applyFileContracts(pb, txn) 236 fcid := txn.FileContractID(0) 237 exists := cst.cs.db.inFileContracts(fcid) 238 if !exists { 239 t.Error("Failed to create file contract") 240 } 241 if cst.cs.db.lenFileContracts() != 1 { 242 t.Error("file contracts not correctly updated") 243 } 244 if len(pb.FileContractDiffs) != 1 { 245 t.Error("block node was not updated for single element transaction") 246 } 247 if pb.FileContractDiffs[0].Direction != modules.DiffApply { 248 t.Error("wrong diff direction applied when creating a file contract") 249 } 250 if pb.FileContractDiffs[0].ID != fcid { 251 t.Error("wrong id used when creating a file contract") 252 } 253 254 // Apply a transaction with 2 file contracts. 255 txn = types.Transaction{ 256 FileContracts: []types.FileContract{ 257 {Payout: types.NewCurrency64(1)}, 258 {Payout: types.NewCurrency64(300e3)}, 259 }, 260 } 261 cst.cs.applyFileContracts(pb, txn) 262 fcid0 := txn.FileContractID(0) 263 fcid1 := txn.FileContractID(1) 264 exists = cst.cs.db.inFileContracts(fcid0) 265 if !exists { 266 t.Error("Failed to create file contract") 267 } 268 exists = cst.cs.db.inFileContracts(fcid1) 269 if !exists { 270 t.Error("Failed to create file contract") 271 } 272 if cst.cs.db.lenFileContracts() != 3 { 273 t.Error("file contracts not correctly updated") 274 } 275 if len(pb.FileContractDiffs) != 3 { 276 t.Error("block node was not updated correctly") 277 } 278 if cst.cs.siafundPool.Cmp64(10e3) != 0 { 279 t.Error("siafund pool did not update correctly upon creation of a file contract") 280 } 281 } 282 283 // TestMisuseApplyFileContracts misuses applyFileContracts and checks that a 284 // panic was triggered. 285 func TestMisuseApplyFileContracts(t *testing.T) { 286 if testing.Short() { 287 t.SkipNow() 288 } 289 cst, err := createConsensusSetTester(t.Name()) 290 if err != nil { 291 t.Fatal(err) 292 } 293 defer cst.closeCst() 294 295 // Create a block node to use with application. 296 pb := new(processedBlock) 297 298 // Apply a transaction with a single file contract. 299 txn := types.Transaction{ 300 FileContracts: []types.FileContract{{}}, 301 } 302 cst.cs.applyFileContracts(pb, txn) 303 304 // Trigger the panic that occurs when an output is applied incorrectly, and 305 // perform a catch to read the error that is created. 306 defer func() { 307 r := recover() 308 if r == nil { 309 t.Error("no panic occurred when misusing applySiacoinInput") 310 } 311 }() 312 cst.cs.applyFileContracts(pb, txn) 313 } 314 315 // TestApplyFileContractRevisions probes the applyFileContractRevisions method 316 // of the consensus set. 317 func TestApplyFileContractRevisions(t *testing.T) { 318 if testing.Short() { 319 t.SkipNow() 320 } 321 cst, err := createConsensusSetTester(t.Name()) 322 if err != nil { 323 t.Fatal(err) 324 } 325 defer cst.closeCst() 326 327 // Create a block node to use with application. 328 pb := new(processedBlock) 329 330 // Apply a transaction with two file contracts - that way there is 331 // something to revise. 332 txn := types.Transaction{ 333 FileContracts: []types.FileContract{ 334 {}, 335 {Payout: types.NewCurrency64(1)}, 336 }, 337 } 338 cst.cs.applyFileContracts(pb, txn) 339 fcid0 := txn.FileContractID(0) 340 fcid1 := txn.FileContractID(1) 341 342 // Apply a single file contract revision. 343 txn = types.Transaction{ 344 FileContractRevisions: []types.FileContractRevision{ 345 { 346 ParentID: fcid0, 347 NewFileSize: 1, 348 }, 349 }, 350 } 351 cst.cs.applyFileContractRevisions(pb, txn) 352 exists := cst.cs.db.inFileContracts(fcid0) 353 if !exists { 354 t.Error("Revision killed a file contract") 355 } 356 fc := cst.cs.db.getFileContracts(fcid0) 357 if fc.FileSize != 1 { 358 t.Error("file contract filesize not properly updated") 359 } 360 if cst.cs.db.lenFileContracts() != 2 { 361 t.Error("file contracts not correctly updated") 362 } 363 if len(pb.FileContractDiffs) != 4 { // 2 creating the initial contracts, 1 to remove the old, 1 to add the revision. 364 t.Error("block node was not updated for single element transaction") 365 } 366 if pb.FileContractDiffs[2].Direction != modules.DiffRevert { 367 t.Error("wrong diff direction applied when revising a file contract") 368 } 369 if pb.FileContractDiffs[3].Direction != modules.DiffApply { 370 t.Error("wrong diff direction applied when revising a file contract") 371 } 372 if pb.FileContractDiffs[2].ID != fcid0 { 373 t.Error("wrong id used when revising a file contract") 374 } 375 if pb.FileContractDiffs[3].ID != fcid0 { 376 t.Error("wrong id used when revising a file contract") 377 } 378 379 // Apply a transaction with 2 file contract revisions. 380 txn = types.Transaction{ 381 FileContractRevisions: []types.FileContractRevision{ 382 { 383 ParentID: fcid0, 384 NewFileSize: 2, 385 }, 386 { 387 ParentID: fcid1, 388 NewFileSize: 3, 389 }, 390 }, 391 } 392 cst.cs.applyFileContractRevisions(pb, txn) 393 exists = cst.cs.db.inFileContracts(fcid0) 394 if !exists { 395 t.Error("Revision ate file contract") 396 } 397 fc0 := cst.cs.db.getFileContracts(fcid0) 398 exists = cst.cs.db.inFileContracts(fcid1) 399 if !exists { 400 t.Error("Revision ate file contract") 401 } 402 fc1 := cst.cs.db.getFileContracts(fcid1) 403 if fc0.FileSize != 2 { 404 t.Error("Revision not correctly applied") 405 } 406 if fc1.FileSize != 3 { 407 t.Error("Revision not correctly applied") 408 } 409 if cst.cs.db.lenFileContracts() != 2 { 410 t.Error("file contracts not correctly updated") 411 } 412 if len(pb.FileContractDiffs) != 8 { 413 t.Error("block node was not updated correctly") 414 } 415 } 416 417 // TestMisuseApplyFileContractRevisions misuses applyFileContractRevisions and 418 // checks that a panic was triggered. 419 func TestMisuseApplyFileContractRevisions(t *testing.T) { 420 if testing.Short() { 421 t.SkipNow() 422 } 423 cst, err := createConsensusSetTester(t.Name()) 424 if err != nil { 425 t.Fatal(err) 426 } 427 defer cst.closeCst() 428 429 // Create a block node to use with application. 430 pb := new(processedBlock) 431 432 // Trigger a panic from revising a nonexistent file contract. 433 defer func() { 434 r := recover() 435 if r != errNilItem { 436 t.Error("no panic occurred when misusing applySiacoinInput") 437 } 438 }() 439 txn := types.Transaction{ 440 FileContractRevisions: []types.FileContractRevision{{}}, 441 } 442 cst.cs.applyFileContractRevisions(pb, txn) 443 } 444 445 // TestApplyStorageProofs probes the applyStorageProofs method of the consensus 446 // set. 447 func TestApplyStorageProofs(t *testing.T) { 448 if testing.Short() { 449 t.SkipNow() 450 } 451 cst, err := createConsensusSetTester(t.Name()) 452 if err != nil { 453 t.Fatal(err) 454 } 455 defer cst.closeCst() 456 457 // Create a block node to use with application. 458 pb := new(processedBlock) 459 pb.Height = cst.cs.height() 460 461 // Apply a transaction with two file contracts - there is a reason to 462 // create a storage proof. 463 txn := types.Transaction{ 464 FileContracts: []types.FileContract{ 465 { 466 Payout: types.NewCurrency64(300e3), 467 ValidProofOutputs: []types.SiacoinOutput{ 468 {Value: types.NewCurrency64(290e3)}, 469 }, 470 }, 471 {}, 472 { 473 Payout: types.NewCurrency64(600e3), 474 ValidProofOutputs: []types.SiacoinOutput{ 475 {Value: types.NewCurrency64(280e3)}, 476 {Value: types.NewCurrency64(300e3)}, 477 }, 478 }, 479 }, 480 } 481 cst.cs.applyFileContracts(pb, txn) 482 fcid0 := txn.FileContractID(0) 483 fcid1 := txn.FileContractID(1) 484 fcid2 := txn.FileContractID(2) 485 486 // Apply a single storage proof. 487 txn = types.Transaction{ 488 StorageProofs: []types.StorageProof{{ParentID: fcid0}}, 489 } 490 cst.cs.applyStorageProofs(pb, txn) 491 exists := cst.cs.db.inFileContracts(fcid0) 492 if exists { 493 t.Error("Storage proof did not disable a file contract.") 494 } 495 if cst.cs.db.lenFileContracts() != 2 { 496 t.Error("file contracts not correctly updated") 497 } 498 if len(pb.FileContractDiffs) != 4 { // 3 creating the initial contracts, 1 for the storage proof. 499 t.Error("block node was not updated for single element transaction") 500 } 501 if pb.FileContractDiffs[3].Direction != modules.DiffRevert { 502 t.Error("wrong diff direction applied when revising a file contract") 503 } 504 if pb.FileContractDiffs[3].ID != fcid0 { 505 t.Error("wrong id used when revising a file contract") 506 } 507 spoid0 := fcid0.StorageProofOutputID(types.ProofValid, 0) 508 exists = cst.cs.db.inDelayedSiacoinOutputsHeight(pb.Height+types.MaturityDelay, spoid0) 509 if !exists { 510 t.Error("storage proof output not created after applying a storage proof") 511 } 512 sco := cst.cs.db.getDelayedSiacoinOutputs(pb.Height+types.MaturityDelay, spoid0) 513 if sco.Value.Cmp64(290e3) != 0 { 514 t.Error("storage proof output was created with the wrong value") 515 } 516 517 // Apply a transaction with 2 storage proofs. 518 txn = types.Transaction{ 519 StorageProofs: []types.StorageProof{ 520 {ParentID: fcid1}, 521 {ParentID: fcid2}, 522 }, 523 } 524 cst.cs.applyStorageProofs(pb, txn) 525 exists = cst.cs.db.inFileContracts(fcid1) 526 if exists { 527 t.Error("Storage proof failed to consume file contract.") 528 } 529 exists = cst.cs.db.inFileContracts(fcid2) 530 if exists { 531 t.Error("storage proof did not consume file contract") 532 } 533 if cst.cs.db.lenFileContracts() != 0 { 534 t.Error("file contracts not correctly updated") 535 } 536 if len(pb.FileContractDiffs) != 6 { 537 t.Error("block node was not updated correctly") 538 } 539 spoid1 := fcid1.StorageProofOutputID(types.ProofValid, 0) 540 exists = cst.cs.db.inSiacoinOutputs(spoid1) 541 if exists { 542 t.Error("output created when file contract had no corresponding output") 543 } 544 spoid2 := fcid2.StorageProofOutputID(types.ProofValid, 0) 545 exists = cst.cs.db.inDelayedSiacoinOutputsHeight(pb.Height+types.MaturityDelay, spoid2) 546 if !exists { 547 t.Error("no output created by first output of file contract") 548 } 549 sco = cst.cs.db.getDelayedSiacoinOutputs(pb.Height+types.MaturityDelay, spoid2) 550 if sco.Value.Cmp64(280e3) != 0 { 551 t.Error("first siacoin output created has wrong value") 552 } 553 spoid3 := fcid2.StorageProofOutputID(types.ProofValid, 1) 554 exists = cst.cs.db.inDelayedSiacoinOutputsHeight(pb.Height+types.MaturityDelay, spoid3) 555 if !exists { 556 t.Error("second output not created for storage proof") 557 } 558 sco = cst.cs.db.getDelayedSiacoinOutputs(pb.Height+types.MaturityDelay, spoid3) 559 if sco.Value.Cmp64(300e3) != 0 { 560 t.Error("second siacoin output has wrong value") 561 } 562 if cst.cs.siafundPool.Cmp64(30e3) != 0 { 563 t.Error("siafund pool not being added up correctly") 564 } 565 } 566 567 // TestNonexistentStorageProof applies a storage proof which points to a 568 // nonextentent parent. 569 func TestNonexistentStorageProof(t *testing.T) { 570 if testing.Short() { 571 t.SkipNow() 572 } 573 cst, err := createConsensusSetTester(t.Name()) 574 if err != nil { 575 t.Fatal(err) 576 } 577 defer cst.closeCst() 578 579 // Create a block node to use with application. 580 pb := new(processedBlock) 581 582 // Trigger a panic by applying a storage proof for a nonexistent file 583 // contract. 584 defer func() { 585 r := recover() 586 if r != errNilItem { 587 t.Error("no panic occurred when misusing applySiacoinInput") 588 } 589 }() 590 txn := types.Transaction{ 591 StorageProofs: []types.StorageProof{{}}, 592 } 593 cst.cs.applyStorageProofs(pb, txn) 594 } 595 596 // TestDuplicateStorageProof applies a storage proof which has already been 597 // applied. 598 func TestDuplicateStorageProof(t *testing.T) { 599 if testing.Short() { 600 t.SkipNow() 601 } 602 cst, err := createConsensusSetTester(t.Name()) 603 if err != nil { 604 t.Fatal(err) 605 } 606 defer cst.closeCst() 607 608 // Create a block node. 609 pb := new(processedBlock) 610 pb.Height = cst.cs.height() 611 612 // Create a file contract for the storage proof to prove. 613 txn0 := types.Transaction{ 614 FileContracts: []types.FileContract{ 615 { 616 Payout: types.NewCurrency64(300e3), 617 ValidProofOutputs: []types.SiacoinOutput{ 618 {Value: types.NewCurrency64(290e3)}, 619 }, 620 }, 621 }, 622 } 623 cst.cs.applyFileContracts(pb, txn0) 624 fcid := txn0.FileContractID(0) 625 626 // Apply a single storage proof. 627 txn1 := types.Transaction{ 628 StorageProofs: []types.StorageProof{{ParentID: fcid}}, 629 } 630 cst.cs.applyStorageProofs(pb, txn1) 631 632 // Trigger a panic by applying the storage proof again. 633 defer func() { 634 r := recover() 635 if r != ErrDuplicateValidProofOutput { 636 t.Error("failed to trigger ErrDuplicateValidProofOutput:", r) 637 } 638 }() 639 cst.cs.applyFileContracts(pb, txn0) // File contract was consumed by the first proof. 640 cst.cs.applyStorageProofs(pb, txn1) 641 } 642 643 // TestApplySiafundInputs probes the applySiafundInputs method of the consensus 644 // set. 645 func TestApplySiafundInputs(t *testing.T) { 646 if testing.Short() { 647 t.SkipNow() 648 } 649 cst, err := createConsensusSetTester(t.Name()) 650 if err != nil { 651 t.Fatal(err) 652 } 653 defer cst.closeCst() 654 655 // Create a block node to use with application. 656 pb := new(processedBlock) 657 pb.Height = cst.cs.height() 658 659 // Fetch the output id's of each siacoin output in the consensus set. 660 var ids []types.SiafundOutputID 661 cst.cs.db.forEachSiafundOutputs(func(sfoid types.SiafundOutputID, sfo types.SiafundOutput) { 662 ids = append(ids, sfoid) 663 }) 664 665 // Apply a transaction with a single siafund input. 666 txn := types.Transaction{ 667 SiafundInputs: []types.SiafundInput{ 668 {ParentID: ids[0]}, 669 }, 670 } 671 cst.cs.applySiafundInputs(pb, txn) 672 exists := cst.cs.db.inSiafundOutputs(ids[0]) 673 if exists { 674 t.Error("Failed to conusme a siafund output") 675 } 676 if cst.cs.db.lenSiafundOutputs() != 2 { 677 t.Error("siafund outputs not correctly updated", cst.cs.db.lenSiafundOutputs()) 678 } 679 if len(pb.SiafundOutputDiffs) != 1 { 680 t.Error("block node was not updated for single transaction") 681 } 682 if pb.SiafundOutputDiffs[0].Direction != modules.DiffRevert { 683 t.Error("wrong diff direction applied when consuming a siafund output") 684 } 685 if pb.SiafundOutputDiffs[0].ID != ids[0] { 686 t.Error("wrong id used when consuming a siafund output") 687 } 688 if cst.cs.db.lenDelayedSiacoinOutputsHeight(cst.cs.height()+types.MaturityDelay) != 2 { // 1 for a block subsidy, 1 for the siafund claim. 689 t.Error("siafund claim was not created") 690 } 691 } 692 693 // TestMisuseApplySiafundInputs misuses applySiafundInputs and checks that a 694 // panic was triggered. 695 func TestMisuseApplySiafundInputs(t *testing.T) { 696 if testing.Short() { 697 t.SkipNow() 698 } 699 cst, err := createConsensusSetTester(t.Name()) 700 if err != nil { 701 t.Fatal(err) 702 } 703 defer cst.closeCst() 704 705 // Create a block node to use with application. 706 pb := new(processedBlock) 707 pb.Height = cst.cs.height() 708 709 // Fetch the output id's of each siacoin output in the consensus set. 710 var ids []types.SiafundOutputID 711 cst.cs.db.forEachSiafundOutputs(func(sfoid types.SiafundOutputID, sfo types.SiafundOutput) { 712 ids = append(ids, sfoid) 713 }) 714 715 // Apply a transaction with a single siafund input. 716 txn := types.Transaction{ 717 SiafundInputs: []types.SiafundInput{ 718 {ParentID: ids[0]}, 719 }, 720 } 721 cst.cs.applySiafundInputs(pb, txn) 722 723 // Trigger the panic that occurs when an output is applied incorrectly, and 724 // perform a catch to read the error that is created. 725 defer func() { 726 r := recover() 727 if r != ErrMisuseApplySiafundInput { 728 t.Error("no panic occurred when misusing applySiacoinInput") 729 t.Error(r) 730 } 731 }() 732 cst.cs.applySiafundInputs(pb, txn) 733 } 734 735 // TestApplySiafundOutputs probes the applySiafundOutputs method of the 736 // consensus set. 737 func TestApplySiafundOutputs(t *testing.T) { 738 if testing.Short() { 739 t.SkipNow() 740 } 741 cst, err := createConsensusSetTester(t.Name()) 742 if err != nil { 743 t.Fatal(err) 744 } 745 defer cst.closeCst() 746 cst.cs.siafundPool = types.NewCurrency64(101) 747 748 // Create a block node to use with application. 749 pb := new(processedBlock) 750 751 // Apply a transaction with a single siafund output. 752 txn := types.Transaction{ 753 SiafundOutputs: []types.SiafundOutput{{}}, 754 } 755 cst.cs.applySiafundOutputs(pb, txn) 756 sfoid := txn.SiafundOutputID(0) 757 exists := cst.cs.db.inSiafundOutputs(sfoid) 758 if !exists { 759 t.Error("Failed to create siafund output") 760 } 761 if cst.cs.db.lenSiafundOutputs() != 4 { 762 t.Error("siafund outputs not correctly updated") 763 } 764 if len(pb.SiafundOutputDiffs) != 1 { 765 t.Error("block node was not updated for single element transaction") 766 } 767 if pb.SiafundOutputDiffs[0].Direction != modules.DiffApply { 768 t.Error("wrong diff direction applied when creating a siafund output") 769 } 770 if pb.SiafundOutputDiffs[0].ID != sfoid { 771 t.Error("wrong id used when creating a siafund output") 772 } 773 if pb.SiafundOutputDiffs[0].SiafundOutput.ClaimStart.Cmp64(101) != 0 { 774 t.Error("claim start set incorrectly when creating a siafund output") 775 } 776 777 // Apply a transaction with 2 siacoin outputs. 778 txn = types.Transaction{ 779 SiafundOutputs: []types.SiafundOutput{ 780 {Value: types.NewCurrency64(1)}, 781 {Value: types.NewCurrency64(2)}, 782 }, 783 } 784 cst.cs.applySiafundOutputs(pb, txn) 785 sfoid0 := txn.SiafundOutputID(0) 786 sfoid1 := txn.SiafundOutputID(1) 787 exists = cst.cs.db.inSiafundOutputs(sfoid0) 788 if !exists { 789 t.Error("Failed to create siafund output") 790 } 791 exists = cst.cs.db.inSiafundOutputs(sfoid1) 792 if !exists { 793 t.Error("Failed to create siafund output") 794 } 795 if cst.cs.db.lenSiafundOutputs() != 6 { 796 t.Error("siafund outputs not correctly updated") 797 } 798 if len(pb.SiafundOutputDiffs) != 3 { 799 t.Error("block node was not updated for single element transaction") 800 } 801 } 802 803 // TestMisuseApplySiafundOutputs misuses applySiafundOutputs and checks that a 804 // panic was triggered. 805 func TestMisuseApplySiafundOutputs(t *testing.T) { 806 if testing.Short() { 807 t.SkipNow() 808 } 809 cst, err := createConsensusSetTester(t.Name()) 810 if err != nil { 811 t.Fatal(err) 812 } 813 defer cst.closeCst() 814 815 // Create a block node to use with application. 816 pb := new(processedBlock) 817 818 // Apply a transaction with a single siacoin output. 819 txn := types.Transaction{ 820 SiafundOutputs: []types.SiafundOutput{{}}, 821 } 822 cst.cs.applySiafundOutputs(pb, txn) 823 824 // Trigger the panic that occurs when an output is applied incorrectly, and 825 // perform a catch to read the error that is created. 826 defer func() { 827 r := recover() 828 if r != ErrMisuseApplySiafundOutput { 829 t.Error("no panic occurred when misusing applySiafundInput") 830 } 831 }() 832 cst.cs.applySiafundOutputs(pb, txn) 833 } 834 */