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