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 }