// openDB is used to open an existing database based on the database type and // name. func openDB(dbType, dbName string) (database.Db, error) { // Handle memdb specially since it has no files on disk. if dbType == "memdb" { db, err := database.OpenDB(dbType) if err != nil { return nil, fmt.Errorf("error opening db: %v", err) } return db, nil } dbPath := filepath.Join(testDbRoot, dbName) db, err := database.OpenDB(dbType, dbPath) if err != nil { return nil, fmt.Errorf("error opening db: %v", err) } return db, nil }
// loadBlockDB opens the block database and returns a handle to it. func loadBlockDB() (database.Db, error) { // The database name is based on the database type. dbType := cfg.DbType dbName := blockDbNamePrefix + "_" + dbType if dbType == "sqlite" { dbName = dbName + ".db" } dbPath := filepath.Join(cfg.DataDir, dbName) fmt.Printf("Loading block database from '%s'\n", dbPath) db, err := database.OpenDB(dbType, dbPath) if err != nil { return nil, err } return db, nil }
func TestEmptyDB(t *testing.T) { dbname := "tstdbempty" dbnamever := dbname + ".ver" _ = os.RemoveAll(dbname) _ = os.RemoveAll(dbnamever) db, err := database.CreateDB("leveldb", dbname) if err != nil { t.Errorf("Failed to open test database %v", err) return } defer os.RemoveAll(dbname) defer os.RemoveAll(dbnamever) sha, height, err := db.NewestSha() if !sha.IsEqual(&wire.ShaHash{}) { t.Errorf("sha not zero hash") } if height != -1 { t.Errorf("height not -1 %v", height) } // This is a reopen test if err := db.Close(); err != nil { t.Errorf("Close: unexpected error: %v", err) } db, err = database.OpenDB("leveldb", dbname) if err != nil { t.Errorf("Failed to open test database %v", err) return } defer func() { if err := db.Close(); err != nil { t.Errorf("Close: unexpected error: %v", err) } }() sha, height, err = db.NewestSha() if !sha.IsEqual(&wire.ShaHash{}) { t.Errorf("sha not zero hash") } if height != -1 { t.Errorf("height not -1 %v", height) } }
// setupBlockDB loads (or creates when needed) the block database taking into // account the selected database backend. It also contains additional logic // such warning the user if there are multiple databases which consume space on // the file system and ensuring the regression test database is clean when in // regression test mode. func setupBlockDB() (database.Db, error) { // The memdb backend does not have a file path associated with it, so // handle it uniquely. We also don't want to worry about the multiple // database type warnings when running with the memory database. if cfg.DbType == "memdb" { btcdLog.Infof("Creating block database in memory.") db, err := database.CreateDB(cfg.DbType) if err != nil { return nil, err } return db, nil } warnMultipeDBs() // The database name is based on the database type. dbPath := blockDbPath(cfg.DbType) // The regression test is special in that it needs a clean database for // each run, so remove it now if it already exists. removeRegressionDB(dbPath) btcdLog.Infof("Loading block database from '%s'", dbPath) db, err := database.OpenDB(cfg.DbType, dbPath) if err != nil { // Return the error if it's not because the database // doesn't exist. if err != database.ErrDbDoesNotExist { return nil, err } // Create the db if it does not exist. err = os.MkdirAll(cfg.DataDir, 0700) if err != nil { return nil, err } db, err = database.CreateDB(cfg.DbType, dbPath) if err != nil { return nil, err } } return db, nil }
// TestCreateOpenUnsupported ensures that attempting to create or open an // unsupported database type is handled properly. func TestCreateOpenUnsupported(t *testing.T) { // Ensure creating a database with an unsupported type fails with the // expected error. dbType := "unsupported" _, err := database.CreateDB(dbType, "unsupportedcreatetest") if err != database.ErrDbUnknownType { t.Errorf("TestCreateOpenUnsupported: expected error not "+ "received - got: %v, want %v", err, database.ErrDbUnknownType) return } // Ensure opening a database with the new type fails with the expected // error. _, err = database.OpenDB(dbType, "unsupportedopentest") if err != database.ErrDbUnknownType { t.Errorf("TestCreateOpenUnsupported: expected error not "+ "received - got: %v, want %v", err, database.ErrDbUnknownType) return } }
// TestCreateOpenFail ensures that errors which occur while opening or closing // a database are handled properly. func TestCreateOpenFail(t *testing.T) { // bogusCreateDB is a function which acts as a bogus create and open // driver function that intentionally returns a failure which can be // detected. dbType := "createopenfail" openError := fmt.Errorf("failed to create or open database for "+ "database type [%v]", dbType) bogusCreateDB := func(args ...interface{}) (database.Db, error) { return nil, openError } // Create and add driver that intentionally fails when created or opened // to ensure errors on database open and create are handled properly. driver := database.DriverDB{ DbType: dbType, CreateDB: bogusCreateDB, OpenDB: bogusCreateDB, } database.AddDBDriver(driver) // Ensure creating a database with the new type fails with the expected // error. _, err := database.CreateDB(dbType, "createfailtest") if err != openError { t.Errorf("TestCreateOpenFail: expected error not received - "+ "got: %v, want %v", err, openError) return } // Ensure opening a database with the new type fails with the expected // error. _, err = database.OpenDB(dbType, "openfailtest") if err != openError { t.Errorf("TestCreateOpenFail: expected error not received - "+ "got: %v, want %v", err, openError) return } }
// loadBlockDB opens the block database and returns a handle to it. func loadBlockDB() (database.Db, error) { // The database name is based on the database type. dbName := blockDbNamePrefix + "_" + cfg.DbType if cfg.DbType == "sqlite" { dbName = dbName + ".db" } dbPath := filepath.Join(cfg.DataDir, dbName) log.Infof("Loading block database from '%s'", dbPath) db, err := database.OpenDB(cfg.DbType, dbPath) if err != nil { // Return the error if it's not because the database doesn't // exist. if err != database.ErrDbDoesNotExist { return nil, err } // Create the db if it does not exist. err = os.MkdirAll(cfg.DataDir, 0700) if err != nil { return nil, err } db, err = database.CreateDB(cfg.DbType, dbPath) if err != nil { return nil, err } } // Get the latest block height from the database. _, height, err := db.NewestSha() if err != nil { db.Close() return nil, err } log.Infof("Block database loaded with block height %d", height) return db, 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) if err == nil { t.Fatalf("Negative value for skip passed, should return an error") } _, err = db.FetchTxsForAddr(fakeAddr, 0, -1) 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) 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) 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.") } }
func testBackout(t *testing.T) { // simplified basic operation is: // 1) fetch block from remote server // 2) look up all txin (except coinbase in db) // 3) insert block testDb, err := setUpTestDb(t, "tstdbbackout") if err != nil { t.Errorf("Failed to open test database %v", err) return } defer testDb.cleanUpFunc() if len(testDb.blocks) < 120 { t.Errorf("test data too small") return } err = nil for height := int32(0); height < int32(len(testDb.blocks)); height++ { if height == 100 { t.Logf("Syncing at block height 100") testDb.db.Sync() } if height == 120 { t.Logf("Simulating unexpected application quit") // Simulate unexpected application quit testDb.db.RollbackClose() break } block := testDb.blocks[height] newheight, err := testDb.db.InsertBlock(block) if err != nil { t.Errorf("failed to insert block %v err %v", height, err) return } if newheight != height { t.Errorf("height mismatch expect %v returned %v", height, newheight) return } } // db was closed at height 120, so no cleanup is possible. // reopen db testDb.db, err = database.OpenDB("leveldb", testDb.dbName) if err != nil { t.Errorf("Failed to open test database %v", err) return } defer func() { if err := testDb.db.Close(); err != nil { t.Errorf("Close: unexpected error: %v", err) } }() sha := testDb.blocks[99].Sha() if _, err := testDb.db.ExistsSha(sha); err != nil { t.Errorf("ExistsSha: unexpected error: %v", err) } _, err = testDb.db.FetchBlockBySha(sha) if err != nil { t.Errorf("failed to load block 99 from db %v", err) return } sha = testDb.blocks[119].Sha() if _, err := testDb.db.ExistsSha(sha); err != nil { t.Errorf("ExistsSha: unexpected error: %v", err) } _, err = testDb.db.FetchBlockBySha(sha) if err != nil { t.Errorf("loaded block 119 from db") return } block := testDb.blocks[119] mblock := block.MsgBlock() txsha := mblock.Transactions[0].TxSha() exists, err := testDb.db.ExistsTxSha(&txsha) if err != nil { t.Errorf("ExistsTxSha: unexpected error %v ", err) } if !exists { t.Errorf("tx %v not located db\n", txsha) } _, err = testDb.db.FetchTxBySha(&txsha) if err != nil { t.Errorf("tx %v not located db\n", txsha) return } }
func main() { cfg := config{ DbType: "leveldb", DataDir: defaultDataDir, } parser := flags.NewParser(&cfg, flags.Default) _, err := parser.Parse() if err != nil { if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp { parser.WriteHelp(os.Stderr) } return } backendLogger := btclog.NewDefaultBackendLogger() defer backendLogger.Flush() log = btclog.NewSubsystemLogger(backendLogger, "") database.UseLogger(log) // Multiple networks can't be selected simultaneously. funcName := "main" numNets := 0 // Count number of network flags passed; assign active network params // while we're at it if cfg.TestNet3 { numNets++ activeNetParams = &chaincfg.TestNet3Params } if cfg.RegressionTest { numNets++ activeNetParams = &chaincfg.RegressionNetParams } if cfg.SimNet { numNets++ activeNetParams = &chaincfg.SimNetParams } if numNets > 1 { str := "%s: The testnet, regtest, and simnet params can't be " + "used together -- choose one of the three" err := fmt.Errorf(str, funcName) fmt.Fprintln(os.Stderr, err) parser.WriteHelp(os.Stderr) return } cfg.DataDir = filepath.Join(cfg.DataDir, netName(activeNetParams)) blockDbNamePrefix := "blocks" dbName := blockDbNamePrefix + "_" + cfg.DbType if cfg.DbType == "sqlite" { dbName = dbName + ".db" } dbPath := filepath.Join(cfg.DataDir, dbName) log.Infof("loading db") db, err := database.OpenDB(cfg.DbType, dbPath) if err != nil { log.Warnf("db open failed: %v", err) return } defer db.Close() log.Infof("db load complete") _, height, err := db.NewestSha() log.Infof("loaded block height %v", height) sha, err := getSha(db, cfg.ShaString) if err != nil { log.Infof("Invalid block hash %v", cfg.ShaString) return } err = db.DropAfterBlockBySha(&sha) if err != nil { log.Warnf("failed %v", err) } }