dcrd/blockchain/blockindex_test.go
Dave Collins 2dac209198
blockchain: Use lastest major version deps.
This udpates the blockchain module to use the lastest module major
versions.

While here, it also corrects a few typos and updates some test function
names to more accurately reflect their purpose.

The updated direct dependencies are as follows:

- github.com/decred/dcrd/blockchain/stake/v2@v2.0.1
- github.com/decred/dcrd/chaincfg/v2@v2.2.0
- github.com/decred/dcrd/database/v2@v2.0.0
- github.com/decred/dcrd/dcrutil/v2@v2.0.0
- github.com/decred/dcrd/txscript/v2@v2.0.0
2019-08-08 13:10:20 -05:00

240 lines
7.4 KiB
Go

// Copyright (c) 2018-2019 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain
import (
"reflect"
"testing"
"time"
"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/chaincfg/v2"
"github.com/decred/dcrd/wire"
)
// mustParseHash converts the passed big-endian hex string into a
// chainhash.Hash and will panic if there is an error. It only differs from the
// one available in chainhash in that it will panic so errors in the source code
// be detected. It will only (and must only) be called with hard-coded, and
// therefore known good, hashes.
func mustParseHash(s string) *chainhash.Hash {
hash, err := chainhash.NewHashFromStr(s)
if err != nil {
panic("invalid hash in source file: " + s)
}
return hash
}
// TestBlockNodeHeader ensures that block nodes reconstruct the correct header
// and fetching the header from the chain reconstructs it from memory.
func TestBlockNodeHeader(t *testing.T) {
// Create a fake chain and block header with all fields set to nondefault
// values.
params := chaincfg.RegNetParams()
bc := newFakeChain(params)
tip := bc.bestChain.Tip()
testHeader := wire.BlockHeader{
Version: 1,
PrevBlock: tip.hash,
MerkleRoot: *mustParseHash("09876543210987654321"),
StakeRoot: *mustParseHash("43210987654321098765"),
VoteBits: 0x03,
FinalState: [6]byte{0xaa},
Voters: 4,
FreshStake: 5,
Revocations: 6,
PoolSize: 20000,
Bits: 0x1234,
SBits: 123456789,
Height: 1,
Size: 393216,
Timestamp: time.Unix(1454954400, 0),
Nonce: 7,
ExtraData: [32]byte{0xbb},
StakeVersion: 5,
}
node := newBlockNode(&testHeader, tip)
bc.index.AddNode(node)
// Ensure reconstructing the header for the node produces the same header
// used to create the node.
gotHeader := node.Header()
if !reflect.DeepEqual(gotHeader, testHeader) {
t.Fatalf("node.Header: mismatched headers: got %+v, want %+v",
gotHeader, testHeader)
}
// Ensure fetching the header from the chain produces the same header used
// to create the node.
testHeaderHash := testHeader.BlockHash()
gotHeader, err := bc.HeaderByHash(&testHeaderHash)
if err != nil {
t.Fatalf("HeaderByHash: unexpected error: %v", err)
}
if !reflect.DeepEqual(gotHeader, testHeader) {
t.Fatalf("HeaderByHash: mismatched headers: got %+v, want %+v",
gotHeader, testHeader)
}
}
// TestCalcPastMedianTime ensures the CalcPastMedianTie function works as
// intended including when there are less than the typical number of blocks
// which happens near the beginning of the chain.
func TestCalcPastMedianTime(t *testing.T) {
tests := []struct {
name string
timestamps []int64
expected int64
}{
{
name: "one block",
timestamps: []int64{1517188771},
expected: 1517188771,
},
{
name: "two blocks, in order",
timestamps: []int64{1517188771, 1517188831},
expected: 1517188771,
},
{
name: "three blocks, in order",
timestamps: []int64{1517188771, 1517188831, 1517188891},
expected: 1517188831,
},
{
name: "three blocks, out of order",
timestamps: []int64{1517188771, 1517188891, 1517188831},
expected: 1517188831,
},
{
name: "four blocks, in order",
timestamps: []int64{1517188771, 1517188831, 1517188891, 1517188951},
expected: 1517188831,
},
{
name: "four blocks, out of order",
timestamps: []int64{1517188831, 1517188771, 1517188951, 1517188891},
expected: 1517188831,
},
{
name: "eleven blocks, in order",
timestamps: []int64{1517188771, 1517188831, 1517188891, 1517188951,
1517189011, 1517189071, 1517189131, 1517189191, 1517189251,
1517189311, 1517189371},
expected: 1517189071,
},
{
name: "eleven blocks, out of order",
timestamps: []int64{1517188831, 1517188771, 1517188891, 1517189011,
1517188951, 1517189071, 1517189131, 1517189191, 1517189251,
1517189371, 1517189311},
expected: 1517189071,
},
{
name: "fifteen blocks, in order",
timestamps: []int64{1517188771, 1517188831, 1517188891, 1517188951,
1517189011, 1517189071, 1517189131, 1517189191, 1517189251,
1517189311, 1517189371, 1517189431, 1517189491, 1517189551,
1517189611},
expected: 1517189311,
},
{
name: "fifteen blocks, out of order",
timestamps: []int64{1517188771, 1517188891, 1517188831, 1517189011,
1517188951, 1517189131, 1517189071, 1517189251, 1517189191,
1517189371, 1517189311, 1517189491, 1517189431, 1517189611,
1517189551},
expected: 1517189311,
},
}
// Ensure the genesis block timestamp of the test params is before the test
// data. Also, clone the provided parameters first to avoid mutating them.
//
// The timestamp corresponds to 2018-01-01 00:00:00 +0000 UTC.
params := chaincfg.RegNetParams()
params.GenesisBlock.Header.Timestamp = time.Unix(1514764800, 0)
params.GenesisHash = params.GenesisBlock.BlockHash()
for _, test := range tests {
// Create a synthetic chain with the correct number of nodes and the
// timestamps as specified by the test.
bc := newFakeChain(params)
node := bc.bestChain.Tip()
for _, timestamp := range test.timestamps {
node = newFakeNode(node, 0, 0, 0, time.Unix(timestamp, 0))
bc.index.AddNode(node)
bc.bestChain.SetTip(node)
}
// Ensure the median time is the expected value.
gotTime := node.CalcPastMedianTime()
wantTime := time.Unix(test.expected, 0)
if !gotTime.Equal(wantTime) {
t.Errorf("%s: mismatched timestamps -- got: %v, want: %v",
test.name, gotTime, wantTime)
continue
}
}
}
// TestChainTips ensures the chain tip tracking in the block index works
// as expected.
func TestChainTips(t *testing.T) {
params := chaincfg.RegNetParams()
bc := newFakeChain(params)
genesis := bc.bestChain.NodeByHeight(0)
// Construct a synthetic simnet chain consisting of the following structure.
// 0 -> 1 -> 2 -> 3 -> 4
// | \-> 2a -> 3a -> 4a -> 5a -> 6a -> 7a -> ... -> 26a
// | | \-> 3b -> 4b -> 5b
// | \-> 2c -> 3c -> 4c -> 5c -> 6c -> 7c -> ... -> 26c
// \-> 1d
// \-> 1e
branches := make([][]*blockNode, 6)
branches[0] = chainedFakeNodes(genesis, 4)
branches[1] = chainedFakeNodes(branches[0][0], 25)
branches[2] = chainedFakeNodes(branches[1][0], 3)
branches[3] = chainedFakeNodes(branches[0][0], 25)
branches[4] = chainedFakeNodes(genesis, 1)
branches[5] = chainedFakeNodes(genesis, 1)
// Add all of the nodes to the index.
for _, branch := range branches {
for _, node := range branch {
bc.index.AddNode(node)
}
}
// Create a map of all of the chain tips the block index believes exist.
chainTips := make(map[*blockNode]struct{})
bc.index.RLock()
for _, nodes := range bc.index.chainTips {
for _, node := range nodes {
chainTips[node] = struct{}{}
}
}
bc.index.RUnlock()
// The expected chain tips are the tips of all of the branches.
expectedTips := make(map[*blockNode]struct{})
for _, branch := range branches {
expectedTips[branchTip(branch)] = struct{}{}
}
// Ensure the chain tips are the expected values.
if len(chainTips) != len(expectedTips) {
t.Fatalf("block index reports %d chain tips, but %d were expected",
len(chainTips), len(expectedTips))
}
for node := range expectedTips {
if _, ok := chainTips[node]; !ok {
t.Fatalf("block index does not contain expected tip %s (height %d)",
node.hash, node.height)
}
}
}