github.com/fozzysec/SiaPrime@v0.0.0-20190612043147-66c8e8d11fe3/modules/consensus/accept_test.go (about) 1 package consensus 2 3 import ( 4 "bytes" 5 "errors" 6 "testing" 7 "time" 8 "unsafe" 9 10 "SiaPrime/modules" 11 "SiaPrime/persist" 12 "SiaPrime/types" 13 14 "gitlab.com/NebulousLabs/bolt" 15 ) 16 17 var ( 18 // validateBlockParamsGot stores the parameters passed to the most recent call 19 // to mockBlockValidator.ValidateBlock. 20 validateBlockParamsGot validateBlockParams 21 22 mockValidBlock = types.Block{ 23 Timestamp: 100, 24 ParentID: mockParentID(), 25 } 26 mockInvalidBlock = types.Block{ 27 Timestamp: 500, 28 ParentID: mockParentID(), 29 } 30 // parentBlockSerialized is a mock serialized form of a processedBlock. 31 parentBlockSerialized = []byte{3, 2, 1} 32 33 parentBlockUnmarshaler = mockBlockMarshaler{ 34 []predefinedBlockUnmarshal{ 35 {parentBlockSerialized, mockParent(), nil}, 36 }, 37 } 38 39 parentBlockHighTargetUnmarshaler = mockBlockMarshaler{ 40 []predefinedBlockUnmarshal{ 41 {parentBlockSerialized, mockParentHighTarget(), nil}, 42 }, 43 } 44 45 parentBlockLowTargetUnmarshaler = mockBlockMarshaler{ 46 []predefinedBlockUnmarshal{ 47 {parentBlockSerialized, mockParentLowTarget(), nil}, 48 }, 49 } 50 51 unmarshalFailedErr = errors.New("mock unmarshal failed") 52 53 failingBlockUnmarshaler = mockBlockMarshaler{ 54 []predefinedBlockUnmarshal{ 55 {parentBlockSerialized, processedBlock{}, unmarshalFailedErr}, 56 }, 57 } 58 59 serializedParentBlockMap = []blockMapPair{ 60 {mockValidBlock.ParentID[:], parentBlockSerialized}, 61 } 62 ) 63 64 type ( 65 // mockDbBucket is an implementation of dbBucket for unit testing. 66 mockDbBucket struct { 67 values map[string][]byte 68 } 69 70 // mockDbTx is an implementation of dbTx for unit testing. It uses an 71 // in-memory key/value store to mock a database. 72 mockDbTx struct { 73 buckets map[string]dbBucket 74 } 75 76 // predefinedBlockUnmarshal is a predefined response from mockBlockMarshaler. 77 // It defines the unmarshaled processedBlock and error code that 78 // mockBlockMarshaler should return in response to an input serialized byte 79 // slice. 80 predefinedBlockUnmarshal struct { 81 serialized []byte 82 unmarshaled processedBlock 83 err error 84 } 85 86 // mockBlockMarshaler is an implementation of the encoding.GenericMarshaler 87 // interface for unit testing. It allows clients to specify mappings of 88 // serialized bytes into unmarshaled blocks. 89 mockBlockMarshaler struct { 90 p []predefinedBlockUnmarshal 91 } 92 93 // mockBlockRuleHelper is an implementation of the blockRuleHelper interface 94 // for unit testing. 95 mockBlockRuleHelper struct { 96 minTimestamp types.Timestamp 97 } 98 99 // mockBlockValidator is an implementation of the blockValidator interface for 100 // unit testing. 101 mockBlockValidator struct { 102 err error 103 } 104 105 // validateBlockParams stores the set of parameters passed to ValidateBlock. 106 validateBlockParams struct { 107 called bool 108 b types.Block 109 minTimestamp types.Timestamp 110 target types.Target 111 height types.BlockHeight 112 } 113 114 // blockMapPair represents a key-value pair in the mock block map. 115 blockMapPair struct { 116 key []byte 117 val []byte 118 } 119 ) 120 121 // Get returns the value associated with a given key. 122 func (bucket mockDbBucket) Get(key []byte) []byte { 123 return bucket.values[string(key)] 124 } 125 126 // Set adds a named value to a mockDbBucket. 127 func (bucket mockDbBucket) Set(key []byte, value []byte) { 128 bucket.values[string(key)] = value 129 } 130 131 // Bucket returns a mock dbBucket object associated with the given bucket name. 132 func (db mockDbTx) Bucket(name []byte) dbBucket { 133 return db.buckets[string(name)] 134 } 135 136 // Marshal is not implemented and panics if called. 137 func (m mockBlockMarshaler) Marshal(interface{}) []byte { 138 panic("not implemented") 139 } 140 141 // Unmarshal unmarshals a byte slice into an object based on a pre-defined map 142 // of deserialized objects. 143 func (m mockBlockMarshaler) Unmarshal(b []byte, v interface{}) error { 144 for _, pu := range m.p { 145 if bytes.Equal(b[:], pu.serialized[:]) { 146 pv, ok := v.(*processedBlock) 147 if !ok { 148 panic("mockBlockMarshaler.Unmarshal expected v to be of type processedBlock") 149 } 150 *pv = pu.unmarshaled 151 return pu.err 152 } 153 } 154 panic("unmarshal failed: predefined unmarshal not found") 155 } 156 157 // AddPredefinedUnmarshal adds a predefinedBlockUnmarshal to mockBlockMarshaler. 158 func (m *mockBlockMarshaler) AddPredefinedUnmarshal(u predefinedBlockUnmarshal) { 159 m.p = append(m.p, u) 160 } 161 162 // minimumValidChildTimestamp returns the minimum timestamp of pb that can be 163 // considered a valid block. 164 func (brh mockBlockRuleHelper) minimumValidChildTimestamp(blockMap dbBucket, pb *processedBlock) types.Timestamp { 165 return brh.minTimestamp 166 } 167 168 // ValidateBlock stores the parameters it receives and returns the mock error 169 // defined by mockBlockValidator.err. 170 func (bv mockBlockValidator) ValidateBlock(b types.Block, id types.BlockID, minTimestamp types.Timestamp, target types.Target, height types.BlockHeight, log *persist.Logger) error { 171 validateBlockParamsGot = validateBlockParams{true, b, minTimestamp, target, height} 172 return bv.err 173 } 174 175 // mockParentID returns a mock BlockID value. 176 func mockParentID() (parentID types.BlockID) { 177 parentID[0] = 42 178 return parentID 179 } 180 181 // mockParent returns a mock processedBlock with its ChildTarget member 182 // initialized to a dummy value. 183 func mockParent() (parent processedBlock) { 184 var mockTarget types.Target 185 mockTarget[0] = 56 186 parent.ChildTarget = mockTarget 187 return parent 188 } 189 190 // mockParent returns a mock processedBlock with its ChildTarget member 191 // initialized to a the maximum value. 192 func mockParentHighTarget() (parent processedBlock) { 193 parent.ChildTarget = types.RootDepth 194 return parent 195 } 196 197 // mockParent returns a mock processedBlock with its ChildTarget member 198 // initialized to the minimum value. 199 func mockParentLowTarget() (parent processedBlock) { 200 return parent 201 } 202 203 // TestUnitValidateHeaderAndBlock runs a series of unit tests for validateHeaderAndBlock. 204 func TestUnitValidateHeaderAndBlock(t *testing.T) { 205 var tests = []struct { 206 block types.Block 207 dosBlocks map[types.BlockID]struct{} 208 blockMapPairs []blockMapPair 209 earliestValidTimestamp types.Timestamp 210 marshaler mockBlockMarshaler 211 useNilBlockMap bool 212 validateBlockErr error 213 errWant error 214 msg string 215 }{ 216 { 217 block: mockValidBlock, 218 dosBlocks: make(map[types.BlockID]struct{}), 219 useNilBlockMap: true, 220 earliestValidTimestamp: mockValidBlock.Timestamp, 221 marshaler: parentBlockUnmarshaler, 222 errWant: errNoBlockMap, 223 msg: "validateHeaderAndBlock should fail when no block map is found in the database", 224 }, 225 { 226 block: mockValidBlock, 227 // Create a dosBlocks map where mockValidBlock is marked as a bad block. 228 dosBlocks: map[types.BlockID]struct{}{ 229 mockValidBlock.ID(): {}, 230 }, 231 earliestValidTimestamp: mockValidBlock.Timestamp, 232 marshaler: parentBlockUnmarshaler, 233 errWant: errDoSBlock, 234 msg: "validateHeaderAndBlock should reject known bad blocks", 235 }, 236 { 237 block: mockValidBlock, 238 dosBlocks: make(map[types.BlockID]struct{}), 239 earliestValidTimestamp: mockValidBlock.Timestamp, 240 marshaler: parentBlockUnmarshaler, 241 errWant: errOrphan, 242 msg: "validateHeaderAndBlock should reject a block if its parent block does not appear in the block database", 243 }, 244 { 245 block: mockValidBlock, 246 dosBlocks: make(map[types.BlockID]struct{}), 247 blockMapPairs: serializedParentBlockMap, 248 earliestValidTimestamp: mockValidBlock.Timestamp, 249 marshaler: failingBlockUnmarshaler, 250 errWant: unmarshalFailedErr, 251 msg: "validateHeaderAndBlock should fail when unmarshaling the parent block fails", 252 }, 253 { 254 block: mockInvalidBlock, 255 dosBlocks: make(map[types.BlockID]struct{}), 256 blockMapPairs: []blockMapPair{ 257 {mockInvalidBlock.ParentID[:], parentBlockSerialized}, 258 }, 259 earliestValidTimestamp: mockInvalidBlock.Timestamp, 260 marshaler: parentBlockUnmarshaler, 261 validateBlockErr: errBadMinerPayouts, 262 errWant: errBadMinerPayouts, 263 msg: "validateHeaderAndBlock should reject a block if ValidateBlock returns an error for the block", 264 }, 265 { 266 block: mockValidBlock, 267 dosBlocks: make(map[types.BlockID]struct{}), 268 blockMapPairs: serializedParentBlockMap, 269 earliestValidTimestamp: mockValidBlock.Timestamp, 270 marshaler: parentBlockUnmarshaler, 271 errWant: nil, 272 msg: "validateHeaderAndBlock should accept a valid block", 273 }, 274 } 275 for _, tt := range tests { 276 // Initialize the blockmap in the tx. 277 bucket := mockDbBucket{map[string][]byte{}} 278 for _, mapPair := range tt.blockMapPairs { 279 bucket.Set(mapPair.key, mapPair.val) 280 } 281 dbBucketMap := map[string]dbBucket{} 282 if tt.useNilBlockMap { 283 dbBucketMap[string(BlockMap)] = nil 284 } else { 285 dbBucketMap[string(BlockMap)] = bucket 286 } 287 tx := mockDbTx{dbBucketMap} 288 289 mockParent := mockParent() 290 cs := ConsensusSet{ 291 dosBlocks: tt.dosBlocks, 292 marshaler: tt.marshaler, 293 blockRuleHelper: mockBlockRuleHelper{ 294 minTimestamp: tt.earliestValidTimestamp, 295 }, 296 blockValidator: mockBlockValidator{tt.validateBlockErr}, 297 } 298 // Reset the stored parameters to ValidateBlock. 299 validateBlockParamsGot = validateBlockParams{} 300 _, err := cs.validateHeaderAndBlock(tx, tt.block, tt.block.ID()) 301 if err != tt.errWant { 302 t.Errorf("%s: expected to fail with `%v', got: `%v'", tt.msg, tt.errWant, err) 303 } 304 if err == nil || validateBlockParamsGot.called { 305 if validateBlockParamsGot.b.ID() != tt.block.ID() { 306 t.Errorf("%s: incorrect parameter passed to ValidateBlock - got: %v, want: %v", tt.msg, validateBlockParamsGot.b, tt.block) 307 } 308 if validateBlockParamsGot.minTimestamp != tt.earliestValidTimestamp { 309 t.Errorf("%s: incorrect parameter passed to ValidateBlock - got: %v, want: %v", tt.msg, validateBlockParamsGot.minTimestamp, tt.earliestValidTimestamp) 310 } 311 if validateBlockParamsGot.target != mockParent.ChildTarget { 312 t.Errorf("%s: incorrect parameter passed to ValidateBlock - got: %v, want: %v", tt.msg, validateBlockParamsGot.target, mockParent.ChildTarget) 313 } 314 } 315 } 316 } 317 318 // TestCheckHeaderTarget probes the checkHeaderTarget function and checks that 319 // the result matches the result of checkTarget. 320 func TestCheckHeaderTarget(t *testing.T) { 321 var b types.Block 322 var h types.BlockHeader 323 324 tests := []struct { 325 target types.Target 326 expected bool 327 msg string 328 }{ 329 {types.RootDepth, true, "checkHeaderTarget failed for a low target"}, 330 {types.Target{}, false, "checkHeaderTarget passed for a high target"}, 331 {types.Target(h.ID()), true, "checkHeaderTarget failed for a same target"}, 332 } 333 for _, tt := range tests { 334 if checkHeaderTarget(h, tt.target) != tt.expected { 335 t.Error(tt.msg) 336 } 337 if checkHeaderTarget(h, tt.target) != checkTarget(b, b.ID(), tt.target) { 338 t.Errorf("checkHeaderTarget and checkTarget do not match for target %v", tt.target) 339 } 340 } 341 } 342 343 // TestUnitValidateHeader runs a series of unit tests for validateHeader. 344 func TestUnitValidateHeader(t *testing.T) { 345 mockValidBlockID := mockValidBlock.ID() 346 347 var tests = []struct { 348 header types.BlockHeader 349 dosBlocks map[types.BlockID]struct{} 350 blockMapPairs []blockMapPair 351 earliestValidTimestamp types.Timestamp 352 marshaler mockBlockMarshaler 353 useNilBlockMap bool 354 errWant error 355 msg string 356 }{ 357 // Test that known dos blocks are rejected. 358 { 359 header: mockValidBlock.Header(), 360 // Create a dosBlocks map where mockValidBlock is marked as a bad block. 361 dosBlocks: map[types.BlockID]struct{}{ 362 mockValidBlock.ID(): {}, 363 }, 364 blockMapPairs: serializedParentBlockMap, 365 earliestValidTimestamp: mockValidBlock.Timestamp, 366 marshaler: parentBlockUnmarshaler, 367 errWant: errDoSBlock, 368 msg: "validateHeader should reject known bad blocks", 369 }, 370 // Test that blocks are rejected if a block map doesn't exist. 371 { 372 header: mockValidBlock.Header(), 373 dosBlocks: make(map[types.BlockID]struct{}), 374 blockMapPairs: serializedParentBlockMap, 375 earliestValidTimestamp: mockValidBlock.Timestamp, 376 marshaler: parentBlockUnmarshaler, 377 useNilBlockMap: true, 378 errWant: errNoBlockMap, 379 msg: "validateHeader should fail when no block map is found in the database", 380 }, 381 // Test that known blocks are rejected. 382 { 383 header: mockValidBlock.Header(), 384 dosBlocks: make(map[types.BlockID]struct{}), 385 blockMapPairs: []blockMapPair{{mockValidBlockID[:], []byte{}}}, 386 earliestValidTimestamp: mockValidBlock.Timestamp, 387 marshaler: parentBlockUnmarshaler, 388 errWant: modules.ErrBlockKnown, 389 msg: "validateHeader should fail when the block has been seen before", 390 }, 391 // Test that blocks with unknown parents (orphans) are rejected. 392 { 393 header: mockValidBlock.Header(), 394 dosBlocks: make(map[types.BlockID]struct{}), 395 earliestValidTimestamp: mockValidBlock.Timestamp, 396 marshaler: parentBlockUnmarshaler, 397 errWant: errOrphan, 398 msg: "validateHeader should reject a block if its parent block does not appear in the block database", 399 }, 400 // Test that blocks whose parents don't unmarshal are rejected. 401 { 402 header: mockValidBlock.Header(), 403 dosBlocks: make(map[types.BlockID]struct{}), 404 blockMapPairs: serializedParentBlockMap, 405 earliestValidTimestamp: mockValidBlock.Timestamp, 406 marshaler: failingBlockUnmarshaler, 407 errWant: unmarshalFailedErr, 408 msg: "validateHeader should fail when unmarshaling the parent block fails", 409 }, 410 // Test that blocks with too early of a timestamp are rejected. 411 { 412 header: mockValidBlock.Header(), 413 dosBlocks: make(map[types.BlockID]struct{}), 414 blockMapPairs: serializedParentBlockMap, 415 earliestValidTimestamp: mockValidBlock.Timestamp + 1, 416 marshaler: parentBlockHighTargetUnmarshaler, 417 errWant: errEarlyTimestamp, 418 msg: "validateHeader should fail when the header's timestamp is too early", 419 }, 420 // Test that headers in the extreme future are rejected. 421 { 422 header: types.BlockHeader{ 423 Timestamp: types.CurrentTimestamp() + types.ExtremeFutureThreshold + 2, 424 ParentID: mockParentID(), 425 }, 426 dosBlocks: make(map[types.BlockID]struct{}), 427 blockMapPairs: serializedParentBlockMap, 428 marshaler: parentBlockHighTargetUnmarshaler, 429 errWant: errExtremeFutureTimestamp, 430 msg: "validateHeader should fail when the header's timestamp is in the extreme future", 431 }, 432 // Test that headers in the near future are not rejected. 433 { 434 header: types.BlockHeader{ 435 Timestamp: types.CurrentTimestamp() + types.FutureThreshold + 2, 436 ParentID: mockParentID(), 437 }, 438 dosBlocks: make(map[types.BlockID]struct{}), 439 blockMapPairs: serializedParentBlockMap, 440 marshaler: parentBlockHighTargetUnmarshaler, 441 errWant: nil, 442 msg: "validateHeader should not reject headers whose timestamps are in the near future", 443 }, 444 // Test that blocks with too large of a target are rejected. 445 { 446 header: mockValidBlock.Header(), 447 dosBlocks: make(map[types.BlockID]struct{}), 448 blockMapPairs: serializedParentBlockMap, 449 earliestValidTimestamp: mockValidBlock.Timestamp, 450 marshaler: parentBlockLowTargetUnmarshaler, 451 errWant: modules.ErrBlockUnsolved, 452 msg: "validateHeader should reject blocks with an insufficiently low target", 453 }, 454 // Test that valid blocks are accepted. 455 { 456 header: mockValidBlock.Header(), 457 dosBlocks: make(map[types.BlockID]struct{}), 458 blockMapPairs: serializedParentBlockMap, 459 earliestValidTimestamp: mockValidBlock.Timestamp, 460 marshaler: parentBlockHighTargetUnmarshaler, 461 errWant: nil, 462 msg: "validateHeader should accept a valid block", 463 }, 464 } 465 for _, tt := range tests { 466 // Initialize the blockmap in the tx. 467 bucket := mockDbBucket{map[string][]byte{}} 468 for _, mapPair := range tt.blockMapPairs { 469 bucket.Set(mapPair.key, mapPair.val) 470 } 471 dbBucketMap := map[string]dbBucket{} 472 if tt.useNilBlockMap { 473 dbBucketMap[string(BlockMap)] = nil 474 } else { 475 dbBucketMap[string(BlockMap)] = bucket 476 } 477 tx := mockDbTx{dbBucketMap} 478 479 cs := ConsensusSet{ 480 dosBlocks: tt.dosBlocks, 481 marshaler: tt.marshaler, 482 blockRuleHelper: mockBlockRuleHelper{ 483 minTimestamp: tt.earliestValidTimestamp, 484 }, 485 } 486 err := cs.validateHeader(tx, tt.header) 487 if err != tt.errWant { 488 t.Errorf("%s: expected to fail with `%v', got: `%v'", tt.msg, tt.errWant, err) 489 } 490 } 491 } 492 493 // TestIntegrationDoSBlockHandling checks that saved bad blocks are correctly 494 // ignored. 495 func TestIntegrationDoSBlockHandling(t *testing.T) { 496 if testing.Short() { 497 t.SkipNow() 498 } 499 t.Parallel() 500 cst, err := createConsensusSetTester(t.Name()) 501 if err != nil { 502 t.Fatal(err) 503 } 504 defer cst.Close() 505 506 // Mine a block that is valid except for containing a buried invalid 507 // transaction. The transaction has more siacoin inputs than outputs. 508 txnBuilder, err := cst.wallet.StartTransaction() 509 if err != nil { 510 t.Fatal(err) 511 } 512 err = txnBuilder.FundSiacoins(types.NewCurrency64(50)) 513 if err != nil { 514 t.Fatal(err) 515 } 516 txnSet, err := txnBuilder.Sign(true) // true sets the 'wholeTransaction' flag 517 if err != nil { 518 t.Fatal(err) 519 } 520 521 // Mine and submit the invalid block to the consensus set. The first time 522 // around, the complaint should be about the rule-breaking transaction. 523 block, target, err := cst.miner.BlockForWork() 524 if err != nil { 525 t.Fatal(err) 526 } 527 block.Transactions = append(block.Transactions, txnSet...) 528 dosBlock, _ := cst.miner.SolveBlock(block, target) 529 err = cst.cs.AcceptBlock(dosBlock) 530 if err != errSiacoinInputOutputMismatch { 531 t.Fatalf("expected %v, got %v", errSiacoinInputOutputMismatch, err) 532 } 533 534 // Submit the same block a second time. The complaint should be that the 535 // block is already known to be invalid. 536 err = cst.cs.AcceptBlock(dosBlock) 537 if err != errDoSBlock { 538 t.Fatalf("expected %v, got %v", errDoSBlock, err) 539 } 540 } 541 542 // TestBlockKnownHandling submits known blocks to the consensus set. 543 func TestBlockKnownHandling(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 // Get a block destined to be stale. 555 block, target, err := cst.miner.BlockForWork() 556 if err != nil { 557 t.Fatal(err) 558 } 559 staleBlock, _ := cst.miner.SolveBlock(block, target) 560 561 // Add two new blocks to the consensus set to block the stale block. 562 block1, err := cst.miner.AddBlock() 563 if err != nil { 564 t.Fatal(err) 565 } 566 block2, err := cst.miner.AddBlock() 567 if err != nil { 568 t.Fatal(err) 569 } 570 571 // Submit the stale block. 572 err = cst.cs.AcceptBlock(staleBlock) 573 if err != nil && err != modules.ErrNonExtendingBlock { 574 t.Fatal(err) 575 } 576 577 // Submit all the blocks again, looking for a 'stale block' error. 578 err = cst.cs.AcceptBlock(block1) 579 if err == nil { 580 t.Fatal("expected an error upon submitting the block") 581 } 582 err = cst.cs.AcceptBlock(block2) 583 if err == nil { 584 t.Fatal("expected an error upon submitting the block") 585 } 586 err = cst.cs.AcceptBlock(staleBlock) 587 if err == nil { 588 t.Fatal("expected an error upon submitting the block") 589 } 590 591 // Try submitting the genesis block. 592 id, err := cst.cs.dbGetPath(0) 593 if err != nil { 594 t.Fatal(err) 595 } 596 genesisBlock, err := cst.cs.dbGetBlockMap(id) 597 if err != nil { 598 t.Fatal(err) 599 } 600 err = cst.cs.AcceptBlock(genesisBlock.Block) 601 if err == nil { 602 t.Fatal("expected an error upon submitting the block") 603 } 604 } 605 606 // TestOrphanHandling passes an orphan block to the consensus set. 607 func TestOrphanHandling(t *testing.T) { 608 if testing.Short() { 609 t.SkipNow() 610 } 611 t.Parallel() 612 cst, err := createConsensusSetTester(t.Name()) 613 if err != nil { 614 t.Fatal(err) 615 } 616 defer cst.Close() 617 618 // Try submitting an orphan block to the consensus set. The empty block can 619 // be used, because looking for a parent is one of the first checks the 620 // consensus set performs. 621 orphan := types.Block{} 622 err = cst.cs.AcceptBlock(orphan) 623 if err != errOrphan { 624 t.Fatalf("expected %v, got %v", errOrphan, err) 625 } 626 err = cst.cs.AcceptBlock(orphan) 627 if err != errOrphan { 628 t.Fatalf("expected %v, got %v", errOrphan, err) 629 } 630 } 631 632 // TestMissedTarget submits a block that does not meet the required target. 633 func TestMissedTarget(t *testing.T) { 634 if testing.Short() { 635 t.SkipNow() 636 } 637 t.Parallel() 638 cst, err := createConsensusSetTester(t.Name()) 639 if err != nil { 640 t.Fatal(err) 641 } 642 defer cst.Close() 643 644 // Mine a block that doesn't meet the target. 645 block, target, err := cst.miner.BlockForWork() 646 if err != nil { 647 t.Fatal(err) 648 } 649 for checkTarget(block, block.ID(), target) { 650 *(*uint64)(unsafe.Pointer(&block.Nonce)) += types.ASICHardforkFactor 651 } 652 if checkTarget(block, block.ID(), target) { 653 t.Fatal("unable to find a failing target") 654 } 655 err = cst.cs.AcceptBlock(block) 656 if err != modules.ErrBlockUnsolved { 657 t.Fatalf("expected %v, got %v", modules.ErrBlockUnsolved, err) 658 } 659 } 660 661 // TestMinerPayoutHandling checks that blocks with incorrect payouts are 662 // rejected. 663 func TestMinerPayoutHandling(t *testing.T) { 664 if testing.Short() { 665 t.SkipNow() 666 } 667 t.Parallel() 668 cst, err := createConsensusSetTester(t.Name()) 669 if err != nil { 670 t.Fatal(err) 671 } 672 defer cst.Close() 673 674 // Create a block with the wrong miner payout structure - testing can be 675 // light here because there is heavier testing in the 'types' package, 676 // where the logic is defined. 677 block, target, err := cst.miner.BlockForWork() 678 if err != nil { 679 t.Fatal(err) 680 } 681 block.MinerPayouts = append(block.MinerPayouts, types.SiacoinOutput{Value: types.NewCurrency64(1)}) 682 solvedBlock, _ := cst.miner.SolveBlock(block, target) 683 err = cst.cs.AcceptBlock(solvedBlock) 684 if err != errBadMinerPayouts { 685 t.Fatalf("expected %v, got %v", errBadMinerPayouts, err) 686 } 687 } 688 689 // TestEarlyTimestampHandling checks that blocks too far in the past are 690 // rejected. 691 func TestEarlyTimestampHandling(t *testing.T) { 692 if testing.Short() { 693 t.SkipNow() 694 } 695 t.Parallel() 696 cst, err := createConsensusSetTester(t.Name()) 697 if err != nil { 698 t.Fatal(err) 699 } 700 defer cst.Close() 701 minTimestamp := types.CurrentTimestamp() 702 cst.cs.blockRuleHelper = mockBlockRuleHelper{ 703 minTimestamp: minTimestamp, 704 } 705 706 // Submit a block with a timestamp in the past, before minTimestamp. 707 block, target, err := cst.miner.BlockForWork() 708 if err != nil { 709 t.Fatal(err) 710 } 711 block.Timestamp = minTimestamp - 1 712 solvedBlock, _ := cst.miner.SolveBlock(block, target) 713 err = cst.cs.AcceptBlock(solvedBlock) 714 if err != errEarlyTimestamp { 715 t.Fatalf("expected %v, got %v", errEarlyTimestamp, err) 716 } 717 } 718 719 // testFutureTimestampHandling checks that blocks in the future (but not 720 // extreme future) are handled correctly. 721 func TestFutureTimestampHandling(t *testing.T) { 722 if testing.Short() { 723 t.SkipNow() 724 } 725 t.Parallel() 726 cst, err := createConsensusSetTester(t.Name()) 727 if err != nil { 728 t.Fatal(err) 729 } 730 defer cst.Close() 731 732 // Submit a block with a timestamp in the future, but not the extreme 733 // future. 734 block, target, err := cst.miner.BlockForWork() 735 if err != nil { 736 t.Fatal(err) 737 } 738 block.Timestamp = types.CurrentTimestamp() + 2 + types.FutureThreshold 739 solvedBlock, _ := cst.miner.SolveBlock(block, target) 740 err = cst.cs.AcceptBlock(solvedBlock) 741 if err != errFutureTimestamp { 742 t.Fatalf("expected %v, got %v", errFutureTimestamp, err) 743 } 744 745 // Poll the consensus set until the future block appears. 746 for i := 0; i < 30; i++ { 747 time.Sleep(time.Second * 3) 748 _, err = cst.cs.dbGetBlockMap(solvedBlock.ID()) 749 if err == nil { 750 break 751 } 752 } 753 _, err = cst.cs.dbGetBlockMap(solvedBlock.ID()) 754 if err != nil { 755 t.Errorf("Future block not added to consensus set.\nCurrent Timestamp %v\nFutureThreshold: %v\nBlock Timestamp %v\n", types.CurrentTimestamp(), types.FutureThreshold, block.Timestamp) 756 } 757 } 758 759 // TestExtremeFutureTimestampHandling checks that blocks in the extreme future 760 // are rejected. 761 func TestExtremeFutureTimestampHandling(t *testing.T) { 762 if testing.Short() { 763 t.SkipNow() 764 } 765 t.Parallel() 766 cst, err := createConsensusSetTester(t.Name()) 767 if err != nil { 768 t.Fatal(err) 769 } 770 defer cst.Close() 771 772 // Submit a block with a timestamp in the extreme future. 773 block, target, err := cst.miner.BlockForWork() 774 if err != nil { 775 t.Fatal(err) 776 } 777 block.Timestamp = types.CurrentTimestamp() + 2 + types.ExtremeFutureThreshold 778 solvedBlock, _ := cst.miner.SolveBlock(block, target) 779 err = cst.cs.AcceptBlock(solvedBlock) 780 if err != errExtremeFutureTimestamp { 781 t.Fatalf("expected %v, got %v", errFutureTimestamp, err) 782 } 783 } 784 785 // TestBuriedBadTransaction tries submitting a block with a bad transaction 786 // that is buried under good transactions. 787 func TestBuriedBadTransaction(t *testing.T) { 788 if testing.Short() { 789 t.SkipNow() 790 } 791 t.Parallel() 792 cst, err := createConsensusSetTester(t.Name()) 793 if err != nil { 794 t.Fatal(err) 795 } 796 defer cst.Close() 797 pb := cst.cs.dbCurrentProcessedBlock() 798 799 // Create a good transaction using the wallet. 800 txnValue := types.NewCurrency64(1200) 801 txnBuilder, err := cst.wallet.StartTransaction() 802 if err != nil { 803 t.Fatal(err) 804 } 805 err = txnBuilder.FundSiacoins(txnValue) 806 if err != nil { 807 t.Fatal(err) 808 } 809 txnBuilder.AddSiacoinOutput(types.SiacoinOutput{Value: txnValue}) 810 txnSet, err := txnBuilder.Sign(true) 811 if err != nil { 812 t.Fatal(err) 813 } 814 err = cst.tpool.AcceptTransactionSet(txnSet) 815 if err != nil { 816 t.Fatal(err) 817 } 818 819 // Create a bad transaction 820 badTxn := types.Transaction{ 821 SiacoinInputs: []types.SiacoinInput{{}}, 822 } 823 txns := append(cst.tpool.TransactionList(), badTxn) 824 825 // Create a block with a buried bad transaction. 826 block := types.Block{ 827 ParentID: pb.Block.ID(), 828 Timestamp: types.CurrentTimestamp(), 829 MinerPayouts: []types.SiacoinOutput{{Value: types.CalculateCoinbase(pb.Height + 1)}}, 830 Transactions: txns, 831 } 832 block, _ = cst.miner.SolveBlock(block, pb.ChildTarget) 833 err = cst.cs.AcceptBlock(block) 834 if err == nil { 835 t.Error("buried transaction didn't cause an error") 836 } 837 } 838 839 // TestInconsistencyCheck puts the consensus set in to an inconsistent state 840 // and makes sure that the santiy checks are triggering panics. 841 func TestInconsistentCheck(t *testing.T) { 842 if testing.Short() { 843 t.SkipNow() 844 } 845 t.Parallel() 846 cst, err := createConsensusSetTester(t.Name()) 847 if err != nil { 848 t.Fatal(err) 849 } 850 851 // Corrupt the consensus set by adding a new siafund output. 852 sfo := types.SiafundOutput{ 853 Value: types.NewCurrency64(1), 854 } 855 cst.cs.dbAddSiafundOutput(types.SiafundOutputID{}, sfo) 856 857 // Catch a panic that should be caused by the inconsistency check after a 858 // block is mined. 859 defer func() { 860 r := recover() 861 if r == nil { 862 t.Fatalf("inconsistency panic not triggered by corrupted database") 863 } 864 }() 865 _, err = cst.miner.AddBlock() 866 if err != nil { 867 t.Fatal(err) 868 } 869 } 870 871 // COMPATv0.4.0 872 // 873 // This test checks that the hardfork scheduled for block 21,000 rolls through 874 // smoothly. 875 func TestTaxHardfork(t *testing.T) { 876 if testing.Short() { 877 t.SkipNow() 878 } 879 t.Parallel() 880 cst, err := createConsensusSetTester(t.Name()) 881 if err != nil { 882 t.Fatal(err) 883 } 884 defer cst.Close() 885 886 // Create a file contract with a payout that is put into the blockchain 887 // before the hardfork block but expires after the hardfork block. 888 payout := types.NewCurrency64(400e6) 889 outputSize := types.PostTax(cst.cs.dbBlockHeight(), payout) 890 fc := types.FileContract{ 891 WindowStart: cst.cs.dbBlockHeight() + 12, 892 WindowEnd: cst.cs.dbBlockHeight() + 14, 893 Payout: payout, 894 ValidProofOutputs: []types.SiacoinOutput{{Value: outputSize}}, 895 MissedProofOutputs: []types.SiacoinOutput{{Value: outputSize}}, 896 UnlockHash: types.UnlockConditions{}.UnlockHash(), // The empty UC is anyone-can-spend 897 } 898 899 // Create and fund a transaction with a file contract. 900 txnBuilder, err := cst.wallet.StartTransaction() 901 if err != nil { 902 t.Fatal(err) 903 } 904 err = txnBuilder.FundSiacoins(payout) 905 if err != nil { 906 t.Fatal(err) 907 } 908 fcIndex := txnBuilder.AddFileContract(fc) 909 txnSet, err := txnBuilder.Sign(true) 910 if err != nil { 911 t.Fatal(err) 912 } 913 err = cst.tpool.AcceptTransactionSet(txnSet) 914 if err != nil { 915 t.Fatal(err) 916 } 917 _, err = cst.miner.AddBlock() 918 if err != nil { 919 t.Fatal(err) 920 } 921 922 // Check that the siafund pool was increased by the faulty float amount. 923 siafundPool := cst.cs.dbGetSiafundPool() 924 if !siafundPool.Equals64(15590e3) { 925 t.Fatal("siafund pool was not increased correctly") 926 } 927 928 // Mine blocks until the hardfork is reached. 929 for i := 0; i < 10; i++ { 930 _, err = cst.miner.AddBlock() 931 if err != nil { 932 t.Fatal(err) 933 } 934 } 935 936 // Submit a file contract revision and check that the payouts are able to 937 // be the same. 938 fcid := txnSet[len(txnSet)-1].FileContractID(fcIndex) 939 fcr := types.FileContractRevision{ 940 ParentID: fcid, 941 UnlockConditions: types.UnlockConditions{}, 942 NewRevisionNumber: 1, 943 944 NewFileSize: 1, 945 NewWindowStart: cst.cs.dbBlockHeight() + 2, 946 NewWindowEnd: cst.cs.dbBlockHeight() + 4, 947 NewValidProofOutputs: fc.ValidProofOutputs, 948 NewMissedProofOutputs: fc.MissedProofOutputs, 949 } 950 txnBuilder, err = cst.wallet.StartTransaction() 951 if err != nil { 952 t.Fatal(err) 953 } 954 txnBuilder.AddFileContractRevision(fcr) 955 txnSet, err = txnBuilder.Sign(true) 956 if err != nil { 957 t.Fatal(err) 958 } 959 err = cst.tpool.AcceptTransactionSet(txnSet) 960 if err != nil { 961 t.Fatal(err) 962 } 963 _, err = cst.miner.AddBlock() 964 if err != nil { 965 t.Fatal(err) 966 } 967 968 // Mine blocks until the revision goes through, such that the sanity checks 969 // can be run. 970 for i := 0; i < 6; i++ { 971 _, err = cst.miner.AddBlock() 972 if err != nil { 973 t.Fatal(err) 974 } 975 } 976 977 // Check that the siafund pool did not change after the submitted revision. 978 siafundPool = cst.cs.dbGetSiafundPool() 979 if !siafundPool.Equals64(15590e3) { 980 t.Fatal("siafund pool was not increased correctly") 981 } 982 } 983 984 // mockGatewayDoesBroadcast implements modules.Gateway to mock the Broadcast 985 // method. 986 type mockGatewayDoesBroadcast struct { 987 modules.Gateway 988 broadcastCalled chan struct{} 989 } 990 991 // Broadcast is a mock implementation of modules.Gateway.Broadcast that 992 // sends a sentinel value down a channel to signal it's been called. 993 func (g *mockGatewayDoesBroadcast) Broadcast(name string, obj interface{}, peers []modules.Peer) { 994 g.Gateway.Broadcast(name, obj, peers) 995 g.broadcastCalled <- struct{}{} 996 } 997 998 // TestAcceptBlockBroadcasts tests that AcceptBlock broadcasts valid blocks and 999 // that managedAcceptBlock does not. 1000 func TestAcceptBlockBroadcasts(t *testing.T) { 1001 if testing.Short() { 1002 t.SkipNow() 1003 } 1004 t.Parallel() 1005 cst, err := blankConsensusSetTester(t.Name(), modules.ProdDependencies) 1006 if err != nil { 1007 t.Fatal(err) 1008 } 1009 defer cst.Close() 1010 mg := &mockGatewayDoesBroadcast{ 1011 Gateway: cst.cs.gateway, 1012 broadcastCalled: make(chan struct{}), 1013 } 1014 cst.cs.gateway = mg 1015 1016 // Test that Broadcast is called for valid blocks. 1017 b, _ := cst.miner.FindBlock() 1018 err = cst.cs.AcceptBlock(b) 1019 if err != nil { 1020 t.Fatal(err) 1021 } 1022 select { 1023 case <-mg.broadcastCalled: 1024 case <-time.After(10 * time.Millisecond): 1025 t.Error("expected AcceptBlock to broadcast a valid block") 1026 } 1027 1028 // Test that Broadcast is not called for invalid blocks. 1029 err = cst.cs.AcceptBlock(types.Block{}) 1030 if err == nil { 1031 t.Fatal("expected AcceptBlock to error on an invalid block") 1032 } 1033 select { 1034 case <-mg.broadcastCalled: 1035 t.Error("AcceptBlock broadcasted an invalid block") 1036 case <-time.After(10 * time.Millisecond): 1037 } 1038 1039 // Test that Broadcast is not called in managedAcceptBlock. 1040 b, _ = cst.miner.FindBlock() 1041 _, err = cst.cs.managedAcceptBlocks([]types.Block{b}) 1042 if err != nil { 1043 t.Fatal(err) 1044 } 1045 select { 1046 case <-mg.broadcastCalled: 1047 t.Errorf("managedAcceptBlock should not broadcast blocks") 1048 case <-time.After(10 * time.Millisecond): 1049 } 1050 } 1051 1052 // blockCountingSubscriber counts the number of blocks that get submitted to the 1053 // subscriber, as well as the number of times that the subscriber has been given 1054 // changes at all. 1055 type blockCountingSubscriber struct { 1056 changes []modules.ConsensusChangeID 1057 1058 appliedBlocks int 1059 revertedBlocks int 1060 } 1061 1062 // ProcessConsensusChange fills the subscription interface for the 1063 // blockCountingSubscriber. 1064 func (bcs *blockCountingSubscriber) ProcessConsensusChange(cc modules.ConsensusChange) { 1065 bcs.changes = append(bcs.changes, cc.ID) 1066 bcs.revertedBlocks += len(cc.RevertedBlocks) 1067 bcs.appliedBlocks += len(cc.AppliedBlocks) 1068 } 1069 1070 // TestChainedAcceptBlock creates series of blocks, some of which are valid, 1071 // some invalid, and submits them to the consensus set, verifying that the 1072 // consensus set updates correctly each time. 1073 func TestChainedAcceptBlock(t *testing.T) { 1074 if testing.Short() { 1075 t.SkipNow() 1076 } 1077 t.Parallel() 1078 // Create a tester to send blocks in a batch to the other tester. 1079 cst, err := createConsensusSetTester(t.Name()) 1080 if err != nil { 1081 t.Fatal(err) 1082 } 1083 defer cst.Close() 1084 cst2, err := blankConsensusSetTester(t.Name()+"2", modules.ProdDependencies) 1085 if err != nil { 1086 t.Fatal(err) 1087 } 1088 defer cst2.Close() 1089 // Subscribe a blockCountingSubscriber to cst2. 1090 var bcs blockCountingSubscriber 1091 cst2.cs.ConsensusSetSubscribe(&bcs, modules.ConsensusChangeBeginning, cst2.cs.tg.StopChan()) 1092 if len(bcs.changes) != 1 || bcs.appliedBlocks != 1 || bcs.revertedBlocks != 0 { 1093 t.Error("consensus changes do not seem to be getting passed to subscribers correctly") 1094 } 1095 1096 // Grab all of the blocks in cst, with the intention of giving them to cst2. 1097 var blocks []types.Block 1098 height := cst.cs.Height() 1099 for i := types.BlockHeight(0); i <= height; i++ { 1100 id, err := cst.cs.dbGetPath(i) 1101 if err != nil { 1102 t.Fatal(err) 1103 } 1104 pb, err := cst.cs.dbGetBlockMap(id) 1105 if err != nil { 1106 t.Fatal(err) 1107 } 1108 blocks = append(blocks, pb.Block) 1109 } 1110 1111 // Create a jumbling of the blocks, so that the set is not in order. 1112 jumble := make([]types.Block, len(blocks)) 1113 jumble[0] = blocks[0] 1114 jumble[1] = blocks[2] 1115 jumble[2] = blocks[1] 1116 for i := 3; i < len(jumble); i++ { 1117 jumble[i] = blocks[i] 1118 } 1119 // Try to submit the blocks out-of-order, which would violate one of the 1120 // assumptions in managedAcceptBlocks. 1121 _, err = cst2.cs.managedAcceptBlocks(jumble) 1122 if err != errNonLinearChain { 1123 t.Fatal(err) 1124 } 1125 if cst2.cs.Height() != 0 { 1126 t.Fatal("blocks added even though the inputs were jumbled") 1127 } 1128 if len(bcs.changes) != 1 || bcs.appliedBlocks != 1 || bcs.revertedBlocks != 0 { 1129 t.Error("consensus changes do not seem to be getting passed to subscribers correctly") 1130 } 1131 1132 // Tag an invalid block onto the end of blocks. 1133 block, err := cst.miner.AddBlock() 1134 if err != nil { 1135 t.Fatal(err) 1136 } 1137 // Adding an invalid transaction to make the block invalid. 1138 badBlock := block 1139 badBlock.Transactions = append(badBlock.Transactions, types.Transaction{ 1140 SiacoinInputs: []types.SiacoinInput{{ 1141 ParentID: types.SiacoinOutputID{1}, 1142 }}, 1143 }) 1144 // Append the invalid transaction to the block. 1145 badBlocks := append(blocks, badBlock) 1146 // Submit the whole invalid set. Result should be that nothing is added. 1147 _, err = cst2.cs.managedAcceptBlocks(badBlocks) 1148 if err == nil { 1149 t.Fatal(err) 1150 } 1151 if cst2.cs.Height() != 0 { 1152 t.Log(cst2.cs.Height()) 1153 t.Log(cst.cs.Height()) 1154 t.Fatal("height is not correct, seems that blocks were added") 1155 } 1156 if bcs.appliedBlocks != 1 || bcs.revertedBlocks != 0 { 1157 t.Error("consensus changes do not seem to be getting passed to subscribers correctly") 1158 } 1159 1160 // Try submitting the good blocks. 1161 _, err = cst2.cs.managedAcceptBlocks(blocks) 1162 if err != nil { 1163 t.Fatal(err) 1164 } 1165 if bcs.appliedBlocks != int(cst2.cs.Height()+1) || bcs.revertedBlocks != 0 { 1166 t.Error("consensus changes do not seem to be getting passed to subscribers correctly") 1167 } 1168 1169 // Check that every change recorded in 'bcs' is also available in the 1170 // consensus set. 1171 for _, change := range bcs.changes { 1172 err := cst2.cs.db.Update(func(tx *bolt.Tx) error { 1173 _, exists := getEntry(tx, change) 1174 if !exists { 1175 t.Error("an entry was provided that doesn't exist") 1176 } 1177 return nil 1178 }) 1179 if err != nil { 1180 t.Fatal(err) 1181 } 1182 } 1183 }