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