github.com/decred/dcrd/blockchain@v1.2.1/blockindex_test.go (about)

     1  // Copyright (c) 2018 The Decred developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package blockchain
     6  
     7  import (
     8  	"reflect"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/decred/dcrd/chaincfg"
    13  	"github.com/decred/dcrd/chaincfg/chainhash"
    14  	"github.com/decred/dcrd/wire"
    15  )
    16  
    17  // mustParseHash converts the passed big-endian hex string into a
    18  // chainhash.Hash and will panic if there is an error.  It only differs from the
    19  // one available in chainhash in that it will panic so errors in the source code
    20  // be detected.  It will only (and must only) be called with hard-coded, and
    21  // therefore known good, hashes.
    22  func mustParseHash(s string) *chainhash.Hash {
    23  	hash, err := chainhash.NewHashFromStr(s)
    24  	if err != nil {
    25  		panic("invalid hash in source file: " + s)
    26  	}
    27  	return hash
    28  }
    29  
    30  // TestBlockNodeHeader ensures that block nodes reconstruct the correct header
    31  // and fetching the header from the chain reconstructs it from memory.
    32  func TestBlockNodeHeader(t *testing.T) {
    33  	// Create a fake chain and block header with all fields set to nondefault
    34  	// values.
    35  	params := &chaincfg.RegNetParams
    36  	bc := newFakeChain(params)
    37  	tip := bc.bestChain.Tip()
    38  	testHeader := wire.BlockHeader{
    39  		Version:      1,
    40  		PrevBlock:    tip.hash,
    41  		MerkleRoot:   *mustParseHash("09876543210987654321"),
    42  		StakeRoot:    *mustParseHash("43210987654321098765"),
    43  		VoteBits:     0x03,
    44  		FinalState:   [6]byte{0xaa},
    45  		Voters:       4,
    46  		FreshStake:   5,
    47  		Revocations:  6,
    48  		PoolSize:     20000,
    49  		Bits:         0x1234,
    50  		SBits:        123456789,
    51  		Height:       1,
    52  		Size:         393216,
    53  		Timestamp:    time.Unix(1454954400, 0),
    54  		Nonce:        7,
    55  		ExtraData:    [32]byte{0xbb},
    56  		StakeVersion: 5,
    57  	}
    58  	node := newBlockNode(&testHeader, tip)
    59  	bc.index.AddNode(node)
    60  
    61  	// Ensure reconstructing the header for the node produces the same header
    62  	// used to create the node.
    63  	gotHeader := node.Header()
    64  	if !reflect.DeepEqual(gotHeader, testHeader) {
    65  		t.Fatalf("node.Header: mismatched headers: got %+v, want %+v",
    66  			gotHeader, testHeader)
    67  	}
    68  
    69  	// Ensure fetching the header from the chain produces the same header used
    70  	// to create the node.
    71  	testHeaderHash := testHeader.BlockHash()
    72  	gotHeader, err := bc.HeaderByHash(&testHeaderHash)
    73  	if err != nil {
    74  		t.Fatalf("HeaderByHash: unexpected error: %v", err)
    75  	}
    76  	if !reflect.DeepEqual(gotHeader, testHeader) {
    77  		t.Fatalf("HeaderByHash: mismatched headers: got %+v, want %+v",
    78  			gotHeader, testHeader)
    79  	}
    80  }
    81  
    82  // TestCalcPastMedianTime ensures the CalcPastMedianTie function works as
    83  // intended including when there are less than the typical number of blocks
    84  // which happens near the beginning of the chain.
    85  func TestCalcPastMedianTime(t *testing.T) {
    86  	tests := []struct {
    87  		name       string
    88  		timestamps []int64
    89  		expected   int64
    90  	}{
    91  		{
    92  			name:       "one block",
    93  			timestamps: []int64{1517188771},
    94  			expected:   1517188771,
    95  		},
    96  		{
    97  			name:       "two blocks, in order",
    98  			timestamps: []int64{1517188771, 1517188831},
    99  			expected:   1517188771,
   100  		},
   101  		{
   102  			name:       "three blocks, in order",
   103  			timestamps: []int64{1517188771, 1517188831, 1517188891},
   104  			expected:   1517188831,
   105  		},
   106  		{
   107  			name:       "three blocks, out of order",
   108  			timestamps: []int64{1517188771, 1517188891, 1517188831},
   109  			expected:   1517188831,
   110  		},
   111  		{
   112  			name:       "four blocks, in order",
   113  			timestamps: []int64{1517188771, 1517188831, 1517188891, 1517188951},
   114  			expected:   1517188831,
   115  		},
   116  		{
   117  			name:       "four blocks, out of order",
   118  			timestamps: []int64{1517188831, 1517188771, 1517188951, 1517188891},
   119  			expected:   1517188831,
   120  		},
   121  		{
   122  			name: "eleven blocks, in order",
   123  			timestamps: []int64{1517188771, 1517188831, 1517188891, 1517188951,
   124  				1517189011, 1517189071, 1517189131, 1517189191, 1517189251,
   125  				1517189311, 1517189371},
   126  			expected: 1517189071,
   127  		},
   128  		{
   129  			name: "eleven blocks, out of order",
   130  			timestamps: []int64{1517188831, 1517188771, 1517188891, 1517189011,
   131  				1517188951, 1517189071, 1517189131, 1517189191, 1517189251,
   132  				1517189371, 1517189311},
   133  			expected: 1517189071,
   134  		},
   135  		{
   136  			name: "fifteen blocks, in order",
   137  			timestamps: []int64{1517188771, 1517188831, 1517188891, 1517188951,
   138  				1517189011, 1517189071, 1517189131, 1517189191, 1517189251,
   139  				1517189311, 1517189371, 1517189431, 1517189491, 1517189551,
   140  				1517189611},
   141  			expected: 1517189311,
   142  		},
   143  		{
   144  			name: "fifteen blocks, out of order",
   145  			timestamps: []int64{1517188771, 1517188891, 1517188831, 1517189011,
   146  				1517188951, 1517189131, 1517189071, 1517189251, 1517189191,
   147  				1517189371, 1517189311, 1517189491, 1517189431, 1517189611,
   148  				1517189551},
   149  			expected: 1517189311,
   150  		},
   151  	}
   152  
   153  	// Ensure the genesis block timestamp of the test params is before the test
   154  	// data.  Also, clone the provided parameters first to avoid mutating them.
   155  	//
   156  	// The timestamp corresponds to 2018-01-01 00:00:00 +0000 UTC.
   157  	params := cloneParams(&chaincfg.RegNetParams)
   158  	params.GenesisBlock.Header.Timestamp = time.Unix(1514764800, 0)
   159  	genesisHash := params.GenesisBlock.BlockHash()
   160  	params.GenesisHash = &genesisHash
   161  
   162  	for _, test := range tests {
   163  		// Create a synthetic chain with the correct number of nodes and the
   164  		// timestamps as specified by the test.
   165  		bc := newFakeChain(params)
   166  		node := bc.bestChain.Tip()
   167  		for _, timestamp := range test.timestamps {
   168  			node = newFakeNode(node, 0, 0, 0, time.Unix(timestamp, 0))
   169  			bc.index.AddNode(node)
   170  			bc.bestChain.SetTip(node)
   171  		}
   172  
   173  		// Ensure the median time is the expected value.
   174  		gotTime := node.CalcPastMedianTime()
   175  		wantTime := time.Unix(test.expected, 0)
   176  		if !gotTime.Equal(wantTime) {
   177  			t.Errorf("%s: mismatched timestamps -- got: %v, want: %v",
   178  				test.name, gotTime, wantTime)
   179  			continue
   180  		}
   181  	}
   182  }
   183  
   184  // TestChainTips ensures the chain tip tracking in the block index works
   185  // as expected.
   186  func TestChainTips(t *testing.T) {
   187  	params := &chaincfg.RegNetParams
   188  	bc := newFakeChain(params)
   189  	genesis := bc.bestChain.NodeByHeight(0)
   190  
   191  	// Construct a synthetic simnet chain consisting of the following structure.
   192  	// 0 -> 1 -> 2  -> 3  -> 4
   193  	//  |    \-> 2a -> 3a -> 4a -> 5a -> 6a -> 7a -> ... -> 26a
   194  	//  |    |     \-> 3b -> 4b -> 5b
   195  	//  |    \-> 2c -> 3c -> 4c -> 5c -> 6c -> 7c -> ... -> 26c
   196  	//  \-> 1d
   197  	//  \-> 1e
   198  	branches := make([][]*blockNode, 6)
   199  	branches[0] = chainedFakeNodes(genesis, 4)
   200  	branches[1] = chainedFakeNodes(branches[0][0], 25)
   201  	branches[2] = chainedFakeNodes(branches[1][0], 3)
   202  	branches[3] = chainedFakeNodes(branches[0][0], 25)
   203  	branches[4] = chainedFakeNodes(genesis, 1)
   204  	branches[5] = chainedFakeNodes(genesis, 1)
   205  
   206  	// Add all of the nodes to the index.
   207  	for _, branch := range branches {
   208  		for _, node := range branch {
   209  			bc.index.AddNode(node)
   210  		}
   211  	}
   212  
   213  	// Create a map of all of the chain tips the block index believes exist.
   214  	chainTips := make(map[*blockNode]struct{})
   215  	bc.index.RLock()
   216  	for _, nodes := range bc.index.chainTips {
   217  		for _, node := range nodes {
   218  			chainTips[node] = struct{}{}
   219  		}
   220  	}
   221  	bc.index.RUnlock()
   222  
   223  	// The expected chain tips are the tips of all of the branches.
   224  	expectedTips := make(map[*blockNode]struct{})
   225  	for _, branch := range branches {
   226  		expectedTips[branchTip(branch)] = struct{}{}
   227  	}
   228  
   229  	// Ensure the chain tips are the expected values.
   230  	if len(chainTips) != len(expectedTips) {
   231  		t.Fatalf("block index reports %d chain tips, but %d were expected",
   232  			len(chainTips), len(expectedTips))
   233  	}
   234  	for node := range expectedTips {
   235  		if _, ok := chainTips[node]; !ok {
   236  			t.Fatalf("block index does not contain expected tip %s (height %d)",
   237  				node.hash, node.height)
   238  		}
   239  	}
   240  }