github.com/Synthesix/Sia@v1.3.3-0.20180413141344-f863baeed3ca/modules/consensus/accept_test.go (about) 1 package consensus 2 3 import ( 4 "bytes" 5 "errors" 6 "testing" 7 "time" 8 9 "github.com/Synthesix/Sia/modules" 10 "github.com/Synthesix/Sia/persist" 11 "github.com/Synthesix/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 := cst.wallet.StartTransaction() 508 err = txnBuilder.FundSiacoins(types.NewCurrency64(50)) 509 if err != nil { 510 t.Fatal(err) 511 } 512 txnSet, err := txnBuilder.Sign(true) // true sets the 'wholeTransaction' flag 513 if err != nil { 514 t.Fatal(err) 515 } 516 517 // Mine and submit the invalid block to the consensus set. The first time 518 // around, the complaint should be about the rule-breaking transaction. 519 block, target, err := cst.miner.BlockForWork() 520 if err != nil { 521 t.Fatal(err) 522 } 523 block.Transactions = append(block.Transactions, txnSet...) 524 dosBlock, _ := cst.miner.SolveBlock(block, target) 525 err = cst.cs.AcceptBlock(dosBlock) 526 if err != errSiacoinInputOutputMismatch { 527 t.Fatalf("expected %v, got %v", errSiacoinInputOutputMismatch, err) 528 } 529 530 // Submit the same block a second time. The complaint should be that the 531 // block is already known to be invalid. 532 err = cst.cs.AcceptBlock(dosBlock) 533 if err != errDoSBlock { 534 t.Fatalf("expected %v, got %v", errDoSBlock, err) 535 } 536 } 537 538 // TestBlockKnownHandling submits known blocks to the consensus set. 539 func TestBlockKnownHandling(t *testing.T) { 540 if testing.Short() { 541 t.SkipNow() 542 } 543 t.Parallel() 544 cst, err := createConsensusSetTester(t.Name()) 545 if err != nil { 546 t.Fatal(err) 547 } 548 defer cst.Close() 549 550 // Get a block destined to be stale. 551 block, target, err := cst.miner.BlockForWork() 552 if err != nil { 553 t.Fatal(err) 554 } 555 staleBlock, _ := cst.miner.SolveBlock(block, target) 556 557 // Add two new blocks to the consensus set to block the stale block. 558 block1, err := cst.miner.AddBlock() 559 if err != nil { 560 t.Fatal(err) 561 } 562 block2, err := cst.miner.AddBlock() 563 if err != nil { 564 t.Fatal(err) 565 } 566 567 // Submit the stale block. 568 err = cst.cs.AcceptBlock(staleBlock) 569 if err != nil && err != modules.ErrNonExtendingBlock { 570 t.Fatal(err) 571 } 572 573 // Submit all the blocks again, looking for a 'stale block' error. 574 err = cst.cs.AcceptBlock(block1) 575 if err == nil { 576 t.Fatal("expected an error upon submitting the block") 577 } 578 err = cst.cs.AcceptBlock(block2) 579 if err == nil { 580 t.Fatal("expected an error upon submitting the block") 581 } 582 err = cst.cs.AcceptBlock(staleBlock) 583 if err == nil { 584 t.Fatal("expected an error upon submitting the block") 585 } 586 587 // Try submitting the genesis block. 588 id, err := cst.cs.dbGetPath(0) 589 if err != nil { 590 t.Fatal(err) 591 } 592 genesisBlock, err := cst.cs.dbGetBlockMap(id) 593 if err != nil { 594 t.Fatal(err) 595 } 596 err = cst.cs.AcceptBlock(genesisBlock.Block) 597 if err == nil { 598 t.Fatal("expected an error upon submitting the block") 599 } 600 } 601 602 // TestOrphanHandling passes an orphan block to the consensus set. 603 func TestOrphanHandling(t *testing.T) { 604 if testing.Short() { 605 t.SkipNow() 606 } 607 t.Parallel() 608 cst, err := createConsensusSetTester(t.Name()) 609 if err != nil { 610 t.Fatal(err) 611 } 612 defer cst.Close() 613 614 // Try submitting an orphan block to the consensus set. The empty block can 615 // be used, because looking for a parent is one of the first checks the 616 // consensus set performs. 617 orphan := types.Block{} 618 err = cst.cs.AcceptBlock(orphan) 619 if err != errOrphan { 620 t.Fatalf("expected %v, got %v", errOrphan, err) 621 } 622 err = cst.cs.AcceptBlock(orphan) 623 if err != errOrphan { 624 t.Fatalf("expected %v, got %v", errOrphan, err) 625 } 626 } 627 628 // TestMissedTarget submits a block that does not meet the required target. 629 func TestMissedTarget(t *testing.T) { 630 if testing.Short() { 631 t.SkipNow() 632 } 633 t.Parallel() 634 cst, err := createConsensusSetTester(t.Name()) 635 if err != nil { 636 t.Fatal(err) 637 } 638 defer cst.Close() 639 640 // Mine a block that doesn't meet the target. 641 block, target, err := cst.miner.BlockForWork() 642 if err != nil { 643 t.Fatal(err) 644 } 645 for checkTarget(block, block.ID(), target) && block.Nonce[0] != 255 { 646 block.Nonce[0]++ 647 } 648 if checkTarget(block, block.ID(), target) { 649 t.Fatal("unable to find a failing target") 650 } 651 err = cst.cs.AcceptBlock(block) 652 if err != modules.ErrBlockUnsolved { 653 t.Fatalf("expected %v, got %v", modules.ErrBlockUnsolved, err) 654 } 655 } 656 657 // TestMinerPayoutHandling checks that blocks with incorrect payouts are 658 // rejected. 659 func TestMinerPayoutHandling(t *testing.T) { 660 if testing.Short() { 661 t.SkipNow() 662 } 663 t.Parallel() 664 cst, err := createConsensusSetTester(t.Name()) 665 if err != nil { 666 t.Fatal(err) 667 } 668 defer cst.Close() 669 670 // Create a block with the wrong miner payout structure - testing can be 671 // light here because there is heavier testing in the 'types' package, 672 // where the logic is defined. 673 block, target, err := cst.miner.BlockForWork() 674 if err != nil { 675 t.Fatal(err) 676 } 677 block.MinerPayouts = append(block.MinerPayouts, types.SiacoinOutput{Value: types.NewCurrency64(1)}) 678 solvedBlock, _ := cst.miner.SolveBlock(block, target) 679 err = cst.cs.AcceptBlock(solvedBlock) 680 if err != errBadMinerPayouts { 681 t.Fatalf("expected %v, got %v", errBadMinerPayouts, err) 682 } 683 } 684 685 // TestEarlyTimestampHandling checks that blocks too far in the past are 686 // rejected. 687 func TestEarlyTimestampHandling(t *testing.T) { 688 if testing.Short() { 689 t.SkipNow() 690 } 691 t.Parallel() 692 cst, err := createConsensusSetTester(t.Name()) 693 if err != nil { 694 t.Fatal(err) 695 } 696 defer cst.Close() 697 minTimestamp := types.CurrentTimestamp() 698 cst.cs.blockRuleHelper = mockBlockRuleHelper{ 699 minTimestamp: minTimestamp, 700 } 701 702 // Submit a block with a timestamp in the past, before minTimestamp. 703 block, target, err := cst.miner.BlockForWork() 704 if err != nil { 705 t.Fatal(err) 706 } 707 block.Timestamp = minTimestamp - 1 708 solvedBlock, _ := cst.miner.SolveBlock(block, target) 709 err = cst.cs.AcceptBlock(solvedBlock) 710 if err != errEarlyTimestamp { 711 t.Fatalf("expected %v, got %v", errEarlyTimestamp, err) 712 } 713 } 714 715 // testFutureTimestampHandling checks that blocks in the future (but not 716 // extreme future) are handled correctly. 717 func TestFutureTimestampHandling(t *testing.T) { 718 if testing.Short() { 719 t.SkipNow() 720 } 721 t.Parallel() 722 cst, err := createConsensusSetTester(t.Name()) 723 if err != nil { 724 t.Fatal(err) 725 } 726 defer cst.Close() 727 728 // Submit a block with a timestamp in the future, but not the extreme 729 // future. 730 block, target, err := cst.miner.BlockForWork() 731 if err != nil { 732 t.Fatal(err) 733 } 734 block.Timestamp = types.CurrentTimestamp() + 2 + types.FutureThreshold 735 solvedBlock, _ := cst.miner.SolveBlock(block, target) 736 err = cst.cs.AcceptBlock(solvedBlock) 737 if err != errFutureTimestamp { 738 t.Fatalf("expected %v, got %v", errFutureTimestamp, err) 739 } 740 741 // Poll the consensus set until the future block appears. 742 for i := 0; i < 30; i++ { 743 time.Sleep(time.Second * 3) 744 _, err = cst.cs.dbGetBlockMap(solvedBlock.ID()) 745 if err == nil { 746 break 747 } 748 } 749 _, err = cst.cs.dbGetBlockMap(solvedBlock.ID()) 750 if err != nil { 751 t.Errorf("Future block not added to consensus set.\nCurrent Timestamp %v\nFutureThreshold: %v\nBlock Timestamp %v\n", types.CurrentTimestamp(), types.FutureThreshold, block.Timestamp) 752 } 753 } 754 755 // TestExtremeFutureTimestampHandling checks that blocks in the extreme future 756 // are rejected. 757 func TestExtremeFutureTimestampHandling(t *testing.T) { 758 if testing.Short() { 759 t.SkipNow() 760 } 761 t.Parallel() 762 cst, err := createConsensusSetTester(t.Name()) 763 if err != nil { 764 t.Fatal(err) 765 } 766 defer cst.Close() 767 768 // Submit a block with a timestamp in the extreme future. 769 block, target, err := cst.miner.BlockForWork() 770 if err != nil { 771 t.Fatal(err) 772 } 773 block.Timestamp = types.CurrentTimestamp() + 2 + types.ExtremeFutureThreshold 774 solvedBlock, _ := cst.miner.SolveBlock(block, target) 775 err = cst.cs.AcceptBlock(solvedBlock) 776 if err != errExtremeFutureTimestamp { 777 t.Fatalf("expected %v, got %v", errFutureTimestamp, err) 778 } 779 } 780 781 // TestBuriedBadTransaction tries submitting a block with a bad transaction 782 // that is buried under good transactions. 783 func TestBuriedBadTransaction(t *testing.T) { 784 if testing.Short() { 785 t.SkipNow() 786 } 787 t.Parallel() 788 cst, err := createConsensusSetTester(t.Name()) 789 if err != nil { 790 t.Fatal(err) 791 } 792 defer cst.Close() 793 pb := cst.cs.dbCurrentProcessedBlock() 794 795 // Create a good transaction using the wallet. 796 txnValue := types.NewCurrency64(1200) 797 txnBuilder := cst.wallet.StartTransaction() 798 err = txnBuilder.FundSiacoins(txnValue) 799 if err != nil { 800 t.Fatal(err) 801 } 802 txnBuilder.AddSiacoinOutput(types.SiacoinOutput{Value: txnValue}) 803 txnSet, err := txnBuilder.Sign(true) 804 if err != nil { 805 t.Fatal(err) 806 } 807 err = cst.tpool.AcceptTransactionSet(txnSet) 808 if err != nil { 809 t.Fatal(err) 810 } 811 812 // Create a bad transaction 813 badTxn := types.Transaction{ 814 SiacoinInputs: []types.SiacoinInput{{}}, 815 } 816 txns := append(cst.tpool.TransactionList(), badTxn) 817 818 // Create a block with a buried bad transaction. 819 block := types.Block{ 820 ParentID: pb.Block.ID(), 821 Timestamp: types.CurrentTimestamp(), 822 MinerPayouts: []types.SiacoinOutput{{Value: types.CalculateCoinbase(pb.Height + 1)}}, 823 Transactions: txns, 824 } 825 block, _ = cst.miner.SolveBlock(block, pb.ChildTarget) 826 err = cst.cs.AcceptBlock(block) 827 if err == nil { 828 t.Error("buried transaction didn't cause an error") 829 } 830 } 831 832 // TestInconsistencyCheck puts the consensus set in to an inconsistent state 833 // and makes sure that the santiy checks are triggering panics. 834 func TestInconsistentCheck(t *testing.T) { 835 if testing.Short() { 836 t.SkipNow() 837 } 838 t.Parallel() 839 cst, err := createConsensusSetTester(t.Name()) 840 if err != nil { 841 t.Fatal(err) 842 } 843 844 // Corrupt the consensus set by adding a new siafund output. 845 sfo := types.SiafundOutput{ 846 Value: types.NewCurrency64(1), 847 } 848 cst.cs.dbAddSiafundOutput(types.SiafundOutputID{}, sfo) 849 850 // Catch a panic that should be caused by the inconsistency check after a 851 // block is mined. 852 defer func() { 853 r := recover() 854 if r == nil { 855 t.Fatalf("inconsistency panic not triggered by corrupted database") 856 } 857 }() 858 _, err = cst.miner.AddBlock() 859 if err != nil { 860 t.Fatal(err) 861 } 862 } 863 864 // COMPATv0.4.0 865 // 866 // This test checks that the hardfork scheduled for block 21,000 rolls through 867 // smoothly. 868 func TestTaxHardfork(t *testing.T) { 869 if testing.Short() { 870 t.SkipNow() 871 } 872 t.Parallel() 873 cst, err := createConsensusSetTester(t.Name()) 874 if err != nil { 875 t.Fatal(err) 876 } 877 defer cst.Close() 878 879 // Create a file contract with a payout that is put into the blockchain 880 // before the hardfork block but expires after the hardfork block. 881 payout := types.NewCurrency64(400e6) 882 outputSize := types.PostTax(cst.cs.dbBlockHeight(), payout) 883 fc := types.FileContract{ 884 WindowStart: cst.cs.dbBlockHeight() + 12, 885 WindowEnd: cst.cs.dbBlockHeight() + 14, 886 Payout: payout, 887 ValidProofOutputs: []types.SiacoinOutput{{Value: outputSize}}, 888 MissedProofOutputs: []types.SiacoinOutput{{Value: outputSize}}, 889 UnlockHash: types.UnlockConditions{}.UnlockHash(), // The empty UC is anyone-can-spend 890 } 891 892 // Create and fund a transaction with a file contract. 893 txnBuilder := cst.wallet.StartTransaction() 894 err = txnBuilder.FundSiacoins(payout) 895 if err != nil { 896 t.Fatal(err) 897 } 898 fcIndex := txnBuilder.AddFileContract(fc) 899 txnSet, err := txnBuilder.Sign(true) 900 if err != nil { 901 t.Fatal(err) 902 } 903 err = cst.tpool.AcceptTransactionSet(txnSet) 904 if err != nil { 905 t.Fatal(err) 906 } 907 _, err = cst.miner.AddBlock() 908 if err != nil { 909 t.Fatal(err) 910 } 911 912 // Check that the siafund pool was increased by the faulty float amount. 913 siafundPool := cst.cs.dbGetSiafundPool() 914 if !siafundPool.Equals64(15590e3) { 915 t.Fatal("siafund pool was not increased correctly") 916 } 917 918 // Mine blocks until the hardfork is reached. 919 for i := 0; i < 10; i++ { 920 _, err = cst.miner.AddBlock() 921 if err != nil { 922 t.Fatal(err) 923 } 924 } 925 926 // Submit a file contract revision and check that the payouts are able to 927 // be the same. 928 fcid := txnSet[len(txnSet)-1].FileContractID(fcIndex) 929 fcr := types.FileContractRevision{ 930 ParentID: fcid, 931 UnlockConditions: types.UnlockConditions{}, 932 NewRevisionNumber: 1, 933 934 NewFileSize: 1, 935 NewWindowStart: cst.cs.dbBlockHeight() + 2, 936 NewWindowEnd: cst.cs.dbBlockHeight() + 4, 937 NewValidProofOutputs: fc.ValidProofOutputs, 938 NewMissedProofOutputs: fc.MissedProofOutputs, 939 } 940 txnBuilder = cst.wallet.StartTransaction() 941 txnBuilder.AddFileContractRevision(fcr) 942 txnSet, err = txnBuilder.Sign(true) 943 if err != nil { 944 t.Fatal(err) 945 } 946 err = cst.tpool.AcceptTransactionSet(txnSet) 947 if err != nil { 948 t.Fatal(err) 949 } 950 _, err = cst.miner.AddBlock() 951 if err != nil { 952 t.Fatal(err) 953 } 954 955 // Mine blocks until the revision goes through, such that the sanity checks 956 // can be run. 957 for i := 0; i < 6; i++ { 958 _, err = cst.miner.AddBlock() 959 if err != nil { 960 t.Fatal(err) 961 } 962 } 963 964 // Check that the siafund pool did not change after the submitted revision. 965 siafundPool = cst.cs.dbGetSiafundPool() 966 if !siafundPool.Equals64(15590e3) { 967 t.Fatal("siafund pool was not increased correctly") 968 } 969 } 970 971 // mockGatewayDoesBroadcast implements modules.Gateway to mock the Broadcast 972 // method. 973 type mockGatewayDoesBroadcast struct { 974 modules.Gateway 975 broadcastCalled chan struct{} 976 } 977 978 // Broadcast is a mock implementation of modules.Gateway.Broadcast that 979 // sends a sentinel value down a channel to signal it's been called. 980 func (g *mockGatewayDoesBroadcast) Broadcast(name string, obj interface{}, peers []modules.Peer) { 981 g.Gateway.Broadcast(name, obj, peers) 982 g.broadcastCalled <- struct{}{} 983 } 984 985 // TestAcceptBlockBroadcasts tests that AcceptBlock broadcasts valid blocks and 986 // that managedAcceptBlock does not. 987 func TestAcceptBlockBroadcasts(t *testing.T) { 988 if testing.Short() { 989 t.SkipNow() 990 } 991 t.Parallel() 992 cst, err := blankConsensusSetTester(t.Name()) 993 if err != nil { 994 t.Fatal(err) 995 } 996 defer cst.Close() 997 mg := &mockGatewayDoesBroadcast{ 998 Gateway: cst.cs.gateway, 999 broadcastCalled: make(chan struct{}), 1000 } 1001 cst.cs.gateway = mg 1002 1003 // Test that Broadcast is called for valid blocks. 1004 b, _ := cst.miner.FindBlock() 1005 err = cst.cs.AcceptBlock(b) 1006 if err != nil { 1007 t.Fatal(err) 1008 } 1009 select { 1010 case <-mg.broadcastCalled: 1011 case <-time.After(10 * time.Millisecond): 1012 t.Error("expected AcceptBlock to broadcast a valid block") 1013 } 1014 1015 // Test that Broadcast is not called for invalid blocks. 1016 err = cst.cs.AcceptBlock(types.Block{}) 1017 if err == nil { 1018 t.Fatal("expected AcceptBlock to error on an invalid block") 1019 } 1020 select { 1021 case <-mg.broadcastCalled: 1022 t.Error("AcceptBlock broadcasted an invalid block") 1023 case <-time.After(10 * time.Millisecond): 1024 } 1025 1026 // Test that Broadcast is not called in managedAcceptBlock. 1027 b, _ = cst.miner.FindBlock() 1028 _, err = cst.cs.managedAcceptBlocks([]types.Block{b}) 1029 if err != nil { 1030 t.Fatal(err) 1031 } 1032 select { 1033 case <-mg.broadcastCalled: 1034 t.Errorf("managedAcceptBlock should not broadcast blocks") 1035 case <-time.After(10 * time.Millisecond): 1036 } 1037 } 1038 1039 // blockCountingSubscriber counts the number of blocks that get submitted to the 1040 // subscriber, as well as the number of times that the subscriber has been given 1041 // changes at all. 1042 type blockCountingSubscriber struct { 1043 changes []modules.ConsensusChangeID 1044 1045 appliedBlocks int 1046 revertedBlocks int 1047 } 1048 1049 // ProcessConsensusChange fills the subscription interface for the 1050 // blockCountingSubscriber. 1051 func (bcs *blockCountingSubscriber) ProcessConsensusChange(cc modules.ConsensusChange) { 1052 bcs.changes = append(bcs.changes, cc.ID) 1053 bcs.revertedBlocks += len(cc.RevertedBlocks) 1054 bcs.appliedBlocks += len(cc.AppliedBlocks) 1055 } 1056 1057 // TestChainedAcceptBlock creates series of blocks, some of which are valid, 1058 // some invalid, and submits them to the consensus set, verifying that the 1059 // consensus set updates correctly each time. 1060 func TestChainedAcceptBlock(t *testing.T) { 1061 if testing.Short() { 1062 t.SkipNow() 1063 } 1064 t.Parallel() 1065 // Create a tester to send blocks in a batch to the other tester. 1066 cst, err := createConsensusSetTester(t.Name()) 1067 if err != nil { 1068 t.Fatal(err) 1069 } 1070 defer cst.Close() 1071 cst2, err := blankConsensusSetTester(t.Name() + "2") 1072 if err != nil { 1073 t.Fatal(err) 1074 } 1075 defer cst2.Close() 1076 // Subscribe a blockCountingSubscriber to cst2. 1077 var bcs blockCountingSubscriber 1078 cst2.cs.ConsensusSetSubscribe(&bcs, modules.ConsensusChangeBeginning, cst2.cs.tg.StopChan()) 1079 if len(bcs.changes) != 1 || bcs.appliedBlocks != 1 || bcs.revertedBlocks != 0 { 1080 t.Error("consensus changes do not seem to be getting passed to subscribers correctly") 1081 } 1082 1083 // Grab all of the blocks in cst, with the intention of giving them to cst2. 1084 var blocks []types.Block 1085 height := cst.cs.Height() 1086 for i := types.BlockHeight(0); i <= height; i++ { 1087 id, err := cst.cs.dbGetPath(i) 1088 if err != nil { 1089 t.Fatal(err) 1090 } 1091 pb, err := cst.cs.dbGetBlockMap(id) 1092 if err != nil { 1093 t.Fatal(err) 1094 } 1095 blocks = append(blocks, pb.Block) 1096 } 1097 1098 // Create a jumbling of the blocks, so that the set is not in order. 1099 jumble := make([]types.Block, len(blocks)) 1100 jumble[0] = blocks[0] 1101 jumble[1] = blocks[2] 1102 jumble[2] = blocks[1] 1103 for i := 3; i < len(jumble); i++ { 1104 jumble[i] = blocks[i] 1105 } 1106 // Try to submit the blocks out-of-order, which would violate one of the 1107 // assumptions in managedAcceptBlocks. 1108 _, err = cst2.cs.managedAcceptBlocks(jumble) 1109 if err != errNonLinearChain { 1110 t.Fatal(err) 1111 } 1112 if cst2.cs.Height() != 0 { 1113 t.Fatal("blocks added even though the inputs were jumbled") 1114 } 1115 if len(bcs.changes) != 1 || bcs.appliedBlocks != 1 || bcs.revertedBlocks != 0 { 1116 t.Error("consensus changes do not seem to be getting passed to subscribers correctly") 1117 } 1118 1119 // Tag an invalid block onto the end of blocks. 1120 block, err := cst.miner.AddBlock() 1121 if err != nil { 1122 t.Fatal(err) 1123 } 1124 // Adding an invalid transaction to make the block invalid. 1125 badBlock := block 1126 badBlock.Transactions = append(badBlock.Transactions, types.Transaction{ 1127 SiacoinInputs: []types.SiacoinInput{{ 1128 ParentID: types.SiacoinOutputID{1}, 1129 }}, 1130 }) 1131 // Append the invalid transaction to the block. 1132 badBlocks := append(blocks, badBlock) 1133 // Submit the whole invalid set. Result should be that nothing is added. 1134 _, err = cst2.cs.managedAcceptBlocks(badBlocks) 1135 if err == nil { 1136 t.Fatal(err) 1137 } 1138 if cst2.cs.Height() != 0 { 1139 t.Log(cst2.cs.Height()) 1140 t.Log(cst.cs.Height()) 1141 t.Fatal("height is not correct, seems that blocks were added") 1142 } 1143 if bcs.appliedBlocks != 1 || bcs.revertedBlocks != 0 { 1144 t.Error("consensus changes do not seem to be getting passed to subscribers correctly") 1145 } 1146 1147 // Try submitting the good blocks. 1148 _, err = cst2.cs.managedAcceptBlocks(blocks) 1149 if err != nil { 1150 t.Fatal(err) 1151 } 1152 if bcs.appliedBlocks != int(cst2.cs.Height()+1) || bcs.revertedBlocks != 0 { 1153 t.Error("consensus changes do not seem to be getting passed to subscribers correctly") 1154 } 1155 1156 // Check that every change recorded in 'bcs' is also available in the 1157 // consensus set. 1158 for _, change := range bcs.changes { 1159 err := cst2.cs.db.Update(func(tx *bolt.Tx) error { 1160 _, exists := getEntry(tx, change) 1161 if !exists { 1162 t.Error("an entry was provided that doesn't exist") 1163 } 1164 return nil 1165 }) 1166 if err != nil { 1167 t.Fatal(err) 1168 } 1169 } 1170 }