// chainSetup is used to create a new db and chain instance with the genesis // block already inserted. In addition to the new chain instnce, it returns // a teardown function the caller should invoke when done testing to clean up. func chainSetup(dbName string) (*blockchain.BlockChain, func(), error) { if !isSupportedDbType(testDbType) { return nil, nil, fmt.Errorf("unsupported db type %v", testDbType) } // Handle memory database specially since it doesn't need the disk // specific handling. var db database.Db var teardown func() if testDbType == "memdb" { ndb, err := database.CreateDB(testDbType) if err != nil { return nil, nil, fmt.Errorf("error creating db: %v", err) } db = ndb // Setup a teardown function for cleaning up. This function is // returned to the caller to be invoked when it is done testing. teardown = func() { db.Close() } } else { // Create the root directory for test databases. if !fileExists(testDbRoot) { if err := os.MkdirAll(testDbRoot, 0700); err != nil { err := fmt.Errorf("unable to create test db "+ "root: %v", err) return nil, nil, err } } // Create a new database to store the accepted blocks into. dbPath := filepath.Join(testDbRoot, dbName) _ = os.RemoveAll(dbPath) ndb, err := database.CreateDB(testDbType, dbPath) if err != nil { return nil, nil, fmt.Errorf("error creating db: %v", err) } db = ndb // Setup a teardown function for cleaning up. This function is // returned to the caller to be invoked when it is done testing. teardown = func() { dbVersionPath := filepath.Join(testDbRoot, dbName+".ver") db.Sync() db.Close() os.RemoveAll(dbPath) os.Remove(dbVersionPath) os.RemoveAll(testDbRoot) } } // Insert the main network genesis block. This is part of the initial // database setup. genesisBlock := btcutil.NewBlock(chaincfg.MainNetParams.GenesisBlock) _, err := db.InsertBlock(genesisBlock) if err != nil { teardown() err := fmt.Errorf("failed to insert genesis block: %v", err) return nil, nil, err } chain := blockchain.New(db, &chaincfg.MainNetParams, nil, nil) return chain, teardown, nil }
// testAddrIndexOperations ensures that all normal operations concerning // the optional address index function correctly. func testAddrIndexOperations(t *testing.T, db database.Db, newestBlock *btcutil.Block, newestSha *wire.ShaHash, newestBlockIdx int32) { // Metadata about the current addr index state should be unset. sha, height, err := db.FetchAddrIndexTip() if err != database.ErrAddrIndexDoesNotExist { t.Fatalf("Address index metadata shouldn't be in db, hasn't been built up yet.") } var zeroHash wire.ShaHash if !sha.IsEqual(&zeroHash) { t.Fatalf("AddrIndexTip wrong hash got: %s, want %s", sha, &zeroHash) } if height != -1 { t.Fatalf("Addrindex not built up, yet a block index tip has been set to: %d.", height) } // Test enforcement of constraints for "limit" and "skip" var fakeAddr btcutil.Address _, _, err = db.FetchTxsForAddr(fakeAddr, -1, 0, false) if err == nil { t.Fatalf("Negative value for skip passed, should return an error") } _, _, err = db.FetchTxsForAddr(fakeAddr, 0, -1, false) if err == nil { t.Fatalf("Negative value for limit passed, should return an error") } // Simple test to index outputs(s) of the first tx. testIndex := make(database.BlockAddrIndex) testTx, err := newestBlock.Tx(0) if err != nil { t.Fatalf("Block has no transactions, unable to test addr "+ "indexing, err %v", err) } // Extract the dest addr from the tx. _, testAddrs, _, err := txscript.ExtractPkScriptAddrs(testTx.MsgTx().TxOut[0].PkScript, &chaincfg.MainNetParams) if err != nil { t.Fatalf("Unable to decode tx output, err %v", err) } // Extract the hash160 from the output script. var hash160Bytes [ripemd160.Size]byte testHash160 := testAddrs[0].(*btcutil.AddressPubKey).AddressPubKeyHash().ScriptAddress() copy(hash160Bytes[:], testHash160[:]) // Create a fake index. blktxLoc, _ := newestBlock.TxLoc() testIndex[hash160Bytes] = []*wire.TxLoc{&blktxLoc[0]} // Insert our test addr index into the DB. err = db.UpdateAddrIndexForBlock(newestSha, newestBlockIdx, testIndex) if err != nil { t.Fatalf("UpdateAddrIndexForBlock: failed to index"+ " addrs for block #%d (%s) "+ "err %v", newestBlockIdx, newestSha, err) } // Chain Tip of address should've been updated. assertAddrIndexTipIsUpdated(db, t, newestSha, newestBlockIdx) // Check index retrieval. txReplies, _, err := db.FetchTxsForAddr(testAddrs[0], 0, 1000, false) if err != nil { t.Fatalf("FetchTxsForAddr failed to correctly fetch txs for an "+ "address, err %v", err) } // Should have one reply. if len(txReplies) != 1 { t.Fatalf("Failed to properly index tx by address.") } // Our test tx and indexed tx should have the same sha. indexedTx := txReplies[0] if !bytes.Equal(indexedTx.Sha.Bytes(), testTx.Sha().Bytes()) { t.Fatalf("Failed to fetch proper indexed tx. Expected sha %v, "+ "fetched %v", testTx.Sha(), indexedTx.Sha) } // Shut down DB. db.Sync() db.Close() // Re-Open, tip still should be updated to current height and sha. db, err = database.OpenDB("leveldb", "tstdbopmode") if err != nil { t.Fatalf("Unable to re-open created db, err %v", err) } assertAddrIndexTipIsUpdated(db, t, newestSha, newestBlockIdx) // Delete the entire index. err = db.DeleteAddrIndex() if err != nil { t.Fatalf("Couldn't delete address index, err %v", err) } // Former index should no longer exist. txReplies, _, err = db.FetchTxsForAddr(testAddrs[0], 0, 1000, false) if err != nil { t.Fatalf("Unable to fetch transactions for address: %v", err) } if len(txReplies) != 0 { t.Fatalf("Address index was not successfully deleted. "+ "Should have 0 tx's indexed, %v were returned.", len(txReplies)) } // Tip should be blanked out. if _, _, err := db.FetchAddrIndexTip(); err != database.ErrAddrIndexDoesNotExist { t.Fatalf("Address index was not fully deleted.") } }