aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2023-08-30 16:51:51 +0200
committerFlorian Dold <florian@dold.me>2023-08-30 16:51:51 +0200
commitaba173d8a906fa0ede0c3660bd37b11fb7a6a127 (patch)
tree0957b732b6892236e5ef1fbc9e93cda56b6908e1
parent53613a137df432878d62317e685bd1737dc6a6dc (diff)
wallet-core: open DB inside wallet handle, back up meta DB as well
-rw-r--r--packages/taler-wallet-core/src/db.ts78
-rw-r--r--packages/taler-wallet-core/src/host-impl.node.ts11
-rw-r--r--packages/taler-wallet-core/src/host-impl.qtart.ts20
-rw-r--r--packages/taler-wallet-core/src/internal-wallet-state.ts4
-rw-r--r--packages/taler-wallet-core/src/util/query.ts2
-rw-r--r--packages/taler-wallet-core/src/wallet.ts50
-rw-r--r--packages/taler-wallet-webextension/src/wxBackend.ts9
7 files changed, 104 insertions, 70 deletions
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index ba5295dda..efc0333f4 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -104,7 +104,7 @@ import { RetryInfo, TaskIdentifiers } from "./operations/common.js";
* for all previous versions must be written, which should be
* avoided.
*/
-export const TALER_DB_NAME = "taler-wallet-main-v9";
+export const TALER_WALLET_MAIN_DB_NAME = "taler-wallet-main-v9";
/**
* Name of the metadata database. This database is used
@@ -112,7 +112,7 @@ export const TALER_DB_NAME = "taler-wallet-main-v9";
*
* (Minor migrations are handled via upgrade transactions.)
*/
-export const TALER_META_DB_NAME = "taler-wallet-meta";
+export const TALER_WALLET_META_DB_NAME = "taler-wallet-meta";
export const CURRENT_DB_CONFIG_KEY = "currentMainDbName";
@@ -2806,25 +2806,36 @@ export interface DbDump {
};
}
-export function exportDb(db: IDBDatabase): Promise<DbDump> {
- const dbDump: DbDump = {
- databases: {},
- };
+export async function exportSingleDb(
+ idb: IDBFactory,
+ dbName: string,
+): Promise<DbDumpDatabase> {
+ const myDb = await openDatabase(
+ idb,
+ dbName,
+ undefined,
+ () => {
+ // May not happen, since we're not requesting a specific version
+ throw Error("unexpected version change");
+ },
+ () => {
+ logger.info("unexpected onupgradeneeded");
+ },
+ );
- const walletDb: DbDumpDatabase = {
- version: db.version,
+ const singleDbDump: DbDumpDatabase = {
+ version: myDb.version,
stores: {},
};
- dbDump.databases[db.name] = walletDb;
return new Promise((resolve, reject) => {
- const tx = db.transaction(Array.from(db.objectStoreNames));
+ const tx = myDb.transaction(Array.from(myDb.objectStoreNames));
tx.addEventListener("complete", () => {
- resolve(dbDump);
+ resolve(singleDbDump);
});
// tslint:disable-next-line:prefer-for-of
- for (let i = 0; i < db.objectStoreNames.length; i++) {
- const name = db.objectStoreNames[i];
+ for (let i = 0; i < myDb.objectStoreNames.length; i++) {
+ const name = myDb.objectStoreNames[i];
const store = tx.objectStore(name);
const storeDump: DbStoreDump = {
autoIncrement: store.autoIncrement,
@@ -2842,7 +2853,7 @@ export function exportDb(db: IDBDatabase): Promise<DbDump> {
unique: index.unique,
};
}
- walletDb.stores[name] = storeDump;
+ singleDbDump.stores[name] = storeDump;
store.openCursor().addEventListener("success", (e: Event) => {
const cursor = (e.target as any).result;
if (cursor) {
@@ -2862,6 +2873,23 @@ export function exportDb(db: IDBDatabase): Promise<DbDump> {
});
}
+export async function exportDb(idb: IDBFactory): Promise<DbDump> {
+ const dbDump: DbDump = {
+ databases: {},
+ };
+
+ dbDump.databases[TALER_WALLET_META_DB_NAME] = await exportSingleDb(
+ idb,
+ TALER_WALLET_META_DB_NAME,
+ );
+ dbDump.databases[TALER_WALLET_MAIN_DB_NAME] = await exportSingleDb(
+ idb,
+ TALER_WALLET_MAIN_DB_NAME,
+ );
+
+ return dbDump;
+}
+
export interface DatabaseDump {
name: string;
stores: { [s: string]: any };
@@ -2902,13 +2930,13 @@ export async function importDb(db: IDBDatabase, object: any): Promise<void> {
// looks like a IDBDatabase
const someDatabase = object.databases;
- if (TALER_META_DB_NAME in someDatabase) {
+ if (TALER_WALLET_META_DB_NAME in someDatabase) {
//looks like a taler database
const currentMainDbValue =
- someDatabase[TALER_META_DB_NAME].objectStores.metaConfig.records[0]
- .value.value;
+ someDatabase[TALER_WALLET_META_DB_NAME].objectStores.metaConfig
+ .records[0].value.value;
- if (currentMainDbValue !== TALER_DB_NAME) {
+ if (currentMainDbValue !== TALER_WALLET_MAIN_DB_NAME) {
console.log("not the current database version");
}
@@ -3236,7 +3264,7 @@ export async function openTalerDatabase(
): Promise<DbAccess<typeof WalletStoresV1>> {
const metaDbHandle = await openDatabase(
idbFactory,
- TALER_META_DB_NAME,
+ TALER_WALLET_META_DB_NAME,
1,
() => {},
onMetaDbUpgradeNeeded,
@@ -3249,17 +3277,17 @@ export async function openTalerDatabase(
.runReadWrite(async (tx) => {
const dbVersionRecord = await tx.metaConfig.get(CURRENT_DB_CONFIG_KEY);
if (!dbVersionRecord) {
- currentMainVersion = TALER_DB_NAME;
+ currentMainVersion = TALER_WALLET_MAIN_DB_NAME;
await tx.metaConfig.put({
key: CURRENT_DB_CONFIG_KEY,
- value: TALER_DB_NAME,
+ value: TALER_WALLET_MAIN_DB_NAME,
});
} else {
currentMainVersion = dbVersionRecord.value;
}
});
- if (currentMainVersion !== TALER_DB_NAME) {
+ if (currentMainVersion !== TALER_WALLET_MAIN_DB_NAME) {
switch (currentMainVersion) {
case "taler-wallet-main-v2":
case "taler-wallet-main-v3":
@@ -3275,7 +3303,7 @@ export async function openTalerDatabase(
.runReadWrite(async (tx) => {
await tx.metaConfig.put({
key: CURRENT_DB_CONFIG_KEY,
- value: TALER_DB_NAME,
+ value: TALER_WALLET_MAIN_DB_NAME,
});
});
break;
@@ -3288,7 +3316,7 @@ export async function openTalerDatabase(
const mainDbHandle = await openDatabase(
idbFactory,
- TALER_DB_NAME,
+ TALER_WALLET_MAIN_DB_NAME,
WALLET_DB_MINOR_VERSION,
onVersionChange,
onTalerDbUpgradeNeeded,
@@ -3305,7 +3333,7 @@ export async function deleteTalerDatabase(
idbFactory: IDBFactory,
): Promise<void> {
return new Promise((resolve, reject) => {
- const req = idbFactory.deleteDatabase(TALER_DB_NAME);
+ const req = idbFactory.deleteDatabase(TALER_WALLET_MAIN_DB_NAME);
req.onerror = () => reject(req.error);
req.onsuccess = () => resolve();
});
diff --git a/packages/taler-wallet-core/src/host-impl.node.ts b/packages/taler-wallet-core/src/host-impl.node.ts
index 6a4f21d79..0b6539306 100644
--- a/packages/taler-wallet-core/src/host-impl.node.ts
+++ b/packages/taler-wallet-core/src/host-impl.node.ts
@@ -139,13 +139,6 @@ export async function createNativeWalletHost2(
});
}
- const myVersionChange = (): Promise<void> => {
- logger.error("version change requested, should not happen");
- throw Error(
- "BUG: wallet DB version change event can't happen with memory IDB",
- );
- };
-
let dbResp: MakeDbResult;
if (args.persistentStoragePath &&args.persistentStoragePath.endsWith(".json")) {
@@ -160,8 +153,6 @@ export async function createNativeWalletHost2(
shimIndexedDB(dbResp.idbFactory);
- const myDb = await openTalerDatabase(myIdbFactory, myVersionChange);
-
let workerFactory;
const cryptoWorkerType = args.cryptoWorkerType ?? "node-worker-thread";
if (cryptoWorkerType === "sync") {
@@ -189,7 +180,7 @@ export async function createNativeWalletHost2(
const timer = new SetTimeoutTimerAPI();
const w = await Wallet.create(
- myDb,
+ myIdbFactory,
myHttpLib,
timer,
workerFactory,
diff --git a/packages/taler-wallet-core/src/host-impl.qtart.ts b/packages/taler-wallet-core/src/host-impl.qtart.ts
index 720f5affb..81dbe0acd 100644
--- a/packages/taler-wallet-core/src/host-impl.qtart.ts
+++ b/packages/taler-wallet-core/src/host-impl.qtart.ts
@@ -110,7 +110,7 @@ async function makeSqliteDb(
return {
...myBackend.accessStats,
primitiveStatements: numStmt,
- }
+ };
},
idbFactory: myBridgeIdbFactory,
};
@@ -167,12 +167,15 @@ export async function createNativeWalletHost2(
let dbResp: MakeDbResult;
- if (args.persistentStoragePath && args.persistentStoragePath.endsWith(".json")) {
+ if (
+ args.persistentStoragePath &&
+ args.persistentStoragePath.endsWith(".json")
+ ) {
logger.info("using JSON file DB backend (slow!)");
dbResp = await makeFileDb(args);
} else {
logger.info("using sqlite3 DB backend (experimental!)");
- dbResp = await makeSqliteDb(args)
+ dbResp = await makeSqliteDb(args);
}
const myIdbFactory: IDBFactory = dbResp.idbFactory as any as IDBFactory;
@@ -189,22 +192,13 @@ export async function createNativeWalletHost2(
});
}
- const myVersionChange = (): Promise<void> => {
- logger.error("version change requested, should not happen");
- throw Error(
- "BUG: wallet DB version change event can't happen with memory IDB",
- );
- };
-
- const myDb = await openTalerDatabase(myIdbFactory, myVersionChange);
-
let workerFactory;
workerFactory = new SynchronousCryptoWorkerFactoryPlain();
const timer = new SetTimeoutTimerAPI();
const w = await Wallet.create(
- myDb,
+ myIdbFactory,
myHttpLib,
timer,
workerFactory,
diff --git a/packages/taler-wallet-core/src/internal-wallet-state.ts b/packages/taler-wallet-core/src/internal-wallet-state.ts
index 76aee05bd..a189c9cb3 100644
--- a/packages/taler-wallet-core/src/internal-wallet-state.ts
+++ b/packages/taler-wallet-core/src/internal-wallet-state.ts
@@ -54,6 +54,7 @@ import {
} from "./util/query.js";
import { TimerGroup } from "./util/timer.js";
import { WalletConfig } from "./wallet-api-types.js";
+import { IDBFactory } from "@gnu-taler/idb-bridge";
export const EXCHANGE_COINS_LOCK = "exchange-coins-lock";
export const EXCHANGE_RESERVES_LOCK = "exchange-reserves-lock";
@@ -203,6 +204,9 @@ export interface InternalWalletState {
denomPubHash: string,
): Promise<DenominationInfo | undefined>;
+ ensureWalletDbOpen(): Promise<void>;
+
+ idb: IDBFactory;
db: DbAccess<typeof WalletStoresV1>;
http: HttpRequestLibrary;
diff --git a/packages/taler-wallet-core/src/util/query.ts b/packages/taler-wallet-core/src/util/query.ts
index 71f80f8aa..eb2bddec1 100644
--- a/packages/taler-wallet-core/src/util/query.ts
+++ b/packages/taler-wallet-core/src/util/query.ts
@@ -239,7 +239,7 @@ class ResultStream<T> {
export function openDatabase(
idbFactory: IDBFactory,
databaseName: string,
- databaseVersion: number,
+ databaseVersion: number | undefined,
onVersionChange: () => void,
onUpgradeNeeded: (
db: IDBDatabase,
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
index c9ccda20d..9f754ed69 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -139,6 +139,7 @@ import {
clearDatabase,
exportDb,
importDb,
+ openTalerDatabase,
} from "./db.js";
import { DevExperimentHttpLib, applyDevExperiment } from "./dev-experiments.js";
import {
@@ -315,6 +316,7 @@ import {
getMaxPeerPushAmount,
convertWithdrawalAmount,
} from "./util/instructedAmountConversion.js";
+import { IDBFactory } from "@gnu-taler/idb-bridge";
const logger = new Logger("wallet.ts");
@@ -1539,7 +1541,7 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>(
return {};
}
case WalletApiOperation.ExportDb: {
- const dbDump = await exportDb(ws.db.idbHandle());
+ const dbDump = await exportDb(ws.idb);
return dbDump;
}
case WalletApiOperation.ImportDb: {
@@ -1654,14 +1656,14 @@ export class Wallet {
private _client: WalletCoreApiClient | undefined;
private constructor(
- db: DbAccess<typeof WalletStoresV1>,
+ idb: IDBFactory,
http: HttpRequestLibrary,
timer: TimerAPI,
cryptoWorkerFactory: CryptoWorkerFactory,
config?: WalletConfigParameter,
) {
this.ws = new InternalWalletStateImpl(
- db,
+ idb,
http,
timer,
cryptoWorkerFactory,
@@ -1677,13 +1679,13 @@ export class Wallet {
}
static async create(
- db: DbAccess<typeof WalletStoresV1>,
+ idb: IDBFactory,
http: HttpRequestLibrary,
timer: TimerAPI,
cryptoWorkerFactory: CryptoWorkerFactory,
config?: WalletConfigParameter,
): Promise<Wallet> {
- const w = new Wallet(db, http, timer, cryptoWorkerFactory, config);
+ const w = new Wallet(idb, http, timer, cryptoWorkerFactory, config);
w._client = await getClientFromWalletState(w.ws);
return w;
}
@@ -1725,19 +1727,22 @@ export class Wallet {
this.ws.stop();
}
- runPending(): Promise<void> {
+ async runPending(): Promise<void> {
+ await this.ws.ensureWalletDbOpen();
return runPending(this.ws);
}
- runTaskLoop(opts?: RetryLoopOpts): Promise<TaskLoopResult> {
+ async runTaskLoop(opts?: RetryLoopOpts): Promise<TaskLoopResult> {
+ await this.ws.ensureWalletDbOpen();
return runTaskLoop(this.ws, opts);
}
- handleCoreApiRequest(
+ async handleCoreApiRequest(
operation: string,
id: string,
payload: unknown,
): Promise<CoreApiResponse> {
+ await this.ws.ensureWalletDbOpen();
return handleCoreApiRequest(this.ws, operation, id, payload);
}
}
@@ -1801,12 +1806,17 @@ class InternalWalletStateImpl implements InternalWalletState {
config: Readonly<WalletConfig>;
+ private _db: DbAccess<typeof WalletStoresV1> | undefined = undefined;
+
+ get db(): DbAccess<typeof WalletStoresV1> {
+ if (!this._db) {
+ throw Error("db not initialized");
+ }
+ return this._db;
+ }
+
constructor(
- // FIXME: Make this a getter and make
- // the actual value nullable.
- // Check if we are in a DB migration / garbage collection
- // and throw an error in that case.
- public db: DbAccess<typeof WalletStoresV1>,
+ public idb: IDBFactory,
public http: HttpRequestLibrary,
public timer: TimerAPI,
cryptoWorkerFactory: CryptoWorkerFactory,
@@ -1821,6 +1831,20 @@ class InternalWalletStateImpl implements InternalWalletState {
}
}
+ async ensureWalletDbOpen(): Promise<void> {
+ if (this._db) {
+ return;
+ }
+ const myVersionChange = (): Promise<void> => {
+ logger.error("version change requested, should not happen");
+ throw Error(
+ "BUG: wallet DB version change event can't happen with memory IDB",
+ );
+ };
+ const myDb = await openTalerDatabase(this.idb, myVersionChange);
+ this._db = myDb;
+ }
+
async getTransactionState(
ws: InternalWalletState,
tx: GetReadOnlyAccess<typeof WalletStoresV1>,
diff --git a/packages/taler-wallet-webextension/src/wxBackend.ts b/packages/taler-wallet-webextension/src/wxBackend.ts
index 95af1a3a4..f071d78df 100644
--- a/packages/taler-wallet-webextension/src/wxBackend.ts
+++ b/packages/taler-wallet-webextension/src/wxBackend.ts
@@ -298,13 +298,6 @@ async function reinitWallet(): Promise<void> {
}
currentDatabase = undefined;
// setBadgeText({ text: "" });
- try {
- currentDatabase = await openTalerDatabase(indexedDB as any, reinitWallet);
- } catch (e) {
- logger.error("could not open database", e);
- walletInit.reject(e);
- return;
- }
let httpLib;
let cryptoWorker;
let timer;
@@ -325,7 +318,7 @@ async function reinitWallet(): Promise<void> {
const settings = await platform.getSettingsFromStorage();
logger.info("Setting up wallet");
const wallet = await Wallet.create(
- currentDatabase,
+ indexedDB as any,
httpLib,
timer,
cryptoWorker,