Skip to content
This repository has been archived by the owner on Nov 2, 2018. It is now read-only.

Commit

Permalink
Merge pull request #1041 from NebulousLabs/miner-testing
Browse files Browse the repository at this point in the history
Miner testing
  • Loading branch information
lukechampine committed Mar 28, 2016
2 parents 5b5aca3 + b654ce9 commit 3a0e7e1
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 20 deletions.
4 changes: 3 additions & 1 deletion build/critical.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import (
// which case panic will be called instead.
func Critical(v ...interface{}) {
s := fmt.Sprintln(v...)
os.Stderr.WriteString(s)
if Release != "testing" || !DEBUG {
os.Stderr.WriteString(s)
}
if DEBUG {
panic(s)
}
Expand Down
11 changes: 8 additions & 3 deletions modules/miner/blockmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"errors"
"time"

"github.com/NebulousLabs/Sia/build"
"github.com/NebulousLabs/Sia/crypto"
"github.com/NebulousLabs/Sia/modules"
"github.com/NebulousLabs/Sia/types"
Expand Down Expand Up @@ -127,10 +126,16 @@ func (m *Miner) managedSubmitBlock(b types.Block) error {
m.mu.Lock()
m.persist.BlocksFound = append(m.persist.BlocksFound, b.ID())
m.mu.Unlock()
m.log.Println("Mined a stale block - block appears valid but does not extend the blockchain")
return err
}
if err == modules.ErrBlockUnsolved {
m.log.Println("Mined an unsolved block - header submission appears to be incorrect")
return err
}
if err != nil {
m.tpool.PurgeTransactionPool()
m.log.Println("ERROR: an invalid block was submitted:", err)
m.log.Critical("ERROR: an invalid block was submitted:", err)
return err
}
m.mu.Lock()
Expand Down Expand Up @@ -180,7 +185,7 @@ func (m *Miner) SubmitHeader(bh types.BlockHeader) error {
// Sanity check - block should have same id as header.
bh.Nonce = nonce
if types.BlockID(crypto.HashObject(bh)) != b.ID() {
build.Critical("block reconstruction failed")
m.log.Critical("block reconstruction failed")
}
return nil
}()
Expand Down
2 changes: 1 addition & 1 deletion modules/miner/blockmanager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func TestIntegrationHeaderForWorkUpdates(t *testing.T) {
}

// TestIntegrationManyHeaders checks that requesting a full set of headers a
// row results in all unique headers, and that all of them can be reassebled
// row results in all unique headers, and that all of them can be reassembled
// into valid blocks.
func TestIntegrationManyHeaders(t *testing.T) {
if testing.Short() {
Expand Down
6 changes: 4 additions & 2 deletions modules/miner/miner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ type minerTester struct {

miner *Miner

persistDir string
minedBlocks []types.Block
persistDir string
}

// createMinerTester creates a minerTester that's ready for use.
Expand Down Expand Up @@ -83,10 +84,11 @@ func createMinerTester(name string) (*minerTester, error) {

// Mine until the wallet has money.
for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ {
_, err = m.AddBlock()
b, err := m.AddBlock()
if err != nil {
return nil, err
}
mt.minedBlocks = append(mt.minedBlocks, b)
}

return mt, nil
Expand Down
33 changes: 20 additions & 13 deletions modules/miner/update.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package miner

import (
"github.com/NebulousLabs/Sia/build"
"github.com/NebulousLabs/Sia/encoding"
"github.com/NebulousLabs/Sia/modules"
"github.com/NebulousLabs/Sia/types"
Expand All @@ -12,16 +11,24 @@ func (m *Miner) ProcessConsensusChange(cc modules.ConsensusChange) {
m.mu.Lock()
defer m.mu.Unlock()

// Adjust the height of the miner. The miner height is initialized to zero,
// but the genesis block is actually height zero. For the genesis block
// only, the height will be left at zero.
//
// Checking the height here eliminates the need to initialize the miner to
// an underflowed types.BlockHeight, which was deemed the worse of the two
// evils.
if m.persist.Height != 0 || cc.AppliedBlocks[len(cc.AppliedBlocks)-1].ID() != types.GenesisBlock.ID() {
m.persist.Height -= types.BlockHeight(len(cc.RevertedBlocks))
m.persist.Height += types.BlockHeight(len(cc.AppliedBlocks))
for _, block := range cc.RevertedBlocks {
if block.ID() != types.GenesisBlock.ID() {
m.persist.Height--
}
}
for _, block := range cc.AppliedBlocks {
if block.ID() != types.GenesisBlock.ID() {
m.persist.Height++
}
}
// Sanity check - if the most recent block in the miner is the same as the
// most recent block in the consensus set, then the height of the consensus
// set and the height of the miner should be the same.
if cc.AppliedBlocks[len(cc.AppliedBlocks)-1].ID() == m.cs.CurrentBlock().ID() {
if m.persist.Height != m.cs.Height() {
m.log.Critical("miner has a height mismatch: have ", m.persist.Height, " expected ", m.cs.Height())
m.persist.Height = m.cs.Height()
}
}

// Update the unsolved block.
Expand All @@ -30,10 +37,10 @@ func (m *Miner) ProcessConsensusChange(cc modules.ConsensusChange) {
m.persist.Target, exists1 = m.cs.ChildTarget(m.persist.UnsolvedBlock.ParentID)
m.persist.UnsolvedBlock.Timestamp, exists2 = m.cs.MinimumValidChildTimestamp(m.persist.UnsolvedBlock.ParentID)
if !exists1 {
build.Critical("miner was unable to find parent id of an unsolved block in the consensus set")
m.log.Critical("miner was unable to find parent id of an unsolved block in the consensus set")
}
if !exists2 {
build.Critical("miner was unable to find child timestamp of an unsovled block in the consensus set")
m.log.Critical("miner was unable to find child timestamp of an unsovled block in the consensus set")
}

// There is a new parent block, the source block should be updated to keep
Expand Down
127 changes: 127 additions & 0 deletions modules/miner/update_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package miner

import (
"testing"

"github.com/NebulousLabs/Sia/modules"
)

// TestIntegrationBlockHeightReorg checks that the miner has the correct block
// height after a series of reorgs that go as far as the genesis block.
func TestIntegrationBlockHeightReorg(t *testing.T) {
if testing.Short() {
t.SkipNow()
}

// Create 3 miner testers that will be used to cause eachother to reorg.
mt1, err := createMinerTester("TestIntegrationBlockHeightReorg - 1")
if err != nil {
t.Fatal(err)
}
mt2, err := createMinerTester("TestIntegrationBlockHeightReorg - 2")
if err != nil {
t.Fatal(err)
}
mt3, err := createMinerTester("TestIntegrationBlockHeightReorg - 3")
if err != nil {
t.Fatal(err)
}

// Put one ahead of the other multiple times, which should thrash around
// the height calculation and cause problems by dipping down to the genesis
// block repeatedly.
for i := 0; i < 2; i++ {
b, err := mt1.miner.AddBlock()
if err != nil {
t.Fatal(err)
}
mt1.minedBlocks = append(mt1.minedBlocks, b)
}
for i := 0; i < 3; i++ {
b, err := mt2.miner.AddBlock()
if err != nil {
t.Fatal(err)
}
mt2.minedBlocks = append(mt2.minedBlocks, b)
}
for _, block := range mt2.minedBlocks {
err = mt1.cs.AcceptBlock(block)
if err != nil && err != modules.ErrNonExtendingBlock {
t.Fatal(err)
}
}
if mt1.cs.CurrentBlock().ID() != mt2.cs.CurrentBlock().ID() {
t.Fatal("mt1 and mt2 should have the same current block")
}
for i := 0; i < 2; i++ {
b, err := mt1.miner.AddBlock()
if err != nil {
t.Fatal(err)
}
mt1.minedBlocks = append(mt1.minedBlocks, b)
}
for i := 0; i < 3; i++ {
b, err := mt2.miner.AddBlock()
if err != nil {
t.Fatal(err)
}
mt2.minedBlocks = append(mt2.minedBlocks, b)
}
for _, block := range mt2.minedBlocks {
err = mt1.cs.AcceptBlock(block)
if err != nil && err != modules.ErrNonExtendingBlock && err != modules.ErrBlockKnown {
t.Fatal(err)
}
}
if mt1.cs.CurrentBlock().ID() != mt2.cs.CurrentBlock().ID() {
t.Fatal("mt1 and mt2 should have the same current block")
}
for i := 0; i < 7; i++ {
b, err := mt3.miner.AddBlock()
if err != nil {
t.Fatal(err)
}
mt3.minedBlocks = append(mt3.minedBlocks, b)
}
for _, block := range mt3.minedBlocks {
err = mt1.cs.AcceptBlock(block)
if err != nil && err != modules.ErrNonExtendingBlock {
t.Fatal(err)
}
}
if mt1.cs.CurrentBlock().ID() == mt2.cs.CurrentBlock().ID() {
t.Fatal("mt1 and mt2 should not have the same block height")
}
if mt1.cs.CurrentBlock().ID() != mt3.cs.CurrentBlock().ID() {
t.Fatal("mt1 and mt3 should have the same current block")
}
}

// TestMinerHeightForcefulMismatch checks that the miner, when not running in
// debug mode, does forceful self-correcting for height calculation.
func TestMinerHeightForcefulMismatch(t *testing.T) {
if testing.Short() {
t.SkipNow()
}
mt, err := createMinerTester("TestMinerHeightForcefulMismatch")
if err != nil {
t.Fatal(err)
}
b, err := mt.miner.FindBlock()
if err != nil {
t.Fatal(err)
}

// A panic should be triggered when AcceptBlock is called on the miner.
mt.miner.persist.Height--
defer func() {
r := recover()
if r == nil {
t.Fatal("expecting a panic upon adding a block")
}
}()
err = mt.cs.AcceptBlock(b)
if err != nil {
t.Fatal(err)
}
}

0 comments on commit 3a0e7e1

Please sign in to comment.