github.com/btcsuite/btcd@v0.24.0/blockchain/common_test.go (about) 1 // Copyright (c) 2013-2017 The btcsuite 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 "compress/bzip2" 9 "encoding/binary" 10 "fmt" 11 "io" 12 "os" 13 "path/filepath" 14 "strings" 15 "time" 16 17 "github.com/btcsuite/btcd/btcutil" 18 "github.com/btcsuite/btcd/chaincfg" 19 "github.com/btcsuite/btcd/chaincfg/chainhash" 20 "github.com/btcsuite/btcd/database" 21 _ "github.com/btcsuite/btcd/database/ffldb" 22 "github.com/btcsuite/btcd/txscript" 23 "github.com/btcsuite/btcd/wire" 24 ) 25 26 const ( 27 // testDbType is the database backend type to use for the tests. 28 testDbType = "ffldb" 29 30 // testDbRoot is the root directory used to create all test databases. 31 testDbRoot = "testdbs" 32 33 // blockDataNet is the expected network in the test block data. 34 blockDataNet = wire.MainNet 35 ) 36 37 // filesExists returns whether or not the named file or directory exists. 38 func fileExists(name string) bool { 39 if _, err := os.Stat(name); err != nil { 40 if os.IsNotExist(err) { 41 return false 42 } 43 } 44 return true 45 } 46 47 // isSupportedDbType returns whether or not the passed database type is 48 // currently supported. 49 func isSupportedDbType(dbType string) bool { 50 supportedDrivers := database.SupportedDrivers() 51 for _, driver := range supportedDrivers { 52 if dbType == driver { 53 return true 54 } 55 } 56 57 return false 58 } 59 60 // loadBlocks reads files containing bitcoin block data (gzipped but otherwise 61 // in the format bitcoind writes) from disk and returns them as an array of 62 // btcutil.Block. This is largely borrowed from the test code in btcdb. 63 func loadBlocks(filename string) (blocks []*btcutil.Block, err error) { 64 filename = filepath.Join("testdata/", filename) 65 66 var network = wire.MainNet 67 var dr io.Reader 68 var fi io.ReadCloser 69 70 fi, err = os.Open(filename) 71 if err != nil { 72 return 73 } 74 75 if strings.HasSuffix(filename, ".bz2") { 76 dr = bzip2.NewReader(fi) 77 } else { 78 dr = fi 79 } 80 defer fi.Close() 81 82 var block *btcutil.Block 83 84 err = nil 85 for height := int64(1); err == nil; height++ { 86 var rintbuf uint32 87 err = binary.Read(dr, binary.LittleEndian, &rintbuf) 88 if err == io.EOF { 89 // hit end of file at expected offset: no warning 90 height-- 91 err = nil 92 break 93 } 94 if err != nil { 95 break 96 } 97 if rintbuf != uint32(network) { 98 break 99 } 100 err = binary.Read(dr, binary.LittleEndian, &rintbuf) 101 blocklen := rintbuf 102 103 rbytes := make([]byte, blocklen) 104 105 // read block 106 dr.Read(rbytes) 107 108 block, err = btcutil.NewBlockFromBytes(rbytes) 109 if err != nil { 110 return 111 } 112 blocks = append(blocks, block) 113 } 114 115 return 116 } 117 118 // chainSetup is used to create a new db and chain instance with the genesis 119 // block already inserted. In addition to the new chain instance, it returns 120 // a teardown function the caller should invoke when done testing to clean up. 121 func chainSetup(dbName string, params *chaincfg.Params) (*BlockChain, func(), error) { 122 if !isSupportedDbType(testDbType) { 123 return nil, nil, fmt.Errorf("unsupported db type %v", testDbType) 124 } 125 126 // Handle memory database specially since it doesn't need the disk 127 // specific handling. 128 var db database.DB 129 var teardown func() 130 if testDbType == "memdb" { 131 ndb, err := database.Create(testDbType) 132 if err != nil { 133 return nil, nil, fmt.Errorf("error creating db: %v", err) 134 } 135 db = ndb 136 137 // Setup a teardown function for cleaning up. This function is 138 // returned to the caller to be invoked when it is done testing. 139 teardown = func() { 140 db.Close() 141 } 142 } else { 143 // Create the root directory for test databases. 144 if !fileExists(testDbRoot) { 145 if err := os.MkdirAll(testDbRoot, 0700); err != nil { 146 err := fmt.Errorf("unable to create test db "+ 147 "root: %v", err) 148 return nil, nil, err 149 } 150 } 151 152 // Create a new database to store the accepted blocks into. 153 dbPath := filepath.Join(testDbRoot, dbName) 154 _ = os.RemoveAll(dbPath) 155 ndb, err := database.Create(testDbType, dbPath, blockDataNet) 156 if err != nil { 157 return nil, nil, fmt.Errorf("error creating db: %v", err) 158 } 159 db = ndb 160 161 // Setup a teardown function for cleaning up. This function is 162 // returned to the caller to be invoked when it is done testing. 163 teardown = func() { 164 db.Close() 165 os.RemoveAll(dbPath) 166 os.RemoveAll(testDbRoot) 167 } 168 } 169 170 // Copy the chain params to ensure any modifications the tests do to 171 // the chain parameters do not affect the global instance. 172 paramsCopy := *params 173 174 // Create the main chain instance. 175 chain, err := New(&Config{ 176 DB: db, 177 ChainParams: ¶msCopy, 178 Checkpoints: nil, 179 TimeSource: NewMedianTime(), 180 SigCache: txscript.NewSigCache(1000), 181 }) 182 if err != nil { 183 teardown() 184 err := fmt.Errorf("failed to create chain instance: %v", err) 185 return nil, nil, err 186 } 187 return chain, teardown, nil 188 } 189 190 // loadUtxoView returns a utxo view loaded from a file. 191 func loadUtxoView(filename string) (*UtxoViewpoint, error) { 192 // The utxostore file format is: 193 // <tx hash><output index><serialized utxo len><serialized utxo> 194 // 195 // The output index and serialized utxo len are little endian uint32s 196 // and the serialized utxo uses the format described in chainio.go. 197 198 filename = filepath.Join("testdata", filename) 199 fi, err := os.Open(filename) 200 if err != nil { 201 return nil, err 202 } 203 204 // Choose read based on whether the file is compressed or not. 205 var r io.Reader 206 if strings.HasSuffix(filename, ".bz2") { 207 r = bzip2.NewReader(fi) 208 } else { 209 r = fi 210 } 211 defer fi.Close() 212 213 view := NewUtxoViewpoint() 214 for { 215 // Hash of the utxo entry. 216 var hash chainhash.Hash 217 _, err := io.ReadAtLeast(r, hash[:], len(hash[:])) 218 if err != nil { 219 // Expected EOF at the right offset. 220 if err == io.EOF { 221 break 222 } 223 return nil, err 224 } 225 226 // Output index of the utxo entry. 227 var index uint32 228 err = binary.Read(r, binary.LittleEndian, &index) 229 if err != nil { 230 return nil, err 231 } 232 233 // Num of serialized utxo entry bytes. 234 var numBytes uint32 235 err = binary.Read(r, binary.LittleEndian, &numBytes) 236 if err != nil { 237 return nil, err 238 } 239 240 // Serialized utxo entry. 241 serialized := make([]byte, numBytes) 242 _, err = io.ReadAtLeast(r, serialized, int(numBytes)) 243 if err != nil { 244 return nil, err 245 } 246 247 // Deserialize it and add it to the view. 248 entry, err := deserializeUtxoEntry(serialized) 249 if err != nil { 250 return nil, err 251 } 252 view.Entries()[wire.OutPoint{Hash: hash, Index: index}] = entry 253 } 254 255 return view, nil 256 } 257 258 // convertUtxoStore reads a utxostore from the legacy format and writes it back 259 // out using the latest format. It is only useful for converting utxostore data 260 // used in the tests, which has already been done. However, the code is left 261 // available for future reference. 262 func convertUtxoStore(r io.Reader, w io.Writer) error { 263 // The old utxostore file format was: 264 // <tx hash><serialized utxo len><serialized utxo> 265 // 266 // The serialized utxo len was a little endian uint32 and the serialized 267 // utxo uses the format described in upgrade.go. 268 269 littleEndian := binary.LittleEndian 270 for { 271 // Hash of the utxo entry. 272 var hash chainhash.Hash 273 _, err := io.ReadAtLeast(r, hash[:], len(hash[:])) 274 if err != nil { 275 // Expected EOF at the right offset. 276 if err == io.EOF { 277 break 278 } 279 return err 280 } 281 282 // Num of serialized utxo entry bytes. 283 var numBytes uint32 284 err = binary.Read(r, littleEndian, &numBytes) 285 if err != nil { 286 return err 287 } 288 289 // Serialized utxo entry. 290 serialized := make([]byte, numBytes) 291 _, err = io.ReadAtLeast(r, serialized, int(numBytes)) 292 if err != nil { 293 return err 294 } 295 296 // Deserialize the entry. 297 entries, err := deserializeUtxoEntryV0(serialized) 298 if err != nil { 299 return err 300 } 301 302 // Loop through all of the utxos and write them out in the new 303 // format. 304 for outputIdx, entry := range entries { 305 // Reserialize the entries using the new format. 306 serialized, err := serializeUtxoEntry(entry) 307 if err != nil { 308 return err 309 } 310 311 // Write the hash of the utxo entry. 312 _, err = w.Write(hash[:]) 313 if err != nil { 314 return err 315 } 316 317 // Write the output index of the utxo entry. 318 err = binary.Write(w, littleEndian, outputIdx) 319 if err != nil { 320 return err 321 } 322 323 // Write num of serialized utxo entry bytes. 324 err = binary.Write(w, littleEndian, uint32(len(serialized))) 325 if err != nil { 326 return err 327 } 328 329 // Write the serialized utxo. 330 _, err = w.Write(serialized) 331 if err != nil { 332 return err 333 } 334 } 335 } 336 337 return nil 338 } 339 340 // TstSetCoinbaseMaturity makes the ability to set the coinbase maturity 341 // available when running tests. 342 func (b *BlockChain) TstSetCoinbaseMaturity(maturity uint16) { 343 b.chainParams.CoinbaseMaturity = maturity 344 } 345 346 // newFakeChain returns a chain that is usable for syntetic tests. It is 347 // important to note that this chain has no database associated with it, so 348 // it is not usable with all functions and the tests must take care when making 349 // use of it. 350 func newFakeChain(params *chaincfg.Params) *BlockChain { 351 // Create a genesis block node and block index index populated with it 352 // for use when creating the fake chain below. 353 node := newBlockNode(¶ms.GenesisBlock.Header, nil) 354 index := newBlockIndex(nil, params) 355 index.AddNode(node) 356 357 targetTimespan := int64(params.TargetTimespan / time.Second) 358 targetTimePerBlock := int64(params.TargetTimePerBlock / time.Second) 359 adjustmentFactor := params.RetargetAdjustmentFactor 360 b := &BlockChain{ 361 chainParams: params, 362 timeSource: NewMedianTime(), 363 minRetargetTimespan: targetTimespan / adjustmentFactor, 364 maxRetargetTimespan: targetTimespan * adjustmentFactor, 365 blocksPerRetarget: int32(targetTimespan / targetTimePerBlock), 366 index: index, 367 bestChain: newChainView(node), 368 warningCaches: newThresholdCaches(vbNumBits), 369 deploymentCaches: newThresholdCaches(chaincfg.DefinedDeployments), 370 } 371 372 for _, deployment := range params.Deployments { 373 deploymentStarter := deployment.DeploymentStarter 374 if clockStarter, ok := deploymentStarter.(chaincfg.ClockConsensusDeploymentStarter); ok { 375 clockStarter.SynchronizeClock(b) 376 } 377 378 deploymentEnder := deployment.DeploymentEnder 379 if clockEnder, ok := deploymentEnder.(chaincfg.ClockConsensusDeploymentEnder); ok { 380 clockEnder.SynchronizeClock(b) 381 } 382 } 383 384 return b 385 } 386 387 // newFakeNode creates a block node connected to the passed parent with the 388 // provided fields populated and fake values for the other fields. 389 func newFakeNode(parent *blockNode, blockVersion int32, bits uint32, timestamp time.Time) *blockNode { 390 // Make up a header and create a block node from it. 391 header := &wire.BlockHeader{ 392 Version: blockVersion, 393 PrevBlock: parent.hash, 394 Bits: bits, 395 Timestamp: timestamp, 396 } 397 return newBlockNode(header, parent) 398 }