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