diff options
-rw-r--r-- | src/background/background.ts | 2 | ||||
-rw-r--r-- | src/cryptoApi.ts | 14 | ||||
-rw-r--r-- | src/cryptoLib.ts | 26 | ||||
-rw-r--r-- | src/db.ts | 117 | ||||
-rw-r--r-- | src/pages/confirm-contract.tsx | 14 | ||||
-rw-r--r-- | src/pages/tree.tsx | 20 | ||||
-rw-r--r-- | src/types.ts | 18 | ||||
-rw-r--r-- | src/wallet.ts | 146 | ||||
-rw-r--r-- | src/wxApi.ts | 14 | ||||
-rw-r--r-- | src/wxBackend.ts (renamed from src/wxMessaging.ts) | 100 | ||||
-rw-r--r-- | tsconfig.json | 9 |
11 files changed, 223 insertions, 257 deletions
diff --git a/src/background/background.ts b/src/background/background.ts index 57335e023..ba9be16e2 100644 --- a/src/background/background.ts +++ b/src/background/background.ts @@ -30,7 +30,7 @@ window.addEventListener("load", () => { defaultJSExtensions: true, }); - System.import("../wxMessaging") + System.import("../wxBackend") .then((wxMessaging: any) => { // Export as global for debugger (window as any).wxMessaging = wxMessaging; diff --git a/src/cryptoApi.ts b/src/cryptoApi.ts index 41f6c9593..1a17f6dca 100644 --- a/src/cryptoApi.ts +++ b/src/cryptoApi.ts @@ -21,12 +21,12 @@ */ -import {PreCoin, Coin, ReserveRecord, AmountJson} from "./types"; +import {PreCoinRecord, CoinRecord, ReserveRecord, AmountJson} from "./types"; import {Denomination} from "./types"; -import {Offer} from "./wallet"; +import {OfferRecord} from "./wallet"; import {CoinWithDenom} from "./wallet"; import {PayCoinInfo} from "./types"; -import {RefreshSession} from "./types"; +import {RefreshSessionRecord} from "./types"; interface WorkerState { @@ -214,7 +214,7 @@ export class CryptoApi { } - createPreCoin(denom: Denomination, reserve: ReserveRecord): Promise<PreCoin> { + createPreCoin(denom: Denomination, reserve: ReserveRecord): Promise<PreCoinRecord> { return this.doRpc("createPreCoin", 1, denom, reserve); } @@ -227,7 +227,7 @@ export class CryptoApi { return this.doRpc("isValidDenom", 2, denom, masterPub); } - signDeposit(offer: Offer, + signDeposit(offer: OfferRecord, cds: CoinWithDenom[]): Promise<PayCoinInfo> { return this.doRpc("signDeposit", 3, offer, cds); } @@ -242,9 +242,9 @@ export class CryptoApi { createRefreshSession(exchangeBaseUrl: string, kappa: number, - meltCoin: Coin, + meltCoin: CoinRecord, newCoinDenoms: Denomination[], - meltFee: AmountJson): Promise<RefreshSession> { + meltFee: AmountJson): Promise<RefreshSessionRecord> { return this.doRpc("createRefreshSession", 4, exchangeBaseUrl, diff --git a/src/cryptoLib.ts b/src/cryptoLib.ts index 1db686756..8dc5c0914 100644 --- a/src/cryptoLib.ts +++ b/src/cryptoLib.ts @@ -23,13 +23,13 @@ import * as native from "./emscriptif"; import { - PreCoin, PayCoinInfo, AmountJson, - RefreshSession, RefreshPreCoin, ReserveRecord + PreCoinRecord, PayCoinInfo, AmountJson, + RefreshSessionRecord, RefreshPreCoinRecord, ReserveRecord } from "./types"; import create = chrome.alarms.create; -import {Offer} from "./wallet"; +import {OfferRecord} from "./wallet"; import {CoinWithDenom} from "./wallet"; -import {CoinPaySig, Coin} from "./types"; +import {CoinPaySig, CoinRecord} from "./types"; import {Denomination, Amounts} from "./types"; import {Amount} from "./emscriptif"; import {HashContext} from "./emscriptif"; @@ -68,7 +68,7 @@ namespace RpcFunctions { * reserve. */ export function createPreCoin(denom: Denomination, - reserve: ReserveRecord): PreCoin { + reserve: ReserveRecord): PreCoinRecord { let reservePriv = new native.EddsaPrivateKey(); reservePriv.loadCrock(reserve.reserve_priv); let reservePub = new native.EddsaPublicKey(); @@ -105,7 +105,7 @@ namespace RpcFunctions { var sig = native.eddsaSign(withdrawRequest.toPurpose(), reservePriv); - let preCoin: PreCoin = { + let preCoin: PreCoinRecord = { reservePub: reservePub.toCrock(), blindingKey: blindingFactor.toCrock(), coinPub: coinPub.toCrock(), @@ -170,7 +170,7 @@ namespace RpcFunctions { * Generate updated coins (to store in the database) * and deposit permissions for each given coin. */ - export function signDeposit(offer: Offer, + export function signDeposit(offer: OfferRecord, cds: CoinWithDenom[]): PayCoinInfo { let ret: PayCoinInfo = []; let amountSpent = native.Amount.getZero(cds[0].coin.currentAmount.currency); @@ -228,9 +228,9 @@ namespace RpcFunctions { export function createRefreshSession(exchangeBaseUrl: string, kappa: number, - meltCoin: Coin, + meltCoin: CoinRecord, newCoinDenoms: Denomination[], - meltFee: AmountJson): RefreshSession { + meltFee: AmountJson): RefreshSessionRecord { let valueWithFee = Amounts.getZero(newCoinDenoms[0].value.currency); @@ -248,7 +248,7 @@ namespace RpcFunctions { let transferPubs: string[] = []; let transferPrivs: string[] = []; - let preCoinsForGammas: RefreshPreCoin[][] = []; + let preCoinsForGammas: RefreshPreCoinRecord[][] = []; for (let i = 0; i < kappa; i++) { let t = native.EcdhePrivateKey.create(); @@ -267,7 +267,7 @@ namespace RpcFunctions { sessionHc.read((new native.Amount(valueWithFee)).toNbo()); for (let i = 0; i < kappa; i++) { - let preCoins: RefreshPreCoin[] = []; + let preCoins: RefreshPreCoinRecord[] = []; for (let j = 0; j < newCoinDenoms.length; j++) { let transferPriv = native.EcdhePrivateKey.fromCrock(transferPrivs[i]); @@ -287,7 +287,7 @@ namespace RpcFunctions { if (!ev) { throw Error("couldn't blind (malicious exchange key?)"); } - let preCoin: RefreshPreCoin = { + let preCoin: RefreshPreCoinRecord = { blindingKey: blindingFactor.toCrock(), coinEv: ev.toCrock(), publicKey: coinPub.toCrock(), @@ -320,7 +320,7 @@ namespace RpcFunctions { valueOutput = Amounts.add(valueOutput, denom.value).amount; } - let refreshSession: RefreshSession = { + let refreshSession: RefreshSessionRecord = { meltCoinPub: meltCoin.coinPub, newDenoms: newCoinDenoms.map((d) => d.denom_pub), confirmSig, diff --git a/src/db.ts b/src/db.ts deleted file mode 100644 index 9cffc164c..000000000 --- a/src/db.ts +++ /dev/null @@ -1,117 +0,0 @@ -/* - This file is part of TALER - (C) 2016 GNUnet e.V. - - 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. - - 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 - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -"use strict"; -import {IExchangeInfo} from "./types"; - -/** - * Declarations and helpers for - * things that are stored in the wallet's - * database. - * @module Db - * @author Florian Dold - */ - -const DB_NAME = "taler"; -const DB_VERSION = 11; - -import {Stores} from "./wallet"; -import {Store, Index} from "./query"; - - - - - -/** - * Return a promise that resolves - * to the taler wallet db. - */ -export function openTalerDb(): Promise<IDBDatabase> { - return new Promise((resolve, reject) => { - const req = indexedDB.open(DB_NAME, DB_VERSION); - req.onerror = (e) => { - reject(e); - }; - req.onsuccess = (e) => { - resolve(req.result); - }; - req.onupgradeneeded = (e) => { - const db = req.result; - console.log("DB: upgrade needed: oldVersion = " + e.oldVersion); - switch (e.oldVersion) { - case 0: // DB does not exist yet - - for (let n in Stores) { - if ((Stores as any)[n] instanceof Store) { - let si: Store<any> = (Stores as any)[n]; - const s = db.createObjectStore(si.name, si.storeParams); - for (let indexName in (si as any)) { - if ((si as any)[indexName] instanceof Index) { - let ii: Index<any,any> = (si as any)[indexName]; - s.createIndex(ii.indexName, ii.keyPath); - } - } - } - } - break; - default: - if (e.oldVersion != DB_VERSION) { - window.alert("Incompatible wallet dababase version, please reset" + - " db."); - chrome.browserAction.setBadgeText({text: "err"}); - chrome.browserAction.setBadgeBackgroundColor({color: "#F00"}); - throw Error("incompatible DB"); - } - break; - } - }; - }); -} - - -export function exportDb(db: IDBDatabase): Promise<any> { - let dump = { - name: db.name, - version: db.version, - stores: {} as {[s: string]: any}, - }; - - return new Promise((resolve, reject) => { - - let tx = db.transaction(Array.from(db.objectStoreNames)); - tx.addEventListener("complete", () => { - resolve(dump); - }); - for (let i = 0; i < db.objectStoreNames.length; i++) { - let name = db.objectStoreNames[i]; - let storeDump = {} as {[s: string]: any}; - dump.stores[name] = storeDump; - let store = tx.objectStore(name) - .openCursor() - .addEventListener("success", (e: Event) => { - let cursor = (e.target as any).result; - if (cursor) { - storeDump[cursor.key] = cursor.value; - cursor.continue(); - } - }); - } - }); -} - -export function deleteDb() { - indexedDB.deleteDatabase(DB_NAME); -} diff --git a/src/pages/confirm-contract.tsx b/src/pages/confirm-contract.tsx index 2beac08f6..238ae2fb5 100644 --- a/src/pages/confirm-contract.tsx +++ b/src/pages/confirm-contract.tsx @@ -24,8 +24,8 @@ "use strict"; import {substituteFulfillmentUrl} from "src/helpers"; -import {Contract, AmountJson, IExchangeInfo} from "src/types"; -import {Offer} from "src/wallet"; +import {Contract, AmountJson, ExchangeRecord} from "src/types"; +import {OfferRecord} from "src/wallet"; import {renderContract, prettyAmount} from "src/renderHtml"; import {getExchanges} from "src/wxApi"; @@ -37,7 +37,7 @@ interface DetailState { interface DetailProps { contract: Contract collapsed: boolean - exchanges: null|IExchangeInfo[]; + exchanges: null|ExchangeRecord[]; } @@ -78,7 +78,7 @@ class Details extends React.Component<DetailProps, DetailState> { Exchanges in the wallet: <ul> {(this.props.exchanges || []).map( - (e: IExchangeInfo) => + (e: ExchangeRecord) => <li>{`${e.baseUrl}: ${e.masterPublicKey}`}</li>)} </ul> </div> @@ -92,10 +92,10 @@ interface ContractPromptProps { } interface ContractPromptState { - offer: Offer|null; + offer: OfferRecord|null; error: string|null; payDisabled: boolean; - exchanges: null|IExchangeInfo[]; + exchanges: null|ExchangeRecord[]; } class ContractPrompt extends React.Component<ContractPromptProps, ContractPromptState> { @@ -125,7 +125,7 @@ class ContractPrompt extends React.Component<ContractPromptProps, ContractPrompt this.setState({exchanges} as any); } - getOffer(): Promise<Offer> { + getOffer(): Promise<OfferRecord> { return new Promise((resolve, reject) => { let msg = { type: 'get-offer', diff --git a/src/pages/tree.tsx b/src/pages/tree.tsx index e368ffe9b..daabdaf49 100644 --- a/src/pages/tree.tsx +++ b/src/pages/tree.tsx @@ -21,8 +21,8 @@ */ -import { IExchangeInfo } from "src/types"; -import { ReserveRecord, Coin, PreCoin, Denomination } from "src/types"; +import { ExchangeRecord } from "src/types"; +import { ReserveRecord, CoinRecord, PreCoinRecord, Denomination } from "src/types"; import { ImplicitStateComponent, StateHolder } from "src/components"; import { getReserves, getExchanges, getCoins, getPreCoins, @@ -87,11 +87,11 @@ class Toggle extends ImplicitStateComponent<ToggleProps> { interface CoinViewProps { - coin: Coin; + coin: CoinRecord; } interface RefreshDialogProps { - coin: Coin; + coin: CoinRecord; } class RefreshDialog extends ImplicitStateComponent<RefreshDialogProps> { @@ -134,7 +134,7 @@ class CoinView extends React.Component<CoinViewProps, void> { interface PreCoinViewProps { - precoin: PreCoin; + precoin: PreCoinRecord; } class PreCoinView extends React.Component<PreCoinViewProps, void> { @@ -155,7 +155,7 @@ interface CoinListProps { } class CoinList extends ImplicitStateComponent<CoinListProps> { - coins = this.makeState<Coin[] | null>(null); + coins = this.makeState<CoinRecord[] | null>(null); expanded = this.makeState<boolean>(false); constructor(props: CoinListProps) { @@ -194,7 +194,7 @@ interface PreCoinListProps { } class PreCoinList extends ImplicitStateComponent<PreCoinListProps> { - precoins = this.makeState<PreCoin[] | null>(null); + precoins = this.makeState<PreCoinRecord[] | null>(null); expanded = this.makeState<boolean>(false); constructor(props: PreCoinListProps) { @@ -224,7 +224,7 @@ class PreCoinList extends ImplicitStateComponent<PreCoinListProps> { } interface DenominationListProps { - exchange: IExchangeInfo; + exchange: ExchangeRecord; } interface ExpanderTextProps { @@ -336,7 +336,7 @@ class ReserveList extends ImplicitStateComponent<ReserveListProps> { } interface ExchangeProps { - exchange: IExchangeInfo; + exchange: ExchangeRecord; } class ExchangeView extends React.Component<ExchangeProps, void> { @@ -358,7 +358,7 @@ class ExchangeView extends React.Component<ExchangeProps, void> { } interface ExchangesListState { - exchanges?: IExchangeInfo[]; + exchanges?: ExchangeRecord[]; } class ExchangesList extends React.Component<any, ExchangesListState> { diff --git a/src/types.ts b/src/types.ts index f6458955b..1a9d45871 100644 --- a/src/types.ts +++ b/src/types.ts @@ -132,7 +132,7 @@ export class Denomination { } -export interface IExchangeInfo { +export interface ExchangeRecord { baseUrl: string; masterPublicKey: string; @@ -159,7 +159,7 @@ export interface WireInfo { } export interface ReserveCreationInfo { - exchangeInfo: IExchangeInfo; + exchangeInfo: ExchangeRecord; wireInfo: WireInfo; selectedDenoms: Denomination[]; withdrawFee: AmountJson; @@ -170,7 +170,7 @@ export interface ReserveCreationInfo { /** * A coin that isn't yet signed by an exchange. */ -export interface PreCoin { +export interface PreCoinRecord { coinPub: string; coinPriv: string; reservePub: string; @@ -182,7 +182,7 @@ export interface PreCoin { coinValue: AmountJson; } -export interface RefreshPreCoin { +export interface RefreshPreCoinRecord { publicKey: string; privateKey: string; coinEv: string; @@ -193,7 +193,7 @@ export interface RefreshPreCoin { /** * Ongoing refresh */ -export interface RefreshSession { +export interface RefreshSessionRecord { /** * Public key that's being melted in this session. */ @@ -222,7 +222,7 @@ export interface RefreshSession { newDenoms: string[]; - preCoinsForGammas: RefreshPreCoin[][]; + preCoinsForGammas: RefreshPreCoinRecord[][]; /** @@ -257,10 +257,10 @@ export interface CoinPaySig { } /** - * Coin as stored in the "coins" data store + * CoinRecord as stored in the "coins" data store * of the wallet database. */ -export interface Coin { +export interface CoinRecord { /** * Public key of the coin. */ @@ -436,7 +436,7 @@ export class Contract { } -export type PayCoinInfo = Array<{ updatedCoin: Coin, sig: CoinPaySig }>; +export type PayCoinInfo = Array<{ updatedCoin: CoinRecord, sig: CoinPaySig }>; export namespace Amounts { diff --git a/src/wallet.ts b/src/wallet.ts index 49b0f1135..4388c61c0 100644 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -25,17 +25,17 @@ import { AmountJson, Amounts, CheckRepurchaseResult, - Coin, + CoinRecord, CoinPaySig, Contract, CreateReserveResponse, Denomination, ExchangeHandle, - IExchangeInfo, + ExchangeRecord, Notifier, PayCoinInfo, - PreCoin, - RefreshSession, + PreCoinRecord, + RefreshSessionRecord, ReserveCreationInfo, ReserveRecord, WalletBalance, @@ -68,7 +68,7 @@ import {CryptoApi} from "./cryptoApi"; "use strict"; export interface CoinWithDenom { - coin: Coin; + coin: CoinRecord; denom: Denomination; } @@ -132,7 +132,7 @@ export class ConfirmReserveRequest { @Checkable.Class -export class Offer { +export class OfferRecord { @Checkable.Value(Contract) contract: Contract; @@ -151,7 +151,7 @@ export class Offer { @Checkable.Optional(Checkable.Number) id?: number; - static checked: (obj: any) => Offer; + static checked: (obj: any) => OfferRecord; } export interface HistoryRecord { @@ -163,10 +163,6 @@ export interface HistoryRecord { } -interface ExchangeCoins { - [exchangeUrl: string]: CoinWithDenom[]; -} - interface PayReq { amount: AmountJson; coins: CoinPaySig[]; @@ -185,7 +181,7 @@ interface PayReq { instance?: string; } -interface Transaction { +interface TransactionRecord { contractHash: string; contract: Contract; payReq: PayReq; @@ -303,20 +299,20 @@ function getWithdrawDenomList(amountAvailable: AmountJson, export namespace Stores { - class ExchangeStore extends Store<IExchangeInfo> { + class ExchangeStore extends Store<ExchangeRecord> { constructor() { super("exchanges", {keyPath: "baseUrl"}); } - pubKeyIndex = new Index<string,IExchangeInfo>(this, "pubKey", "masterPublicKey"); + pubKeyIndex = new Index<string,ExchangeRecord>(this, "pubKey", "masterPublicKey"); } - class CoinsStore extends Store<Coin> { + class CoinsStore extends Store<CoinRecord> { constructor() { super("coins", {keyPath: "coinPub"}); } - exchangeBaseUrlIndex = new Index<string,Coin>(this, "exchangeBaseUrl", "exchangeBaseUrl"); + exchangeBaseUrlIndex = new Index<string,CoinRecord>(this, "exchangeBaseUrl", "exchangeBaseUrl"); } class HistoryStore extends Store<HistoryRecord> { @@ -330,7 +326,7 @@ export namespace Stores { timestampIndex = new Index<number,HistoryRecord>(this, "timestamp", "timestamp"); } - class OffersStore extends Store<Offer> { + class OffersStore extends Store<OfferRecord> { constructor() { super("offers", { keyPath: "id", @@ -339,12 +335,12 @@ export namespace Stores { } } - class TransactionsStore extends Store<Transaction> { + class TransactionsStore extends Store<TransactionRecord> { constructor() { super("transactions", {keyPath: "contractHash"}); } - repurchaseIndex = new Index<[string,string],Transaction>(this, "repurchase", [ + repurchaseIndex = new Index<[string,string],TransactionRecord>(this, "repurchase", [ "contract.merchant_pub", "contract.repurchase_correlation_id" ]); @@ -354,10 +350,10 @@ export namespace Stores { export let transactions: TransactionsStore = new TransactionsStore(); export let reserves: Store<ReserveRecord> = new Store<ReserveRecord>("reserves", {keyPath: "reserve_pub"}); export let coins: CoinsStore = new CoinsStore(); - export let refresh: Store<RefreshSession> = new Store<RefreshSession>("refresh", {keyPath: "meltCoinPub"}); + export let refresh: Store<RefreshSessionRecord> = new Store<RefreshSessionRecord>("refresh", {keyPath: "meltCoinPub"}); export let history: HistoryStore = new HistoryStore(); export let offers: OffersStore = new OffersStore(); - export let precoins: Store<PreCoin> = new Store<PreCoin>("precoins", {keyPath: "coinPub"}); + export let precoins: Store<PreCoinRecord> = new Store<PreCoinRecord>("precoins", {keyPath: "coinPub"}); } @@ -442,14 +438,14 @@ export class Wallet { this.q() .iter(Stores.refresh) - .reduce((r: RefreshSession) => { + .reduce((r: RefreshSessionRecord) => { this.continueRefreshSession(r); }); // FIXME: optimize via index this.q() .iter(Stores.coins) - .reduce((c: Coin) => { + .reduce((c: CoinRecord) => { if (c.dirty && !c.transactionPending) { this.refresh(c.coinPub); } @@ -471,7 +467,7 @@ export class Wallet { console.error("db inconsistent"); continue; } - let coins: Coin[] = await this.q().iterIndex(Stores.coins.exchangeBaseUrlIndex, exchangeHandle.url).toArray(); + let coins: CoinRecord[] = await this.q().iterIndex(Stores.coins.exchangeBaseUrlIndex, exchangeHandle.url).toArray(); if (!coins || coins.length == 0) { continue; } @@ -515,7 +511,7 @@ export class Wallet { * Record all information that is necessary to * pay for a contract in the wallet's database. */ - private async recordConfirmPay(offer: Offer, + private async recordConfirmPay(offer: OfferRecord, payCoinInfo: PayCoinInfo, chosenExchange: string): Promise<void> { let payReq: PayReq = { @@ -531,7 +527,7 @@ export class Wallet { transaction_id: offer.contract.transaction_id, instance: offer.contract.merchant.instance }; - let t: Transaction = { + let t: TransactionRecord = { contractHash: offer.H_contract, contract: offer.contract, payReq: payReq, @@ -568,7 +564,7 @@ export class Wallet { } - async saveOffer(offer: Offer): Promise<number> { + async saveOffer(offer: OfferRecord): Promise<number> { console.log(`saving offer in wallet.ts`); let id = await this.q().putWithResult(Stores.offers, offer); this.notifier.notify(); @@ -584,7 +580,7 @@ export class Wallet { * Add a contract to the wallet and sign coins, * but do not send them yet. */ - async confirmPay(offer: Offer): Promise<any> { + async confirmPay(offer: OfferRecord): Promise<any> { console.log("executing confirmPay"); let transaction = await this.q().get(Stores.transactions, offer.H_contract); @@ -618,7 +614,7 @@ export class Wallet { * Add a contract to the wallet and sign coins, * but do not send them yet. */ - async checkPay(offer: Offer): Promise<any> { + async checkPay(offer: OfferRecord): Promise<any> { // First check if we already payed for it. let transaction = await this.q().get(Stores.transactions, offer.H_contract); if (transaction) { @@ -645,7 +641,7 @@ export class Wallet { * with the given hash. */ async executePayment(H_contract: string): Promise<any> { - let t = await this.q().get<Transaction>(Stores.transactions, H_contract); + let t = await this.q().get<TransactionRecord>(Stores.transactions, H_contract); if (!t) { return { success: false, @@ -704,7 +700,7 @@ export class Wallet { } - private async processPreCoin(preCoin: PreCoin, + private async processPreCoin(preCoin: PreCoinRecord, retryDelayMs = 100): Promise<void> { let exchange = await this.q().get(Stores.exchanges, @@ -852,7 +848,7 @@ export class Wallet { } - private async withdrawExecute(pc: PreCoin): Promise<Coin> { + private async withdrawExecute(pc: PreCoinRecord): Promise<CoinRecord> { let reserve = await this.q().get<ReserveRecord>(Stores.reserves, pc.reservePub); @@ -879,7 +875,7 @@ export class Wallet { let denomSig = await this.cryptoApi.rsaUnblind(r.ev_sig, pc.blindingKey, pc.denomPub); - let coin: Coin = { + let coin: CoinRecord = { coinPub: pc.coinPub, coinPriv: pc.coinPriv, denomPub: pc.denomPub, @@ -897,7 +893,7 @@ export class Wallet { * Withdraw coins from a reserve until it is empty. */ private async depleteReserve(reserve: ReserveRecord, - exchange: IExchangeInfo): Promise<number> { + exchange: ExchangeRecord): Promise<number> { if (!reserve.current_amount) { throw Error("can't withdraw when amount is unknown"); } @@ -947,7 +943,7 @@ export class Wallet { * by quering the reserve's exchange. */ private async updateReserve(reservePub: string, - exchange: IExchangeInfo): Promise<ReserveRecord> { + exchange: ExchangeRecord): Promise<ReserveRecord> { let reserve = await this.q() .get<ReserveRecord>(Stores.reserves, reservePub); if (!reserve) { @@ -1037,7 +1033,7 @@ export class Wallet { * Optionally link the reserve entry to the new or existing * exchange entry in then DB. */ - async updateExchangeFromUrl(baseUrl: string): Promise<IExchangeInfo> { + async updateExchangeFromUrl(baseUrl: string): Promise<ExchangeRecord> { baseUrl = canonicalizeBaseUrl(baseUrl); let reqUrl = URI("keys").absoluteTo(baseUrl); let resp = await this.http.get(reqUrl); @@ -1048,11 +1044,11 @@ export class Wallet { return this.updateExchangeFromJson(baseUrl, exchangeKeysJson); } - private async suspendCoins(exchangeInfo: IExchangeInfo): Promise<void> { + private async suspendCoins(exchangeInfo: ExchangeRecord): Promise<void> { let suspendedCoins = await ( this.q() .iterIndex(Stores.coins.exchangeBaseUrlIndex, exchangeInfo.baseUrl) - .reduce((coin: Coin, suspendedCoins: Coin[]) => { + .reduce((coin: CoinRecord, suspendedCoins: CoinRecord[]) => { if (!exchangeInfo.active_denoms.find((c) => c.denom_pub == coin.denomPub)) { return Array.prototype.concat(suspendedCoins, [coin]); } @@ -1070,15 +1066,15 @@ export class Wallet { private async updateExchangeFromJson(baseUrl: string, - exchangeKeysJson: KeysJson): Promise<IExchangeInfo> { + exchangeKeysJson: KeysJson): Promise<ExchangeRecord> { const updateTimeSec = getTalerStampSec(exchangeKeysJson.list_issue_date); if (updateTimeSec === null) { throw Error("invalid update time"); } - let r = await this.q().get<IExchangeInfo>(Stores.exchanges, baseUrl); + let r = await this.q().get<ExchangeRecord>(Stores.exchanges, baseUrl); - let exchangeInfo: IExchangeInfo; + let exchangeInfo: ExchangeRecord; if (!r) { exchangeInfo = { @@ -1110,8 +1106,8 @@ export class Wallet { } - private async updateExchangeInfo(exchangeInfo: IExchangeInfo, - newKeys: KeysJson): Promise<IExchangeInfo> { + private async updateExchangeInfo(exchangeInfo: ExchangeRecord, + newKeys: KeysJson): Promise<ExchangeRecord> { if (exchangeInfo.masterPublicKey != newKeys.master_public_key) { throw Error("public keys do not match"); } @@ -1186,7 +1182,7 @@ export class Wallet { return entry; } - function collectBalances(c: Coin, balance: WalletBalance) { + function collectBalances(c: CoinRecord, balance: WalletBalance) { if (c.suspended) { return balance; } @@ -1213,7 +1209,7 @@ export class Wallet { return balance; } - function collectPendingRefresh(r: RefreshSession, balance: WalletBalance) { + function collectPendingRefresh(r: RefreshSessionRecord, balance: WalletBalance) { if (!r.finished) { return balance; } @@ -1224,7 +1220,7 @@ export class Wallet { return balance; } - function collectPayments(t: Transaction, balance: WalletBalance) { + function collectPayments(t: TransactionRecord, balance: WalletBalance) { if (t.finished) { return balance; } @@ -1235,7 +1231,7 @@ export class Wallet { return balance; } - function collectSmallestWithdraw(e: IExchangeInfo, sw: any) { + function collectSmallestWithdraw(e: ExchangeRecord, sw: any) { let min: AmountJson|undefined; for (let d of e.active_denoms) { let v = Amounts.add(d.value, d.fee_withdraw).amount; @@ -1277,8 +1273,8 @@ export class Wallet { } - async createRefreshSession(oldCoinPub: string): Promise<RefreshSession|undefined> { - let coin = await this.q().get<Coin>(Stores.coins, oldCoinPub); + async createRefreshSession(oldCoinPub: string): Promise<RefreshSessionRecord|undefined> { + let coin = await this.q().get<CoinRecord>(Stores.coins, oldCoinPub); if (!coin) { throw Error("coin not found"); @@ -1312,14 +1308,14 @@ export class Wallet { } - let refreshSession: RefreshSession = await ( + let refreshSession: RefreshSessionRecord = await ( this.cryptoApi.createRefreshSession(exchange.baseUrl, 3, coin, newCoinDenoms, oldDenom.fee_refresh)); - function mutateCoin(c: Coin): Coin { + function mutateCoin(c: CoinRecord): CoinRecord { let r = Amounts.sub(c.currentAmount, refreshSession.valueWithFee); if (r.saturated) { @@ -1340,7 +1336,7 @@ export class Wallet { async refresh(oldCoinPub: string): Promise<void> { - let refreshSession: RefreshSession|undefined; + let refreshSession: RefreshSessionRecord|undefined; let oldSession = await this.q().get(Stores.refresh, oldCoinPub); if (oldSession) { refreshSession = oldSession; @@ -1354,14 +1350,14 @@ export class Wallet { this.continueRefreshSession(refreshSession); } - async continueRefreshSession(refreshSession: RefreshSession) { + async continueRefreshSession(refreshSession: RefreshSessionRecord) { if (refreshSession.finished) { return; } if (typeof refreshSession.norevealIndex !== "number") { let coinPub = refreshSession.meltCoinPub; await this.refreshMelt(refreshSession); - let r = await this.q().get<RefreshSession>(Stores.refresh, coinPub); + let r = await this.q().get<RefreshSessionRecord>(Stores.refresh, coinPub); if (!r) { throw Error("refresh session does not exist anymore"); } @@ -1372,14 +1368,14 @@ export class Wallet { } - async refreshMelt(refreshSession: RefreshSession): Promise<void> { + async refreshMelt(refreshSession: RefreshSessionRecord): Promise<void> { if (refreshSession.norevealIndex != undefined) { console.error("won't melt again"); return; } - let coin = await this.q().get<Coin>(Stores.coins, - refreshSession.meltCoinPub); + let coin = await this.q().get<CoinRecord>(Stores.coins, + refreshSession.meltCoinPub); if (!coin) { console.error("can't melt coin, it does not exist"); return; @@ -1429,7 +1425,7 @@ export class Wallet { } - async refreshReveal(refreshSession: RefreshSession): Promise<void> { + async refreshReveal(refreshSession: RefreshSessionRecord): Promise<void> { let norevealIndex = refreshSession.norevealIndex; if (norevealIndex == undefined) { throw Error("can't reveal without melting first"); @@ -1461,14 +1457,14 @@ export class Wallet { console.log("/refresh/reveal did not contain ev_sigs"); } - let exchange = await this.q().get<IExchangeInfo>(Stores.exchanges, - refreshSession.exchangeBaseUrl); + let exchange = await this.q().get<ExchangeRecord>(Stores.exchanges, + refreshSession.exchangeBaseUrl); if (!exchange) { console.error(`exchange ${refreshSession.exchangeBaseUrl} not found`); return; } - let coins: Coin[] = []; + let coins: CoinRecord[] = []; for (let i = 0; i < respJson.ev_sigs.length; i++) { let denom = exchange.all_denoms.find((d) => d.denom_pub == refreshSession.newDenoms[i]); @@ -1480,7 +1476,7 @@ export class Wallet { let denomSig = await this.cryptoApi.rsaUnblind(respJson.ev_sigs[i].ev_sig, pc.blindingKey, denom.denom_pub); - let coin: Coin = { + let coin: CoinRecord = { coinPub: pc.publicKey, coinPriv: pc.privateKey, denomPub: denom.denom_pub, @@ -1526,9 +1522,9 @@ export class Wallet { return offer; } - async getExchanges(): Promise<IExchangeInfo[]> { + async getExchanges(): Promise<ExchangeRecord[]> { return this.q() - .iter<IExchangeInfo>(Stores.exchanges) + .iter<ExchangeRecord>(Stores.exchanges) .flatMap((e) => [e]) .toArray(); } @@ -1540,17 +1536,17 @@ export class Wallet { .toArray(); } - async getCoins(exchangeBaseUrl: string): Promise<Coin[]> { + async getCoins(exchangeBaseUrl: string): Promise<CoinRecord[]> { return this.q() - .iter<Coin>(Stores.coins) - .filter((c: Coin) => c.exchangeBaseUrl === exchangeBaseUrl) + .iter<CoinRecord>(Stores.coins) + .filter((c: CoinRecord) => c.exchangeBaseUrl === exchangeBaseUrl) .toArray(); } - async getPreCoins(exchangeBaseUrl: string): Promise<PreCoin[]> { + async getPreCoins(exchangeBaseUrl: string): Promise<PreCoinRecord[]> { return this.q() - .iter<PreCoin>(Stores.precoins) - .filter((c: PreCoin) => c.exchangeBaseUrl === exchangeBaseUrl) + .iter<PreCoinRecord>(Stores.precoins) + .filter((c: PreCoinRecord) => c.exchangeBaseUrl === exchangeBaseUrl) .toArray(); } @@ -1566,7 +1562,7 @@ export class Wallet { console.log("no repurchase: no correlation id"); return {isRepurchase: false}; } - let result: Transaction|undefined = await ( + let result: TransactionRecord|undefined = await ( this.q() .getIndexed(Stores.transactions.repurchaseIndex, [ @@ -1589,16 +1585,16 @@ export class Wallet { async paymentSucceeded(contractHash: string): Promise<any> { const doPaymentSucceeded = async() => { - let t = await this.q().get<Transaction>(Stores.transactions, - contractHash); + let t = await this.q().get<TransactionRecord>(Stores.transactions, + contractHash); if (!t) { console.error("contract not found"); return; } t.finished = true; - let modifiedCoins: Coin[] = []; + let modifiedCoins: CoinRecord[] = []; for (let pc of t.payReq.coins) { - let c = await this.q().get<Coin>(Stores.coins, pc.coin_pub); + let c = await this.q().get<CoinRecord>(Stores.coins, pc.coin_pub); if (!c) { console.error("coin not found"); return; diff --git a/src/wxApi.ts b/src/wxApi.ts index a85b56c28..110ba442f 100644 --- a/src/wxApi.ts +++ b/src/wxApi.ts @@ -16,10 +16,10 @@ import { AmountJson, - Coin, - PreCoin, + CoinRecord, + PreCoinRecord, ReserveCreationInfo, - IExchangeInfo, + ExchangeRecord, ReserveRecord } from "./types"; @@ -47,14 +47,14 @@ export function getReserveCreationInfo(baseUrl: string, } export async function callBackend(type: string, detail?: any): Promise<any> { - return new Promise<IExchangeInfo[]>((resolve, reject) => { + return new Promise<ExchangeRecord[]>((resolve, reject) => { chrome.runtime.sendMessage({ type, detail }, (resp) => { resolve(resp); }); }); } -export async function getExchanges(): Promise<IExchangeInfo[]> { +export async function getExchanges(): Promise<ExchangeRecord[]> { return await callBackend("get-exchanges"); } @@ -62,11 +62,11 @@ export async function getReserves(exchangeBaseUrl: string): Promise<ReserveRecor return await callBackend("get-reserves", { exchangeBaseUrl }); } -export async function getCoins(exchangeBaseUrl: string): Promise<Coin[]> { +export async function getCoins(exchangeBaseUrl: string): Promise<CoinRecord[]> { return await callBackend("get-coins", { exchangeBaseUrl }); } -export async function getPreCoins(exchangeBaseUrl: string): Promise<PreCoin[]> { +export async function getPreCoins(exchangeBaseUrl: string): Promise<PreCoinRecord[]> { return await callBackend("get-precoins", { exchangeBaseUrl }); } diff --git a/src/wxMessaging.ts b/src/wxBackend.ts index 990f1488b..f556d0de4 100644 --- a/src/wxMessaging.ts +++ b/src/wxBackend.ts @@ -17,12 +17,11 @@ import { Wallet, - Offer, + OfferRecord, Badge, ConfirmReserveRequest, CreateReserveRequest } from "./wallet"; -import { deleteDb, exportDb, openTalerDb } from "./db"; import { BrowserHttpLib } from "./http"; import { Checkable } from "./checkable"; import { AmountJson } from "./types"; @@ -34,6 +33,12 @@ import { ChromeBadge } from "./chromeBadge"; "use strict"; +const DB_NAME = "taler"; +const DB_VERSION = 11; + +import {Stores} from "./wallet"; +import {Store, Index} from "./query"; + /** * Messaging for the WebExtensions wallet. Should contain * parts that are specific for WebExtensions, but as little business @@ -97,9 +102,9 @@ function makeHandlers(db: IDBDatabase, return wallet.confirmReserve(req); }, ["confirm-pay"]: function (detail, sender) { - let offer: Offer; + let offer: OfferRecord; try { - offer = Offer.checked(detail.offer); + offer = OfferRecord.checked(detail.offer); } catch (e) { if (e instanceof Checkable.SchemaError) { console.error("schema error:", e.message); @@ -116,9 +121,9 @@ function makeHandlers(db: IDBDatabase, return wallet.confirmPay(offer); }, ["check-pay"]: function (detail, sender) { - let offer: Offer; + let offer: OfferRecord; try { - offer = Offer.checked(detail.offer); + offer = OfferRecord.checked(detail.offer); } catch (e) { if (e instanceof Checkable.SchemaError) { console.error("schema error:", e.message); @@ -437,3 +442,86 @@ export function wxMain() { console.error(e); }); } + + + +/** + * Return a promise that resolves + * to the taler wallet db. + */ +function openTalerDb(): Promise<IDBDatabase> { + return new Promise((resolve, reject) => { + const req = indexedDB.open(DB_NAME, DB_VERSION); + req.onerror = (e) => { + reject(e); + }; + req.onsuccess = (e) => { + resolve(req.result); + }; + req.onupgradeneeded = (e) => { + const db = req.result; + console.log("DB: upgrade needed: oldVersion = " + e.oldVersion); + switch (e.oldVersion) { + case 0: // DB does not exist yet + + for (let n in Stores) { + if ((Stores as any)[n] instanceof Store) { + let si: Store<any> = (Stores as any)[n]; + const s = db.createObjectStore(si.name, si.storeParams); + for (let indexName in (si as any)) { + if ((si as any)[indexName] instanceof Index) { + let ii: Index<any,any> = (si as any)[indexName]; + s.createIndex(ii.indexName, ii.keyPath); + } + } + } + } + break; + default: + if (e.oldVersion != DB_VERSION) { + window.alert("Incompatible wallet dababase version, please reset" + + " db."); + chrome.browserAction.setBadgeText({text: "err"}); + chrome.browserAction.setBadgeBackgroundColor({color: "#F00"}); + throw Error("incompatible DB"); + } + break; + } + }; + }); +} + + +function exportDb(db: IDBDatabase): Promise<any> { + let dump = { + name: db.name, + version: db.version, + stores: {} as {[s: string]: any}, + }; + + return new Promise((resolve, reject) => { + + let tx = db.transaction(Array.from(db.objectStoreNames)); + tx.addEventListener("complete", () => { + resolve(dump); + }); + for (let i = 0; i < db.objectStoreNames.length; i++) { + let name = db.objectStoreNames[i]; + let storeDump = {} as {[s: string]: any}; + dump.stores[name] = storeDump; + let store = tx.objectStore(name) + .openCursor() + .addEventListener("success", (e: Event) => { + let cursor = (e.target as any).result; + if (cursor) { + storeDump[cursor.key] = cursor.value; + cursor.continue(); + } + }); + } + }); +} + +function deleteDb() { + indexedDB.deleteDatabase(DB_NAME); +} diff --git a/tsconfig.json b/tsconfig.json index 2002012d0..502512ae0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,20 +16,19 @@ "src/checkable.ts", "decl/lib.es6.d.ts", "src/chromeBadge.ts", - "decl/urijs/URIjs.d.ts", "src/cryptoApi-test.ts", + "decl/urijs/URIjs.d.ts", "src/components.ts", - "decl/systemjs/systemjs.d.ts", "src/emscriptif-test.ts", + "decl/systemjs/systemjs.d.ts", "src/cryptoApi.ts", - "decl/react-global.d.ts", "src/helpers-test.ts", + "decl/react-global.d.ts", "src/cryptoLib.ts", "src/types-test.ts", "decl/chrome/chrome.d.ts", "src/cryptoWorker.ts", "src/wallet-test.ts", - "src/db.ts", "src/emscriptif.ts", "src/helpers.ts", "src/http.ts", @@ -39,7 +38,7 @@ "src/types.ts", "src/wallet.ts", "src/wxApi.ts", - "src/wxMessaging.ts", + "src/wxBackend.ts", "src/renderHtml.tsx", "src/background/background.ts", "src/content_scripts/notify.ts", |