[DEV-149] Add test case for CVE-2018-17144 (Bitcoin DoS/Double Spend bug) (#77)

* [DEV-149] Add test case for CVE-2018-17144 (Bitcoin DoS/Double Spend bug)

* [DEV-149] change t.Errorf + return to t.Fatalf

* [DEV-149] fix malformed blocks

* [DEV-149] change test blocks to use simnet genesis
This commit is contained in:
Ori Newman 2018-10-09 15:40:00 +03:00 committed by stasatdaglabs
parent 45e0e6707b
commit 030469f035
11 changed files with 114 additions and 84 deletions

View File

@ -1,18 +1,19 @@
package blockdag
import (
"bou.ke/monkey"
"errors"
"strings"
"testing"
"bou.ke/monkey"
"github.com/daglabs/btcd/dagconfig"
"github.com/daglabs/btcd/database"
"github.com/daglabs/btcd/util"
"strings"
"testing"
)
func TestMaybeAcceptBlockErrors(t *testing.T) {
// Create a new database and DAG instance to run tests against.
dag, teardownFunc, err := DAGSetup("TestMaybeAcceptBlockErrors", &dagconfig.MainNetParams)
dag, teardownFunc, err := DAGSetup("TestMaybeAcceptBlockErrors", &dagconfig.SimNetParams)
if err != nil {
t.Fatalf("TestMaybeAcceptBlockErrors: Failed to setup DAG instance: %v", err)
}
@ -24,22 +25,22 @@ func TestMaybeAcceptBlockErrors(t *testing.T) {
orphanBlockFile := "blk_3B.dat"
loadedBlocks, err := loadBlocks(orphanBlockFile)
if err != nil {
t.Fatalf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are missing: " +
t.Fatalf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are missing: "+
"Error loading file '%s': %s\n", orphanBlockFile, err)
}
block := loadedBlocks[0]
err = dag.maybeAcceptBlock(block, BFNone)
if err == nil {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are missing: " +
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are missing: "+
"Expected: %s, got: <nil>", ErrPreviousBlockUnknown)
}
ruleErr, ok := err.(RuleError)
if !ok {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are missing: " +
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are missing: "+
"Expected RuleError but got %s", err)
} else if ruleErr.ErrorCode != ErrPreviousBlockUnknown {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are missing: " +
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are missing: "+
"Unexpected error code. Want: %s, got: %s", ErrPreviousBlockUnknown, ruleErr.ErrorCode)
}
@ -47,7 +48,7 @@ func TestMaybeAcceptBlockErrors(t *testing.T) {
blocksFile := "blk_0_to_4.dat"
blocks, err := loadBlocks(blocksFile)
if err != nil {
t.Fatalf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are invalid: " +
t.Fatalf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are invalid: "+
"Error loading file '%s': %s\n", blocksFile, err)
}
@ -63,15 +64,15 @@ func TestMaybeAcceptBlockErrors(t *testing.T) {
block2 := blocks[2]
err = dag.maybeAcceptBlock(block2, BFNone)
if err == nil {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are invalid: " +
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are invalid: "+
"Expected: %s, got: <nil>", ErrInvalidAncestorBlock)
}
ruleErr, ok = err.(RuleError)
if !ok {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are invalid: " +
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are invalid: "+
"Expected RuleError but got %s", err)
} else if ruleErr.ErrorCode != ErrInvalidAncestorBlock {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are invalid: " +
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block if its parents are invalid: "+
"Unexpected error. Want: %s, got: %s", ErrInvalidAncestorBlock, ruleErr.ErrorCode)
}
@ -83,15 +84,15 @@ func TestMaybeAcceptBlockErrors(t *testing.T) {
block2.MsgBlock().Header.Bits = 0
err = dag.maybeAcceptBlock(block2, BFNone)
if err == nil {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block due to bad context: " +
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block due to bad context: "+
"Expected: %s, got: <nil>", ErrUnexpectedDifficulty)
}
ruleErr, ok = err.(RuleError)
if !ok {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block due to bad context: " +
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block due to bad context: "+
"Expected RuleError but got %s", err)
} else if ruleErr.ErrorCode != ErrUnexpectedDifficulty {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block due to bad context: " +
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the block due to bad context: "+
"Unexpected error. Want: %s, got: %s", ErrUnexpectedDifficulty, ruleErr.ErrorCode)
}
@ -105,11 +106,11 @@ func TestMaybeAcceptBlockErrors(t *testing.T) {
})
err = dag.maybeAcceptBlock(block2, BFNone)
if err == nil {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the node due to database error: " +
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the node due to database error: "+
"Expected: %s, got: <nil>", databaseErrorMessage)
}
if !strings.Contains(err.Error(), databaseErrorMessage) {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the node due to database error: " +
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the node due to database error: "+
"Unexpected error. Want: %s, got: %s", databaseErrorMessage, err)
}
monkey.Unpatch(dbStoreBlock)
@ -121,11 +122,11 @@ func TestMaybeAcceptBlockErrors(t *testing.T) {
})
err = dag.maybeAcceptBlock(block2, BFNone)
if err == nil {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the node due to index error: " +
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the node due to index error: "+
"Expected %s, got: <nil>", indexErrorMessage)
}
if !strings.Contains(err.Error(), indexErrorMessage) {
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the node due to index error: " +
t.Errorf("TestMaybeAcceptBlockErrors: rejecting the node due to index error: "+
"Unexpected error. Want: %s, got: %s", indexErrorMessage, err)
}
monkey.Unpatch((*blockIndex).flushToDB)

View File

@ -1,17 +1,18 @@
package blockdag
import (
"github.com/bouk/monkey"
"github.com/daglabs/btcd/dagconfig"
"github.com/daglabs/btcd/database"
"github.com/pkg/errors"
"strings"
"testing"
"time"
"bou.ke/monkey"
"github.com/daglabs/btcd/dagconfig"
"github.com/daglabs/btcd/database"
"github.com/pkg/errors"
)
func TestAncestorErrors(t *testing.T) {
node := newTestNode(newSet(), int32(0x10000000), 0, time.Unix(0,0), dagconfig.MainNetParams.K)
node := newTestNode(newSet(), int32(0x10000000), 0, time.Unix(0, 0), dagconfig.MainNetParams.K)
node.height = 2
ancestor := node.Ancestor(3)
if ancestor != nil {
@ -21,7 +22,7 @@ func TestAncestorErrors(t *testing.T) {
func TestFlushToDBErrors(t *testing.T) {
// Create a new database and DAG instance to run tests against.
dag, teardownFunc, err := DAGSetup("TestMaybeAcceptBlockErrors", &dagconfig.MainNetParams)
dag, teardownFunc, err := DAGSetup("TestFlushToDBErrors", &dagconfig.MainNetParams)
if err != nil {
t.Fatalf("TestFlushToDBErrors: Failed to setup DAG instance: %s", err)
}
@ -30,7 +31,7 @@ func TestFlushToDBErrors(t *testing.T) {
// Call flushToDB without anything to flush. This should succeed
err = dag.index.flushToDB()
if err != nil {
t.Errorf("TestFlushToDBErrors: flushToDB without anything to flush: " +
t.Errorf("TestFlushToDBErrors: flushToDB without anything to flush: "+
"Unexpected flushToDB error: %s", err)
}
@ -39,17 +40,17 @@ func TestFlushToDBErrors(t *testing.T) {
// Test flushToDB failure due to database error
databaseErrorMessage := "database error"
monkey.Patch(dbStoreBlockNode, func (_ database.Tx, _ *blockNode) error{
monkey.Patch(dbStoreBlockNode, func(_ database.Tx, _ *blockNode) error {
return errors.New(databaseErrorMessage)
})
err = dag.index.flushToDB()
if err == nil {
t.Errorf("TestFlushToDBErrors: flushToDB failure due to database error: " +
t.Errorf("TestFlushToDBErrors: flushToDB failure due to database error: "+
"Expected: %s, got: <nil>", databaseErrorMessage)
}
if !strings.Contains(err.Error(), databaseErrorMessage) {
t.Errorf("TestFlushToDBErrors: flushToDB failure due to database error: " +
t.Errorf("TestFlushToDBErrors: flushToDB failure due to database error: "+
"Unexpected flushToDB error. Expected: %s, got: %s", databaseErrorMessage, err)
}
monkey.Unpatch(dbStoreBlockNode)
}
}

View File

@ -36,18 +36,16 @@ func TestHaveBlock(t *testing.T) {
for _, file := range testFiles {
blockTmp, err := loadBlocks(file)
if err != nil {
t.Errorf("Error loading file: %v\n", err)
return
t.Fatalf("Error loading file: %v\n", err)
}
blocks = append(blocks, blockTmp...)
}
// Create a new database and chain instance to run tests against.
dag, teardownFunc, err := DAGSetup("haveblock",
&dagconfig.MainNetParams)
&dagconfig.SimNetParams)
if err != nil {
t.Errorf("Failed to setup chain instance: %v", err)
return
t.Fatalf("Failed to setup chain instance: %v", err)
}
defer teardownFunc()
@ -58,16 +56,15 @@ func TestHaveBlock(t *testing.T) {
for i := 1; i < len(blocks); i++ {
isOrphan, err := dag.ProcessBlock(blocks[i], BFNone)
if err != nil {
t.Errorf("ProcessBlock fail on block %v: %v\n", i, err)
return
t.Fatalf("ProcessBlock fail on block %v: %v\n", i, err)
}
if isOrphan {
t.Errorf("ProcessBlock incorrectly returned block %v "+
t.Fatalf("ProcessBlock incorrectly returned block %v "+
"is an orphan\n", i)
return
}
}
// Test a block with related parents
testFiles = []string{
"blk_3C.dat",
}
@ -75,35 +72,60 @@ func TestHaveBlock(t *testing.T) {
for _, file := range testFiles {
blockTmp, err := loadBlocks(file)
if err != nil {
t.Errorf("Error loading file: %v\n", err)
return
t.Fatalf("Error loading file: %v\n", err)
}
blocks = append(blocks, blockTmp...)
}
isOrphan, err := dag.ProcessBlock(blocks[6], BFNone)
// Block 3c should fail to connect since its parents are related. (It points to 1 and 2, and 1 is the parent of 2)
// Block 3C should fail to connect since its parents are related. (It points to 1 and 2, and 1 is the parent of 2)
if err == nil {
t.Errorf("ProcessBlock for block 3c has no error when expected to have an error\n")
return
t.Fatalf("ProcessBlock for block 3C has no error when expected to have an error\n")
}
if isOrphan {
t.Errorf("ProcessBlock incorrectly returned block 3c " +
t.Fatalf("ProcessBlock incorrectly returned block 3C " +
"is an orphan\n")
}
// Test a block with the same input twice
testFiles = []string{
"blk_3D.dat",
}
for _, file := range testFiles {
blockTmp, err := loadBlocks(file)
if err != nil {
t.Fatalf("Error loading file: %v\n", err)
}
blocks = append(blocks, blockTmp...)
}
isOrphan, err = dag.ProcessBlock(blocks[7], BFNone)
// Block 3D should fail to connect since it has a transaction with the same input twice
if err == nil {
t.Fatalf("ProcessBlock for block 3D has no error when expected to have an error\n")
}
rErr, ok := err.(RuleError)
if !ok {
t.Fatalf("ProcessBlock for block 3D expected a RuleError, but got something else\n")
}
if !ok || rErr.ErrorCode != ErrDuplicateTxInputs {
t.Fatalf("ProcessBlock for block 3D expected error code %s but got %s\n", ErrDuplicateTxInputs, rErr.ErrorCode)
}
if isOrphan {
t.Fatalf("ProcessBlock incorrectly returned block 3D " +
"is an orphan\n")
return
}
// Insert an orphan block.
isOrphan, err = dag.ProcessBlock(util.NewBlock(&Block100000),
BFNone)
if err != nil {
t.Errorf("Unable to process block: %v", err)
return
t.Fatalf("Unable to process block: %v", err)
}
if !isOrphan {
t.Errorf("ProcessBlock indicated block is an not orphan when " +
t.Fatalf("ProcessBlock indicated block is an not orphan when " +
"it should be\n")
return
}
tests := []struct {
@ -111,13 +133,13 @@ func TestHaveBlock(t *testing.T) {
want bool
}{
// Genesis block should be present.
{hash: dagconfig.MainNetParams.GenesisHash.String(), want: true},
{hash: dagconfig.SimNetParams.GenesisHash.String(), want: true},
// Block 3b should be present (as a second child of Block 2).
{hash: "00000093c8f2ab3444502da0754fc8149d738701aef9b2e0f32f32c078039295", want: true},
{hash: "00cd35debc62fd60b6fbda1925894db5996c02bcd575a4130fdb4d6071537152", want: true},
// Block 100000 should be present (as an orphan).
{hash: "000000e46b5f4f7bfecff77f2f30f2ab90d08e3c5a55784080f97689bcd92786", want: true},
{hash: "66cdaddc8884c99ccc46c2f34f579903a223cc12b44c239938af47ee0c7193b4", want: true},
// Random hashes should not be available.
{hash: "123", want: false},
@ -126,19 +148,16 @@ func TestHaveBlock(t *testing.T) {
for i, test := range tests {
hash, err := daghash.NewHashFromStr(test.hash)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
continue
t.Fatalf("NewHashFromStr: %v", err)
}
result, err := dag.HaveBlock(hash)
if err != nil {
t.Errorf("HaveBlock #%d unexpected error: %v", i, err)
return
t.Fatalf("HaveBlock #%d unexpected error: %v", i, err)
}
if result != test.want {
t.Errorf("HaveBlock #%d got %v want %v", i, result,
t.Fatalf("HaveBlock #%d got %v want %v", i, result,
test.want)
continue
}
}
}
@ -745,7 +764,7 @@ func testErrorThroughPatching(t *testing.T, expectedErrorMessage string, targetF
}
// Create a new database and dag instance to run tests against.
dag, teardownFunc, err := DAGSetup("testErrorThroughPatching", &dagconfig.MainNetParams)
dag, teardownFunc, err := DAGSetup("testErrorThroughPatching", &dagconfig.SimNetParams)
if err != nil {
t.Fatalf("Failed to setup dag instance: %v", err)
}
@ -759,7 +778,12 @@ func testErrorThroughPatching(t *testing.T, expectedErrorMessage string, targetF
err = nil
for i := 1; i < len(blocks); i++ {
_, err = dag.ProcessBlock(blocks[i], BFNone)
var isOrphan bool
isOrphan, err = dag.ProcessBlock(blocks[i], BFNone)
if isOrphan {
t.Fatalf("ProcessBlock incorrectly returned block %v "+
"is an orphan\n", i)
}
if err != nil {
break
}

View File

@ -17,11 +17,11 @@ func TestNotifications(t *testing.T) {
t.Fatalf("Error loading file: %v\n", err)
}
// Create a new database and chain instance to run tests against.
chain, teardownFunc, err := DAGSetup("notifications",
&dagconfig.MainNetParams)
// Create a new database and dag instance to run tests against.
dag, teardownFunc, err := DAGSetup("notifications",
&dagconfig.SimNetParams)
if err != nil {
t.Fatalf("Failed to setup chain instance: %v", err)
t.Fatalf("Failed to setup dag instance: %v", err)
}
defer teardownFunc()
@ -36,10 +36,14 @@ func TestNotifications(t *testing.T) {
// times.
const numSubscribers = 3
for i := 0; i < numSubscribers; i++ {
chain.Subscribe(callback)
dag.Subscribe(callback)
}
_, err = chain.ProcessBlock(blocks[1], BFNone)
isOrphan, err := dag.ProcessBlock(blocks[1], BFNone)
if isOrphan {
t.Fatalf("ProcessBlock incorrectly returned block " +
"is an orphan\n")
}
if err != nil {
t.Fatalf("ProcessBlock fail on block 1: %v\n", err)
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
blockdag/testdata/blk_3D.dat vendored Normal file

Binary file not shown.

View File

@ -435,7 +435,7 @@ func checkBlockParentsOrder(header *wire.BlockHeader) error {
sortedHashes = append(sortedHashes, hash)
}
sort.Slice(sortedHashes, func(i, j int) bool {
return daghash.Less(&sortedHashes[j], &sortedHashes[i])
return daghash.Less(&sortedHashes[i], &sortedHashes[j])
})
if !daghash.AreEqual(header.PrevBlocks, sortedHashes) {
return ruleError(ErrWrongParentsOrder, "block parents are not ordered by hash")

View File

@ -68,7 +68,7 @@ func TestSequenceLocksActive(t *testing.T) {
func TestCheckConnectBlockTemplate(t *testing.T) {
// Create a new database and chain instance to run tests against.
dag, teardownFunc, err := DAGSetup("checkconnectblocktemplate",
&dagconfig.MainNetParams)
&dagconfig.SimNetParams)
if err != nil {
t.Errorf("Failed to setup chain instance: %v", err)
return
@ -190,9 +190,9 @@ func TestCheckBlockSanity(t *testing.T) {
0x6f, 0xff, 0xfb, 0xb7, 0xdc, 0x39, 0x9d, 0x76,
0x8d, 0xb0, 0xe1, 0x9c, 0x2e, 0x6d, 0x22, 0xd9,
}), // f3e94742aca4b5ef85488dc37c06c3282295ffec960994b2c0d5ac2a25a95766
Timestamp: time.Unix(0x5b7142b4, 0), // 2018-07-19 15:11:10 +0000 UTC
Timestamp: time.Unix(0x5bbc64c9, 0), // 2018-10-09 08:17:35 +0000 UTC
Bits: 0x1e00ffff, // 503382015
Nonce: 0xa05433ab, // 2148280951
Nonce: 0xe00edcf9, // 3759070457
},
Transactions: []*wire.MsgTx{
{
@ -551,17 +551,17 @@ var Block100000 = wire.MsgBlock{
NumPrevBlocks: 2,
PrevBlocks: []daghash.Hash{
[32]byte{ // Make go vet happy.
0xa5, 0x60, 0xe4, 0x59, 0x61, 0x5d, 0xbb, 0x4e,
0xae, 0x33, 0x40, 0x2a, 0xc0, 0xb8, 0x8a, 0xb6,
0x53, 0xc5, 0x11, 0xe3, 0x35, 0xd0, 0xa6, 0x1e,
0x0a, 0xc4, 0x06, 0x96, 0x97, 0x00, 0x00, 0x00,
}, // MainNet genesis
0x16, 0x5e, 0x38, 0xe8, 0xb3, 0x91, 0x45, 0x95,
0xd9, 0xc6, 0x41, 0xf3, 0xb8, 0xee, 0xc2, 0xf3,
0x46, 0x11, 0x89, 0x6b, 0x82, 0x1a, 0x68, 0x3b,
0x7a, 0x4e, 0xde, 0xfe, 0x2c, 0x00, 0x00, 0x00,
},
[32]byte{ // Make go vet happy.
0x2a, 0x9a, 0x2a, 0xe4, 0x3b, 0xc1, 0x6b, 0x55,
0x06, 0x39, 0xc2, 0xe6, 0xbb, 0x4a, 0xec, 0xeb,
0x7a, 0x49, 0x7c, 0xe9, 0xf3, 0x60, 0x20, 0x7d,
0x63, 0xc7, 0x26, 0x89, 0xf6, 0x00, 0x00, 0x00,
}, // SimNet genesis
0x4b, 0xb0, 0x75, 0x35, 0xdf, 0xd5, 0x8e, 0x0b,
0x3c, 0xd6, 0x4f, 0xd7, 0x15, 0x52, 0x80, 0x87,
0x2a, 0x04, 0x71, 0xbc, 0xf8, 0x30, 0x95, 0x52,
0x6a, 0xce, 0x0e, 0x38, 0xc6, 0x00, 0x00, 0x00,
},
},
MerkleRoot: daghash.Hash([32]byte{ // Make go vet happy.
0xc0, 0x92, 0x53, 0x8f, 0x6f, 0xf7, 0xf5, 0x24,
@ -569,9 +569,9 @@ var Block100000 = wire.MsgBlock{
0x6f, 0xff, 0xfb, 0xb7, 0xdc, 0x39, 0x9d, 0x76,
0x8d, 0xb0, 0xe1, 0x9c, 0x2e, 0x6d, 0x22, 0xd9,
}), // f3e94742aca4b5ef85488dc37c06c3282295ffec960994b2c0d5ac2a25a95766
Timestamp: time.Unix(0x5bb9f3b4, 0), // 2018-07-19 15:11:10 +0000 UTC
Bits: 0x1e00ffff, // 503382015
Nonce: 0x203602e3, // 2148280951
Timestamp: time.Unix(0x5bbc7588, 0), // 2018-10-09 07:49:35 +0000 UTC
Bits: 0x207fffff, // 503382015
Nonce: 0xdffffff9,
},
Transactions: []*wire.MsgTx{
{