aboutsummaryrefslogtreecommitdiff
path: root/extension/lib/wallet
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2016-02-29 18:03:02 +0100
committerFlorian Dold <florian.dold@gmail.com>2016-02-29 18:03:02 +0100
commitc962e9402123900c53967c14cf809ea10576cdb8 (patch)
treee7df9cfdd6fceae30fb99c8ec6be5e07c8b153a8 /extension/lib/wallet
parent30ee3320c788129b258ed8b42f4fc63d28431e2f (diff)
downloadwallet-core-c962e9402123900c53967c14cf809ea10576cdb8.tar.xz
restructure
Diffstat (limited to 'extension/lib/wallet')
-rw-r--r--extension/lib/wallet/checkable.ts241
-rw-r--r--extension/lib/wallet/cryptoApi.ts93
-rw-r--r--extension/lib/wallet/cryptoLib.ts222
-rw-r--r--extension/lib/wallet/cryptoWorker.ts65
-rw-r--r--extension/lib/wallet/db.ts109
-rw-r--r--extension/lib/wallet/emscriptif.ts1006
-rw-r--r--extension/lib/wallet/helpers.ts65
-rw-r--r--extension/lib/wallet/http.ts84
-rw-r--r--extension/lib/wallet/query.ts360
-rw-r--r--extension/lib/wallet/types.ts269
-rw-r--r--extension/lib/wallet/wallet.ts957
-rw-r--r--extension/lib/wallet/wxApi.ts40
-rw-r--r--extension/lib/wallet/wxMessaging.ts230
13 files changed, 0 insertions, 3741 deletions
diff --git a/extension/lib/wallet/checkable.ts b/extension/lib/wallet/checkable.ts
deleted file mode 100644
index 27ea9bf74..000000000
--- a/extension/lib/wallet/checkable.ts
+++ /dev/null
@@ -1,241 +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, If not, see <http://www.gnu.org/licenses/>
- */
-
-
-"use strict";
-
-/**
- * Decorators for type-checking JSON into
- * an object.
- * @module Checkable
- * @author Florian Dold
- */
-
-export namespace Checkable {
- export function SchemaError(message) {
- this.name = 'SchemaError';
- this.message = message;
- this.stack = (<any>new Error()).stack;
- }
-
- SchemaError.prototype = new Error;
-
- let chkSym = Symbol("checkable");
-
-
- function checkNumber(target, prop, path): any {
- if ((typeof target) !== "number") {
- throw new SchemaError(`expected number for ${path}`);
- }
- return target;
- }
-
-
- function checkString(target, prop, path): any {
- if (typeof target !== "string") {
- throw new SchemaError(`expected string for ${path}, got ${typeof target} instead`);
- }
- return target;
- }
-
-
- function checkAnyObject(target, prop, path): any {
- if (typeof target !== "object") {
- throw new SchemaError(`expected (any) object for ${path}, got ${typeof target} instead`);
- }
- return target;
- }
-
-
- function checkAny(target, prop, path): any {
- return target;
- }
-
-
- function checkList(target, prop, path): any {
- if (!Array.isArray(target)) {
- throw new SchemaError(`array expected for ${path}, got ${typeof target} instead`);
- }
- for (let i = 0; i < target.length; i++) {
- let v = target[i];
- prop.elementChecker(v, prop.elementProp, path.concat([i]));
- }
- return target;
- }
-
-
- function checkOptional(target, prop, path): any {
- console.assert(prop.propertyKey);
- prop.elementChecker(target,
- prop.elementProp,
- path.concat([prop.propertyKey]));
- return target;
- }
-
-
- function checkValue(target, prop, path): any {
- let type = prop.type;
- if (!type) {
- throw Error(`assertion failed (prop is ${JSON.stringify(prop)})`);
- }
- let v = target;
- if (!v || typeof v !== "object") {
- throw new SchemaError(
- `expected object for ${path.join(".")}, got ${typeof v} instead`);
- }
- let props = type.prototype[chkSym].props;
- let remainingPropNames = new Set(Object.getOwnPropertyNames(v));
- let obj = new type();
- for (let prop of props) {
- if (!remainingPropNames.has(prop.propertyKey)) {
- if (prop.optional) {
- continue;
- }
- throw new SchemaError("Property missing: " + prop.propertyKey);
- }
- if (!remainingPropNames.delete(prop.propertyKey)) {
- throw new SchemaError("assertion failed");
- }
- let propVal = v[prop.propertyKey];
- obj[prop.propertyKey] = prop.checker(propVal,
- prop,
- path.concat([prop.propertyKey]));
- }
-
- if (remainingPropNames.size != 0) {
- throw new SchemaError("superfluous properties " + JSON.stringify(Array.from(
- remainingPropNames.values())));
- }
- return obj;
- }
-
-
- export function Class(target) {
- target.checked = (v) => {
- return checkValue(v, {
- propertyKey: "(root)",
- type: target,
- checker: checkValue
- }, ["(root)"]);
- };
- return target;
- }
-
-
- export function Value(type) {
- if (!type) {
- throw Error("Type does not exist yet (wrong order of definitions?)");
- }
- function deco(target: Object, propertyKey: string | symbol): void {
- let chk = mkChk(target);
- chk.props.push({
- propertyKey: propertyKey,
- checker: checkValue,
- type: type
- });
- }
-
- return deco;
- }
-
-
- export function List(type) {
- let stub = {};
- type(stub, "(list-element)");
- let elementProp = mkChk(stub).props[0];
- let elementChecker = elementProp.checker;
- if (!elementChecker) {
- throw Error("assertion failed");
- }
- function deco(target: Object, propertyKey: string | symbol): void {
- let chk = mkChk(target);
- chk.props.push({
- elementChecker,
- elementProp,
- propertyKey: propertyKey,
- checker: checkList,
- });
- }
-
- return deco;
- }
-
-
- export function Optional(type) {
- let stub = {};
- type(stub, "(optional-element)");
- let elementProp = mkChk(stub).props[0];
- let elementChecker = elementProp.checker;
- if (!elementChecker) {
- throw Error("assertion failed");
- }
- function deco(target: Object, propertyKey: string | symbol): void {
- let chk = mkChk(target);
- chk.props.push({
- elementChecker,
- elementProp,
- propertyKey: propertyKey,
- checker: checkOptional,
- optional: true,
- });
- }
-
- return deco;
- }
-
-
- export function Number(target: Object, propertyKey: string | symbol): void {
- let chk = mkChk(target);
- chk.props.push({propertyKey: propertyKey, checker: checkNumber});
- }
-
-
- export function AnyObject(target: Object,
- propertyKey: string | symbol): void {
- let chk = mkChk(target);
- chk.props.push({
- propertyKey: propertyKey,
- checker: checkAnyObject
- });
- }
-
-
- export function Any(target: Object,
- propertyKey: string | symbol): void {
- let chk = mkChk(target);
- chk.props.push({
- propertyKey: propertyKey,
- checker: checkAny,
- optional: true
- });
- }
-
-
- export function String(target: Object, propertyKey: string | symbol): void {
- let chk = mkChk(target);
- chk.props.push({propertyKey: propertyKey, checker: checkString});
- }
-
-
- function mkChk(target) {
- let chk = target[chkSym];
- if (!chk) {
- chk = {props: []};
- target[chkSym] = chk;
- }
- return chk;
- }
-} \ No newline at end of file
diff --git a/extension/lib/wallet/cryptoApi.ts b/extension/lib/wallet/cryptoApi.ts
deleted file mode 100644
index 300b928db..000000000
--- a/extension/lib/wallet/cryptoApi.ts
+++ /dev/null
@@ -1,93 +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, If not, see <http://www.gnu.org/licenses/>
- */
-
-
-import {PreCoin} from "./types";
-import {Reserve} from "./types";
-import {Denomination} from "./types";
-import {Offer} from "./wallet";
-import {CoinWithDenom} from "./wallet";
-import {PayCoinInfo} from "./types";
-export class CryptoApi {
- private nextRpcId: number = 1;
- private rpcRegistry = {};
- private cryptoWorker: Worker;
-
-
- constructor() {
- this.cryptoWorker = new Worker("/lib/wallet/cryptoWorker.js");
-
- this.cryptoWorker.onmessage = (msg: MessageEvent) => {
- let id = msg.data.id;
- if (typeof id !== "number") {
- console.error("rpc id must be number");
- return;
- }
- if (!this.rpcRegistry[id]) {
- console.error(`RPC with id ${id} has no registry entry`);
- return;
- }
- let {resolve, reject} = this.rpcRegistry[id];
- resolve(msg.data.result);
- }
- }
-
-
- private registerRpcId(resolve, reject): number {
- let id = this.nextRpcId++;
- this.rpcRegistry[id] = {resolve, reject};
- return id;
- }
-
-
- private doRpc<T>(methodName: string, ...args): Promise<T> {
- return new Promise<T>((resolve, reject) => {
- let msg = {
- operation: methodName,
- id: this.registerRpcId(resolve, reject),
- args: args,
- };
- this.cryptoWorker.postMessage(msg);
- });
- }
-
-
- createPreCoin(denom: Denomination, reserve: Reserve): Promise<PreCoin> {
- return this.doRpc("createPreCoin", denom, reserve);
- }
-
- hashRsaPub(rsaPub: string): Promise<string> {
- return this.doRpc("hashRsaPub", rsaPub);
- }
-
- isValidDenom(denom: Denomination,
- masterPub: string): Promise<boolean> {
- return this.doRpc("isValidDenom", denom, masterPub);
- }
-
- signDeposit(offer: Offer,
- cds: CoinWithDenom[]): Promise<PayCoinInfo> {
- return this.doRpc("signDeposit", offer, cds);
- }
-
- createEddsaKeypair(): Promise<{priv: string, pub: string}> {
- return this.doRpc("createEddsaKeypair");
- }
-
- rsaUnblind(sig: string, bk: string, pk: string): Promise<string> {
- return this.doRpc("rsaUnblind", sig, bk, pk);
- }
-} \ No newline at end of file
diff --git a/extension/lib/wallet/cryptoLib.ts b/extension/lib/wallet/cryptoLib.ts
deleted file mode 100644
index 869ddbaff..000000000
--- a/extension/lib/wallet/cryptoLib.ts
+++ /dev/null
@@ -1,222 +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, If not, see <http://www.gnu.org/licenses/>
- */
-
-import {Denomination} from "./types";
-/**
- * Web worker for crypto operations.
- * @author Florian Dold
- */
-
-"use strict";
-
-import * as native from "./emscriptif";
-import {PreCoin, Reserve, PayCoinInfo} from "./types";
-import create = chrome.alarms.create;
-import {Offer} from "./wallet";
-import {CoinWithDenom} from "./wallet";
-import {CoinPaySig} from "./types";
-
-
-export function main(worker: Worker) {
- worker.onmessage = (msg: MessageEvent) => {
- console.log("got data", msg.data);
- if (!Array.isArray(msg.data.args)) {
- console.error("args must be array");
- return;
- }
- if (typeof msg.data.id != "number") {
- console.error("RPC id must be number");
- }
- if (typeof msg.data.operation != "string") {
- console.error("RPC operation must be string");
- }
- let f = RpcFunctions[msg.data.operation];
- if (!f) {
- console.error(`unknown operation: '${msg.data.operation}'`);
- return;
- }
- let res = f(...msg.data.args);
- worker.postMessage({result: res, id: msg.data.id});
- }
-}
-
-console.log("hello, this is the crypto lib");
-
-namespace RpcFunctions {
-
- /**
- * Create a pre-coin of the given denomination to be withdrawn from then given
- * reserve.
- */
- export function createPreCoin(denom: Denomination,
- reserve: Reserve): PreCoin {
- let reservePriv = new native.EddsaPrivateKey();
- reservePriv.loadCrock(reserve.reserve_priv);
- let reservePub = new native.EddsaPublicKey();
- reservePub.loadCrock(reserve.reserve_pub);
- let denomPub = native.RsaPublicKey.fromCrock(denom.denom_pub);
- let coinPriv = native.EddsaPrivateKey.create();
- let coinPub = coinPriv.getPublicKey();
- let blindingFactor = native.RsaBlindingKey.create(1024);
- let pubHash: native.HashCode = coinPub.hash();
- let ev: native.ByteArray = native.rsaBlind(pubHash,
- blindingFactor,
- denomPub);
-
- if (!denom.fee_withdraw) {
- throw Error("Field fee_withdraw missing");
- }
-
- let amountWithFee = new native.Amount(denom.value);
- amountWithFee.add(new native.Amount(denom.fee_withdraw));
- let withdrawFee = new native.Amount(denom.fee_withdraw);
-
- // Signature
- let withdrawRequest = new native.WithdrawRequestPS({
- reserve_pub: reservePub,
- amount_with_fee: amountWithFee.toNbo(),
- withdraw_fee: withdrawFee.toNbo(),
- h_denomination_pub: denomPub.encode().hash(),
- h_coin_envelope: ev.hash()
- });
-
- var sig = native.eddsaSign(withdrawRequest.toPurpose(), reservePriv);
-
- let preCoin: PreCoin = {
- reservePub: reservePub.toCrock(),
- blindingKey: blindingFactor.toCrock(),
- coinPub: coinPub.toCrock(),
- coinPriv: coinPriv.toCrock(),
- denomPub: denomPub.encode().toCrock(),
- mintBaseUrl: reserve.mint_base_url,
- withdrawSig: sig.toCrock(),
- coinEv: ev.toCrock(),
- coinValue: denom.value
- };
- return preCoin;
- }
-
-
- export function isValidDenom(denom: Denomination,
- masterPub: string): boolean {
- let p = new native.DenominationKeyValidityPS({
- master: native.EddsaPublicKey.fromCrock(masterPub),
- denom_hash: native.RsaPublicKey.fromCrock(denom.denom_pub)
- .encode()
- .hash(),
- expire_legal: native.AbsoluteTimeNbo.fromTalerString(denom.stamp_expire_legal),
- expire_spend: native.AbsoluteTimeNbo.fromTalerString(denom.stamp_expire_deposit),
- expire_withdraw: native.AbsoluteTimeNbo.fromTalerString(denom.stamp_expire_withdraw),
- start: native.AbsoluteTimeNbo.fromTalerString(denom.stamp_start),
- value: (new native.Amount(denom.value)).toNbo(),
- fee_deposit: (new native.Amount(denom.fee_deposit)).toNbo(),
- fee_refresh: (new native.Amount(denom.fee_refresh)).toNbo(),
- fee_withdraw: (new native.Amount(denom.fee_withdraw)).toNbo(),
- });
-
- let nativeSig = new native.EddsaSignature();
- nativeSig.loadCrock(denom.master_sig);
-
- let nativePub = native.EddsaPublicKey.fromCrock(masterPub);
-
- return native.eddsaVerify(native.SignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY,
- p.toPurpose(),
- nativeSig,
- nativePub);
-
- }
-
-
- export function hashRsaPub(rsaPub: string): string {
- return native.RsaPublicKey.fromCrock(rsaPub)
- .encode()
- .hash()
- .toCrock();
- }
-
-
- export function createEddsaKeypair(): {priv: string, pub: string} {
- const priv = native.EddsaPrivateKey.create();
- const pub = priv.getPublicKey();
- return {priv: priv.toCrock(), pub: pub.toCrock()};
- }
-
-
- export function rsaUnblind(sig, bk, pk): string {
- let denomSig = native.rsaUnblind(native.RsaSignature.fromCrock(sig),
- native.RsaBlindingKey.fromCrock(bk),
- native.RsaPublicKey.fromCrock(pk));
- return denomSig.encode().toCrock()
- }
-
-
- /**
- * Generate updated coins (to store in the database)
- * and deposit permissions for each given coin.
- */
- export function signDeposit(offer: Offer,
- cds: CoinWithDenom[]): PayCoinInfo {
- let ret = [];
- let amountSpent = native.Amount.getZero(cds[0].coin.currentAmount.currency);
- let amountRemaining = new native.Amount(offer.contract.amount);
- for (let cd of cds) {
- let coinSpend;
-
- if (amountRemaining.value == 0 && amountRemaining.fraction == 0) {
- break;
- }
-
- if (amountRemaining.cmp(new native.Amount(cd.coin.currentAmount)) < 0) {
- coinSpend = new native.Amount(amountRemaining.toJson());
- } else {
- coinSpend = new native.Amount(cd.coin.currentAmount);
- }
-
- amountSpent.add(coinSpend);
- amountRemaining.sub(coinSpend);
-
- let newAmount = new native.Amount(cd.coin.currentAmount);
- newAmount.sub(coinSpend);
- cd.coin.currentAmount = newAmount.toJson();
-
- let d = new native.DepositRequestPS({
- h_contract: native.HashCode.fromCrock(offer.H_contract),
- h_wire: native.HashCode.fromCrock(offer.contract.H_wire),
- amount_with_fee: coinSpend.toNbo(),
- coin_pub: native.EddsaPublicKey.fromCrock(cd.coin.coinPub),
- deposit_fee: new native.Amount(cd.denom.fee_deposit).toNbo(),
- merchant: native.EddsaPublicKey.fromCrock(offer.contract.merchant_pub),
- refund_deadline: native.AbsoluteTimeNbo.fromTalerString(offer.contract.refund_deadline),
- timestamp: native.AbsoluteTimeNbo.fromTalerString(offer.contract.timestamp),
- transaction_id: native.UInt64.fromNumber(offer.contract.transaction_id),
- });
-
- let coinSig = native.eddsaSign(d.toPurpose(),
- native.EddsaPrivateKey.fromCrock(cd.coin.coinPriv))
- .toCrock();
-
- let s: CoinPaySig = {
- coin_sig: coinSig,
- coin_pub: cd.coin.coinPub,
- ub_sig: cd.coin.denomSig,
- denom_pub: cd.coin.denomPub,
- f: coinSpend.toJson(),
- };
- ret.push({sig: s, updatedCoin: cd.coin});
- }
- return ret;
- }
-}
diff --git a/extension/lib/wallet/cryptoWorker.ts b/extension/lib/wallet/cryptoWorker.ts
deleted file mode 100644
index 958c2de74..000000000
--- a/extension/lib/wallet/cryptoWorker.ts
+++ /dev/null
@@ -1,65 +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, If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- * Web worker for crypto operations.
- * @author Florian Dold
- */
-
-"use strict";
-
-
-importScripts("../emscripten/libwrapper.js",
- "../vendor/system-csp-production.src.js");
-
-
-// TypeScript does not allow ".js" extensions in the
-// module name, so SystemJS must add it.
-System.config({
- defaultJSExtensions: true,
- });
-
-// We expect that in the manifest, the emscripten js is loaded
-// becore the background page.
-// Currently it is not possible to use SystemJS to load the emscripten js.
-declare var Module: any;
-if ("object" !== typeof Module) {
- throw Error("emscripten not loaded, no 'Module' defined");
-}
-
-
-// Manually register the emscripten js as a SystemJS, so that
-// we can use it from TypeScript by importing it.
-
-{
- let mod = System.newModule({Module: Module});
- let modName = System.normalizeSync("../emscripten/emsc");
- console.log("registering", modName);
- System.set(modName, mod);
-}
-
-System.import("./cryptoLib")
- .then((m) => {
- m.main(self);
- console.log("loaded");
- })
- .catch((e) => {
- console.log("crypto worker failed");
- console.error(e.stack);
- });
-
-console.log("in worker thread");
-
diff --git a/extension/lib/wallet/db.ts b/extension/lib/wallet/db.ts
deleted file mode 100644
index c7621c5ff..000000000
--- a/extension/lib/wallet/db.ts
+++ /dev/null
@@ -1,109 +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, If not, see <http://www.gnu.org/licenses/>
- */
-
-"use strict";
-
-/**
- * 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 = 1;
-
-/**
- * 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) => {
- let db = req.result;
- console.log("DB: upgrade needed: oldVersion = " + e.oldVersion);
- switch (e.oldVersion) {
- case 0: // DB does not exist yet
- const mints = db.createObjectStore("mints", {keyPath: "baseUrl"});
- mints.createIndex("pubKey", "masterPublicKey");
- db.createObjectStore("reserves", {keyPath: "reserve_pub"});
- db.createObjectStore("denoms", {keyPath: "denomPub"});
- const coins = db.createObjectStore("coins", {keyPath: "coinPub"});
- coins.createIndex("mintBaseUrl", "mintBaseUrl");
- const transactions = db.createObjectStore("transactions",
- {keyPath: "contractHash"});
- transactions.createIndex("repurchase",
- [
- "contract.merchant_pub",
- "contract.repurchase_correlation_id"
- ]);
-
- db.createObjectStore("precoins",
- {keyPath: "coinPub", autoIncrement: true});
- const history = db.createObjectStore("history",
- {
- keyPath: "id",
- autoIncrement: true
- });
- history.createIndex("timestamp", "timestamp");
- break;
- }
- };
- });
-}
-
-
-export function exportDb(db): Promise<any> {
- let dump = {
- name: db.name,
- version: db.version,
- stores: {}
- };
-
- return new Promise((resolve, reject) => {
-
- let tx = db.transaction(db.objectStoreNames);
- tx.addEventListener("complete", (e) => {
- resolve(dump);
- });
- for (let i = 0; i < db.objectStoreNames.length; i++) {
- let name = db.objectStoreNames[i];
- let storeDump = {};
- dump.stores[name] = storeDump;
- let store = tx.objectStore(name)
- .openCursor()
- .addEventListener("success", (e) => {
- let cursor = e.target.result;
- if (cursor) {
- storeDump[cursor.key] = cursor.value;
- cursor.continue();
- }
- });
- }
- });
-}
-
-export function deleteDb() {
- indexedDB.deleteDatabase(DB_NAME);
-} \ No newline at end of file
diff --git a/extension/lib/wallet/emscriptif.ts b/extension/lib/wallet/emscriptif.ts
deleted file mode 100644
index b03bc9bc7..000000000
--- a/extension/lib/wallet/emscriptif.ts
+++ /dev/null
@@ -1,1006 +0,0 @@
-/*
- This file is part of TALER
- (C) 2015 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, If not, see <http://www.gnu.org/licenses/>
- */
-
-import {AmountJson} from "./types";
-import * as EmscWrapper from "../emscripten/emsc";
-
-/**
- * High-level interface to emscripten-compiled modules used
- * by the wallet.
- * @module EmscriptIf
- * @author Florian Dold
- */
-
-"use strict";
-
-// Size of a native pointer.
-const PTR_SIZE = 4;
-
-const GNUNET_OK = 1;
-const GNUNET_YES = 1;
-const GNUNET_NO = 0;
-const GNUNET_SYSERR = -1;
-
-let Module = EmscWrapper.Module;
-
-let getEmsc: EmscWrapper.EmscFunGen = (...args) => Module.cwrap.apply(null,
- args);
-
-var emsc = {
- free: (ptr) => Module._free(ptr),
- get_value: getEmsc('TALER_WR_get_value',
- 'number',
- ['number']),
- get_fraction: getEmsc('TALER_WR_get_fraction',
- 'number',
- ['number']),
- get_currency: getEmsc('TALER_WR_get_currency',
- 'string',
- ['number']),
- amount_add: getEmsc('TALER_amount_add',
- 'number',
- ['number', 'number', 'number']),
- amount_subtract: getEmsc('TALER_amount_subtract',
- 'number',
- ['number', 'number', 'number']),
- amount_normalize: getEmsc('TALER_amount_normalize',
- 'void',
- ['number']),
- amount_get_zero: getEmsc('TALER_amount_get_zero',
- 'number',
- ['string', 'number']),
- amount_cmp: getEmsc('TALER_amount_cmp',
- 'number',
- ['number', 'number']),
- amount_hton: getEmsc('TALER_amount_hton',
- 'void',
- ['number', 'number']),
- amount_ntoh: getEmsc('TALER_amount_ntoh',
- 'void',
- ['number', 'number']),
- hash: getEmsc('GNUNET_CRYPTO_hash',
- 'void',
- ['number', 'number', 'number']),
- memmove: getEmsc('memmove',
- 'number',
- ['number', 'number', 'number']),
- rsa_public_key_free: getEmsc('GNUNET_CRYPTO_rsa_public_key_free',
- 'void',
- ['number']),
- rsa_signature_free: getEmsc('GNUNET_CRYPTO_rsa_signature_free',
- 'void',
- ['number']),
- string_to_data: getEmsc('GNUNET_STRINGS_string_to_data',
- 'number',
- ['number', 'number', 'number', 'number']),
- eddsa_sign: getEmsc('GNUNET_CRYPTO_eddsa_sign',
- 'number',
- ['number', 'number', 'number']),
- eddsa_verify: getEmsc('GNUNET_CRYPTO_eddsa_verify',
- 'number',
- ['number', 'number', 'number', 'number']),
- hash_create_random: getEmsc('GNUNET_CRYPTO_hash_create_random',
- 'void',
- ['number', 'number']),
- rsa_blinding_key_destroy: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_free',
- 'void',
- ['number']),
-};
-
-var emscAlloc = {
- get_amount: getEmsc('TALER_WRALL_get_amount',
- 'number',
- ['number', 'number', 'number', 'string']),
- eddsa_key_create: getEmsc('GNUNET_CRYPTO_eddsa_key_create',
- 'number', []),
- eddsa_public_key_from_private: getEmsc(
- 'TALER_WRALL_eddsa_public_key_from_private',
- 'number',
- ['number']),
- data_to_string_alloc: getEmsc('GNUNET_STRINGS_data_to_string_alloc',
- 'number',
- ['number', 'number']),
- purpose_create: getEmsc('TALER_WRALL_purpose_create',
- 'number',
- ['number', 'number', 'number']),
- rsa_blind: getEmsc('GNUNET_CRYPTO_rsa_blind',
- 'number',
- ['number', 'number', 'number', 'number']),
- rsa_blinding_key_create: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_create',
- 'number',
- ['number']),
- rsa_blinding_key_encode: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_encode',
- 'number',
- ['number', 'number']),
- rsa_signature_encode: getEmsc('GNUNET_CRYPTO_rsa_signature_encode',
- 'number',
- ['number', 'number']),
- rsa_blinding_key_decode: getEmsc('GNUNET_CRYPTO_rsa_blinding_key_decode',
- 'number',
- ['number', 'number']),
- rsa_public_key_decode: getEmsc('GNUNET_CRYPTO_rsa_public_key_decode',
- 'number',
- ['number', 'number']),
- rsa_signature_decode: getEmsc('GNUNET_CRYPTO_rsa_signature_decode',
- 'number',
- ['number', 'number']),
- rsa_public_key_encode: getEmsc('GNUNET_CRYPTO_rsa_public_key_encode',
- 'number',
- ['number', 'number']),
- rsa_unblind: getEmsc('GNUNET_CRYPTO_rsa_unblind',
- 'number',
- ['number', 'number', 'number']),
- malloc: (size: number) => Module._malloc(size),
-};
-
-
-export enum SignaturePurpose {
- RESERVE_WITHDRAW = 1200,
- WALLET_COIN_DEPOSIT = 1201,
- MASTER_DENOMINATION_KEY_VALIDITY = 1025,
-}
-
-enum RandomQuality {
- WEAK = 0,
- STRONG = 1,
- NONCE = 2
-}
-
-
-abstract class ArenaObject {
- private _nativePtr: number;
- arena: Arena;
-
- abstract destroy(): void;
-
- constructor(arena?: Arena) {
- this.nativePtr = null;
- if (!arena) {
- if (arenaStack.length == 0) {
- throw Error("No arena available")
- }
- arena = arenaStack[arenaStack.length - 1];
- }
- arena.put(this);
- this.arena = arena;
- }
-
- getNative(): number {
- // We want to allow latent allocation
- // of native wrappers, but we never want to
- // pass 'undefined' to emscripten.
- if (this._nativePtr === undefined) {
- throw Error("Native pointer not initialized");
- }
- return this._nativePtr;
- }
-
- free() {
- if (this.nativePtr !== undefined) {
- emsc.free(this.nativePtr);
- this.nativePtr = undefined;
- }
- }
-
- alloc(size: number) {
- if (this.nativePtr !== undefined) {
- throw Error("Double allocation");
- }
- this.nativePtr = emscAlloc.malloc(size);
- }
-
- setNative(n: number) {
- if (n === undefined) {
- throw Error("Native pointer must be a number or null");
- }
- this._nativePtr = n;
- }
-
- set nativePtr(v) {
- this.setNative(v);
- }
-
- get nativePtr() {
- return this.getNative();
- }
-
-}
-
-interface Arena {
- put(obj: ArenaObject): void;
- destroy(): void;
-}
-
-class DefaultArena implements Arena {
- heap: Array<ArenaObject>;
-
- constructor() {
- this.heap = [];
- }
-
- put(obj) {
- this.heap.push(obj);
- }
-
- destroy() {
- for (let obj of this.heap) {
- obj.destroy();
- }
- this.heap = []
- }
-}
-
-
-function mySetTimeout(ms: number, fn: () => void) {
- // We need to use different timeouts, depending on whether
- // we run in node or a web extension
- if ("function" === typeof setTimeout) {
- setTimeout(fn, ms);
- } else {
- chrome.extension.getBackgroundPage().setTimeout(fn, ms);
- }
-}
-
-
-/**
- * Arena that destroys all its objects once control has returned to the message
- * loop and a small interval has passed.
- */
-class SyncArena extends DefaultArena {
- private isScheduled: boolean;
-
- constructor() {
- super();
- }
-
- pub(obj) {
- super.put(obj);
- if (!this.isScheduled) {
- this.schedule();
- }
- this.heap.push(obj);
- }
-
- destroy() {
- super.destroy();
- }
-
- private schedule() {
- this.isScheduled = true;
- mySetTimeout(50, () => {
- this.isScheduled = false;
- this.destroy();
- });
- }
-}
-
-let arenaStack: Arena[] = [];
-arenaStack.push(new SyncArena());
-
-
-export class Amount extends ArenaObject {
- constructor(args?: AmountJson, arena?: Arena) {
- super(arena);
- if (args) {
- this.nativePtr = emscAlloc.get_amount(args.value,
- 0,
- args.fraction,
- args.currency);
- } else {
- this.nativePtr = emscAlloc.get_amount(0, 0, 0, "");
- }
- }
-
- destroy() {
- if (this.nativePtr != 0) {
- emsc.free(this.nativePtr);
- }
- }
-
-
- static getZero(currency: string, a?: Arena): Amount {
- let am = new Amount(null, a);
- let r = emsc.amount_get_zero(currency, am.getNative());
- if (r != GNUNET_OK) {
- throw Error("invalid currency");
- }
- return am;
- }
-
-
- toNbo(a?: Arena): AmountNbo {
- let x = new AmountNbo(a);
- x.alloc();
- emsc.amount_hton(x.nativePtr, this.nativePtr);
- return x;
- }
-
- fromNbo(nbo: AmountNbo): void {
- emsc.amount_ntoh(this.nativePtr, nbo.nativePtr);
- }
-
- get value() {
- return emsc.get_value(this.nativePtr);
- }
-
- get fraction() {
- return emsc.get_fraction(this.nativePtr);
- }
-
- get currency(): String {
- return emsc.get_currency(this.nativePtr);
- }
-
- toJson(): AmountJson {
- return {
- value: emsc.get_value(this.nativePtr),
- fraction: emsc.get_fraction(this.nativePtr),
- currency: emsc.get_currency(this.nativePtr)
- };
- }
-
- /**
- * Add an amount to this amount.
- */
- add(a: Amount) {
- let res = emsc.amount_add(this.nativePtr, a.nativePtr, this.nativePtr);
- if (res < 1) {
- // Overflow
- return false;
- }
- return true;
- }
-
- /**
- * Perform saturating subtraction on amounts.
- */
- sub(a: Amount) {
- // this = this - a
- let res = emsc.amount_subtract(this.nativePtr, this.nativePtr, a.nativePtr);
- if (res == 0) {
- // Underflow
- return false;
- }
- if (res > 0) {
- return true;
- }
- throw Error("Incompatible currencies");
- }
-
- cmp(a: Amount) {
- // If we don't check this, the c code aborts.
- if (this.currency !== a.currency) {
- throw Error(`incomparable currencies (${this.currency} and ${a.currency})`);
- }
- return emsc.amount_cmp(this.nativePtr, a.nativePtr);
- }
-
- normalize() {
- emsc.amount_normalize(this.nativePtr);
- }
-}
-
-
-abstract class PackedArenaObject extends ArenaObject {
- abstract size(): number;
-
- constructor(a?: Arena) {
- super(a);
- }
-
- toCrock(): string {
- var d = emscAlloc.data_to_string_alloc(this.nativePtr, this.size());
- var s = Module.Pointer_stringify(d);
- emsc.free(d);
- return s;
- }
-
- toJson(): any {
- // Per default, the json encoding of
- // packed arena objects is just the crockford encoding.
- // Subclasses typically want to override this.
- return this.toCrock();
- }
-
- loadCrock(s: string) {
- this.alloc();
- // We need to get the javascript string
- // to the emscripten heap first.
- let buf = ByteArray.fromString(s);
- let res = emsc.string_to_data(buf.nativePtr,
- s.length,
- this.nativePtr,
- this.size());
- buf.destroy();
- if (res < 1) {
- throw {error: "wrong encoding"};
- }
- }
-
- alloc() {
- if (this.nativePtr === null) {
- this.nativePtr = emscAlloc.malloc(this.size());
- }
- }
-
- destroy() {
- emsc.free(this.nativePtr);
- this.nativePtr = 0;
- }
-
- hash(): HashCode {
- var x = new HashCode();
- x.alloc();
- emsc.hash(this.nativePtr, this.size(), x.nativePtr);
- return x;
- }
-
- hexdump() {
- let bytes: string[] = [];
- for (let i = 0; i < this.size(); i++) {
- let b = Module.getValue(this.getNative() + i, "i8");
- b = (b + 256) % 256;
- bytes.push("0".concat(b.toString(16)).slice(-2));
- }
- let lines = [];
- for (let i = 0; i < bytes.length; i += 8) {
- lines.push(bytes.slice(i, i + 8).join(","));
- }
- return lines.join("\n");
- }
-}
-
-
-export class AmountNbo extends PackedArenaObject {
- size() {
- return 24;
- }
-
- toJson(): any {
- let a = new DefaultArena();
- let am = new Amount(null, a);
- am.fromNbo(this);
- let json = am.toJson();
- a.destroy();
- return json;
- }
-}
-
-
-export class EddsaPrivateKey extends PackedArenaObject {
- static create(a?: Arena): EddsaPrivateKey {
- let obj = new EddsaPrivateKey(a);
- obj.nativePtr = emscAlloc.eddsa_key_create();
- return obj;
- }
-
- size() {
- return 32;
- }
-
- getPublicKey(a?: Arena): EddsaPublicKey {
- let obj = new EddsaPublicKey(a);
- obj.nativePtr = emscAlloc.eddsa_public_key_from_private(this.nativePtr);
- return obj;
- }
-
- static fromCrock: (string) => EddsaPrivateKey;
-}
-mixinStatic(EddsaPrivateKey, fromCrock);
-
-
-function fromCrock(s: string) {
- let x = new this();
- x.alloc();
- x.loadCrock(s);
- return x;
-}
-
-
-function mixin(obj, method, name?: string) {
- if (!name) {
- name = method.name;
- }
- if (!name) {
- throw Error("Mixin needs a name.");
- }
- obj.prototype[method.name] = method;
-}
-
-
-function mixinStatic(obj, method, name?: string) {
- if (!name) {
- name = method.name;
- }
- if (!name) {
- throw Error("Mixin needs a name.");
- }
- obj[method.name] = method;
-}
-
-
-export class EddsaPublicKey extends PackedArenaObject {
- size() {
- return 32;
- }
-
- static fromCrock: (s: string) => EddsaPublicKey;
-}
-mixinStatic(EddsaPublicKey, fromCrock);
-
-function makeFromCrock(decodeFn: (p: number, s: number) => number) {
- function fromCrock(s: string, a?: Arena) {
- let obj = new this(a);
- let buf = ByteArray.fromCrock(s);
- obj.setNative(decodeFn(buf.getNative(),
- buf.size()));
- buf.destroy();
- return obj;
- }
-
- return fromCrock;
-}
-
-function makeToCrock(encodeFn: (po: number,
- ps: number) => number): () => string {
- function toCrock() {
- let ptr = emscAlloc.malloc(PTR_SIZE);
- let size = emscAlloc.rsa_blinding_key_encode(this.nativePtr, ptr);
- let res = new ByteArray(size, Module.getValue(ptr, '*'));
- let s = res.toCrock();
- emsc.free(ptr);
- res.destroy();
- return s;
- }
-
- return toCrock;
-}
-
-export class RsaBlindingKey extends ArenaObject {
- static create(len: number, a?: Arena) {
- let o = new RsaBlindingKey(a);
- o.nativePtr = emscAlloc.rsa_blinding_key_create(len);
- return o;
- }
-
- static fromCrock: (s: string, a?: Arena) => RsaBlindingKey;
- toCrock = makeToCrock(emscAlloc.rsa_blinding_key_encode);
-
- destroy() {
- // TODO
- }
-}
-mixinStatic(RsaBlindingKey, makeFromCrock(emscAlloc.rsa_blinding_key_decode));
-
-
-export class HashCode extends PackedArenaObject {
- size() {
- return 64;
- }
-
- static fromCrock: (s: string) => HashCode;
-
- random(qualStr: string) {
- let qual: RandomQuality;
- switch (qualStr) {
- case "weak":
- qual = RandomQuality.WEAK;
- break;
- case "strong":
- case null:
- case undefined:
- qual = RandomQuality.STRONG;
- break;
- case "nonce":
- qual = RandomQuality.NONCE;
- break;
- default:
- throw Error(`unknown crypto quality: ${qual}`);
- }
- this.alloc();
- emsc.hash_create_random(qual, this.nativePtr);
- }
-}
-mixinStatic(HashCode, fromCrock);
-
-
-export class ByteArray extends PackedArenaObject {
- private allocatedSize: number;
-
- size() {
- return this.allocatedSize;
- }
-
- constructor(desiredSize: number, init: number, a?: Arena) {
- super(a);
- if (init === undefined || init === null) {
- this.nativePtr = emscAlloc.malloc(desiredSize);
- } else {
- this.nativePtr = init;
- }
- this.allocatedSize = desiredSize;
- }
-
- static fromString(s: string, a?: Arena): ByteArray {
- let hstr = emscAlloc.malloc(s.length + 1);
- Module.writeStringToMemory(s, hstr);
- return new ByteArray(s.length, hstr, a);
- }
-
- static fromCrock(s: string, a?: Arena): ByteArray {
- let hstr = emscAlloc.malloc(s.length + 1);
- Module.writeStringToMemory(s, hstr);
- let decodedLen = Math.floor((s.length * 5) / 8);
- let ba = new ByteArray(decodedLen, null, a);
- let res = emsc.string_to_data(hstr, s.length, ba.nativePtr, decodedLen);
- emsc.free(hstr);
- if (res != GNUNET_OK) {
- throw Error("decoding failed");
- }
- return ba;
- }
-}
-
-
-export class EccSignaturePurpose extends PackedArenaObject {
- size() {
- return this.payloadSize + 8;
- }
-
- payloadSize: number;
-
- constructor(purpose: SignaturePurpose,
- payload: PackedArenaObject,
- a?: Arena) {
- super(a);
- this.nativePtr = emscAlloc.purpose_create(purpose,
- payload.nativePtr,
- payload.size());
- this.payloadSize = payload.size();
- }
-}
-
-
-abstract class SignatureStruct {
- abstract fieldTypes(): Array<any>;
-
- abstract purpose(): SignaturePurpose;
-
- private members: any = {};
-
- constructor(x: { [name: string]: any }) {
- for (let k in x) {
- this.set(k, x[k]);
- }
- }
-
- toPurpose(a?: Arena): EccSignaturePurpose {
- let totalSize = 0;
- for (let f of this.fieldTypes()) {
- let name = f[0];
- let member = this.members[name];
- if (!member) {
- throw Error(`Member ${name} not set`);
- }
- totalSize += member.size();
- }
-
- let buf = emscAlloc.malloc(totalSize);
- let ptr = buf;
- for (let f of this.fieldTypes()) {
- let name = f[0];
- let member = this.members[name];
- let size = member.size();
- emsc.memmove(ptr, member.nativePtr, size);
- ptr += size;
- }
- let ba = new ByteArray(totalSize, buf, a);
- return new EccSignaturePurpose(this.purpose(), ba);
- }
-
-
- toJson() {
- let res: any = {};
- for (let f of this.fieldTypes()) {
- let name = f[0];
- let member = this.members[name];
- if (!member) {
- throw Error(`Member ${name} not set`);
- }
- res[name] = member.toJson();
- }
- res["purpose"] = this.purpose();
- return res;
- }
-
- protected set(name: string, value: PackedArenaObject) {
- let typemap: any = {};
- for (let f of this.fieldTypes()) {
- typemap[f[0]] = f[1];
- }
- if (!(name in typemap)) {
- throw Error(`Key ${name} not found`);
- }
- if (!(value instanceof typemap[name])) {
- throw Error("Wrong type for ${name}");
- }
- this.members[name] = value;
- }
-}
-
-
-// It's redundant, but more type safe.
-export interface WithdrawRequestPS_Args {
- reserve_pub: EddsaPublicKey;
- amount_with_fee: AmountNbo;
- withdraw_fee: AmountNbo;
- h_denomination_pub: HashCode;
- h_coin_envelope: HashCode;
-}
-
-
-export class WithdrawRequestPS extends SignatureStruct {
- constructor(w: WithdrawRequestPS_Args) {
- super(w);
- }
-
- purpose() {
- return SignaturePurpose.RESERVE_WITHDRAW;
- }
-
- fieldTypes() {
- return [
- ["reserve_pub", EddsaPublicKey],
- ["amount_with_fee", AmountNbo],
- ["withdraw_fee", AmountNbo],
- ["h_denomination_pub", HashCode],
- ["h_coin_envelope", HashCode]
- ];
- }
-}
-
-
-export class AbsoluteTimeNbo extends PackedArenaObject {
- static fromTalerString(s: string): AbsoluteTimeNbo {
- let x = new AbsoluteTimeNbo();
- x.alloc();
- let r = /Date\(([0-9]+)\)/;
- let m = r.exec(s);
- if (m.length != 2) {
- throw Error();
- }
- let n = parseInt(m[1]) * 1000000;
- // XXX: This only works up to 54 bit numbers.
- set64(x.getNative(), n);
- return x;
- }
-
- size() {
- return 8;
- }
-}
-
-
-// XXX: This only works up to 54 bit numbers.
-function set64(p: number, n: number) {
- for (let i = 0; i < 8; ++i) {
- Module.setValue(p + (7 - i), n & 0xFF, "i8");
- n = Math.floor(n / 256);
- }
-
-}
-
-
-export class UInt64 extends PackedArenaObject {
- static fromNumber(n: number): UInt64 {
- let x = new UInt64();
- x.alloc();
- set64(x.getNative(), n);
- return x;
- }
-
- size() {
- return 8;
- }
-}
-
-
-// It's redundant, but more type safe.
-export interface DepositRequestPS_Args {
- h_contract: HashCode;
- h_wire: HashCode;
- timestamp: AbsoluteTimeNbo;
- refund_deadline: AbsoluteTimeNbo;
- transaction_id: UInt64;
- amount_with_fee: AmountNbo;
- deposit_fee: AmountNbo;
- merchant: EddsaPublicKey;
- coin_pub: EddsaPublicKey;
-}
-
-
-export class DepositRequestPS extends SignatureStruct {
- constructor(w: DepositRequestPS_Args) {
- super(w);
- }
-
- purpose() {
- return SignaturePurpose.WALLET_COIN_DEPOSIT;
- }
-
- fieldTypes() {
- return [
- ["h_contract", HashCode],
- ["h_wire", HashCode],
- ["timestamp", AbsoluteTimeNbo],
- ["refund_deadline", AbsoluteTimeNbo],
- ["transaction_id", UInt64],
- ["amount_with_fee", AmountNbo],
- ["deposit_fee", AmountNbo],
- ["merchant", EddsaPublicKey],
- ["coin_pub", EddsaPublicKey],
- ];
- }
-}
-
-export interface DenominationKeyValidityPS_args {
- master: EddsaPublicKey;
- start: AbsoluteTimeNbo;
- expire_withdraw: AbsoluteTimeNbo;
- expire_spend: AbsoluteTimeNbo;
- expire_legal: AbsoluteTimeNbo;
- value: AmountNbo;
- fee_withdraw: AmountNbo;
- fee_deposit: AmountNbo;
- fee_refresh: AmountNbo;
- denom_hash: HashCode;
-}
-
-export class DenominationKeyValidityPS extends SignatureStruct {
- constructor(w: DenominationKeyValidityPS_args) {
- super(w);
- }
-
- purpose() {
- return SignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY;
- }
-
- fieldTypes() {
- return [
- ["master", EddsaPublicKey],
- ["start", AbsoluteTimeNbo],
- ["expire_withdraw", AbsoluteTimeNbo],
- ["expire_spend", AbsoluteTimeNbo],
- ["expire_legal", AbsoluteTimeNbo],
- ["value", AmountNbo],
- ["fee_withdraw", AmountNbo],
- ["fee_deposit", AmountNbo],
- ["fee_refresh", AmountNbo],
- ["denom_hash", HashCode]
- ];
- }
-}
-
-
-interface Encodeable {
- encode(arena?: Arena): ByteArray;
-}
-
-function makeEncode(encodeFn) {
- function encode(arena?: Arena) {
- let ptr = emscAlloc.malloc(PTR_SIZE);
- let len = encodeFn(this.getNative(), ptr);
- let res = new ByteArray(len, null, arena);
- res.setNative(Module.getValue(ptr, '*'));
- emsc.free(ptr);
- return res;
- }
-
- return encode;
-}
-
-
-export class RsaPublicKey extends ArenaObject implements Encodeable {
- static fromCrock: (s: string, a?: Arena) => RsaPublicKey;
-
- toCrock() {
- return this.encode().toCrock();
- }
-
- destroy() {
- emsc.rsa_public_key_free(this.nativePtr);
- this.nativePtr = 0;
- }
-
- encode: (arena?: Arena) => ByteArray;
-}
-mixinStatic(RsaPublicKey, makeFromCrock(emscAlloc.rsa_public_key_decode));
-mixin(RsaPublicKey, makeEncode(emscAlloc.rsa_public_key_encode));
-
-
-export class EddsaSignature extends PackedArenaObject {
- size() {
- return 64;
- }
-}
-
-
-export class RsaSignature extends ArenaObject implements Encodeable {
- static fromCrock: (s: string, a?: Arena) => RsaSignature;
-
- encode: (arena?: Arena) => ByteArray;
-
- destroy() {
- emsc.rsa_signature_free(this.getNative());
- this.setNative(0);
- }
-}
-mixinStatic(RsaSignature, makeFromCrock(emscAlloc.rsa_signature_decode));
-mixin(RsaSignature, makeEncode(emscAlloc.rsa_signature_encode));
-
-
-export function rsaBlind(hashCode: HashCode,
- blindingKey: RsaBlindingKey,
- pkey: RsaPublicKey,
- arena?: Arena): ByteArray {
- let ptr = emscAlloc.malloc(PTR_SIZE);
- let s = emscAlloc.rsa_blind(hashCode.nativePtr,
- blindingKey.nativePtr,
- pkey.nativePtr,
- ptr);
- return new ByteArray(s, Module.getValue(ptr, '*'), arena);
-}
-
-
-export function eddsaSign(purpose: EccSignaturePurpose,
- priv: EddsaPrivateKey,
- a?: Arena): EddsaSignature {
- let sig = new EddsaSignature(a);
- sig.alloc();
- let res = emsc.eddsa_sign(priv.nativePtr, purpose.nativePtr, sig.nativePtr);
- if (res < 1) {
- throw Error("EdDSA signing failed");
- }
- return sig;
-}
-
-
-export function eddsaVerify(purposeNum: number,
- verify: EccSignaturePurpose,
- sig: EddsaSignature,
- pub: EddsaPublicKey,
- a?: Arena): boolean {
- let r = emsc.eddsa_verify(purposeNum,
- verify.nativePtr,
- sig.nativePtr,
- pub.nativePtr);
- if (r === GNUNET_OK) {
- return true;
- }
- return false;
-}
-
-
-export function rsaUnblind(sig: RsaSignature,
- bk: RsaBlindingKey,
- pk: RsaPublicKey,
- a?: Arena): RsaSignature {
- let x = new RsaSignature(a);
- x.nativePtr = emscAlloc.rsa_unblind(sig.nativePtr,
- bk.nativePtr,
- pk.nativePtr);
- return x;
-}
diff --git a/extension/lib/wallet/helpers.ts b/extension/lib/wallet/helpers.ts
deleted file mode 100644
index 99913e558..000000000
--- a/extension/lib/wallet/helpers.ts
+++ /dev/null
@@ -1,65 +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, If not, see <http://www.gnu.org/licenses/>
- */
-
-
-/**
- * Smaller helper functions that do not depend
- * on the emscripten machinery.
- */
-
-import {AmountJson} from "./types";
-
-export function substituteFulfillmentUrl(url: string, vars) {
- url = url.replace("${H_contract}", vars.H_contract);
- url = url.replace("${$}", "$");
- return url;
-}
-
-
-export function amountToPretty(amount: AmountJson): string {
- let x = amount.value + amount.fraction / 1e6;
- return `${x} ${amount.currency}`;
-}
-
-
-/**
- * Canonicalize a base url, typically for the mint.
- *
- * See http://api.taler.net/wallet.html#general
- */
-export function canonicalizeBaseUrl(url) {
- let x = new URI(url);
- if (!x.protocol()) {
- x.protocol("https");
- }
- x.path(x.path() + "/").normalizePath();
- x.fragment();
- x.query();
- return x.href()
-}
-
-
-export function parsePrettyAmount(pretty: string): AmountJson {
- const res = /([0-9]+)(.[0-9]+)?\s*(\w+)/.exec(pretty);
- if (!res) {
- return null;
- }
- return {
- value: parseInt(res[1], 10),
- fraction: res[2] ? (parseFloat(`0.${res[2]}`) * 1e-6) : 0,
- currency: res[3]
- }
-} \ No newline at end of file
diff --git a/extension/lib/wallet/http.ts b/extension/lib/wallet/http.ts
deleted file mode 100644
index 3f7244e40..000000000
--- a/extension/lib/wallet/http.ts
+++ /dev/null
@@ -1,84 +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, If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- * Helpers for doing XMLHttpRequest-s that are based on ES6 promises.
- * @module Http
- * @author Florian Dold
- */
-
-"use strict";
-
-
-export interface HttpResponse {
- status: number;
- responseText: string;
-}
-
-
-export class BrowserHttpLib {
- req(method: string,
- url: string|uri.URI,
- options?: any): Promise<HttpResponse> {
- let urlString: string;
- if (url instanceof URI) {
- urlString = url.href();
- } else if (typeof url === "string") {
- urlString = url;
- }
-
- return new Promise((resolve, reject) => {
- let myRequest = new XMLHttpRequest();
- myRequest.open(method, urlString);
- if (options && options.req) {
- myRequest.send(options.req);
- } else {
- myRequest.send();
- }
- myRequest.addEventListener("readystatechange", (e) => {
- if (myRequest.readyState == XMLHttpRequest.DONE) {
- let resp = {
- status: myRequest.status,
- responseText: myRequest.responseText
- };
- resolve(resp);
- }
- });
- });
- }
-
-
- get(url: string|uri.URI) {
- return this.req("get", url);
- }
-
-
- postJson(url: string|uri.URI, body) {
- return this.req("post", url, {req: JSON.stringify(body)});
- }
-
-
- postForm(url: string|uri.URI, form) {
- return this.req("post", url, {req: form});
- }
-}
-
-
-export class RequestException {
- constructor(detail) {
-
- }
-} \ No newline at end of file
diff --git a/extension/lib/wallet/query.ts b/extension/lib/wallet/query.ts
deleted file mode 100644
index 62411dab3..000000000
--- a/extension/lib/wallet/query.ts
+++ /dev/null
@@ -1,360 +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, If not, see <http://www.gnu.org/licenses/>
- */
-
-
-/**
- * Database query abstractions.
- * @module Query
- * @author Florian Dold
- */
-
-"use strict";
-
-
-export function Query(db) {
- return new QueryRoot(db);
-}
-
-/**
- * Stream that can be filtered, reduced or joined
- * with indices.
- */
-export interface QueryStream<T> {
- indexJoin<S>(storeName: string,
- indexName: string,
- keyFn: (obj: any) => any): QueryStream<[T,S]>;
- filter(f: (any) => boolean): QueryStream<T>;
- reduce<S>(f: (v: T, acc: S) => S, start?: S): Promise<S>;
-}
-
-
-/**
- * Get an unresolved promise together with its extracted resolve / reject
- * function.
- *
- * @returns {{resolve: any, reject: any, promise: Promise<T>}}
- */
-function openPromise<T>() {
- let resolve, reject;
- const promise = new Promise<T>((res, rej) => {
- resolve = res;
- reject = rej;
- });
- return {resolve, reject, promise};
-}
-
-
-abstract class QueryStreamBase<T> implements QueryStream<T> {
- abstract subscribe(f: (isDone: boolean,
- value: any,
- tx: IDBTransaction) => void);
-
- root: QueryRoot;
-
- constructor(root: QueryRoot) {
- this.root = root;
- }
-
- indexJoin<S>(storeName: string,
- indexName: string,
- key: any): QueryStream<[T,S]> {
- this.root.addWork(null, storeName, false);
- return new QueryStreamIndexJoin(this, storeName, indexName, key);
- }
-
- filter(f: (any) => boolean): QueryStream<T> {
- return new QueryStreamFilter(this, f);
- }
-
- reduce(f, acc?): Promise<any> {
- let leakedResolve;
- let p = new Promise((resolve, reject) => {
- leakedResolve = resolve;
- });
-
- this.subscribe((isDone, value) => {
- if (isDone) {
- leakedResolve(acc);
- return;
- }
- acc = f(value, acc);
- });
-
- return Promise.resolve()
- .then(() => this.root.finish())
- .then(() => p);
- }
-}
-
-
-class QueryStreamFilter<T> extends QueryStreamBase<T> {
- s: QueryStreamBase<T>;
- filterFn;
-
- constructor(s: QueryStreamBase<T>, filterFn) {
- super(s.root);
- this.s = s;
- this.filterFn = filterFn;
- }
-
- subscribe(f) {
- this.s.subscribe((isDone, value, tx) => {
- if (isDone) {
- f(true, undefined, tx);
- return;
- }
- if (this.filterFn(value)) {
- f(false, value, tx)
- }
- });
- }
-}
-
-
-class QueryStreamIndexJoin<T> extends QueryStreamBase<T> {
- s: QueryStreamBase<T>;
- storeName;
- key;
- indexName;
-
- constructor(s, storeName: string, indexName: string, key: any) {
- super(s.root);
- this.s = s;
- this.storeName = storeName;
- this.key = key;
- this.indexName = indexName;
- }
-
- subscribe(f) {
- this.s.subscribe((isDone, value, tx) => {
- if (isDone) {
- f(true, undefined, tx);
- return;
- }
- let s = tx.objectStore(this.storeName).index(this.indexName);
- let req = s.openCursor(IDBKeyRange.only(this.key(value)));
- req.onsuccess = () => {
- let cursor = req.result;
- if (cursor) {
- f(false, [value, cursor.value], tx);
- cursor.continue();
- } else {
- f(true, undefined, tx);
- }
- }
- });
- }
-}
-
-
-class IterQueryStream<T> extends QueryStreamBase<T> {
- private storeName;
- private options;
-
- constructor(qr, storeName, options) {
- super(qr);
- this.options = options;
- this.storeName = storeName;
- }
-
- subscribe(f) {
- let doIt = (tx) => {
- const {indexName = void 0, only = void 0} = this.options;
- let s;
- if (indexName !== void 0) {
- s = tx.objectStore(this.storeName)
- .index(this.options.indexName);
- } else {
- s = tx.objectStore(this.storeName);
- }
- let kr = undefined;
- if (only !== void 0) {
- kr = IDBKeyRange.only(this.options.only);
- }
- let req = s.openCursor(kr);
- req.onsuccess = (e) => {
- let cursor: IDBCursorWithValue = req.result;
- if (cursor) {
- f(false, cursor.value, tx);
- cursor.continue();
- } else {
- f(true, undefined, tx);
- }
- }
- };
-
- this.root.addWork(doIt, null, false);
- }
-}
-
-
-class QueryRoot {
- private work = [];
- private db: IDBDatabase;
- private stores = new Set();
- private kickoffPromise;
-
- /**
- * Some operations is a write operation,
- * and we need to do a "readwrite" transaction/
- */
- private hasWrite;
-
- constructor(db) {
- this.db = db;
- }
-
- iter<T>(storeName, {only = void 0, indexName = void 0} = {}): QueryStream<T> {
- this.stores.add(storeName);
- return new IterQueryStream(this, storeName, {only, indexName});
- }
-
- /**
- * Put an object into the given object store.
- * Overrides if an existing object with the same key exists
- * in the store.
- */
- put(storeName, val): QueryRoot {
- let doPut = (tx: IDBTransaction) => {
- tx.objectStore(storeName).put(val);
- };
- this.addWork(doPut, storeName, true);
- return this;
- }
-
-
- /**
- * Add all object from an iterable to the given object store.
- * Fails if the object's key is already present
- * in the object store.
- */
- putAll(storeName, iterable): QueryRoot {
- const doPutAll = (tx: IDBTransaction) => {
- for (const obj of iterable) {
- tx.objectStore(storeName).put(obj);
- }
- };
- this.addWork(doPutAll, storeName, true);
- return this;
- }
-
- /**
- * Add an object to the given object store.
- * Fails if the object's key is already present
- * in the object store.
- */
- add(storeName, val): QueryRoot {
- const doAdd = (tx: IDBTransaction) => {
- tx.objectStore(storeName).add(val);
- };
- this.addWork(doAdd, storeName, true);
- return this;
- }
-
- /**
- * Get one object from a store by its key.
- */
- get(storeName, key): Promise<any> {
- if (key === void 0) {
- throw Error("key must not be undefined");
- }
-
- const {resolve, promise} = openPromise();
-
- const doGet = (tx) => {
- const req = tx.objectStore(storeName).get(key);
- req.onsuccess = (r) => {
- resolve(req.result);
- };
- };
-
- this.addWork(doGet, storeName, false);
- return Promise.resolve()
- .then(() => this.finish())
- .then(() => promise);
- }
-
- /**
- * Get one object from a store by its key.
- */
- getIndexed(storeName, indexName, key): Promise<any> {
- if (key === void 0) {
- throw Error("key must not be undefined");
- }
-
- const {resolve, promise} = openPromise();
-
- const doGetIndexed = (tx) => {
- const req = tx.objectStore(storeName).index(indexName).get(key);
- req.onsuccess = (r) => {
- resolve(req.result);
- };
- };
-
- this.addWork(doGetIndexed, storeName, false);
- return Promise.resolve()
- .then(() => this.finish())
- .then(() => promise);
- }
-
- /**
- * Finish the query, and start the query in the first place if necessary.
- */
- finish(): Promise<void> {
- if (this.kickoffPromise) {
- return this.kickoffPromise;
- }
- this.kickoffPromise = new Promise((resolve, reject) => {
- const mode = this.hasWrite ? "readwrite" : "readonly";
- const tx = this.db.transaction(Array.from(this.stores), mode);
- tx.oncomplete = () => {
- resolve();
- };
- for (let w of this.work) {
- w(tx);
- }
- });
- return this.kickoffPromise;
- }
-
- /**
- * Delete an object by from the given object store.
- */
- delete(storeName: string, key): QueryRoot {
- const doDelete = (tx) => {
- tx.objectStore(storeName).delete(key);
- };
- this.addWork(doDelete, storeName, true);
- return this;
- }
-
- /**
- * Low-level function to add a task to the internal work queue.
- */
- addWork(workFn: (IDBTransaction) => void,
- storeName: string,
- isWrite: boolean) {
- if (storeName) {
- this.stores.add(storeName);
- }
- if (isWrite) {
- this.hasWrite = true;
- }
- if (workFn) {
- this.work.push(workFn);
- }
- }
-} \ No newline at end of file
diff --git a/extension/lib/wallet/types.ts b/extension/lib/wallet/types.ts
deleted file mode 100644
index 9c7b21b7c..000000000
--- a/extension/lib/wallet/types.ts
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- This file is part of TALER
- (C) 2015 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, If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- * Common types that are used by Taler.
- *
- * Note most types are defined in wallet.ts, types that
- * are defined in types.ts are intended to be used by components
- * that do not depend on the whole wallet implementation (which depends on
- * emscripten).
- */
-
-import {Checkable} from "./checkable";
-
-@Checkable.Class
-export class AmountJson {
- @Checkable.Number
- value: number;
-
- @Checkable.Number
- fraction: number;
-
- @Checkable.String
- currency: string;
-
- static checked: (obj: any) => AmountJson;
-}
-
-
-@Checkable.Class
-export class CreateReserveResponse {
- /**
- * Mint URL where the bank should create the reserve.
- * The URL is canonicalized in the response.
- */
- @Checkable.String
- mint: string;
-
- @Checkable.String
- reservePub: string;
-
- static checked: (obj: any) => CreateReserveResponse;
-}
-
-
-@Checkable.Class
-export class Denomination {
- @Checkable.Value(AmountJson)
- value: AmountJson;
-
- @Checkable.String
- denom_pub: string;
-
- @Checkable.Value(AmountJson)
- fee_withdraw: AmountJson;
-
- @Checkable.Value(AmountJson)
- fee_deposit: AmountJson;
-
- @Checkable.Value(AmountJson)
- fee_refresh: AmountJson;
-
- @Checkable.String
- stamp_start: string;
-
- @Checkable.String
- stamp_expire_withdraw: string;
-
- @Checkable.String
- stamp_expire_legal: string;
-
- @Checkable.String
- stamp_expire_deposit: string;
-
- @Checkable.String
- master_sig: string;
-
- @Checkable.Optional(Checkable.String)
- pub_hash: string;
-
- static checked: (obj: any) => Denomination;
-}
-
-
-export interface IMintInfo {
- baseUrl: string;
- masterPublicKey: string;
- denoms: Denomination[];
-}
-
-export interface ReserveCreationInfo {
- mintInfo: IMintInfo;
- selectedDenoms: Denomination[];
- withdrawFee: AmountJson;
- overhead: AmountJson;
-}
-
-
-export interface PreCoin {
- coinPub: string;
- coinPriv: string;
- reservePub: string;
- denomPub: string;
- blindingKey: string;
- withdrawSig: string;
- coinEv: string;
- mintBaseUrl: string;
- coinValue: AmountJson;
-}
-
-
-export interface Reserve {
- mint_base_url: string
- reserve_priv: string;
- reserve_pub: string;
-}
-
-
-export interface CoinPaySig {
- coin_sig: string;
- coin_pub: string;
- ub_sig: string;
- denom_pub: string;
- f: AmountJson;
-}
-
-
-export interface Coin {
- coinPub: string;
- coinPriv: string;
- denomPub: string;
- denomSig: string;
- currentAmount: AmountJson;
- mintBaseUrl: string;
-}
-
-
-export type PayCoinInfo = Array<{ updatedCoin: Coin, sig: CoinPaySig }>;
-
-
-export namespace Amounts {
- export interface Result {
- amount: AmountJson;
- // Was there an over-/underflow?
- saturated: boolean;
- }
-
- function getMaxAmount(currency: string): AmountJson {
- return {
- currency,
- value: Number.MAX_SAFE_INTEGER,
- fraction: 2**32,
- }
- }
-
- export function getZero(currency: string): AmountJson {
- return {
- currency,
- value: 0,
- fraction: 0,
- }
- }
-
- export function add(first: AmountJson, ...rest: AmountJson[]): Result {
- let currency = first.currency;
- let value = first.value + Math.floor(first.fraction / 1e6);
- if (value > Number.MAX_SAFE_INTEGER) {
- return {amount: getMaxAmount(currency), saturated: true};
- }
- let fraction = first.fraction % 1e6;
- for (let x of rest) {
- if (x.currency !== currency) {
- throw Error(`Mismatched currency: ${x.currency} and ${currency}`);
- }
-
- value = value + x.value + Math.floor((fraction + x.fraction) / 1e6);
- fraction = (fraction + x.fraction) % 1e6;
- if (value > Number.MAX_SAFE_INTEGER) {
- return {amount: getMaxAmount(currency), saturated: true};
- }
- }
- return {amount: {currency, value, fraction}, saturated: false};
- }
-
-
- export function sub(a: AmountJson, b: AmountJson): Result {
- if (a.currency !== b.currency) {
- throw Error(`Mismatched currency: ${a.currency} and ${b.currency}`);
- }
- let currency = a.currency;
- let value = a.value;
- let fraction = a.fraction;
- if (fraction < b.fraction) {
- if (value < 1) {
- return {amount: {currency, value: 0, fraction: 0}, saturated: true};
- }
- value--;
- fraction += 1e6;
- }
- console.assert(fraction >= b.fraction);
- fraction -= b.fraction;
- if (value < b.value) {
- return {amount: {currency, value: 0, fraction: 0}, saturated: true};
- }
- value -= b.value;
- return {amount: {currency, value, fraction}, saturated: false};
- }
-
- export function cmp(a: AmountJson, b: AmountJson): number {
- if (a.currency !== b.currency) {
- throw Error(`Mismatched currency: ${a.currency} and ${b.currency}`);
- }
- let av = a.value + Math.floor(a.fraction / 1e6);
- let af = a.fraction % 1e6;
- let bv = b.value + Math.floor(b.fraction / 1e6);
- let bf = b.fraction % 1e6;
- switch (true) {
- case av < bv:
- return -1;
- case av > bv:
- return 1;
- case af < bf:
- return -1;
- case af > bf:
- return 1;
- case af == bf:
- return 0;
- default:
- throw Error("assertion failed");
- }
- }
-
- export function copy(a: AmountJson): AmountJson {
- return {
- value: a.value,
- fraction: a.fraction,
- currency: a.currency,
- }
- }
-
- export function isNonZero(a: AmountJson) {
- return a.value > 0 || a.fraction > 0;
- }
-}
-
-
-export interface CheckRepurchaseResult {
- isRepurchase: boolean;
- existingContractHash?: string;
- existingFulfillmentUrl?: string;
-}
-
-
-export interface Notifier {
- notify();
-} \ No newline at end of file
diff --git a/extension/lib/wallet/wallet.ts b/extension/lib/wallet/wallet.ts
deleted file mode 100644
index 92fb92a4a..000000000
--- a/extension/lib/wallet/wallet.ts
+++ /dev/null
@@ -1,957 +0,0 @@
-/*
- This file is part of TALER
- (C) 2015 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, If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- * High-level wallet operations that should be indepentent from the underlying
- * browser extension interface.
- * @module Wallet
- * @author Florian Dold
- */
-
-import {AmountJson, CreateReserveResponse, IMintInfo, Denomination, Notifier} from "./types";
-import {HttpResponse, RequestException} from "./http";
-import {Query} from "./query";
-import {Checkable} from "./checkable";
-import {canonicalizeBaseUrl} from "./helpers";
-import {ReserveCreationInfo, Amounts} from "./types";
-import {PreCoin} from "./types";
-import {Reserve} from "./types";
-import {CryptoApi} from "./cryptoApi";
-import {Coin} from "./types";
-import {PayCoinInfo} from "./types";
-import {CheckRepurchaseResult} from "./types";
-
-"use strict";
-
-
-export interface CoinWithDenom {
- coin: Coin;
- denom: Denomination;
-}
-
-
-@Checkable.Class
-export class KeysJson {
- @Checkable.List(Checkable.Value(Denomination))
- denoms: Denomination[];
-
- @Checkable.String
- master_public_key: string;
-
- @Checkable.Any
- auditors: any[];
-
- @Checkable.String
- list_issue_date: string;
-
- @Checkable.Any
- signkeys: any;
-
- @Checkable.String
- eddsa_pub: string;
-
- @Checkable.String
- eddsa_sig: string;
-
- static checked: (obj: any) => KeysJson;
-}
-
-
-class MintInfo implements IMintInfo {
- baseUrl: string;
- masterPublicKey: string;
- denoms: Denomination[];
-
- constructor(obj: {baseUrl: string} & any) {
- this.baseUrl = obj.baseUrl;
-
- if (obj.denoms) {
- this.denoms = Array.from(<Denomination[]>obj.denoms);
- } else {
- this.denoms = [];
- }
-
- if (typeof obj.masterPublicKey === "string") {
- this.masterPublicKey = obj.masterPublicKey;
- }
- }
-
- static fresh(baseUrl: string): MintInfo {
- return new MintInfo({baseUrl});
- }
-
- /**
- * Merge new key information into the mint info.
- * If the new key information is invalid (missing fields,
- * invalid signatures), an exception is thrown, but the
- * mint info is updated with the new information up until
- * the first error.
- */
- mergeKeys(newKeys: KeysJson, cryptoApi: CryptoApi): Promise<void> {
- if (!this.masterPublicKey) {
- this.masterPublicKey = newKeys.master_public_key;
- }
-
- if (this.masterPublicKey != newKeys.master_public_key) {
- throw Error("public keys do not match");
- }
-
- let ps = newKeys.denoms.map((newDenom) => {
- let found = false;
- for (let oldDenom of this.denoms) {
- if (oldDenom.denom_pub === newDenom.denom_pub) {
- let a = Object.assign({}, oldDenom);
- let b = Object.assign({}, newDenom);
- // pub hash is only there for convenience in the wallet
- delete a["pub_hash"];
- delete b["pub_hash"];
- if (!deepEquals(a, b)) {
- console.log("old/new:");
- console.dir(a);
- console.dir(b);
- throw Error("denomination modified");
- }
- found = true;
- break;
- }
- }
-
- if (found) {
- return Promise.resolve();
- }
-
- return cryptoApi
- .isValidDenom(newDenom, this.masterPublicKey)
- .then((valid) => {
- if (!valid) {
- throw Error("signature on denomination invalid");
- }
- return cryptoApi.hashRsaPub(newDenom.denom_pub);
- })
- .then((h) => {
- this.denoms.push(Object.assign({}, newDenom, {pub_hash: h}));
- });
- });
-
- return Promise.all(ps).then(() => void 0);
- }
-}
-
-
-@Checkable.Class
-export class CreateReserveRequest {
- /**
- * The initial amount for the reserve.
- */
- @Checkable.Value(AmountJson)
- amount: AmountJson;
-
- /**
- * Mint URL where the bank should create the reserve.
- */
- @Checkable.String
- mint: string;
-
- static checked: (obj: any) => CreateReserveRequest;
-}
-
-
-@Checkable.Class
-export class ConfirmReserveRequest {
- /**
- * Public key of then reserve that should be marked
- * as confirmed.
- */
- @Checkable.String
- reservePub: string;
-
- static checked: (obj: any) => ConfirmReserveRequest;
-}
-
-
-@Checkable.Class
-export class MintHandle {
- @Checkable.String
- master_pub: string;
-
- @Checkable.String
- url: string;
-
- static checked: (obj: any) => MintHandle;
-}
-
-
-@Checkable.Class
-export class Contract {
- @Checkable.String
- H_wire: string;
-
- @Checkable.Value(AmountJson)
- amount: AmountJson;
-
- @Checkable.List(Checkable.AnyObject)
- auditors: any[];
-
- @Checkable.String
- expiry: string;
-
- @Checkable.Any
- locations: any;
-
- @Checkable.Value(AmountJson)
- max_fee: AmountJson;
-
- @Checkable.Any
- merchant: any;
-
- @Checkable.String
- merchant_pub: string;
-
- @Checkable.List(Checkable.Value(MintHandle))
- mints: MintHandle[];
-
- @Checkable.List(Checkable.AnyObject)
- products: any[];
-
- @Checkable.String
- refund_deadline: string;
-
- @Checkable.String
- timestamp: string;
-
- @Checkable.Number
- transaction_id: number;
-
- @Checkable.String
- fulfillment_url: string;
-
- @Checkable.Optional(Checkable.String)
- repurchase_correlation_id: string;
-
- static checked: (obj: any) => Contract;
-}
-
-
-@Checkable.Class
-export class Offer {
- @Checkable.Value(Contract)
- contract: Contract;
-
- @Checkable.String
- merchant_sig: string;
-
- @Checkable.String
- H_contract: string;
-
- static checked: (obj: any) => Offer;
-}
-
-
-interface ConfirmPayRequest {
- offer: Offer;
-}
-
-interface MintCoins {
- [mintUrl: string]: CoinWithDenom[];
-}
-
-
-interface CoinPaySig {
- coin_sig: string;
- coin_pub: string;
- ub_sig: string;
- denom_pub: string;
- f: AmountJson;
-}
-
-
-interface Transaction {
- contractHash: string;
- contract: Contract;
- payReq: any;
- merchantSig: string;
-}
-
-
-export interface Badge {
- setText(s: string): void;
- setColor(c: string): void;
-}
-
-
-function deepEquals(x, y) {
- if (x === y) {
- return true;
- }
-
- if (Array.isArray(x) && x.length !== y.length) {
- return false;
- }
-
- var p = Object.keys(x);
- return Object.keys(y).every((i) => p.indexOf(i) !== -1) &&
- p.every((i) => deepEquals(x[i], y[i]));
-}
-
-
-function getTalerStampSec(stamp: string) {
- const m = stamp.match(/\/?Date\(([0-9]*)\)\/?/);
- if (!m) {
- return null;
- }
- return parseInt(m[1]);
-}
-
-
-function isWithdrawableDenom(d: Denomination) {
- const now_sec = (new Date).getTime() / 1000;
- const stamp_withdraw_sec = getTalerStampSec(d.stamp_expire_withdraw);
- // Withdraw if still possible to withdraw within a minute
- if (stamp_withdraw_sec + 60 > now_sec) {
- return true;
- }
- return false;
-}
-
-
-interface HttpRequestLibrary {
- req(method: string,
- url: string|uri.URI,
- options?: any): Promise<HttpResponse>;
-
- get(url: string|uri.URI): Promise<HttpResponse>;
-
- postJson(url: string|uri.URI, body): Promise<HttpResponse>;
-
- postForm(url: string|uri.URI, form): Promise<HttpResponse>;
-}
-
-
-function copy(o) {
- return JSON.parse(JSON.stringify(o));
-}
-
-
-/**
- * Get a list of denominations (with repetitions possible)
- * whose total value is as close as possible to the available
- * amount, but never larger.
- */
-function getWithdrawDenomList(amountAvailable: AmountJson,
- denoms: Denomination[]): Denomination[] {
- let remaining = Amounts.copy(amountAvailable);
- let ds: Denomination[] = [];
-
- denoms = denoms.filter(isWithdrawableDenom);
- denoms.sort((d1, d2) => Amounts.cmp(d2.value, d1.value));
-
- // This is an arbitrary number of coins
- // we can withdraw in one go. It's not clear if this limit
- // is useful ...
- for (let i = 0; i < 1000; i++) {
- let found = false;
- for (let d of denoms) {
- let cost = Amounts.add(d.value, d.fee_withdraw).amount;
- if (Amounts.cmp(remaining, cost) < 0) {
- continue;
- }
- found = true;
- remaining = Amounts.sub(remaining, cost).amount;
- ds.push(d);
- break;
- }
- if (!found) {
- break;
- }
- }
- return ds;
-}
-
-
-export class Wallet {
- private db: IDBDatabase;
- private http: HttpRequestLibrary;
- private badge: Badge;
- private notifier: Notifier;
- public cryptoApi: CryptoApi;
-
-
- constructor(db: IDBDatabase,
- http: HttpRequestLibrary,
- badge: Badge,
- notifier: Notifier) {
- this.db = db;
- this.http = http;
- this.badge = badge;
- this.notifier = notifier;
- this.cryptoApi = new CryptoApi();
- }
-
-
- /**
- * Get mints and associated coins that are still spendable,
- * but only if the sum the coins' remaining value exceeds the payment amount.
- */
- private getPossibleMintCoins(paymentAmount: AmountJson,
- depositFeeLimit: AmountJson,
- allowedMints: MintHandle[]): Promise<MintCoins> {
- // Mapping from mint base URL to list of coins together with their
- // denomination
- let m: MintCoins = {};
-
- function storeMintCoin(mc) {
- let mint: IMintInfo = mc[0];
- let coin: Coin = mc[1];
- let cd = {
- coin: coin,
- denom: mint.denoms.find((e) => e.denom_pub === coin.denomPub)
- };
- if (!cd.denom) {
- throw Error("denom not found (database inconsistent)");
- }
- if (cd.denom.value.currency !== paymentAmount.currency) {
- console.warn("same pubkey for different currencies");
- return;
- }
- let x = m[mint.baseUrl];
- if (!x) {
- m[mint.baseUrl] = [cd];
- } else {
- x.push(cd);
- }
- }
-
- let ps = allowedMints.map((info) => {
- console.log("Checking for merchant's mint", JSON.stringify(info));
- return Query(this.db)
- .iter("mints", {indexName: "pubKey", only: info.master_pub})
- .indexJoin("coins", "mintBaseUrl", (mint) => mint.baseUrl)
- .reduce(storeMintCoin);
- });
-
- return Promise.all(ps).then(() => {
- let ret: MintCoins = {};
-
- if (Object.keys(m).length == 0) {
- console.log("not suitable mints found");
- }
-
- console.dir(m);
-
- // We try to find the first mint where we have
- // enough coins to cover the paymentAmount with fees
- // under depositFeeLimit
-
- nextMint:
- for (let key in m) {
- let coins = m[key];
- // Sort by ascending deposit fee
- coins.sort((o1, o2) => Amounts.cmp(o1.denom.fee_deposit,
- o2.denom.fee_deposit));
- let maxFee = Amounts.copy(depositFeeLimit);
- let minAmount = Amounts.copy(paymentAmount);
- let accFee = Amounts.copy(coins[0].denom.fee_deposit);
- let accAmount = Amounts.getZero(coins[0].coin.currentAmount.currency);
- let usableCoins: CoinWithDenom[] = [];
- nextCoin:
- for (let i = 0; i < coins.length; i++) {
- let coinAmount = Amounts.copy(coins[i].coin.currentAmount);
- let coinFee = coins[i].denom.fee_deposit;
- if (Amounts.cmp(coinAmount, coinFee) <= 0) {
- continue nextCoin;
- }
- accFee = Amounts.add(accFee, coinFee).amount;
- accAmount = Amounts.add(accAmount, coinAmount).amount;
- if (Amounts.cmp(accFee, maxFee) >= 0) {
- // FIXME: if the fees are too high, we have
- // to cover them ourselves ....
- console.log("too much fees");
- continue nextMint;
- }
- usableCoins.push(coins[i]);
- if (Amounts.cmp(accAmount, minAmount) >= 0) {
- ret[key] = usableCoins;
- continue nextMint;
- }
- }
- }
- return ret;
- });
- }
-
-
- /**
- * Record all information that is necessary to
- * pay for a contract in the wallet's database.
- */
- private recordConfirmPay(offer: Offer,
- payCoinInfo: PayCoinInfo,
- chosenMint: string): Promise<void> {
- let payReq = {};
- payReq["amount"] = offer.contract.amount;
- payReq["coins"] = payCoinInfo.map((x) => x.sig);
- payReq["H_contract"] = offer.H_contract;
- payReq["max_fee"] = offer.contract.max_fee;
- payReq["merchant_sig"] = offer.merchant_sig;
- payReq["mint"] = URI(chosenMint).href();
- payReq["refund_deadline"] = offer.contract.refund_deadline;
- payReq["timestamp"] = offer.contract.timestamp;
- payReq["transaction_id"] = offer.contract.transaction_id;
- let t: Transaction = {
- contractHash: offer.H_contract,
- contract: offer.contract,
- payReq: payReq,
- merchantSig: offer.merchant_sig,
- };
-
- console.log("pay request");
- console.dir(payReq);
-
- let historyEntry = {
- type: "pay",
- timestamp: (new Date).getTime(),
- detail: {
- merchantName: offer.contract.merchant.name,
- amount: offer.contract.amount,
- contractHash: offer.H_contract,
- fulfillmentUrl: offer.contract.fulfillment_url
- }
- };
-
- return Query(this.db)
- .put("transactions", t)
- .put("history", historyEntry)
- .putAll("coins", payCoinInfo.map((pci) => pci.updatedCoin))
- .finish()
- .then(() => {
- this.notifier.notify();
- });
- }
-
-
- /**
- * Add a contract to the wallet and sign coins,
- * but do not send them yet.
- */
- confirmPay(offer: Offer): Promise<any> {
- console.log("executing confirmPay");
- return Promise.resolve().then(() => {
- return this.getPossibleMintCoins(offer.contract.amount,
- offer.contract.max_fee,
- offer.contract.mints)
- }).then((mcs) => {
- if (Object.keys(mcs).length == 0) {
- console.log("not confirming payment, insufficient coins");
- return {
- error: "coins-insufficient",
- };
- }
- let mintUrl = Object.keys(mcs)[0];
-
- return this.cryptoApi.signDeposit(offer, mcs[mintUrl])
- .then((ds) => this.recordConfirmPay(offer, ds, mintUrl))
- .then(() => ({}));
- });
- }
-
-
- /**
- * Retrieve all necessary information for looking up the contract
- * with the given hash.
- */
- executePayment(H_contract): Promise<any> {
- return Promise.resolve().then(() => {
- return Query(this.db)
- .get("transactions", H_contract)
- .then((t) => {
- if (!t) {
- return {
- success: false,
- contractFound: false,
- }
- }
- let resp = {
- success: true,
- payReq: t.payReq,
- contract: t.contract,
- };
- return resp;
- });
- });
- }
-
-
- /**
- * First fetch information requred to withdraw from the reserve,
- * then deplete the reserve, withdrawing coins until it is empty.
- */
- private initReserve(reserveRecord) {
- this.updateMintFromUrl(reserveRecord.mint_base_url)
- .then((mint) =>
- this.updateReserve(reserveRecord.reserve_pub, mint)
- .then((reserve) => this.depleteReserve(reserve,
- mint)))
- .then(() => {
- let depleted = {
- type: "depleted-reserve",
- timestamp: (new Date).getTime(),
- detail: {
- reservePub: reserveRecord.reserve_pub,
- }
- };
- return Query(this.db).put("history", depleted).finish();
- })
- .catch((e) => {
- console.error("Failed to deplete reserve");
- console.error(e);
- });
- }
-
-
- /**
- * Create a reserve, but do not flag it as confirmed yet.
- */
- createReserve(req: CreateReserveRequest): Promise<CreateReserveResponse> {
- return this.cryptoApi.createEddsaKeypair().then((keypair) => {
- const now = (new Date).getTime();
- const canonMint = canonicalizeBaseUrl(req.mint);
-
- const reserveRecord = {
- reserve_pub: keypair.pub,
- reserve_priv: keypair.priv,
- mint_base_url: canonMint,
- created: now,
- last_query: null,
- current_amount: null,
- requested_amount: req.amount,
- confirmed: false,
- };
-
-
- const historyEntry = {
- type: "create-reserve",
- timestamp: now,
- detail: {
- requestedAmount: req.amount,
- reservePub: reserveRecord.reserve_pub,
- }
- };
-
- return Query(this.db)
- .put("reserves", reserveRecord)
- .put("history", historyEntry)
- .finish()
- .then(() => {
- let r: CreateReserveResponse = {
- mint: canonMint,
- reservePub: keypair.pub,
- };
- return r;
- });
- });
- }
-
-
- /**
- * Mark an existing reserve as confirmed. The wallet will start trying
- * to withdraw from that reserve. This may not immediately succeed,
- * since the mint might not know about the reserve yet, even though the
- * bank confirmed its creation.
- *
- * A confirmed reserve should be shown to the user in the UI, while
- * an unconfirmed reserve should be hidden.
- */
- confirmReserve(req: ConfirmReserveRequest): Promise<void> {
- const now = (new Date).getTime();
- const historyEntry = {
- type: "confirm-reserve",
- timestamp: now,
- detail: {
- reservePub: req.reservePub,
- }
- };
- return Query(this.db)
- .get("reserves", req.reservePub)
- .then((r) => {
- r.confirmed = true;
- return Query(this.db)
- .put("reserves", r)
- .put("history", historyEntry)
- .finish()
- .then(() => {
- // Do this in the background
- this.initReserve(r);
- });
- });
- }
-
-
- private withdrawExecute(pc: PreCoin): Promise<Coin> {
- return Query(this.db)
- .get("reserves", pc.reservePub)
- .then((r) => {
- let wd: any = {};
- wd.denom_pub = pc.denomPub;
- wd.reserve_pub = pc.reservePub;
- wd.reserve_sig = pc.withdrawSig;
- wd.coin_ev = pc.coinEv;
- let reqUrl = URI("reserve/withdraw").absoluteTo(r.mint_base_url);
- return this.http.postJson(reqUrl, wd);
- })
- .then(resp => {
- if (resp.status != 200) {
- throw new RequestException({
- hint: "Withdrawal failed",
- status: resp.status
- });
- }
- let r = JSON.parse(resp.responseText);
- return this.cryptoApi.rsaUnblind(r.ev_sig, pc.blindingKey, pc.denomPub)
- .then((denomSig) => {
- let coin: Coin = {
- coinPub: pc.coinPub,
- coinPriv: pc.coinPriv,
- denomPub: pc.denomPub,
- denomSig: denomSig,
- currentAmount: pc.coinValue,
- mintBaseUrl: pc.mintBaseUrl,
- };
- return coin;
-
- });
- });
- }
-
- storeCoin(coin: Coin): Promise<void> {
- let historyEntry = {
- type: "withdraw",
- timestamp: (new Date).getTime(),
- detail: {
- coinPub: coin.coinPub,
- }
- };
- return Query(this.db)
- .delete("precoins", coin.coinPub)
- .add("coins", coin)
- .add("history", historyEntry)
- .finish()
- .then(() => {
- this.notifier.notify();
- });
- }
-
-
- /**
- * Withdraw one coins of the given denomination from the given reserve.
- */
- private withdraw(denom: Denomination, reserve: Reserve): Promise<void> {
- console.log("creating pre coin at", new Date());
- return this.cryptoApi
- .createPreCoin(denom, reserve)
- .then((preCoin) => {
- return Query(this.db)
- .put("precoins", preCoin)
- .finish()
- .then(() => this.withdrawExecute(preCoin))
- .then((c) => this.storeCoin(c));
- });
-
- }
-
-
- /**
- * Withdraw coins from a reserve until it is empty.
- */
- private depleteReserve(reserve, mint: MintInfo): Promise<void> {
- let denomsAvailable: Denomination[] = copy(mint.denoms);
- let denomsForWithdraw = getWithdrawDenomList(reserve.current_amount,
- denomsAvailable);
-
- let ps = denomsForWithdraw.map((denom) => {
- console.log("withdrawing", JSON.stringify(denom));
- // Do the withdraw asynchronously, so crypto is interleaved
- // with requests
- return this.withdraw(denom, reserve);
- });
-
- return Promise.all(ps).then(() => void 0);
- }
-
-
- /**
- * Update the information about a reserve that is stored in the wallet
- * by quering the reserve's mint.
- */
- private updateReserve(reservePub: string, mint: MintInfo): Promise<Reserve> {
- return Query(this.db)
- .get("reserves", reservePub)
- .then((reserve) => {
- let reqUrl = URI("reserve/status").absoluteTo(mint.baseUrl);
- reqUrl.query({'reserve_pub': reservePub});
- return this.http.get(reqUrl).then(resp => {
- if (resp.status != 200) {
- throw Error();
- }
- let reserveInfo = JSON.parse(resp.responseText);
- if (!reserveInfo) {
- throw Error();
- }
- let oldAmount = reserve.current_amount;
- let newAmount = reserveInfo.balance;
- reserve.current_amount = reserveInfo.balance;
- let historyEntry = {
- type: "reserve-update",
- timestamp: (new Date).getTime(),
- detail: {
- reservePub,
- oldAmount,
- newAmount
- }
- };
- return Query(this.db)
- .put("reserves", reserve)
- .finish()
- .then(() => reserve);
- });
- });
- }
-
-
- getReserveCreationInfo(baseUrl: string,
- amount: AmountJson): Promise<ReserveCreationInfo> {
- return this.updateMintFromUrl(baseUrl)
- .then((mintInfo: IMintInfo) => {
- let selectedDenoms = getWithdrawDenomList(amount,
- mintInfo.denoms);
-
- let acc = Amounts.getZero(amount.currency);
- for (let d of selectedDenoms) {
- acc = Amounts.add(acc, d.fee_withdraw).amount;
- }
- let actualCoinCost = selectedDenoms
- .map((d: Denomination) => Amounts.add(d.value,
- d.fee_withdraw).amount)
- .reduce((a, b) => Amounts.add(a, b).amount);
- let ret: ReserveCreationInfo = {
- mintInfo,
- selectedDenoms,
- withdrawFee: acc,
- overhead: Amounts.sub(amount, actualCoinCost).amount,
- };
- return ret;
- });
- }
-
-
- /**
- * Update or add mint DB entry by fetching the /keys information.
- * Optionally link the reserve entry to the new or existing
- * mint entry in then DB.
- */
- updateMintFromUrl(baseUrl): Promise<MintInfo> {
- baseUrl = canonicalizeBaseUrl(baseUrl);
- let reqUrl = URI("keys").absoluteTo(baseUrl);
- return this.http.get(reqUrl).then((resp) => {
- if (resp.status != 200) {
- throw Error("/keys request failed");
- }
- let mintKeysJson = KeysJson.checked(JSON.parse(resp.responseText));
-
- return Query(this.db).get("mints", baseUrl).then((r) => {
- let mintInfo;
- console.dir(r);
-
- if (!r) {
- mintInfo = MintInfo.fresh(baseUrl);
- console.log("making fresh mint");
- } else {
- mintInfo = new MintInfo(r);
- console.log("using old mint");
- }
-
- return mintInfo.mergeKeys(mintKeysJson, this.cryptoApi)
- .then(() => {
- return Query(this.db)
- .put("mints", mintInfo)
- .finish()
- .then(() => mintInfo);
- });
-
- });
- });
- }
-
-
- /**
- * Retrieve a mapping from currency name to the amount
- * that is currenctly available for spending in the wallet.
- */
- getBalances(): Promise<any> {
- function collectBalances(c: Coin, byCurrency) {
- let acc: AmountJson = byCurrency[c.currentAmount.currency];
- if (!acc) {
- acc = Amounts.getZero(c.currentAmount.currency);
- }
- byCurrency[c.currentAmount.currency] = Amounts.add(c.currentAmount,
- acc).amount;
- return byCurrency;
- }
-
- return Query(this.db)
- .iter("coins")
- .reduce(collectBalances, {});
- }
-
-
- /**
- * Retrive the full event history for this wallet.
- */
- getHistory(): Promise<any[]> {
- function collect(x, acc) {
- acc.push(x);
- return acc;
- }
-
- return Query(this.db)
- .iter("history", {indexName: "timestamp"})
- .reduce(collect, [])
- }
-
- checkRepurchase(contract: Contract): Promise<CheckRepurchaseResult> {
- if (!contract.repurchase_correlation_id) {
- console.log("no repurchase: no correlation id");
- return Promise.resolve({isRepurchase: false});
- }
- return Query(this.db)
- .getIndexed("transactions",
- "repurchase",
- [contract.merchant_pub, contract.repurchase_correlation_id])
- .then((result: Transaction) => {
- console.log("db result", result);
- let isRepurchase;
- if (result) {
- console.assert(result.contract.repurchase_correlation_id == contract.repurchase_correlation_id);
- return {
- isRepurchase: true,
- existingContractHash: result.contractHash,
- existingFulfillmentUrl: result.contract.fulfillment_url,
- };
- } else {
- return {isRepurchase: false};
- }
- });
- }
-} \ No newline at end of file
diff --git a/extension/lib/wallet/wxApi.ts b/extension/lib/wallet/wxApi.ts
deleted file mode 100644
index 9871b6e7f..000000000
--- a/extension/lib/wallet/wxApi.ts
+++ /dev/null
@@ -1,40 +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, If not, see <http://www.gnu.org/licenses/>
- */
-
-import {AmountJson} from "./types";
-import {ReserveCreationInfo} from "./types";
-
-/**
- * Interface to the wallet through WebExtension messaging.
- */
-
-
-export function getReserveCreationInfo(baseUrl: string,
- amount: AmountJson): Promise<ReserveCreationInfo> {
- let m = {type: "reserve-creation-info", detail: {baseUrl, amount}};
- return new Promise((resolve, reject) => {
- chrome.runtime.sendMessage(m, (resp) => {
- if (resp.error) {
- console.error("error response", resp);
- let e = Error("call to reserve-creation-info failed");
- (e as any).errorResponse = resp;
- reject(e);
- return;
- }
- resolve(resp);
- });
- });
-} \ No newline at end of file
diff --git a/extension/lib/wallet/wxMessaging.ts b/extension/lib/wallet/wxMessaging.ts
deleted file mode 100644
index 740873d88..000000000
--- a/extension/lib/wallet/wxMessaging.ts
+++ /dev/null
@@ -1,230 +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, If not, see <http://www.gnu.org/licenses/>
- */
-
-
-import {Wallet, Offer, Badge, ConfirmReserveRequest, CreateReserveRequest} from "./wallet";
-import {deleteDb, exportDb, openTalerDb} from "./db";
-import {BrowserHttpLib} from "./http";
-import {Checkable} from "./checkable";
-import {AmountJson} from "./types";
-import Port = chrome.runtime.Port;
-import {Notifier} from "./types";
-import {Contract} from "./wallet";
-
-"use strict";
-
-/**
- * Messaging for the WebExtensions wallet. Should contain
- * parts that are specific for WebExtensions, but as little business
- * logic as possible.
- *
- * @author Florian Dold
- */
-
-
-type Handler = (detail: any) => Promise<any>;
-
-function makeHandlers(db: IDBDatabase,
- wallet: Wallet): {[msg: string]: Handler} {
- return {
- ["balances"]: function(detail) {
- return wallet.getBalances();
- },
- ["dump-db"]: function(detail) {
- return exportDb(db);
- },
- ["reset"]: function(detail) {
- let tx = db.transaction(db.objectStoreNames, 'readwrite');
- for (let i = 0; i < db.objectStoreNames.length; i++) {
- tx.objectStore(db.objectStoreNames[i]).clear();
- }
- deleteDb();
-
- chrome.browserAction.setBadgeText({text: ""});
- console.log("reset done");
- // Response is synchronous
- return Promise.resolve({});
- },
- ["create-reserve"]: function(detail) {
- const d = {
- mint: detail.mint,
- amount: detail.amount,
- };
- const req = CreateReserveRequest.checked(d);
- return wallet.createReserve(req);
- },
- ["confirm-reserve"]: function(detail) {
- // TODO: make it a checkable
- const d = {
- reservePub: detail.reservePub
- };
- const req = ConfirmReserveRequest.checked(d);
- return wallet.confirmReserve(req);
- },
- ["confirm-pay"]: function(detail) {
- let offer;
- try {
- offer = Offer.checked(detail.offer);
- } catch (e) {
- if (e instanceof Checkable.SchemaError) {
- console.error("schema error:", e.message);
- return Promise.resolve({
- error: "invalid contract",
- hint: e.message,
- detail: detail
- });
- } else {
- throw e;
- }
- }
-
- return wallet.confirmPay(offer);
- },
- ["execute-payment"]: function(detail) {
- return wallet.executePayment(detail.H_contract);
- },
- ["mint-info"]: function(detail) {
- if (!detail.baseUrl) {
- return Promise.resolve({error: "bad url"});
- }
- return wallet.updateMintFromUrl(detail.baseUrl);
- },
- ["reserve-creation-info"]: function(detail) {
- if (!detail.baseUrl || typeof detail.baseUrl !== "string") {
- return Promise.resolve({error: "bad url"});
- }
- let amount = AmountJson.checked(detail.amount);
- return wallet.getReserveCreationInfo(detail.baseUrl, amount);
- },
- ["check-repurchase"]: function(detail) {
- let contract = Contract.checked(detail.contract);
- return wallet.checkRepurchase(contract);
- },
- ["get-history"]: function(detail) {
- // TODO: limit history length
- return wallet.getHistory();
- },
- };
-}
-
-
-class ChromeBadge implements Badge {
- setText(s: string) {
- chrome.browserAction.setBadgeText({text: s});
- }
-
- setColor(c: string) {
- chrome.browserAction.setBadgeBackgroundColor({color: c});
- }
-}
-
-
-function dispatch(handlers, req, sendResponse) {
- if (req.type in handlers) {
- Promise
- .resolve()
- .then(() => {
- const p = handlers[req.type](req.detail);
-
- return p.then((r) => {
- sendResponse(r);
- })
- })
- .catch((e) => {
- console.log(`exception during wallet handler for '${req.type}'`);
- console.log("request", req);
- console.error(e);
- sendResponse({
- error: "exception",
- hint: e.message,
- stack: e.stack.toString()
- });
- });
- // The sendResponse call is async
- return true;
- } else {
- console.error(`Request type ${JSON.stringify(req)} unknown, req ${req.type}`);
- sendResponse({error: "request unknown"});
- // The sendResponse call is sync
- return false;
- }
-}
-
-class ChromeNotifier implements Notifier {
- ports: Port[] = [];
-
- constructor() {
- chrome.runtime.onConnect.addListener((port) => {
- console.log("got connect!");
- this.ports.push(port);
- port.onDisconnect.addListener(() => {
- let i = this.ports.indexOf(port);
- if (i >= 0) {
- this.ports.splice(i, 1);
- } else {
- console.error("port already removed");
- }
- });
- });
- }
-
- notify() {
- console.log("notifying all ports");
- for (let p of this.ports) {
- p.postMessage({notify: true});
- }
- }
-}
-
-
-export function wxMain() {
- chrome.browserAction.setBadgeText({text: ""});
-
- Promise.resolve()
- .then(() => {
- return openTalerDb();
- })
- .catch((e) => {
- console.error("could not open database");
- console.error(e);
- })
- .then((db) => {
- let http = new BrowserHttpLib();
- let badge = new ChromeBadge();
- let notifier = new ChromeNotifier();
- let wallet = new Wallet(db, http, badge, notifier);
- let handlers = makeHandlers(db, wallet);
- chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
- try {
- return dispatch(handlers, req, sendResponse)
- } catch (e) {
- console.log(`exception during wallet handler (dispatch)`);
- console.log("request", req);
- console.error(e);
- sendResponse({
- error: "exception",
- hint: e.message,
- stack: e.stack.toString()
- });
- return false;
- }
- });
- })
- .catch((e) => {
- console.error("could not initialize wallet messaging");
- console.error(e);
- });
-} \ No newline at end of file