From 49e3b3e5b9bbf1ce356ef68f301d50c689ceecb9 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Thu, 19 Dec 2019 13:48:37 +0100 Subject: prepare for schema migrations --- src/db.ts | 37 +++++++++++++++++++++++++------ src/headless/helpers.ts | 16 ++------------ src/types/dbTypes.ts | 35 +++++++++++++++++++++++++++++ src/types/schemacore.ts | 58 +++++++++++++++++++++++++++++++++++++++++++++++++ src/util/query.ts | 26 ++-------------------- src/webex/wxBackend.ts | 11 ---------- tsconfig.json | 1 + 7 files changed, 128 insertions(+), 56 deletions(-) create mode 100644 src/types/schemacore.ts diff --git a/src/db.ts b/src/db.ts index e1c5c8f85..da43cf827 100644 --- a/src/db.ts +++ b/src/db.ts @@ -1,7 +1,7 @@ import { Stores } from "./types/dbTypes"; -import { openDatabase, Database } from "./util/query"; +import { openDatabase, Database, Store, Index } from "./util/query"; -const TALER_DB_NAME = "taler"; +const TALER_DB_NAME = "taler-wallet"; /** * Current database version, should be incremented @@ -9,7 +9,7 @@ const TALER_DB_NAME = "taler"; * In the future we might consider adding migration functions for * each version increment. */ -export const WALLET_DB_VERSION = 28; +export const WALLET_DB_VERSION = 1; /** * Return a promise that resolves @@ -18,18 +18,41 @@ export const WALLET_DB_VERSION = 28; export function openTalerDatabase( idbFactory: IDBFactory, onVersionChange: () => void, - onUpgradeUnsupported: (oldVersion: number, newVersion: number) => void, ): Promise { + const onUpgradeNeeded = ( + db: IDBDatabase, + oldVersion: number, + newVersion: number, + ) => { + switch (oldVersion) { + case 0: // DB does not exist yet + for (const n in Stores) { + if ((Stores as any)[n] instanceof Store) { + const si: Store = (Stores as any)[n]; + const s = db.createObjectStore(si.name, si.storeParams); + for (const indexName in si as any) { + if ((si as any)[indexName] instanceof Index) { + const ii: Index = (si as any)[indexName]; + s.createIndex(ii.indexName, ii.keyPath, ii.options); + } + } + } + } + break; + default: + throw Error("unsupported existig DB version"); + } + }; + return openDatabase( idbFactory, TALER_DB_NAME, WALLET_DB_VERSION, - Stores, onVersionChange, - onUpgradeUnsupported, + onUpgradeNeeded, ); } export function deleteTalerDatabase(idbFactory: IDBFactory) { Database.deleteDatabase(idbFactory, TALER_DB_NAME); -} \ No newline at end of file +} diff --git a/src/headless/helpers.ts b/src/headless/helpers.ts index e65914a01..6832cd4f4 100644 --- a/src/headless/helpers.ts +++ b/src/headless/helpers.ts @@ -25,9 +25,7 @@ import { Wallet } from "../wallet"; import { MemoryBackend, BridgeIDBFactory, shimIndexedDB } from "idb-bridge"; import { openTalerDatabase } from "../db"; -import { - HttpRequestLibrary, -} from "../util/http"; +import { HttpRequestLibrary } from "../util/http"; import * as amounts from "../util/amounts"; import { Bank } from "./bank"; import fs = require("fs"); @@ -39,7 +37,6 @@ import { Logger } from "../util/logging"; const logger = new Logger("helpers.ts"); - export interface DefaultNodeWalletArgs { /** * Location of the wallet database. @@ -112,18 +109,9 @@ export async function getDefaultNodeWallet( throw Error(); }; - const myUnsupportedUpgrade = () => { - console.error("unsupported database migration"); - throw Error(); - }; - shimIndexedDB(myBridgeIdbFactory); - const myDb = await openTalerDatabase( - myIdbFactory, - myVersionChange, - myUnsupportedUpgrade, - ); + const myDb = await openTalerDatabase(myIdbFactory, myVersionChange); //const worker = new SynchronousCryptoWorkerFactory(); //const worker = new NodeCryptoWorkerFactory(); diff --git a/src/types/dbTypes.ts b/src/types/dbTypes.ts index a289eeb44..f8f9880dd 100644 --- a/src/types/dbTypes.ts +++ b/src/types/dbTypes.ts @@ -1366,6 +1366,34 @@ export interface BankWithdrawUriRecord { reservePub: string; } +export const enum ImportPayloadType { + CoreSchema = "core-schema", +} + +/** + * Record to keep track of data imported into the wallet. + */ +export class WalletImportRecord { + /** + * Unique ID to reference this import record. + */ + walletImportId: string; + + /** + * When was the data imported? + */ + timestampImportStarted: Timestamp; + + timestampImportFinished: Timestamp | undefined; + + payloadType: ImportPayloadType; + + /** + * The actual data to import. + */ + payload: any; +} + /* tslint:disable:completed-docs */ /** @@ -1517,6 +1545,12 @@ export namespace Stores { } } + class WalletImportsStore extends Store { + constructor() { + super("walletImports", { keyPath: "walletImportId" }); + } + } + export const coins = new CoinsStore(); export const coinsReturns = new Store("coinsReturns", { keyPath: "contractTermsHash", @@ -1539,6 +1573,7 @@ export namespace Stores { export const payEvents = new PayEventsStore(); export const reserveUpdatedEvents = new ReserveUpdatedEventsStore(); export const exchangeUpdatedEvents = new ExchangeUpdatedEventsStore(); + export const walletImports = new WalletImportsStore(); } /* tslint:enable:completed-docs */ diff --git a/src/types/schemacore.ts b/src/types/schemacore.ts new file mode 100644 index 000000000..c709e3d7a --- /dev/null +++ b/src/types/schemacore.ts @@ -0,0 +1,58 @@ +/* + This file is part of GNU Taler + (C) 2019 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +/** + * Core of the wallet's schema, used for painless export, import + * and schema migration. + * + * If this schema is extended, it must be extended in a completely + * backwards-compatible way. + */ + +interface CoreCoin { + exchangeBaseUrl: string; + coinPub: string; + coinPriv: string; + amountRemaining: string; +} + +interface CorePurchase { + noncePub: string; + noncePriv: string; + paySig: string; + contractTerms: any; +} + +interface CoreReserve { + reservePub: string; + reservePriv: string; + exchangeBaseUrl: string; +} + +interface SchemaCore { + coins: CoreCoin[]; + purchases: CorePurchase[]; + + /** + * Schema version (of full schema) of wallet that exported the core schema. + */ + versionExporter: number; + + /** + * Schema version of the database that has been exported to the core schema + */ + versionSourceDatabase: number; +} \ No newline at end of file diff --git a/src/util/query.ts b/src/util/query.ts index 217c0674e..95ef30e1b 100644 --- a/src/util/query.ts +++ b/src/util/query.ts @@ -383,9 +383,8 @@ export function openDatabase( idbFactory: IDBFactory, databaseName: string, databaseVersion: number, - schema: any, onVersionChange: () => void, - onUpgradeUnsupported: (oldVersion: number, newVersion: number) => void, + onUpgradeNeeded: (db: IDBDatabase, oldVersion: number, newVersion: number) => void, ): Promise { return new Promise((resolve, reject) => { const req = idbFactory.open(databaseName, databaseVersion); @@ -405,31 +404,10 @@ export function openDatabase( }; req.onupgradeneeded = e => { const db = req.result; + onUpgradeNeeded(db, e.oldVersion, e.newVersion!); console.log( `DB: upgrade needed: oldVersion=${e.oldVersion}, newVersion=${e.newVersion}`, ); - switch (e.oldVersion) { - case 0: // DB does not exist yet - for (const n in schema) { - if (schema[n] instanceof Store) { - const si: Store = schema[n]; - const s = db.createObjectStore(si.name, si.storeParams); - for (const indexName in si as any) { - if ((si as any)[indexName] instanceof Index) { - const ii: Index = (si as any)[indexName]; - s.createIndex(ii.indexName, ii.keyPath, ii.options); - } - } - } - } - break; - default: - if (e.oldVersion !== databaseVersion) { - onUpgradeUnsupported(e.oldVersion, databaseVersion); - throw Error("incompatible DB"); - } - break; - } }; }); } diff --git a/src/webex/wxBackend.ts b/src/webex/wxBackend.ts index d5a08b5d7..97774a5c2 100644 --- a/src/webex/wxBackend.ts +++ b/src/webex/wxBackend.ts @@ -390,16 +390,6 @@ let outdatedDbVersion: number | undefined; let walletInit: OpenedPromise = openPromise(); -function handleUpgradeUnsupported(oldDbVersion: number, newDbVersion: number) { - console.log("DB migration not supported"); - outdatedDbVersion = oldDbVersion; - chrome.tabs.create({ - url: chrome.extension.getURL("/src/webex/pages/reset-required.html"), - }); - setBadgeText({ text: "err" }); - chrome.browserAction.setBadgeBackgroundColor({ color: "#F00" }); -} - async function reinitWallet() { if (currentWallet) { currentWallet.stop(); @@ -412,7 +402,6 @@ async function reinitWallet() { currentDatabase = await openTalerDatabase( indexedDB, reinitWallet, - handleUpgradeUnsupported, ); } catch (e) { console.error("could not open database", e); diff --git a/tsconfig.json b/tsconfig.json index 209721e7c..4156247a2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -67,6 +67,7 @@ "src/types/history.ts", "src/types/notifications.ts", "src/types/pending.ts", + "src/types/schemacore.ts", "src/types/talerTypes.ts", "src/types/types-test.ts", "src/types/walletTypes.ts", -- cgit v1.2.3