[DEV-84] validate block parents order

* [DEV-84] validate block parents order

* [DEV-84] change AreEqual to look for order equality as well
This commit is contained in:
Ori Newman 2018-08-14 16:15:12 +03:00 committed by stasatdaglabs
parent 6475e03b93
commit 4b78fd007d
9 changed files with 326 additions and 41 deletions

View File

@ -19,7 +19,7 @@ import (
// The flags are also passed to checkBlockContext and connectToDAG. See
// their documentation for how the flags modify their behavior.
//
// This function MUST be called with the chain state lock held (for writes).
// This function MUST be called with the dagLock held (for writes).
func (dag *BlockDAG) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) error {
// The height of this block is one more than the referenced previous
// block.
@ -28,12 +28,12 @@ func (dag *BlockDAG) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags)
return err
}
selectedParent := parents.first()
blockHeight := selectedParent.height + 1
selectedParent := parents.first() //TODO (Ori): This is wrong, done only for compilation
blockHeight := parents.maxHeight() + 1
block.SetHeight(blockHeight)
// The block must pass all of the validation rules which depend on the
// position of the block within the block chain.
// position of the block within the block DAG.
err = dag.checkBlockContext(block, selectedParent, flags)
if err != nil {
return err

View File

@ -128,9 +128,9 @@ type blockNode struct {
// initially creating a node.
func initBlockNode(node *blockNode, blockHeader *wire.BlockHeader, parents blockSet, phantomK uint32) {
*node = blockNode{
parents: parents,
children: make(blockSet),
workSum: big.NewInt(0),
parents: parents,
children: make(blockSet),
workSum: big.NewInt(0),
timestamp: time.Now().Unix(),
}
@ -160,13 +160,7 @@ func addNodeAsChildToParents(node *blockNode) {
}
func calculateNodeHeight(node *blockNode) int32 {
var maxHeight int32
for _, parent := range node.parents {
if maxHeight < parent.height {
maxHeight = parent.height
}
}
return maxHeight + 1
return node.parents.maxHeight() + 1
}
// newBlockNode returns a new block node for the given block header and parent

View File

@ -24,6 +24,17 @@ func setFromSlice(blocks ...*blockNode) blockSet {
return set
}
// maxHeight returns the height of the highest block in the block set
func (bs blockSet) maxHeight() int32 {
var maxHeight int32
for _, node := range bs {
if maxHeight < node.height {
maxHeight = node.height
}
}
return maxHeight
}
// add adds a block to this BlockSet
func (bs blockSet) add(block *blockNode) {
bs[block.hash] = block

View File

@ -87,7 +87,7 @@ func TestHaveBlock(t *testing.T) {
{hash: "000000bce70562ed076f269c5c4e39c590abb29428c573c02ab970e17931f8a4", want: true},
// Block 100000 should be present (as an orphan).
{hash: "000000824dca82828b9a18ac09ed7c93292f6a042fae580c1192c8cd086fd990", want: true},
{hash: "000000a805b083e0ef1f516b1153828724c235d6e6f0fabb47b869f6d054ac3f", want: true},
// Random hashes should not be available.
{hash: "123", want: false},

View File

@ -60,6 +60,9 @@ const (
// the current time.
ErrTimeTooNew
// ErrWrongParentsOrder indicates that the block's parents are not ordered by hash, as expected
ErrWrongParentsOrder
// ErrDifficultyTooLow indicates the difficulty for the block is lower
// than the difficulty required by the most recent checkpoint.
ErrDifficultyTooLow

View File

@ -9,6 +9,7 @@ import (
"fmt"
"math"
"math/big"
"sort"
"time"
"github.com/daglabs/btcd/dagconfig"
@ -416,6 +417,11 @@ func checkBlockHeaderSanity(header *wire.BlockHeader, powLimit *big.Int, timeSou
return err
}
err = checkBlockParentsOrder(header)
if err != nil {
return err
}
// A block timestamp must not have a greater precision than one second.
// This check is necessary because Go time.Time values support
// nanosecond precision whereas the consensus rules only apply to
@ -439,6 +445,21 @@ func checkBlockHeaderSanity(header *wire.BlockHeader, powLimit *big.Int, timeSou
return nil
}
//checkBlockParentsOrder ensures that the block's parents are ordered by hash
func checkBlockParentsOrder(header *wire.BlockHeader) error {
sortedHashes := make([]daghash.Hash, 0, len(header.PrevBlocks))
for _, hash := range header.PrevBlocks {
sortedHashes = append(sortedHashes, hash)
}
sort.Slice(sortedHashes, func(i, j int) bool {
return daghash.Less(&sortedHashes[i], &sortedHashes[j])
})
if !daghash.AreEqual(header.PrevBlocks, sortedHashes) {
return ruleError(ErrWrongParentsOrder, "block parents are not ordered by hash")
}
return nil
}
// checkBlockSanity performs some preliminary checks on a block to ensure it is
// sane before continuing with block processing. These checks are context free.
//

View File

@ -163,6 +163,273 @@ func TestCheckBlockSanity(t *testing.T) {
if err == nil {
t.Errorf("CheckBlockSanity: error is nil when it shouldn't be")
}
var invalidParentsOrderBlock = wire.MsgBlock{
Header: wire.BlockHeader{
Version: 1,
NumPrevBlocks: 2,
PrevBlocks: []daghash.Hash{
[32]byte{ // Make go vet happy.
0x0f, 0xea, 0x33, 0x21, 0x67, 0xd0, 0x25, 0x05,
0x89, 0x83, 0x19, 0x9f, 0x47, 0x50, 0x3a, 0x3b,
0x91, 0xd2, 0x0a, 0xec, 0x57, 0x7a, 0x10, 0x89,
0xd6, 0x36, 0xf8, 0x6d, 0x29, 0x00, 0x00, 0x00,
}, // MainNet genesis
[32]byte{ // Make go vet happy.
0x42, 0xeb, 0x66, 0x1f, 0x71, 0xb2, 0xb9, 0x26,
0x05, 0xae, 0xe7, 0xf4, 0x8a, 0x6b, 0x7c, 0x86,
0x33, 0x54, 0x0a, 0x27, 0xe5, 0x47, 0xb9, 0xb5,
0xee, 0x13, 0x18, 0x83, 0x46, 0x00, 0x00, 0x00,
}}, // SimNet genesis
MerkleRoot: daghash.Hash([32]byte{ // Make go vet happy.
0xc0, 0x92, 0x53, 0x8f, 0x6f, 0xf7, 0xf5, 0x24,
0xd5, 0x33, 0xd4, 0x8b, 0xf3, 0xc0, 0xf8, 0xf9,
0x6f, 0xff, 0xfb, 0xb7, 0xdc, 0x39, 0x9d, 0x76,
0x8d, 0xb0, 0xe1, 0x9c, 0x2e, 0x6d, 0x22, 0xd9,
}), // f3e94742aca4b5ef85488dc37c06c3282295ffec960994b2c0d5ac2a25a95766
Timestamp: time.Unix(0x5b50aa0e, 0), // 2018-07-19 15:11:10 +0000 UTC
Bits: 0x1e00ffff, // 503382015
Nonce: 0x800c2a77, // 2148280951
},
Transactions: []*wire.MsgTx{
{
Version: 1,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
Hash: daghash.Hash{},
Index: 0xffffffff,
},
SignatureScript: []byte{
0x04, 0x4c, 0x86, 0x04, 0x1b, 0x02, 0x06, 0x02,
},
Sequence: math.MaxUint64,
},
},
TxOut: []*wire.TxOut{
{
Value: 0x12a05f200, // 5000000000
PkScript: []byte{
0x41, // OP_DATA_65
0x04, 0x1b, 0x0e, 0x8c, 0x25, 0x67, 0xc1, 0x25,
0x36, 0xaa, 0x13, 0x35, 0x7b, 0x79, 0xa0, 0x73,
0xdc, 0x44, 0x44, 0xac, 0xb8, 0x3c, 0x4e, 0xc7,
0xa0, 0xe2, 0xf9, 0x9d, 0xd7, 0x45, 0x75, 0x16,
0xc5, 0x81, 0x72, 0x42, 0xda, 0x79, 0x69, 0x24,
0xca, 0x4e, 0x99, 0x94, 0x7d, 0x08, 0x7f, 0xed,
0xf9, 0xce, 0x46, 0x7c, 0xb9, 0xf7, 0xc6, 0x28,
0x70, 0x78, 0xf8, 0x01, 0xdf, 0x27, 0x6f, 0xdf,
0x84, // 65-byte signature
0xac, // OP_CHECKSIG
},
},
},
LockTime: 0,
},
{
Version: 1,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
Hash: daghash.Hash([32]byte{ // Make go vet happy.
0x03, 0x2e, 0x38, 0xe9, 0xc0, 0xa8, 0x4c, 0x60,
0x46, 0xd6, 0x87, 0xd1, 0x05, 0x56, 0xdc, 0xac,
0xc4, 0x1d, 0x27, 0x5e, 0xc5, 0x5f, 0xc0, 0x07,
0x79, 0xac, 0x88, 0xfd, 0xf3, 0x57, 0xa1, 0x87,
}), // 87a157f3fd88ac7907c05fc55e271dc4acdc5605d187d646604ca8c0e9382e03
Index: 0,
},
SignatureScript: []byte{
0x49, // OP_DATA_73
0x30, 0x46, 0x02, 0x21, 0x00, 0xc3, 0x52, 0xd3,
0xdd, 0x99, 0x3a, 0x98, 0x1b, 0xeb, 0xa4, 0xa6,
0x3a, 0xd1, 0x5c, 0x20, 0x92, 0x75, 0xca, 0x94,
0x70, 0xab, 0xfc, 0xd5, 0x7d, 0xa9, 0x3b, 0x58,
0xe4, 0xeb, 0x5d, 0xce, 0x82, 0x02, 0x21, 0x00,
0x84, 0x07, 0x92, 0xbc, 0x1f, 0x45, 0x60, 0x62,
0x81, 0x9f, 0x15, 0xd3, 0x3e, 0xe7, 0x05, 0x5c,
0xf7, 0xb5, 0xee, 0x1a, 0xf1, 0xeb, 0xcc, 0x60,
0x28, 0xd9, 0xcd, 0xb1, 0xc3, 0xaf, 0x77, 0x48,
0x01, // 73-byte signature
0x41, // OP_DATA_65
0x04, 0xf4, 0x6d, 0xb5, 0xe9, 0xd6, 0x1a, 0x9d,
0xc2, 0x7b, 0x8d, 0x64, 0xad, 0x23, 0xe7, 0x38,
0x3a, 0x4e, 0x6c, 0xa1, 0x64, 0x59, 0x3c, 0x25,
0x27, 0xc0, 0x38, 0xc0, 0x85, 0x7e, 0xb6, 0x7e,
0xe8, 0xe8, 0x25, 0xdc, 0xa6, 0x50, 0x46, 0xb8,
0x2c, 0x93, 0x31, 0x58, 0x6c, 0x82, 0xe0, 0xfd,
0x1f, 0x63, 0x3f, 0x25, 0xf8, 0x7c, 0x16, 0x1b,
0xc6, 0xf8, 0xa6, 0x30, 0x12, 0x1d, 0xf2, 0xb3,
0xd3, // 65-byte pubkey
},
Sequence: math.MaxUint64,
},
},
TxOut: []*wire.TxOut{
{
Value: 0x2123e300, // 556000000
PkScript: []byte{
0x76, // OP_DUP
0xa9, // OP_HASH160
0x14, // OP_DATA_20
0xc3, 0x98, 0xef, 0xa9, 0xc3, 0x92, 0xba, 0x60,
0x13, 0xc5, 0xe0, 0x4e, 0xe7, 0x29, 0x75, 0x5e,
0xf7, 0xf5, 0x8b, 0x32,
0x88, // OP_EQUALVERIFY
0xac, // OP_CHECKSIG
},
},
{
Value: 0x108e20f00, // 4444000000
PkScript: []byte{
0x76, // OP_DUP
0xa9, // OP_HASH160
0x14, // OP_DATA_20
0x94, 0x8c, 0x76, 0x5a, 0x69, 0x14, 0xd4, 0x3f,
0x2a, 0x7a, 0xc1, 0x77, 0xda, 0x2c, 0x2f, 0x6b,
0x52, 0xde, 0x3d, 0x7c,
0x88, // OP_EQUALVERIFY
0xac, // OP_CHECKSIG
},
},
},
LockTime: 0,
},
{
Version: 1,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
Hash: daghash.Hash([32]byte{ // Make go vet happy.
0xc3, 0x3e, 0xbf, 0xf2, 0xa7, 0x09, 0xf1, 0x3d,
0x9f, 0x9a, 0x75, 0x69, 0xab, 0x16, 0xa3, 0x27,
0x86, 0xaf, 0x7d, 0x7e, 0x2d, 0xe0, 0x92, 0x65,
0xe4, 0x1c, 0x61, 0xd0, 0x78, 0x29, 0x4e, 0xcf,
}), // cf4e2978d0611ce46592e02d7e7daf8627a316ab69759a9f3df109a7f2bf3ec3
Index: 1,
},
SignatureScript: []byte{
0x47, // OP_DATA_71
0x30, 0x44, 0x02, 0x20, 0x03, 0x2d, 0x30, 0xdf,
0x5e, 0xe6, 0xf5, 0x7f, 0xa4, 0x6c, 0xdd, 0xb5,
0xeb, 0x8d, 0x0d, 0x9f, 0xe8, 0xde, 0x6b, 0x34,
0x2d, 0x27, 0x94, 0x2a, 0xe9, 0x0a, 0x32, 0x31,
0xe0, 0xba, 0x33, 0x3e, 0x02, 0x20, 0x3d, 0xee,
0xe8, 0x06, 0x0f, 0xdc, 0x70, 0x23, 0x0a, 0x7f,
0x5b, 0x4a, 0xd7, 0xd7, 0xbc, 0x3e, 0x62, 0x8c,
0xbe, 0x21, 0x9a, 0x88, 0x6b, 0x84, 0x26, 0x9e,
0xae, 0xb8, 0x1e, 0x26, 0xb4, 0xfe, 0x01,
0x41, // OP_DATA_65
0x04, 0xae, 0x31, 0xc3, 0x1b, 0xf9, 0x12, 0x78,
0xd9, 0x9b, 0x83, 0x77, 0xa3, 0x5b, 0xbc, 0xe5,
0xb2, 0x7d, 0x9f, 0xff, 0x15, 0x45, 0x68, 0x39,
0xe9, 0x19, 0x45, 0x3f, 0xc7, 0xb3, 0xf7, 0x21,
0xf0, 0xba, 0x40, 0x3f, 0xf9, 0x6c, 0x9d, 0xee,
0xb6, 0x80, 0xe5, 0xfd, 0x34, 0x1c, 0x0f, 0xc3,
0xa7, 0xb9, 0x0d, 0xa4, 0x63, 0x1e, 0xe3, 0x95,
0x60, 0x63, 0x9d, 0xb4, 0x62, 0xe9, 0xcb, 0x85,
0x0f, // 65-byte pubkey
},
Sequence: math.MaxUint64,
},
},
TxOut: []*wire.TxOut{
{
Value: 0xf4240, // 1000000
PkScript: []byte{
0x76, // OP_DUP
0xa9, // OP_HASH160
0x14, // OP_DATA_20
0xb0, 0xdc, 0xbf, 0x97, 0xea, 0xbf, 0x44, 0x04,
0xe3, 0x1d, 0x95, 0x24, 0x77, 0xce, 0x82, 0x2d,
0xad, 0xbe, 0x7e, 0x10,
0x88, // OP_EQUALVERIFY
0xac, // OP_CHECKSIG
},
},
{
Value: 0x11d260c0, // 299000000
PkScript: []byte{
0x76, // OP_DUP
0xa9, // OP_HASH160
0x14, // OP_DATA_20
0x6b, 0x12, 0x81, 0xee, 0xc2, 0x5a, 0xb4, 0xe1,
0xe0, 0x79, 0x3f, 0xf4, 0xe0, 0x8a, 0xb1, 0xab,
0xb3, 0x40, 0x9c, 0xd9,
0x88, // OP_EQUALVERIFY
0xac, // OP_CHECKSIG
},
},
},
LockTime: 0,
},
{
Version: 1,
TxIn: []*wire.TxIn{
{
PreviousOutPoint: wire.OutPoint{
Hash: daghash.Hash([32]byte{ // Make go vet happy.
0x0b, 0x60, 0x72, 0xb3, 0x86, 0xd4, 0xa7, 0x73,
0x23, 0x52, 0x37, 0xf6, 0x4c, 0x11, 0x26, 0xac,
0x3b, 0x24, 0x0c, 0x84, 0xb9, 0x17, 0xa3, 0x90,
0x9b, 0xa1, 0xc4, 0x3d, 0xed, 0x5f, 0x51, 0xf4,
}), // f4515fed3dc4a19b90a317b9840c243bac26114cf637522373a7d486b372600b
Index: 0,
},
SignatureScript: []byte{
0x49, // OP_DATA_73
0x30, 0x46, 0x02, 0x21, 0x00, 0xbb, 0x1a, 0xd2,
0x6d, 0xf9, 0x30, 0xa5, 0x1c, 0xce, 0x11, 0x0c,
0xf4, 0x4f, 0x7a, 0x48, 0xc3, 0xc5, 0x61, 0xfd,
0x97, 0x75, 0x00, 0xb1, 0xae, 0x5d, 0x6b, 0x6f,
0xd1, 0x3d, 0x0b, 0x3f, 0x4a, 0x02, 0x21, 0x00,
0xc5, 0xb4, 0x29, 0x51, 0xac, 0xed, 0xff, 0x14,
0xab, 0xba, 0x27, 0x36, 0xfd, 0x57, 0x4b, 0xdb,
0x46, 0x5f, 0x3e, 0x6f, 0x8d, 0xa1, 0x2e, 0x2c,
0x53, 0x03, 0x95, 0x4a, 0xca, 0x7f, 0x78, 0xf3,
0x01, // 73-byte signature
0x41, // OP_DATA_65
0x04, 0xa7, 0x13, 0x5b, 0xfe, 0x82, 0x4c, 0x97,
0xec, 0xc0, 0x1e, 0xc7, 0xd7, 0xe3, 0x36, 0x18,
0x5c, 0x81, 0xe2, 0xaa, 0x2c, 0x41, 0xab, 0x17,
0x54, 0x07, 0xc0, 0x94, 0x84, 0xce, 0x96, 0x94,
0xb4, 0x49, 0x53, 0xfc, 0xb7, 0x51, 0x20, 0x65,
0x64, 0xa9, 0xc2, 0x4d, 0xd0, 0x94, 0xd4, 0x2f,
0xdb, 0xfd, 0xd5, 0xaa, 0xd3, 0xe0, 0x63, 0xce,
0x6a, 0xf4, 0xcf, 0xaa, 0xea, 0x4e, 0xa1, 0x4f,
0xbb, // 65-byte pubkey
},
Sequence: math.MaxUint64,
},
},
TxOut: []*wire.TxOut{
{
Value: 0xf4240, // 1000000
PkScript: []byte{
0x76, // OP_DUP
0xa9, // OP_HASH160
0x14, // OP_DATA_20
0x39, 0xaa, 0x3d, 0x56, 0x9e, 0x06, 0xa1, 0xd7,
0x92, 0x6d, 0xc4, 0xbe, 0x11, 0x93, 0xc9, 0x9b,
0xf2, 0xeb, 0x9e, 0xe0,
0x88, // OP_EQUALVERIFY
0xac, // OP_CHECKSIG
},
},
},
LockTime: 0,
},
},
}
btcutilInvalidBlock := btcutil.NewBlock(&invalidParentsOrderBlock)
err = CheckBlockSanity(btcutilInvalidBlock, powLimit, timeSource)
if err == nil {
t.Errorf("CheckBlockSanity: error is nil when it shouldn't be")
}
rError := err.(RuleError)
if rError.ErrorCode != ErrWrongParentsOrder {
t.Errorf("CheckBlockSanity: Expected error was ErrWrongParentsOrder but got %v", err)
}
}
// TestCheckSerializedHeight tests the checkSerializedHeight function with
@ -238,26 +505,27 @@ var Block100000 = wire.MsgBlock{
NumPrevBlocks: 2,
PrevBlocks: []daghash.Hash{
[32]byte{ // Make go vet happy.
0x0f, 0xea, 0x33, 0x21, 0x67, 0xd0, 0x25, 0x05,
0x89, 0x83, 0x19, 0x9f, 0x47, 0x50, 0x3a, 0x3b,
0x91, 0xd2, 0x0a, 0xec, 0x57, 0x7a, 0x10, 0x89,
0xd6, 0x36, 0xf8, 0x6d, 0x29, 0x00, 0x00, 0x00,
}, // MainNet 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,
}, // SimNet genesis
[32]byte{ // Make go vet happy.
0x42, 0xeb, 0x66, 0x1f, 0x71, 0xb2, 0xb9, 0x26,
0x05, 0xae, 0xe7, 0xf4, 0x8a, 0x6b, 0x7c, 0x86,
0x33, 0x54, 0x0a, 0x27, 0xe5, 0x47, 0xb9, 0xb5,
0xee, 0x13, 0x18, 0x83, 0x46, 0x00, 0x00, 0x00,
}}, // SimNet 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,
}, // MainNet genesis
},
MerkleRoot: daghash.Hash([32]byte{ // Make go vet happy.
0xc0, 0x92, 0x53, 0x8f, 0x6f, 0xf7, 0xf5, 0x24,
0xd5, 0x33, 0xd4, 0x8b, 0xf3, 0xc0, 0xf8, 0xf9,
0x6f, 0xff, 0xfb, 0xb7, 0xdc, 0x39, 0x9d, 0x76,
0x8d, 0xb0, 0xe1, 0x9c, 0x2e, 0x6d, 0x22, 0xd9,
}), // f3e94742aca4b5ef85488dc37c06c3282295ffec960994b2c0d5ac2a25a95766
Timestamp: time.Unix(0x5b50aa0e, 0), // 2018-07-19 15:11:10 +0000 UTC
Timestamp: time.Unix(0x5b7142b4, 0), // 2018-07-19 15:11:10 +0000 UTC
Bits: 0x1e00ffff, // 503382015
Nonce: 0x800c2a77, // 2148280951
Nonce: 0xa05433ab, // 2148280951
},
Transactions: []*wire.MsgTx{
{

View File

@ -86,13 +86,8 @@ func AreEqual(first []Hash, second []Hash) bool {
return false
}
hashSet := make(map[Hash]bool)
for _, hash := range first {
hashSet[hash] = true
}
for _, hash := range second {
if !hashSet[hash] {
for i := range first {
if first[i] != second[i] {
return false
}
}

View File

@ -234,7 +234,6 @@ func TestAreEqual(t *testing.T) {
hash2, _ := NewHashFromStr("2222222222222222222222222222222222222222222222222222222222222222")
hash3, _ := NewHashFromStr("3333333333333333333333333333333333333333333333333333333333333333")
hashes0To2 := []Hash{*hash0, *hash1, *hash2}
hashes0To2Shifted := []Hash{*hash2, *hash0, *hash1}
hashes1To3 := []Hash{*hash1, *hash2, *hash3}
hashes0To3 := []Hash{*hash0, *hash1, *hash2, *hash3}
@ -250,12 +249,6 @@ func TestAreEqual(t *testing.T) {
second: hashes0To2,
expected: true,
},
{
name: "same members, different order",
first: hashes0To2,
second: hashes0To2Shifted,
expected: true,
},
{
name: "same slice length but only some members are equal",
first: hashes0To2,