aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2018-01-03 14:42:06 +0100
committerFlorian Dold <florian.dold@gmail.com>2018-01-03 14:42:06 +0100
commitfd2cd9c383b07cd681c18137396deae025d98047 (patch)
tree05de454bb6fc0fcb95b66b6ef87bcd555e81b58c
parenteb689d60aca8fc80e27ea60c4e4d7e848b01bea9 (diff)
downloadwallet-core-fd2cd9c383b07cd681c18137396deae025d98047.tar.xz
fix lint issues and separate message types into multiple files
-rw-r--r--.vscode/settings.json3
-rw-r--r--.vscode/tasks.json44
-rw-r--r--gulpfile.js3
-rw-r--r--src/amounts.ts257
-rw-r--r--src/crypto/cryptoApi-test.ts6
-rw-r--r--src/crypto/cryptoApi.ts19
-rw-r--r--src/crypto/cryptoWorker.ts37
-rw-r--r--src/crypto/emscInterface.ts4
-rw-r--r--src/dbTypes.ts811
-rw-r--r--src/helpers.ts3
-rw-r--r--src/query.ts2
-rw-r--r--src/talerTypes.ts632
-rw-r--r--src/types-test.ts16
-rw-r--r--src/types.ts2048
-rw-r--r--src/wallet-test.ts18
-rw-r--r--src/wallet.ts130
-rw-r--r--src/walletTypes.ts572
-rw-r--r--src/webex/messages.ts51
-rw-r--r--src/webex/notify.ts14
-rw-r--r--src/webex/pages/add-auditor.tsx2
-rw-r--r--src/webex/pages/auditors.tsx2
-rw-r--r--src/webex/pages/confirm-contract.tsx9
-rw-r--r--src/webex/pages/confirm-create-reserve.tsx27
-rw-r--r--src/webex/pages/payback.tsx2
-rw-r--r--src/webex/pages/popup.tsx11
-rw-r--r--src/webex/pages/refund.tsx21
-rw-r--r--src/webex/pages/return-coins.tsx7
-rw-r--r--src/webex/pages/tip.tsx27
-rw-r--r--src/webex/pages/tree.tsx3
-rw-r--r--src/webex/renderHtml.tsx17
-rw-r--r--src/webex/wxApi.ts40
-rw-r--r--src/webex/wxBackend.ts19
-rw-r--r--tooling/pogen/dumpTree.ts6
-rw-r--r--tsconfig.json16
-rw-r--r--tslint.json1
35 files changed, 2628 insertions, 2252 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index e17e44c92..565900b97 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,7 +1,7 @@
// Place your settings in this file to overwrite default and user settings.
{
// Use latest language servicesu
- "typescript.tsdk": "node_modules/typescript/lib",
+ "typescript.tsdk": "./node_modules/typescript/lib",
// Defines space handling after a comma delimiter
"typescript.format.insertSpaceAfterCommaDelimiter": true,
// Defines space handling after a semicolon in a for statement
@@ -34,5 +34,6 @@
},
"**/*.js.map": true
},
+ "tslint.enable": true,
"editor.wrappingIndent": "same"
} \ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 000000000..a14159944
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,44 @@
+{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "type": "typescript",
+ "tsconfig": "tsconfig.json",
+ "option": "watch",
+ "problemMatcher": [
+ "$tsc-watch"
+ ],
+ "group": "build",
+ "isBackground": true,
+ "promptOnClose": false
+ },
+ {
+ "type": "typescript",
+ "tsconfig": "tsconfig.json",
+ "problemMatcher": [
+ "$tsc"
+ ],
+ "group": "build"
+ },
+ {
+ "label": "tslint",
+ "type": "shell",
+ "command": "make lint",
+ "problemMatcher": {
+ "owner": "tslint",
+ "applyTo": "allDocuments",
+ "fileLocation": "absolute",
+ "severity": "warning",
+ "pattern": "$tslint5"
+ },
+ "group": "build"
+ },
+ {
+ "label": "My Task",
+ "type": "shell",
+ "command": "echo Hello"
+ }
+ ]
+} \ No newline at end of file
diff --git a/gulpfile.js b/gulpfile.js
index cb385f043..f8e0c90fa 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -107,7 +107,7 @@ const tsBaseArgs = {
experimentalDecorators: true,
module: "commonjs",
sourceMap: true,
- lib: ["ES6", "DOM"],
+ lib: ["es6", "dom"],
noImplicitReturns: true,
noFallthroughCasesInSwitch: true,
strict: true,
@@ -266,6 +266,7 @@ gulp.task("pogen", function (cb) {
*/
function tsconfig(confBase) {
let conf = {
+ compileOnSave: true,
compilerOptions: {},
files: []
};
diff --git a/src/amounts.ts b/src/amounts.ts
new file mode 100644
index 000000000..a31bec3da
--- /dev/null
+++ b/src/amounts.ts
@@ -0,0 +1,257 @@
+/*
+ This file is part of TALER
+ (C) 2018 GNUnet e.V. and INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+
+/**
+ * Types and helper functions for dealing with Taler amounts.
+ */
+
+/**
+ * Imports.
+ */
+import { Checkable } from "./checkable";
+
+/**
+ * Number of fractional units that one value unit represents.
+ */
+export const fractionalBase = 1e8;
+
+/**
+ * Non-negative financial amount. Fractional values are expressed as multiples
+ * of 1e-8.
+ */
+@Checkable.Class()
+export class AmountJson {
+ /**
+ * Value, must be an integer.
+ */
+ @Checkable.Number
+ readonly value: number;
+
+ /**
+ * Fraction, must be an integer. Represent 1/1e8 of a unit.
+ */
+ @Checkable.Number
+ readonly fraction: number;
+
+ /**
+ * Currency of the amount.
+ */
+ @Checkable.String
+ readonly currency: string;
+
+ /**
+ * Verify that a value matches the schema of this class and convert it into a
+ * member.
+ */
+ static checked: (obj: any) => AmountJson;
+}
+
+/**
+ * Result of a possibly overflowing operation.
+ */
+export interface Result {
+ /**
+ * Resulting, possibly saturated amount.
+ */
+ amount: AmountJson;
+ /**
+ * Was there an over-/underflow?
+ */
+ saturated: boolean;
+}
+
+/**
+ * Get the largest amount that is safely representable.
+ */
+export function getMaxAmount(currency: string): AmountJson {
+ return {
+ currency,
+ fraction: 2 ** 32,
+ value: Number.MAX_SAFE_INTEGER,
+ };
+}
+
+/**
+ * Get an amount that represents zero units of a currency.
+ */
+export function getZero(currency: string): AmountJson {
+ return {
+ currency,
+ fraction: 0,
+ value: 0,
+ };
+}
+
+/**
+ * Add two amounts. Return the result and whether
+ * the addition overflowed. The overflow is always handled
+ * by saturating and never by wrapping.
+ *
+ * Throws when currencies don't match.
+ */
+export function add(first: AmountJson, ...rest: AmountJson[]): Result {
+ const currency = first.currency;
+ let value = first.value + Math.floor(first.fraction / fractionalBase);
+ if (value > Number.MAX_SAFE_INTEGER) {
+ return { amount: getMaxAmount(currency), saturated: true };
+ }
+ let fraction = first.fraction % fractionalBase;
+ for (const x of rest) {
+ if (x.currency !== currency) {
+ throw Error(`Mismatched currency: ${x.currency} and ${currency}`);
+ }
+
+ value = value + x.value + Math.floor((fraction + x.fraction) / fractionalBase);
+ fraction = Math.floor((fraction + x.fraction) % fractionalBase);
+ if (value > Number.MAX_SAFE_INTEGER) {
+ return { amount: getMaxAmount(currency), saturated: true };
+ }
+ }
+ return { amount: { currency, value, fraction }, saturated: false };
+}
+
+/**
+ * Subtract two amounts. Return the result and whether
+ * the subtraction overflowed. The overflow is always handled
+ * by saturating and never by wrapping.
+ *
+ * Throws when currencies don't match.
+ */
+export function sub(a: AmountJson, ...rest: AmountJson[]): Result {
+ const currency = a.currency;
+ let value = a.value;
+ let fraction = a.fraction;
+
+ for (const b of rest) {
+ if (b.currency !== currency) {
+ throw Error(`Mismatched currency: ${b.currency} and ${currency}`);
+ }
+ if (fraction < b.fraction) {
+ if (value < 1) {
+ return { amount: { currency, value: 0, fraction: 0 }, saturated: true };
+ }
+ value--;
+ fraction += fractionalBase;
+ }
+ 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 };
+}
+
+/**
+ * Compare two amounts. Returns 0 when equal, -1 when a < b
+ * and +1 when a > b. Throws when currencies don't match.
+ */
+export function cmp(a: AmountJson, b: AmountJson): number {
+ if (a.currency !== b.currency) {
+ throw Error(`Mismatched currency: ${a.currency} and ${b.currency}`);
+ }
+ const av = a.value + Math.floor(a.fraction / fractionalBase);
+ const af = a.fraction % fractionalBase;
+ const bv = b.value + Math.floor(b.fraction / fractionalBase);
+ const bf = b.fraction % fractionalBase;
+ 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");
+ }
+}
+
+/**
+ * Create a copy of an amount.
+ */
+export function copy(a: AmountJson): AmountJson {
+ return {
+ currency: a.currency,
+ fraction: a.fraction,
+ value: a.value,
+ };
+}
+
+/**
+ * Divide an amount. Throws on division by zero.
+ */
+export function divide(a: AmountJson, n: number): AmountJson {
+ if (n === 0) {
+ throw Error(`Division by 0`);
+ }
+ if (n === 1) {
+ return {value: a.value, fraction: a.fraction, currency: a.currency};
+ }
+ const r = a.value % n;
+ return {
+ currency: a.currency,
+ fraction: Math.floor(((r * fractionalBase) + a.fraction) / n),
+ value: Math.floor(a.value / n),
+ };
+}
+
+/**
+ * Check if an amount is non-zero.
+ */
+export function isNonZero(a: AmountJson): boolean {
+ return a.value > 0 || a.fraction > 0;
+}
+
+/**
+ * Parse an amount like 'EUR:20.5' for 20 Euros and 50 ct.
+ */
+export function parse(s: string): AmountJson|undefined {
+ const res = s.match(/([a-zA-Z0-9_*-]+):([0-9])+([.][0-9]+)?/);
+ if (!res) {
+ return undefined;
+ }
+ return {
+ currency: res[1],
+ fraction: Math.round(fractionalBase * Number.parseFloat(res[3] || "0")),
+ value: Number.parseInt(res[2]),
+ };
+}
+
+/**
+ * Convert the amount to a float.
+ */
+export function toFloat(a: AmountJson): number {
+ return a.value + (a.fraction / fractionalBase);
+}
+
+/**
+ * Convert a float to a Taler amount.
+ * Loss of precision possible.
+ */
+export function fromFloat(floatVal: number, currency: string) {
+ return {
+ currency,
+ fraction: Math.floor((floatVal - Math.floor(floatVal)) * fractionalBase),
+ value: Math.floor(floatVal),
+ };
+}
diff --git a/src/crypto/cryptoApi-test.ts b/src/crypto/cryptoApi-test.ts
index d96d69e40..88099e3eb 100644
--- a/src/crypto/cryptoApi-test.ts
+++ b/src/crypto/cryptoApi-test.ts
@@ -16,15 +16,15 @@
// tslint:disable:max-line-length
-import {test} from "ava";
+import { test } from "ava";
import {
DenominationRecord,
DenominationStatus,
ReserveRecord,
-} from "../types";
+} from "../dbTypes";
-import {CryptoApi} from "./cryptoApi";
+import { CryptoApi } from "./cryptoApi";
const masterPub1: string = "CQQZ9DY3MZ1ARMN5K1VKDETS04Y2QCKMMCFHZSWJWWVN82BTTH00";
diff --git a/src/crypto/cryptoApi.ts b/src/crypto/cryptoApi.ts
index 12b1c9708..1f45ba8eb 100644
--- a/src/crypto/cryptoApi.ts
+++ b/src/crypto/cryptoApi.ts
@@ -23,20 +23,27 @@
/**
* Imports.
*/
+import { AmountJson } from "../amounts";
+
import {
- AmountJson,
CoinRecord,
- CoinWithDenom,
- ContractTerms,
DenominationRecord,
- PayCoinInfo,
- PaybackRequest,
PreCoinRecord,
RefreshSessionRecord,
ReserveRecord,
TipPlanchet,
WireFee,
-} from "../types";
+} from "../dbTypes";
+
+import {
+ ContractTerms,
+ PaybackRequest,
+} from "../talerTypes";
+
+import {
+ CoinWithDenom,
+ PayCoinInfo,
+} from "../walletTypes";
import * as timer from "../timer";
diff --git a/src/crypto/cryptoWorker.ts b/src/crypto/cryptoWorker.ts
index 3b954811a..1e5f10c20 100644
--- a/src/crypto/cryptoWorker.ts
+++ b/src/crypto/cryptoWorker.ts
@@ -22,27 +22,33 @@
/**
* Imports.
*/
+import * as Amounts from "../amounts";
+import { AmountJson } from "../amounts";
+
import {
- canonicalJson,
-} from "../helpers";
-import {
- AmountJson,
- Amounts,
- CoinPaySig,
CoinRecord,
CoinStatus,
- CoinWithDenom,
- ContractTerms,
DenominationRecord,
- PayCoinInfo,
- PaybackRequest,
PreCoinRecord,
RefreshPreCoinRecord,
RefreshSessionRecord,
ReserveRecord,
TipPlanchet,
WireFee,
-} from "../types";
+} from "../dbTypes";
+
+import {
+ CoinPaySig,
+ ContractTerms,
+ PaybackRequest,
+} from "../talerTypes";
+
+import {
+ CoinWithDenom,
+ PayCoinInfo,
+} from "../walletTypes";
+
+import { canonicalJson } from "../helpers";
import {
Amount,
@@ -112,6 +118,9 @@ namespace RpcFunctions {
}
+ /**
+ * Create a planchet used for tipping, including the private keys.
+ */
export function createTipPlanchet(denom: DenominationRecord): TipPlanchet {
const denomPub = native.RsaPublicKey.fromCrock(denom.denomPub);
const coinPriv = native.EddsaPrivateKey.create();
@@ -134,8 +143,8 @@ namespace RpcFunctions {
coinPriv: coinPriv.toCrock(),
coinPub: coinPub.toCrock(),
coinValue: denom.value,
- denomPubHash: denomPub.encode().hash().toCrock(),
denomPub: denomPub.encode().toCrock(),
+ denomPubHash: denomPub.encode().hash().toCrock(),
};
return tipPlanchet;
}
@@ -263,8 +272,8 @@ namespace RpcFunctions {
cds: CoinWithDenom[]): PayCoinInfo {
const ret: PayCoinInfo = {
originalCoins: [],
- updatedCoins: [],
sigs: [],
+ updatedCoins: [],
};
const contractTermsHash = hashString(canonicalJson(contractTerms));
@@ -325,8 +334,8 @@ namespace RpcFunctions {
const s: CoinPaySig = {
coin_pub: cd.coin.coinPub,
coin_sig: coinSig,
- denom_pub: cd.coin.denomPub,
contribution: coinSpend.toJson(),
+ denom_pub: cd.coin.denomPub,
ub_sig: cd.coin.denomSig,
};
ret.sigs.push(s);
diff --git a/src/crypto/emscInterface.ts b/src/crypto/emscInterface.ts
index 8c9a34132..ce52c88bd 100644
--- a/src/crypto/emscInterface.ts
+++ b/src/crypto/emscInterface.ts
@@ -26,9 +26,9 @@
/**
* Imports.
*/
-import {AmountJson} from "../types";
+import { AmountJson } from "../amounts";
-import {EmscFunGen, getLib} from "./emscLoader";
+import { EmscFunGen, getLib } from "./emscLoader";
const emscLib = getLib();
diff --git a/src/dbTypes.ts b/src/dbTypes.ts
new file mode 100644
index 000000000..e0adb6fc4
--- /dev/null
+++ b/src/dbTypes.ts
@@ -0,0 +1,811 @@
+/*
+ This file is part of TALER
+ (C) 2018 GNUnet e.V. and INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+
+/**
+ * Types for records stored in the wallet's database.
+ *
+ * Types for the objects in the database should end in "-Record".
+ */
+
+/**
+ * Imports.
+ */
+import { AmountJson } from "./amounts";
+import { Checkable } from "./checkable";
+import {
+ Auditor,
+ CoinPaySig,
+ ContractTerms,
+ Denomination,
+ PayReq,
+ RefundPermission,
+ TipResponse,
+} from "./talerTypes";
+
+
+/**
+ * A reserve record as stored in the wallet's database.
+ */
+export interface ReserveRecord {
+ /**
+ * The reserve public key.
+ */
+ reserve_pub: string;
+
+ /**
+ * The reserve private key.
+ */
+ reserve_priv: string;
+
+ /**
+ * The exchange base URL.
+ */
+ exchange_base_url: string;
+
+ /**
+ * Time when the reserve was created.
+ */
+ created: number;
+
+ /**
+ * Time when the reserve was depleted.
+ * Set to 0 if not depleted yet.
+ */
+ timestamp_depleted: number;
+
+ /**
+ * Time when the reserve was confirmed.
+ *
+ * Set to 0 if not confirmed yet.
+ */
+ timestamp_confirmed: number;
+
+ /**
+ * Current amount left in the reserve
+ */
+ current_amount: AmountJson | null;
+
+ /**
+ * Amount requested when the reserve was created.
+ * When a reserve is re-used (rare!) the current_amount can
+ * be higher than the requested_amount
+ */
+ requested_amount: AmountJson;
+
+ /**
+ * What's the current amount that sits
+ * in precoins?
+ */
+ precoin_amount: AmountJson;
+
+ /**
+ * We got some payback to this reserve. We'll cease to automatically
+ * withdraw money from it.
+ */
+ hasPayback: boolean;
+
+ /**
+ * Wire information for the bank account that
+ * transfered funds for this reserve.
+ */
+ senderWire?: object;
+}
+
+
+/**
+ * Auditor record as stored with currencies in the exchange database.
+ */
+export interface AuditorRecord {
+ /**
+ * Base url of the auditor.
+ */
+ baseUrl: string;
+ /**
+ * Public signing key of the auditor.
+ */
+ auditorPub: string;
+ /**
+ * Time when the auditing expires.
+ */
+ expirationStamp: number;
+}
+
+
+/**
+ * Exchange for currencies as stored in the wallet's currency
+ * information database.
+ */
+export interface ExchangeForCurrencyRecord {
+ /**
+ * FIXME: unused?
+ */
+ exchangePub: string;
+ /**
+ * Base URL of the exchange.
+ */
+ baseUrl: string;
+}
+
+
+/**
+ * Information about a currency as displayed in the wallet's database.
+ */
+export interface CurrencyRecord {
+ /**
+ * Name of the currency.
+ */
+ name: string;
+ /**
+ * Number of fractional digits to show when rendering the currency.
+ */
+ fractionalDigits: number;
+ /**
+ * Auditors that the wallet trusts for this currency.
+ */
+ auditors: AuditorRecord[];
+ /**
+ * Exchanges that the wallet trusts for this currency.
+ */
+ exchanges: ExchangeForCurrencyRecord[];
+}
+
+
+/**
+ * Status of a denomination.
+ */
+export enum DenominationStatus {
+ /**
+ * Verification was delayed.
+ */
+ Unverified,
+ /**
+ * Verified as valid.
+ */
+ VerifiedGood,
+ /**
+ * Verified as invalid.
+ */
+ VerifiedBad,
+}
+
+
+/**
+ * Denomination record as stored in the wallet's database.
+ */
+@Checkable.Class()
+export class DenominationRecord {
+ /**
+ * Value of one coin of the denomination.
+ */
+ @Checkable.Value(AmountJson)
+ value: AmountJson;
+
+ /**
+ * The denomination public key.
+ */
+ @Checkable.String
+ denomPub: string;
+
+ /**
+ * Hash of the denomination public key.
+ * Stored in the database for faster lookups.
+ */
+ @Checkable.String
+ denomPubHash: string;
+
+ /**
+ * Fee for withdrawing.
+ */
+ @Checkable.Value(AmountJson)
+ feeWithdraw: AmountJson;
+
+ /**
+ * Fee for depositing.
+ */
+ @Checkable.Value(AmountJson)
+ feeDeposit: AmountJson;
+
+ /**
+ * Fee for refreshing.
+ */
+ @Checkable.Value(AmountJson)
+ feeRefresh: AmountJson;
+
+ /**
+ * Fee for refunding.
+ */
+ @Checkable.Value(AmountJson)
+ feeRefund: AmountJson;
+
+ /**
+ * Validity start date of the denomination.
+ */
+ @Checkable.String
+ stampStart: string;
+
+ /**
+ * Date after which the currency can't be withdrawn anymore.
+ */
+ @Checkable.String
+ stampExpireWithdraw: string;
+
+ /**
+ * Date after the denomination officially doesn't exist anymore.
+ */
+ @Checkable.String
+ stampExpireLegal: string;
+
+ /**
+ * Data after which coins of this denomination can't be deposited anymore.
+ */
+ @Checkable.String
+ stampExpireDeposit: string;
+
+ /**
+ * Signature by the exchange's master key over the denomination
+ * information.
+ */
+ @Checkable.String
+ masterSig: string;
+
+ /**
+ * Did we verify the signature on the denomination?
+ */
+ @Checkable.Number
+ status: DenominationStatus;
+
+ /**
+ * Was this denomination still offered by the exchange the last time
+ * we checked?
+ * Only false when the exchange redacts a previously published denomination.
+ */
+ @Checkable.Boolean
+ isOffered: boolean;
+
+ /**
+ * Base URL of the exchange.
+ */
+ @Checkable.String
+ exchangeBaseUrl: string;
+
+ /**
+ * Verify that a value matches the schema of this class and convert it into a
+ * member.
+ */
+ static checked: (obj: any) => Denomination;
+}
+
+
+/**
+ * Exchange record as stored in the wallet's database.
+ */
+export interface ExchangeRecord {
+ /**
+ * Base url of the exchange.
+ */
+ baseUrl: string;
+ /**
+ * Master public key of the exchange.
+ */
+ masterPublicKey: string;
+ /**
+ * Auditors (partially) auditing the exchange.
+ */
+ auditors: Auditor[];
+
+ /**
+ * Currency that the exchange offers.
+ */
+ currency: string;
+
+ /**
+ * Timestamp for last update.
+ */
+ lastUpdateTime: number;
+
+ /**
+ * When did we actually use this exchange last (in milliseconds). If we
+ * never used the exchange for anything but just updated its info, this is
+ * set to 0. (Currently only updated when reserves are created.)
+ */
+ lastUsedTime: number;
+
+ /**
+ * Last observed protocol version.
+ */
+ protocolVersion?: string;
+}
+
+
+/**
+ * A coin that isn't yet signed by an exchange.
+ */
+export interface PreCoinRecord {
+ coinPub: string;
+ coinPriv: string;
+ reservePub: string;
+ denomPub: string;
+ blindingKey: string;
+ withdrawSig: string;
+ coinEv: string;
+ exchangeBaseUrl: string;
+ coinValue: AmountJson;
+ /**
+ * Set to true if this pre-coin came from a tip.
+ * Until the tip is marked as "accepted", the resulting
+ * coin will not be used for payments.
+ */
+ isFromTip: boolean;
+}
+
+
+/**
+ * Planchet for a coin during refrehs.
+ */
+export interface RefreshPreCoinRecord {
+ /**
+ * Public key for the coin.
+ */
+ publicKey: string;
+ /**
+ * Private key for the coin.
+ */
+ privateKey: string;
+ /**
+ * Blinded public key.
+ */
+ coinEv: string;
+ /**
+ * Blinding key used.
+ */
+ blindingKey: string;
+}
+
+
+/**
+ * State of returning a list of coins
+ * to the customer's bank account.
+ */
+export interface CoinsReturnRecord {
+ /**
+ * Coins that we're returning.
+ */
+ coins: CoinPaySig[];
+
+ /**
+ * Responses to the deposit requests.
+ */
+ responses: any;
+
+ /**
+ * Ephemeral dummy merchant key for
+ * the coins returns operation.
+ */
+ dummyMerchantPub: string;
+
+ /**
+ * Ephemeral dummy merchant key for
+ * the coins returns operation.
+ */
+ dummyMerchantPriv: string;
+
+ /**
+ * Contract terms.
+ */
+ contractTerms: string;
+
+ /**
+ * Hash of contract terms.
+ */
+ contractTermsHash: string;
+
+ /**
+ * Wire info to send the money for the coins to.
+ */
+ wire: object;
+
+ /**
+ * Hash of the wire object.
+ */
+ wireHash: string;
+
+ /**
+ * All coins were deposited.
+ */
+ finished: boolean;
+}
+
+
+/**
+ * Status of a coin.
+ */
+export enum CoinStatus {
+ /**
+ * Withdrawn and never shown to anybody.
+ */
+ Fresh,
+ /**
+ * Currently planned to be sent to a merchant for a purchase.
+ */
+ PurchasePending,
+ /**
+ * Used for a completed transaction and now dirty.
+ */
+ Dirty,
+ /**
+ * A coin that was refreshed.
+ */
+ Refreshed,
+ /**
+ * Coin marked to be paid back, but payback not finished.
+ */
+ PaybackPending,
+ /**
+ * Coin fully paid back.
+ */
+ PaybackDone,
+ /**
+ * Coin was dirty but can't be refreshed.
+ */
+ Useless,
+ /**
+ * The coin was withdrawn for a tip that the user hasn't accepted yet.
+ */
+ TainedByTip,
+}
+
+
+/**
+ * CoinRecord as stored in the "coins" data store
+ * of the wallet database.
+ */
+export interface CoinRecord {
+ /**
+ * Public key of the coin.
+ */
+ coinPub: string;
+
+ /**
+ * Private key to authorize operations on the coin.
+ */
+ coinPriv: string;
+
+ /**
+ * Key used by the exchange used to sign the coin.
+ */
+ denomPub: string;
+
+ /**
+ * Unblinded signature by the exchange.
+ */
+ denomSig: string;
+
+ /**
+ * Amount that's left on the coin.
+ */
+ currentAmount: AmountJson;
+
+ /**
+ * Base URL that identifies the exchange from which we got the
+ * coin.
+ */
+ exchangeBaseUrl: string;
+
+ /**
+ * We have withdrawn the coin, but it's not accepted by the exchange anymore.
+ * We have to tell an auditor and wait for compensation or for the exchange
+ * to fix it.
+ */
+ suspended?: boolean;
+
+ /**
+ * Blinding key used when withdrawing the coin.
+ * Potentionally sed again during payback.
+ */
+ blindingKey: string;
+
+ /**
+ * Reserve public key for the reserve we got this coin from,
+ * or zero when we got the coin from refresh.
+ */
+ reservePub: string|undefined;
+
+ /**
+ * Status of the coin.
+ */
+ status: CoinStatus;
+}
+
+/**
+ * Proposal record, stored in the wallet's database.
+ */
+@Checkable.Class()
+export class ProposalRecord {
+ /**
+ * The contract that was offered by the merchant.
+ */
+ @Checkable.Value(ContractTerms)
+ contractTerms: ContractTerms;
+
+ /**
+ * Signature by the merchant over the contract details.
+ */
+ @Checkable.String
+ merchantSig: string;
+
+ /**
+ * Hash of the contract terms.
+ */
+ @Checkable.String
+ contractTermsHash: string;
+
+ /**
+ * Serial ID when the offer is stored in the wallet DB.
+ */
+ @Checkable.Optional(Checkable.Number)
+ id?: number;
+
+ /**
+ * Timestamp (in ms) of when the record
+ * was created.
+ */
+ @Checkable.Number
+ timestamp: number;
+
+ /**
+ * Verify that a value matches the schema of this class and convert it into a
+ * member.
+ */
+ static checked: (obj: any) => ProposalRecord;
+}
+
+
+/**
+ * Wire fees for an exchange.
+ */
+export interface ExchangeWireFeesRecord {
+ /**
+ * Base URL of the exchange.
+ */
+ exchangeBaseUrl: string;
+
+ /**
+ * Mapping from wire method type to the wire fee.
+ */
+ feesForType: { [wireMethod: string]: WireFee[] };
+}
+
+
+/**
+ * Status of a tip we got from a merchant.
+ */
+export interface TipRecord {
+ /**
+ * Has the user accepted the tip? Only after the tip has been accepted coins
+ * withdrawn from the tip may be used.
+ */
+ accepted: boolean;
+
+ /**
+ * The tipped amount.
+ */
+ amount: AmountJson;
+
+ /**
+ * Coin public keys from the planchets.
+ * This field is redundant and used for indexing the record via
+ * a multi-entry index to look up tip records by coin public key.
+ */
+ coinPubs: string[];
+
+ /**
+ * Timestamp, the tip can't be picked up anymore after this deadline.
+ */
+ deadline: number;
+
+ /**
+ * The exchange that will sign our coins, chosen by the merchant.
+ */
+ exchangeUrl: string;
+
+ /**
+ * Domain of the merchant, necessary to uniquely identify the tip since
+ * merchants can freely choose the ID and a malicious merchant might cause a
+ * collision.
+ */
+ merchantDomain: string;
+
+ /**
+ * Planchets, the members included in TipPlanchetDetail will be sent to the
+ * merchant.
+ */
+ planchets: TipPlanchet[];
+
+ /**
+ * Response if the merchant responded,
+ * undefined otherwise.
+ */
+ response?: TipResponse[];
+
+ /**
+ * Identifier for the tip, chosen by the merchant.
+ */
+ tipId: string;
+
+ /**
+ * URL to go to once the tip has been accepted.
+ */
+ nextUrl: string;
+
+ timestamp: number;
+}
+
+
+/**
+ * Ongoing refresh
+ */
+export interface RefreshSessionRecord {
+ /**
+ * Public key that's being melted in this session.
+ */
+ meltCoinPub: string;
+
+ /**
+ * How much of the coin's value is melted away
+ * with this refresh session?
+ */
+ valueWithFee: AmountJson;
+
+ /**
+ * Sum of the value of denominations we want
+ * to withdraw in this session, without fees.
+ */
+ valueOutput: AmountJson;
+
+ /**
+ * Signature to confirm the melting.
+ */
+ confirmSig: string;
+
+ /**
+ * Hased denominations of the newly requested coins.
+ */
+ newDenomHashes: string[];
+
+ /**
+ * Denominations of the newly requested coins.
+ */
+ newDenoms: string[];
+
+ /**
+ * Precoins for each cut-and-choose instance.
+ */
+ preCoinsForGammas: RefreshPreCoinRecord[][];
+
+ /**
+ * The transfer keys, kappa of them.
+ */
+ transferPubs: string[];
+
+ /**
+ * Private keys for the transfer public keys.
+ */
+ transferPrivs: string[];
+
+ /**
+ * The no-reveal-index after we've done the melting.
+ */
+ norevealIndex?: number;
+
+ /**
+ * Hash of the session.
+ */
+ hash: string;
+
+ /**
+ * Base URL for the exchange we're doing the refresh with.
+ */
+ exchangeBaseUrl: string;
+
+ /**
+ * Is this session finished?
+ */
+ finished: boolean;
+
+ /**
+ * Record ID when retrieved from the DB.
+ */
+ id?: number;
+}
+
+
+/**
+ * Tipping planchet stored in the database.
+ */
+export interface TipPlanchet {
+ blindingKey: string;
+ coinEv: string;
+ coinPriv: string;
+ coinPub: string;
+ coinValue: AmountJson;
+ denomPubHash: string;
+ denomPub: string;
+}
+
+
+/**
+ * Wire fee for one wire method as stored in the
+ * wallet's database.
+ */
+export interface WireFee {
+ /**
+ * Fee for wire transfers.
+ */
+ wireFee: AmountJson;
+
+ /**
+ * Fees to close and refund a reserve.
+ */
+ closingFee: AmountJson;
+
+ /**
+ * Start date of the fee.
+ */
+ startStamp: number;
+
+ /**
+ * End date of the fee.
+ */
+ endStamp: number;
+
+ /**
+ * Signature made by the exchange master key.
+ */
+ sig: string;
+}
+
+/**
+ * Record that stores status information about one purchase, starting from when
+ * the customer accepts a proposal. Includes refund status if applicable.
+ */
+export interface PurchaseRecord {
+ contractTermsHash: string;
+ contractTerms: ContractTerms;
+ payReq: PayReq;
+ merchantSig: string;
+
+ /**
+ * The purchase isn't active anymore, it's either successfully paid or
+ * refunded/aborted.
+ */
+ finished: boolean;
+
+ refundsPending: { [refundSig: string]: RefundPermission };
+ refundsDone: { [refundSig: string]: RefundPermission };
+
+ /**
+ * When was the purchase made?
+ * Refers to the time that the user accepted.
+ */
+ timestamp: number;
+
+ /**
+ * When was the last refund made?
+ * Set to 0 if no refund was made on the purchase.
+ */
+ timestamp_refund: number;
+}
diff --git a/src/helpers.ts b/src/helpers.ts
index 6dc080b2e..d85808acb 100644
--- a/src/helpers.ts
+++ b/src/helpers.ts
@@ -21,7 +21,8 @@
/**
* Imports.
*/
-import {AmountJson, Amounts} from "./types";
+import { AmountJson } from "./amounts";
+import * as Amounts from "./amounts";
import URI = require("urijs");
diff --git a/src/query.ts b/src/query.ts
index b199e0e9c..e45596c66 100644
--- a/src/query.ts
+++ b/src/query.ts
@@ -825,7 +825,7 @@ export class QueryRoot {
const req = tx.objectStore(store.name).get(key);
req.onsuccess = () => {
results.push(req.result);
- if (results.length == keys.length) {
+ if (results.length === keys.length) {
resolve(results);
}
};
diff --git a/src/talerTypes.ts b/src/talerTypes.ts
new file mode 100644
index 000000000..2ac2a5155
--- /dev/null
+++ b/src/talerTypes.ts
@@ -0,0 +1,632 @@
+/*
+ This file is part of TALER
+ (C) 2018 GNUnet e.V. and INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Type and schema definitions for the base taler protocol.
+ *
+ * All types here should be "@Checkable".
+ *
+ * Even though the rest of the wallet uses camelCase for fields, use snake_case
+ * here, since that's the convention for the Taler JSON+HTTP API.
+ */
+
+/**
+ * Imports.
+ */
+import { AmountJson } from "./amounts";
+import { Checkable } from "./checkable";
+
+
+/**
+ * Denomination as found in the /keys response from the exchange.
+ */
+@Checkable.Class()
+export class Denomination {
+ /**
+ * Value of one coin of the denomination.
+ */
+ @Checkable.Value(AmountJson)
+ value: AmountJson;
+
+ /**
+ * Public signing key of the denomination.
+ */
+ @Checkable.String
+ denom_pub: string;
+
+ /**
+ * Fee for withdrawing.
+ */
+ @Checkable.Value(AmountJson)
+ fee_withdraw: AmountJson;
+
+ /**
+ * Fee for depositing.
+ */
+ @Checkable.Value(AmountJson)
+ fee_deposit: AmountJson;
+
+ /**
+ * Fee for refreshing.
+ */
+ @Checkable.Value(AmountJson)
+ fee_refresh: AmountJson;
+
+ /**
+ * Fee for refunding.
+ */
+ @Checkable.Value(AmountJson)
+ fee_refund: AmountJson;
+
+ /**
+ * Start date from which withdraw is allowed.
+ */
+ @Checkable.String
+ stamp_start: string;
+
+ /**
+ * End date for withdrawing.
+ */
+ @Checkable.String
+ stamp_expire_withdraw: string;
+
+ /**
+ * Expiration date after which the exchange can forget about
+ * the currency.
+ */
+ @Checkable.String
+ stamp_expire_legal: string;
+
+ /**
+ * Date after which the coins of this denomination can't be
+ * deposited anymore.
+ */
+ @Checkable.String
+ stamp_expire_deposit: string;
+
+ /**
+ * Signature over the denomination information by the exchange's master
+ * signing key.
+ */
+ @Checkable.String
+ master_sig: string;
+
+ /**
+ * Verify that a value matches the schema of this class and convert it into a
+ * member.
+ */
+ static checked: (obj: any) => Denomination;
+}
+
+
+/**
+ * Signature by the auditor that a particular denomination key is audited.
+ */
+@Checkable.Class()
+export class AuditorDenomSig {
+ /**
+ * Denomination public key's hash.
+ */
+ @Checkable.String
+ denom_pub_h: string;
+
+ /**
+ * The signature.
+ */
+ @Checkable.String
+ auditor_sig: string;
+}
+
+
+/**
+ * Auditor information as given by the exchange in /keys.
+ */
+@Checkable.Class()
+export class Auditor {
+ /**
+ * Auditor's public key.
+ */
+ @Checkable.String
+ auditor_pub: string;
+
+ /**
+ * Base URL of the auditor.
+ */
+ @Checkable.String
+ auditor_url: string;
+
+ /**
+ * List of signatures for denominations by the auditor.
+ */
+ @Checkable.List(Checkable.Value(AuditorDenomSig))
+ denomination_keys: AuditorDenomSig[];
+}
+
+
+/**
+ * Request that we send to the exchange to get a payback.
+ */
+export interface PaybackRequest {
+ /**
+ * Denomination public key of the coin we want to get
+ * paid back.
+ */
+ denom_pub: string;
+
+ /**
+ * Signature over the coin public key by the denomination.
+ */
+ denom_sig: string;
+
+ /**
+ * Coin public key of the coin we want to refund.
+ */
+ coin_pub: string;
+
+ /**
+ * Blinding key that was used during withdraw,
+ * used to prove that we were actually withdrawing the coin.
+ */
+ coin_blind_key_secret: string;
+
+ /**
+ * Signature made by the coin, authorizing the payback.
+ */
+ coin_sig: string;
+}
+
+
+/**
+ * Response that we get from the exchange for a payback request.
+ */
+@Checkable.Class()
+export class PaybackConfirmation {
+ /**
+ * public key of the reserve that will receive the payback.
+ */
+ @Checkable.String
+ reserve_pub: string;
+
+ /**
+ * How much will the exchange pay back (needed by wallet in
+ * case coin was partially spent and wallet got restored from backup)
+ */
+ @Checkable.Value(AmountJson)
+ amount: AmountJson;
+
+ /**
+ * Time by which the exchange received the /payback request.
+ */
+ @Checkable.String
+ timestamp: string;
+
+ /**
+ * the EdDSA signature of TALER_PaybackConfirmationPS using a current
+ * signing key of the exchange affirming the successful
+ * payback request, and that the exchange promises to transfer the funds
+ * by the date specified (this allows the exchange delaying the transfer
+ * a bit to aggregate additional payback requests into a larger one).
+ */
+ @Checkable.String
+ exchange_sig: string;
+
+ /**
+ * Public EdDSA key of the exchange that was used to generate the signature.
+ * Should match one of the exchange's signing keys from /keys. It is given
+ * explicitly as the client might otherwise be confused by clock skew as to
+ * which signing key was used.
+ */
+ @Checkable.String
+ exchange_pub: string;
+
+ /**
+ * Verify that a value matches the schema of this class and convert it into a
+ * member.
+ */
+ static checked: (obj: any) => PaybackConfirmation;
+}
+
+
+/**
+ * Deposit permission for a single coin.
+ */
+export interface CoinPaySig {
+ /**
+ * Signature by the coin.
+ */
+ coin_sig: string;
+ /**
+ * Public key of the coin being spend.
+ */
+ coin_pub: string;
+ /**
+ * Signature made by the denomination public key.
+ */
+ ub_sig: string;
+ /**
+ * The denomination public key associated with this coin.
+ */
+ denom_pub: string;
+ /**
+ * The amount that is subtracted from this coin with this payment.
+ */
+ contribution: AmountJson;
+}
+
+
+/**
+ * Information about an exchange as stored inside a
+ * merchant's contract terms.
+ */
+@Checkable.Class()
+export class ExchangeHandle {
+ /**
+ * Master public signing key of the exchange.
+ */
+ @Checkable.String
+ master_pub: string;
+
+ /**
+ * Base URL of the exchange.
+ */
+ @Checkable.String
+ url: string;
+
+ /**
+ * Verify that a value matches the schema of this class and convert it into a
+ * member.
+ */
+ static checked: (obj: any) => ExchangeHandle;
+}
+
+
+/**
+ * Contract terms from a merchant.
+ */
+@Checkable.Class({validate: true})
+export class ContractTerms {
+ static validate(x: ContractTerms) {
+ if (x.exchanges.length === 0) {
+ throw Error("no exchanges in contract terms");
+ }
+ }
+
+ /**
+ * Hash of the merchant's wire details.
+ */
+ @Checkable.String
+ H_wire: string;
+
+ /**
+ * Wire method the merchant wants to use.
+ */
+ @Checkable.String
+ wire_method: string;
+
+ /**
+ * Human-readable short summary of the contract.
+ */
+ @Checkable.Optional(Checkable.String)
+ summary?: string;
+
+ /**
+ * Nonce used to ensure freshness.
+ */
+ @Checkable.Optional(Checkable.String)
+ nonce?: string;
+
+ /**
+ * Total amount payable.
+ */
+ @Checkable.Value(AmountJson)
+ amount: AmountJson;
+
+ /**
+ * Auditors accepted by the merchant.
+ */
+ @Checkable.List(Checkable.AnyObject)
+ auditors: any[];
+
+ /**
+ * Deadline to pay for the contract.
+ */
+ @Checkable.Optional(Checkable.String)
+ pay_deadline: string;
+
+ /**
+ * Delivery locations.
+ */
+ @Checkable.Any
+ locations: any;
+
+ /**
+ * Maximum deposit fee covered by the merchant.
+ */
+ @Checkable.Value(AmountJson)
+ max_fee: AmountJson;
+
+ /**
+ * Information about the merchant.
+ */
+ @Checkable.Any
+ merchant: any;
+
+ /**
+ * Public key of the merchant.
+ */
+ @Checkable.String
+ merchant_pub: string;
+
+ /**
+ * List of accepted exchanges.
+ */
+ @Checkable.List(Checkable.Value(ExchangeHandle))
+ exchanges: ExchangeHandle[];
+
+ /**
+ * Products that are sold in this contract.
+ */
+ @Checkable.List(Checkable.AnyObject)
+ products: any[];
+
+ /**
+ * Deadline for refunds.
+ */
+ @Checkable.String
+ refund_deadline: string;
+
+ /**
+ * Time when the contract was generated by the merchant.
+ */
+ @Checkable.String
+ timestamp: string;
+
+ /**
+ * Order id to uniquely identify the purchase within
+ * one merchant instance.
+ */
+ @Checkable.String
+ order_id: string;
+
+ /**
+ * URL to post the payment to.
+ */
+ @Checkable.String
+ pay_url: string;
+
+ /**
+ * Fulfillment URL to view the product or
+ * delivery status.
+ */
+ @Checkable.String
+ fulfillment_url: string;
+
+ /**
+ * Share of the wire fee that must be settled with one payment.
+ */
+ @Checkable.Optional(Checkable.Number)
+ wire_fee_amortization?: number;
+
+ /**
+ * Maximum wire fee that the merchant agrees to pay for.
+ */
+ @Checkable.Optional(Checkable.Value(AmountJson))
+ max_wire_fee?: AmountJson;
+
+ /**
+ * Extra data, interpreted by the mechant only.
+ */
+ @Checkable.Any
+ extra: any;
+
+ /**
+ * Verify that a value matches the schema of this class and convert it into a
+ * member.
+ */
+ static checked: (obj: any) => ContractTerms;
+}
+
+
+/**
+ * Payment body sent to the merchant's /pay.
+ */
+export interface PayReq {
+ /**
+ * Coins with signature.
+ */
+ coins: CoinPaySig[];
+
+ /**
+ * The merchant public key, used to uniquely
+ * identify the merchant instance.
+ */
+ merchant_pub: string;
+
+ /**
+ * Order ID that's being payed for.
+ */
+ order_id: string;
+
+ /**
+ * Exchange that the coins are from (base URL).
+ */
+ exchange: string;
+}
+
+
+/**
+ * Refund permission in the format that the merchant gives it to us.
+ */
+export interface RefundPermission {
+ /**
+ * Amount to be refunded.
+ */
+ refund_amount: AmountJson;
+
+ /**
+ * Fee for the refund.
+ */
+ refund_fee: AmountJson;
+
+ /**
+ * Contract terms hash to identify the contract that this
+ * refund is for.
+ */
+ h_contract_terms: string;
+
+ /**
+ * Public key of the coin being refunded.
+ */
+ coin_pub: string;
+
+ /**
+ * Refund transaction ID between merchant and exchange.
+ */
+ rtransaction_id: number;
+
+ /**
+ * Public key of the merchant.
+ */
+ merchant_pub: string;
+
+ /**
+ * Signature made by the merchant over the refund permission.
+ */
+ merchant_sig: string;
+}
+
+
+/**
+ * Planchet detail sent to the merchant.
+ */
+export interface TipPlanchetDetail {
+ /**
+ * Hashed denomination public key.
+ */
+ denom_pub_hash: string;
+
+ /**
+ * Coin's blinded public key.
+ */
+ coin_ev: string;
+}
+
+
+/**
+ * Request sent to the merchant to pick up a tip.
+ */
+export interface TipPickupRequest {
+ /**
+ * Identifier of the tip.
+ */
+ tip_id: string;
+
+ /**
+ * List of planchets the wallet wants to use for the tip.
+ */
+ planchets: TipPlanchetDetail[];
+}
+
+/**
+ * Reserve signature, defined as separate class to facilitate
+ * schema validation with "@Checkable".
+ */
+@Checkable.Class()
+export class ReserveSigSingleton {
+ /**
+ * Reserve signature.
+ */
+ @Checkable.String
+ reserve_sig: string;
+
+ /**
+ * Create a ReserveSigSingleton from untyped JSON.
+ */
+ static checked: (obj: any) => ReserveSigSingleton;
+}
+
+/**
+ * Response of the merchant
+ * to the TipPickupRequest.
+ */
+@Checkable.Class()
+export class TipResponse {
+ /**
+ * Public key of the reserve
+ */
+ @Checkable.String
+ reserve_pub: string;
+
+ /**
+ * The order of the signatures matches the planchets list.
+ */
+ @Checkable.List(Checkable.Value(ReserveSigSingleton))
+ reserve_sigs: ReserveSigSingleton[];
+
+ /**
+ * Create a TipResponse from untyped JSON.
+ */
+ static checked: (obj: any) => TipResponse;
+}
+
+/**
+ * Token containing all the information for the wallet
+ * to process a tip. Given by the merchant to the wallet.
+ */
+@Checkable.Class()
+export class TipToken {
+ /**
+ * Expiration for the tip.
+ */
+ @Checkable.String
+ expiration: string;
+
+ /**
+ * URL of the exchange that the tip can be withdrawn from.
+ */
+ @Checkable.String
+ exchange_url: string;
+
+ /**
+ * Merchant's URL to pick up the tip.
+ */
+ @Checkable.String
+ pickup_url: string;
+
+ /**
+ * Merchant-chosen tip identifier.
+ */
+ @Checkable.String
+ tip_id: string;
+
+ /**
+ * Amount of tip.
+ */
+ @Checkable.Value(AmountJson)
+ amount: AmountJson;
+
+ /**
+ * URL to navigate after finishing tip processing.
+ */
+ @Checkable.String
+ next_url: string;
+
+ /**
+ * Create a TipToken from untyped JSON.
+ * Validates the schema and throws on error.
+ */
+ static checked: (obj: any) => TipToken;
+}
diff --git a/src/types-test.ts b/src/types-test.ts
index 3657d6d26..097235a77 100644
--- a/src/types-test.ts
+++ b/src/types-test.ts
@@ -14,17 +14,17 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import {test} from "ava";
-import {Amounts} from "./types";
-import * as types from "./types";
+import { test } from "ava";
+import * as Amounts from "./amounts";
+import { ContractTerms } from "./talerTypes";
-const amt = (value: number, fraction: number, currency: string): types.AmountJson => ({value, fraction, currency});
+const amt = (value: number, fraction: number, currency: string): Amounts.AmountJson => ({value, fraction, currency});
test("amount addition (simple)", (t) => {
const a1 = amt(1, 0, "EUR");
const a2 = amt(1, 0, "EUR");
const a3 = amt(2, 0, "EUR");
- t.true(0 === types.Amounts.cmp(Amounts.add(a1, a2).amount, a3));
+ t.true(0 === Amounts.cmp(Amounts.add(a1, a2).amount, a3));
t.pass();
});
@@ -39,7 +39,7 @@ test("amount subtraction (simple)", (t) => {
const a1 = amt(2, 5, "EUR");
const a2 = amt(1, 0, "EUR");
const a3 = amt(1, 5, "EUR");
- t.true(0 === types.Amounts.cmp(Amounts.sub(a1, a2).amount, a3));
+ t.true(0 === Amounts.cmp(Amounts.sub(a1, a2).amount, a3));
t.pass();
});
@@ -73,13 +73,13 @@ test("contract terms validation", (t) => {
wire_method: "test",
};
- types.ContractTerms.checked(c);
+ ContractTerms.checked(c);
const c1 = JSON.parse(JSON.stringify(c));
c1.exchanges = [];
try {
- types.ContractTerms.checked(c1);
+ ContractTerms.checked(c1);
} catch (e) {
t.pass();
return;
diff --git a/src/types.ts b/src/types.ts
deleted file mode 100644
index ae4454a83..000000000
--- a/src/types.ts
+++ /dev/null
@@ -1,2048 +0,0 @@
-/*
- This file is part of TALER
- (C) 2015-2017 GNUnet e.V. and INRIA
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- * Common types that are used by Taler and some helper functions
- * to deal with them.
- *
- * 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.
- */
-
-/**
- * Imports.
- */
-import { Checkable } from "./checkable";
-import * as LibtoolVersion from "./libtoolVersion";
-
-/**
- * Non-negative financial amount. Fractional values are expressed as multiples
- * of 1e-8.
- */
-@Checkable.Class()
-export class AmountJson {
- /**
- * Value, must be an integer.
- */
- @Checkable.Number
- readonly value: number;
-
- /**
- * Fraction, must be an integer. Represent 1/1e8 of a unit.
- */
- @Checkable.Number
- readonly fraction: number;
-
- /**
- * Currency of the amount.
- */
- @Checkable.String
- readonly currency: string;
-
- /**
- * Verify that a value matches the schema of this class and convert it into a
- * member.
- */
- static checked: (obj: any) => AmountJson;
-}
-
-
-/**
- * Amount with a sign.
- */
-export interface SignedAmountJson {
- /**
- * The absolute amount.
- */
- amount: AmountJson;
- /**
- * Sign.
- */
- isNegative: boolean;
-}
-
-
-/**
- * A reserve record as stored in the wallet's database.
- */
-export interface ReserveRecord {
- /**
- * The reserve public key.
- */
- reserve_pub: string;
-
- /**
- * The reserve private key.
- */
- reserve_priv: string;
-
- /**
- * The exchange base URL.
- */
- exchange_base_url: string;
-
- /**
- * Time when the reserve was created.
- */
- created: number;
-
- /**
- * Time when the reserve was depleted.
- * Set to 0 if not depleted yet.
- */
- timestamp_depleted: number;
-
- /**
- * Time when the reserve was confirmed.
- *
- * Set to 0 if not confirmed yet.
- */
- timestamp_confirmed: number;
-
- /**
- * Current amount left in the reserve
- */
- current_amount: AmountJson | null;
-
- /**
- * Amount requested when the reserve was created.
- * When a reserve is re-used (rare!) the current_amount can
- * be higher than the requested_amount
- */
- requested_amount: AmountJson;
-
- /**
- * What's the current amount that sits
- * in precoins?
- */
- precoin_amount: AmountJson;
-
- /**
- * We got some payback to this reserve. We'll cease to automatically
- * withdraw money from it.
- */
- hasPayback: boolean;
-
- /**
- * Wire information for the bank account that
- * transfered funds for this reserve.
- */
- senderWire?: object;
-}
-
-
-/**
- * Auditor record as stored with currencies in the exchange database.
- */
-export interface AuditorRecord {
- /**
- * Base url of the auditor.
- */
- baseUrl: string;
- /**
- * Public signing key of the auditor.
- */
- auditorPub: string;
- /**
- * Time when the auditing expires.
- */
- expirationStamp: number;
-}
-
-
-/**
- * Exchange for currencies as stored in the wallet's currency
- * information database.
- */
-export interface ExchangeForCurrencyRecord {
- /**
- * FIXME: unused?
- */
- exchangePub: string;
- /**
- * Base URL of the exchange.
- */
- baseUrl: string;
-}
-
-
-/**
- * Information about a currency as displayed in the wallet's database.
- */
-export interface CurrencyRecord {
- /**
- * Name of the currency.
- */
- name: string;
- /**
- * Number of fractional digits to show when rendering the currency.
- */
- fractionalDigits: number;
- /**
- * Auditors that the wallet trusts for this currency.
- */
- auditors: AuditorRecord[];
- /**
- * Exchanges that the wallet trusts for this currency.
- */
- exchanges: ExchangeForCurrencyRecord[];
-}
-
-
-/**
- * Response for the create reserve request to the wallet.
- */
-@Checkable.Class()
-export class CreateReserveResponse {
- /**
- * Exchange URL where the bank should create the reserve.
- * The URL is canonicalized in the response.
- */
- @Checkable.String
- exchange: string;
-
- /**
- * Reserve public key of the newly created reserve.
- */
- @Checkable.String
- reservePub: string;
-
- /**
- * Verify that a value matches the schema of this class and convert it into a
- * member.
- */
- static checked: (obj: any) => CreateReserveResponse;
-}
-
-
-/**
- * Status of a denomination.
- */
-export enum DenominationStatus {
- /**
- * Verification was delayed.
- */
- Unverified,
- /**
- * Verified as valid.
- */
- VerifiedGood,
- /**
- * Verified as invalid.
- */
- VerifiedBad,
-}
-
-/**
- * Denomination record as stored in the wallet's database.
- */
-@Checkable.Class()
-export class DenominationRecord {
- /**
- * Value of one coin of the denomination.
- */
- @Checkable.Value(AmountJson)
- value: AmountJson;
-
- /**
- * The denomination public key.
- */
- @Checkable.String
- denomPub: string;
-
- /**
- * Hash of the denomination public key.
- * Stored in the database for faster lookups.
- */
- @Checkable.String
- denomPubHash: string;
-
- /**
- * Fee for withdrawing.
- */
- @Checkable.Value(AmountJson)
- feeWithdraw: AmountJson;
-
- /**
- * Fee for depositing.
- */
- @Checkable.Value(AmountJson)
- feeDeposit: AmountJson;
-
- /**
- * Fee for refreshing.
- */
- @Checkable.Value(AmountJson)
- feeRefresh: AmountJson;
-
- /**
- * Fee for refunding.
- */
- @Checkable.Value(AmountJson)
- feeRefund: AmountJson;
-
- /**
- * Validity start date of the denomination.
- */
- @Checkable.String
- stampStart: string;
-
- /**
- * Date after which the currency can't be withdrawn anymore.
- */
- @Checkable.String
- stampExpireWithdraw: string;
-
- /**
- * Date after the denomination officially doesn't exist anymore.
- */
- @Checkable.String
- stampExpireLegal: string;
-
- /**
- * Data after which coins of this denomination can't be deposited anymore.
- */
- @Checkable.String
- stampExpireDeposit: string;
-
- /**
- * Signature by the exchange's master key over the denomination
- * information.
- */
- @Checkable.String
- masterSig: string;
-
- /**
- * Did we verify the signature on the denomination?
- */
- @Checkable.Number
- status: DenominationStatus;
-
- /**
- * Was this denomination still offered by the exchange the last time
- * we checked?
- * Only false when the exchange redacts a previously published denomination.
- */
- @Checkable.Boolean
- isOffered: boolean;
-
- /**
- * Base URL of the exchange.
- */
- @Checkable.String
- exchangeBaseUrl: string;
-
- /**
- * Verify that a value matches the schema of this class and convert it into a
- * member.
- */
- static checked: (obj: any) => Denomination;
-}
-
-/**
- * Denomination as found in the /keys response from the exchange.
- */
-@Checkable.Class()
-export class Denomination {
- /**
- * Value of one coin of the denomination.
- */
- @Checkable.Value(AmountJson)
- value: AmountJson;
-
- /**
- * Public signing key of the denomination.
- */
- @Checkable.String
- denom_pub: string;
-
- /**
- * Fee for withdrawing.
- */
- @Checkable.Value(AmountJson)
- fee_withdraw: AmountJson;
-
- /**
- * Fee for depositing.
- */
- @Checkable.Value(AmountJson)
- fee_deposit: AmountJson;
-
- /**
- * Fee for refreshing.
- */
- @Checkable.Value(AmountJson)
- fee_refresh: AmountJson;
-
- /**
- * Fee for refunding.
- */
- @Checkable.Value(AmountJson)
- fee_refund: AmountJson;
-
- /**
- * Start date from which withdraw is allowed.
- */
- @Checkable.String
- stamp_start: string;
-
- /**
- * End date for withdrawing.
- */
- @Checkable.String
- stamp_expire_withdraw: string;
-
- /**
- * Expiration date after which the exchange can forget about
- * the currency.
- */
- @Checkable.String
- stamp_expire_legal: string;
-
- /**
- * Date after which the coins of this denomination can't be
- * deposited anymore.
- */
- @Checkable.String
- stamp_expire_deposit: string;
-
- /**
- * Signature over the denomination information by the exchange's master
- * signing key.
- */
- @Checkable.String
- master_sig: string;
-
- /**
- * Verify that a value matches the schema of this class and convert it into a
- * member.
- */
- static checked: (obj: any) => Denomination;
-}
-
-
-/**
- * Signature by the auditor that a particular denomination key is audited.
- */
-@Checkable.Class()
-export class AuditorDenomSig {
- /**
- * Denomination public key's hash.
- */
- @Checkable.String
- denom_pub_h: string;
-
- /**
- * The signature.
- */
- @Checkable.String
- auditor_sig: string;
-}
-
-/**
- * Auditor information as given by the exchange in /keys.
- */
-@Checkable.Class()
-export class Auditor {
- /**
- * Auditor's public key.
- */
- @Checkable.String
- auditor_pub: string;
-
- /**
- * Base URL of the auditor.
- */
- @Checkable.String
- auditor_url: string;
-
- /**
- * List of signatures for denominations by the auditor.
- */
- @Checkable.List(Checkable.Value(AuditorDenomSig))
- denomination_keys: AuditorDenomSig[];
-}
-
-
-/**
- * Exchange record as stored in the wallet's database.
- */
-export interface ExchangeRecord {
- /**
- * Base url of the exchange.
- */
- baseUrl: string;
- /**
- * Master public key of the exchange.
- */
- masterPublicKey: string;
- /**
- * Auditors (partially) auditing the exchange.
- */
- auditors: Auditor[];
-
- /**
- * Currency that the exchange offers.
- */
- currency: string;
-
- /**
- * Timestamp for last update.
- */
- lastUpdateTime: number;
-
- /**
- * When did we actually use this exchange last (in milliseconds). If we
- * never used the exchange for anything but just updated its info, this is
- * set to 0. (Currently only updated when reserves are created.)
- */
- lastUsedTime: number;
-
- /**
- * Last observed protocol version.
- */
- protocolVersion?: string;
-}
-
-/**
- * Wire info, sent to the bank when creating a reserve. Fee information will
- * be filtered out. Only methods that the bank also supports should be sent.
- */
-export interface WireInfo {
- /**
- * Mapping from wire method type to the exchange's wire info,
- * excluding fees.
- */
- [type: string]: any;
-}
-
-
-/**
- * Information about what will happen when creating a reserve.
- *
- * Sent to the wallet frontend to be rendered and shown to the user.
- */
-export interface ReserveCreationInfo {
- /**
- * Exchange that the reserve will be created at.
- */
- exchangeInfo: ExchangeRecord;
- /**
- * Filtered wire info to send to the bank.
- */
- wireInfo: WireInfo;
- /**
- * Selected denominations for withdraw.
- */
- selectedDenoms: DenominationRecord[];
- /**
- * Fees for withdraw.
- */
- withdrawFee: AmountJson;
- /**
- * Remaining balance that is too small to be withdrawn.
- */
- overhead: AmountJson;
- /**
- * Wire fees from the exchange.
- */
- wireFees: ExchangeWireFeesRecord;
- /**
- * Does the wallet know about an auditor for
- * the exchange that the reserve.
- */
- isAudited: boolean;
- /**
- * The exchange is trusted directly.
- */
- isTrusted: boolean;
- /**
- * The earliest deposit expiration of the selected coins.
- */
- earliestDepositExpiration: number;
- /**
- * Number of currently offered denominations.
- */
- numOfferedDenoms: number;
- /**
- * Public keys of trusted auditors for the currency we're withdrawing.
- */
- trustedAuditorPubs: string[];
- /**
- * Result of checking the wallet's version
- * against the exchange's version.
- *
- * Older exchanges don't return version information.
- */
- versionMatch: LibtoolVersion.VersionMatchResult|undefined;
-
- /**
- * Libtool-style version string for the exchange or "unknown"
- * for older exchanges.
- */
- exchangeVersion: string;
-
- /**
- * Libtool-style version string for the wallet.
- */
- walletVersion: string;
-}
-
-
-/**
- * A coin that isn't yet signed by an exchange.
- */
-export interface PreCoinRecord {
- coinPub: string;
- coinPriv: string;
- reservePub: string;
- denomPub: string;
- blindingKey: string;
- withdrawSig: string;
- coinEv: string;
- exchangeBaseUrl: string;
- coinValue: AmountJson;
- /**
- * Set to true if this pre-coin came from a tip.
- * Until the tip is marked as "accepted", the resulting
- * coin will not be used for payments.
- */
- isFromTip: boolean;
-}
-
-/**
- * Planchet for a coin during refrehs.
- */
-export interface RefreshPreCoinRecord {
- /**
- * Public key for the coin.
- */
- publicKey: string;
- /**
- * Private key for the coin.
- */
- privateKey: string;
- /**
- * Blinded public key.
- */
- coinEv: string;
- /**
- * Blinding key used.
- */
- blindingKey: string;
-}
-
-/**
- * Request that we send to the exchange to get a payback.
- */
-export interface PaybackRequest {
- /**
- * Denomination public key of the coin we want to get
- * paid back.
- */
- denom_pub: string;
-
- /**
- * Signature over the coin public key by the denomination.
- */
- denom_sig: string;
-
- /**
- * Coin public key of the coin we want to refund.
- */
- coin_pub: string;
-
- /**
- * Blinding key that was used during withdraw,
- * used to prove that we were actually withdrawing the coin.
- */
- coin_blind_key_secret: string;
-
- /**
- * Signature made by the coin, authorizing the payback.
- */
- coin_sig: string;
-}
-
-/**
- * Response that we get from the exchange for a payback request.
- */
-@Checkable.Class()
-export class PaybackConfirmation {
- /**
- * public key of the reserve that will receive the payback.
- */
- @Checkable.String
- reserve_pub: string;
-
- /**
- * How much will the exchange pay back (needed by wallet in
- * case coin was partially spent and wallet got restored from backup)
- */
- @Checkable.Value(AmountJson)
- amount: AmountJson;
-
- /**
- * Time by which the exchange received the /payback request.
- */
- @Checkable.String
- timestamp: string;
-
- /**
- * the EdDSA signature of TALER_PaybackConfirmationPS using a current
- * signing key of the exchange affirming the successful
- * payback request, and that the exchange promises to transfer the funds
- * by the date specified (this allows the exchange delaying the transfer
- * a bit to aggregate additional payback requests into a larger one).
- */
- @Checkable.String
- exchange_sig: string;
-
- /**
- * Public EdDSA key of the exchange that was used to generate the signature.
- * Should match one of the exchange's signing keys from /keys. It is given
- * explicitly as the client might otherwise be confused by clock skew as to
- * which signing key was used.
- */
- @Checkable.String
- exchange_pub: string;
-
- /**
- * Verify that a value matches the schema of this class and convert it into a
- * member.
- */
- static checked: (obj: any) => PaybackConfirmation;
-}
-
-/**
- * Ongoing refresh
- */
-export interface RefreshSessionRecord {
- /**
- * Public key that's being melted in this session.
- */
- meltCoinPub: string;
-
- /**
- * How much of the coin's value is melted away
- * with this refresh session?
- */
- valueWithFee: AmountJson;
-
- /**
- * Sum of the value of denominations we want
- * to withdraw in this session, without fees.
- */
- valueOutput: AmountJson;
-
- /**
- * Signature to confirm the melting.
- */
- confirmSig: string;
-
- /**
- * Hased denominations of the newly requested coins.
- */
- newDenomHashes: string[];
-
- /**
- * Denominations of the newly requested coins.
- */
- newDenoms: string[];
-
- /**
- * Precoins for each cut-and-choose instance.
- */
- preCoinsForGammas: RefreshPreCoinRecord[][];
-
- /**
- * The transfer keys, kappa of them.
- */
- transferPubs: string[];
-
- /**
- * Private keys for the transfer public keys.
- */
- transferPrivs: string[];
-
- /**
- * The no-reveal-index after we've done the melting.
- */
- norevealIndex?: number;
-
- /**
- * Hash of the session.
- */
- hash: string;
-
- /**
- * Base URL for the exchange we're doing the refresh with.
- */
- exchangeBaseUrl: string;
-
- /**
- * Is this session finished?
- */
- finished: boolean;
-
- /**
- * Record ID when retrieved from the DB.
- */
- id?: number;
-}
-
-
-/**
- * Deposit permission for a single coin.
- */
-export interface CoinPaySig {
- /**
- * Signature by the coin.
- */
- coin_sig: string;
- /**
- * Public key of the coin being spend.
- */
- coin_pub: string;
- /**
- * Signature made by the denomination public key.
- */
- ub_sig: string;
- /**
- * The denomination public key associated with this coin.
- */
- denom_pub: string;
- /**
- * The amount that is subtracted from this coin with this payment.
- */
- contribution: AmountJson;
-}
-
-
-/**
- * Status of a coin.
- */
-export enum CoinStatus {
- /**
- * Withdrawn and never shown to anybody.
- */
- Fresh,
- /**
- * Currently planned to be sent to a merchant for a purchase.
- */
- PurchasePending,
- /**
- * Used for a completed transaction and now dirty.
- */
- Dirty,
- /**
- * A coin that was refreshed.
- */
- Refreshed,
- /**
- * Coin marked to be paid back, but payback not finished.
- */
- PaybackPending,
- /**
- * Coin fully paid back.
- */
- PaybackDone,
- /**
- * Coin was dirty but can't be refreshed.
- */
- Useless,
- /**
- * The coin was withdrawn for a tip that the user hasn't accepted yet.
- */
- TainedByTip,
-}
-
-
-/**
- * State of returning a list of coins
- * to the customer's bank account.
- */
-export interface CoinsReturnRecord {
- /**
- * Coins that we're returning.
- */
- coins: CoinPaySig[];
-
- /**
- * Responses to the deposit requests.
- */
- responses: any;
-
- /**
- * Ephemeral dummy merchant key for
- * the coins returns operation.
- */
- dummyMerchantPub: string;
-
- /**
- * Ephemeral dummy merchant key for
- * the coins returns operation.
- */
- dummyMerchantPriv: string;
-
- /**
- * Contract terms.
- */
- contractTerms: string;
-
- /**
- * Hash of contract terms.
- */
- contractTermsHash: string;
-
- /**
- * Wire info to send the money for the coins to.
- */
- wire: object;
-
- /**
- * Hash of the wire object.
- */
- wireHash: string;
-
- /**
- * All coins were deposited.
- */
- finished: boolean;
-}
-
-
-/**
- * CoinRecord as stored in the "coins" data store
- * of the wallet database.
- */
-export interface CoinRecord {
- /**
- * Public key of the coin.
- */
- coinPub: string;
-
- /**
- * Private key to authorize operations on the coin.
- */
- coinPriv: string;
-
- /**
- * Key used by the exchange used to sign the coin.
- */
- denomPub: string;
-
- /**
- * Unblinded signature by the exchange.
- */
- denomSig: string;
-
- /**
- * Amount that's left on the coin.
- */
- currentAmount: AmountJson;
-
- /**
- * Base URL that identifies the exchange from which we got the
- * coin.
- */
- exchangeBaseUrl: string;
-
- /**
- * We have withdrawn the coin, but it's not accepted by the exchange anymore.
- * We have to tell an auditor and wait for compensation or for the exchange
- * to fix it.
- */
- suspended?: boolean;
-
- /**
- * Blinding key used when withdrawing the coin.
- * Potentionally sed again during payback.
- */
- blindingKey: string;
-
- /**
- * Reserve public key for the reserve we got this coin from,
- * or zero when we got the coin from refresh.
- */
- reservePub: string|undefined;
-
- /**
- * Status of the coin.
- */
- status: CoinStatus;
-}
-
-
-/**
- * Information about an exchange as stored inside a
- * merchant's contract terms.
- */
-@Checkable.Class()
-export class ExchangeHandle {
- /**
- * Master public signing key of the exchange.
- */
- @Checkable.String
- master_pub: string;
-
- /**
- * Base URL of the exchange.
- */
- @Checkable.String
- url: string;
-
- /**
- * Verify that a value matches the schema of this class and convert it into a
- * member.
- */
- static checked: (obj: any) => ExchangeHandle;
-}
-
-
-/**
- * Mapping from currency/exchange to detailed balance
- * information.
- */
-export interface WalletBalance {
- /**
- * Mapping from currency name to detailed balance info.
- */
- byExchange: { [exchangeBaseUrl: string]: WalletBalanceEntry };
-
- /**
- * Mapping from currency name to detailed balance info.
- */
- byCurrency: { [currency: string]: WalletBalanceEntry };
-}
-
-
-/**
- * Detailed wallet balance for a particular currency.
- */
-export interface WalletBalanceEntry {
- /**
- * Directly available amount.
- */
- available: AmountJson;
- /**
- * Amount that we're waiting for (refresh, withdrawal).
- */
- pendingIncoming: AmountJson;
- /**
- * Amount that's marked for a pending payment.
- */
- pendingPayment: AmountJson;
- /**
- * Amount that was paid back and we could withdraw again.
- */
- paybackAmount: AmountJson;
-}
-
-
-/**
- * Contract terms from a merchant.
- */
-@Checkable.Class({validate: true})
-export class ContractTerms {
- static validate(x: ContractTerms) {
- if (x.exchanges.length === 0) {
- throw Error("no exchanges in contract terms");
- }
- }
-
- /**
- * Hash of the merchant's wire details.
- */
- @Checkable.String
- H_wire: string;
-
- /**
- * Wire method the merchant wants to use.
- */
- @Checkable.String
- wire_method: string;
-
- /**
- * Human-readable short summary of the contract.
- */
- @Checkable.Optional(Checkable.String)
- summary?: string;
-
- /**
- * Nonce used to ensure freshness.
- */
- @Checkable.Optional(Checkable.String)
- nonce?: string;
-
- /**
- * Total amount payable.
- */
- @Checkable.Value(AmountJson)
- amount: AmountJson;
-
- /**
- * Auditors accepted by the merchant.
- */
- @Checkable.List(Checkable.AnyObject)
- auditors: any[];
-
- /**
- * Deadline to pay for the contract.
- */
- @Checkable.Optional(Checkable.String)
- pay_deadline: string;
-
- /**
- * Delivery locations.
- */
- @Checkable.Any
- locations: any;
-
- /**
- * Maximum deposit fee covered by the merchant.
- */
- @Checkable.Value(AmountJson)
- max_fee: AmountJson;
-
- /**
- * Information about the merchant.
- */
- @Checkable.Any
- merchant: any;
-
- /**
- * Public key of the merchant.
- */
- @Checkable.String
- merchant_pub: string;
-
- /**
- * List of accepted exchanges.
- */
- @Checkable.List(Checkable.Value(ExchangeHandle))
- exchanges: ExchangeHandle[];
-
- /**
- * Products that are sold in this contract.
- */
- @Checkable.List(Checkable.AnyObject)
- products: any[];
-
- /**
- * Deadline for refunds.
- */
- @Checkable.String
- refund_deadline: string;
-
- /**
- * Time when the contract was generated by the merchant.
- */
- @Checkable.String
- timestamp: string;
-
- /**
- * Order id to uniquely identify the purchase within
- * one merchant instance.
- */
- @Checkable.String
- order_id: string;
-
- /**
- * URL to post the payment to.
- */
- @Checkable.String
- pay_url: string;
-
- /**
- * Fulfillment URL to view the product or
- * delivery status.
- */
- @Checkable.String
- fulfillment_url: string;
-
- /**
- * Share of the wire fee that must be settled with one payment.
- */
- @Checkable.Optional(Checkable.Number)
- wire_fee_amortization?: number;
-
- /**
- * Maximum wire fee that the merchant agrees to pay for.
- */
- @Checkable.Optional(Checkable.Value(AmountJson))
- max_wire_fee?: AmountJson;
-
- /**
- * Extra data, interpreted by the mechant only.
- */
- @Checkable.Any
- extra: any;
-
- /**
- * Verify that a value matches the schema of this class and convert it into a
- * member.
- */
- static checked: (obj: any) => ContractTerms;
-}
-
-
-/**
- * Proposal record, stored in the wallet's database.
- */
-@Checkable.Class()
-export class ProposalRecord {
- /**
- * The contract that was offered by the merchant.
- */
- @Checkable.Value(ContractTerms)
- contractTerms: ContractTerms;
-
- /**
- * Signature by the merchant over the contract details.
- */
- @Checkable.String
- merchantSig: string;
-
- /**
- * Hash of the contract terms.
- */
- @Checkable.String
- contractTermsHash: string;
-
- /**
- * Serial ID when the offer is stored in the wallet DB.
- */
- @Checkable.Optional(Checkable.Number)
- id?: number;
-
- /**
- * Timestamp (in ms) of when the record
- * was created.
- */
- @Checkable.Number
- timestamp: number;
-
- /**
- * Verify that a value matches the schema of this class and convert it into a
- * member.
- */
- static checked: (obj: any) => ProposalRecord;
-}
-
-
-/**
- * Wire fee for one wire method as stored in the
- * wallet's database.
- */
-export interface WireFee {
- /**
- * Fee for wire transfers.
- */
- wireFee: AmountJson;
-
- /**
- * Fees to close and refund a reserve.
- */
- closingFee: AmountJson;
-
- /**
- * Start date of the fee.
- */
- startStamp: number;
-
- /**
- * End date of the fee.
- */
- endStamp: number;
-
- /**
- * Signature made by the exchange master key.
- */
- sig: string;
-}
-
-
-/**
- * Wire fees for an exchange.
- */
-export interface ExchangeWireFeesRecord {
- /**
- * Base URL of the exchange.
- */
- exchangeBaseUrl: string;
-
- /**
- * Mapping from wire method type to the wire fee.
- */
- feesForType: { [wireMethod: string]: WireFee[] };
-}
-
-
-/**
- * Coins used for a payment, with signatures authorizing the payment and the
- * coins with remaining value updated to accomodate for a payment.
- */
-export interface PayCoinInfo {
- originalCoins: CoinRecord[];
- updatedCoins: CoinRecord[];
- sigs: CoinPaySig[];
-}
-
-
-/**
- * Amount helpers.
- */
-export namespace Amounts {
- /**
- * Number of fractional units that one value unit represents.
- */
- export const fractionalBase = 1e8;
-
- /**
- * Result of a possibly overflowing operation.
- */
- export interface Result {
- /**
- * Resulting, possibly saturated amount.
- */
- amount: AmountJson;
- /**
- * Was there an over-/underflow?
- */
- saturated: boolean;
- }
-
- /**
- * Get the largest amount that is safely representable.
- */
- export function getMaxAmount(currency: string): AmountJson {
- return {
- currency,
- fraction: 2 ** 32,
- value: Number.MAX_SAFE_INTEGER,
- };
- }
-
- /**
- * Get an amount that represents zero units of a currency.
- */
- export function getZero(currency: string): AmountJson {
- return {
- currency,
- fraction: 0,
- value: 0,
- };
- }
-
- /**
- * Add two amounts. Return the result and whether
- * the addition overflowed. The overflow is always handled
- * by saturating and never by wrapping.
- *
- * Throws when currencies don't match.
- */
- export function add(first: AmountJson, ...rest: AmountJson[]): Result {
- const currency = first.currency;
- let value = first.value + Math.floor(first.fraction / fractionalBase);
- if (value > Number.MAX_SAFE_INTEGER) {
- return { amount: getMaxAmount(currency), saturated: true };
- }
- let fraction = first.fraction % fractionalBase;
- for (const x of rest) {
- if (x.currency !== currency) {
- throw Error(`Mismatched currency: ${x.currency} and ${currency}`);
- }
-
- value = value + x.value + Math.floor((fraction + x.fraction) / fractionalBase);
- fraction = Math.floor((fraction + x.fraction) % fractionalBase);
- if (value > Number.MAX_SAFE_INTEGER) {
- return { amount: getMaxAmount(currency), saturated: true };
- }
- }
- return { amount: { currency, value, fraction }, saturated: false };
- }
-
- /**
- * Subtract two amounts. Return the result and whether
- * the subtraction overflowed. The overflow is always handled
- * by saturating and never by wrapping.
- *
- * Throws when currencies don't match.
- */
- export function sub(a: AmountJson, ...rest: AmountJson[]): Result {
- const currency = a.currency;
- let value = a.value;
- let fraction = a.fraction;
-
- for (const b of rest) {
- if (b.currency !== currency) {
- throw Error(`Mismatched currency: ${b.currency} and ${currency}`);
- }
- if (fraction < b.fraction) {
- if (value < 1) {
- return { amount: { currency, value: 0, fraction: 0 }, saturated: true };
- }
- value--;
- fraction += fractionalBase;
- }
- 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 };
- }
-
- /**
- * Compare two amounts. Returns 0 when equal, -1 when a < b
- * and +1 when a > b. Throws when currencies don't match.
- */
- export function cmp(a: AmountJson, b: AmountJson): number {
- if (a.currency !== b.currency) {
- throw Error(`Mismatched currency: ${a.currency} and ${b.currency}`);
- }
- const av = a.value + Math.floor(a.fraction / fractionalBase);
- const af = a.fraction % fractionalBase;
- const bv = b.value + Math.floor(b.fraction / fractionalBase);
- const bf = b.fraction % fractionalBase;
- 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");
- }
- }
-
- /**
- * Create a copy of an amount.
- */
- export function copy(a: AmountJson): AmountJson {
- return {
- currency: a.currency,
- fraction: a.fraction,
- value: a.value,
- };
- }
-
- /**
- * Divide an amount. Throws on division by zero.
- */
- export function divide(a: AmountJson, n: number): AmountJson {
- if (n === 0) {
- throw Error(`Division by 0`);
- }
- if (n === 1) {
- return {value: a.value, fraction: a.fraction, currency: a.currency};
- }
- const r = a.value % n;
- return {
- currency: a.currency,
- fraction: Math.floor(((r * fractionalBase) + a.fraction) / n),
- value: Math.floor(a.value / n),
- };
- }
-
- /**
- * Check if an amount is non-zero.
- */
- export function isNonZero(a: AmountJson): boolean {
- return a.value > 0 || a.fraction > 0;
- }
-
- /**
- * Parse an amount like 'EUR:20.5' for 20 Euros and 50 ct.
- */
- export function parse(s: string): AmountJson|undefined {
- const res = s.match(/([a-zA-Z0-9_*-]+):([0-9])+([.][0-9]+)?/);
- if (!res) {
- return undefined;
- }
- return {
- currency: res[1],
- fraction: Math.round(fractionalBase * Number.parseFloat(res[3] || "0")),
- value: Number.parseInt(res[2]),
- };
- }
-
- /**
- * Convert the amount to a float.
- */
- export function toFloat(a: AmountJson): number {
- return a.value + (a.fraction / fractionalBase);
- }
-
- /**
- * Convert a float to a Taler amount.
- * Loss of precision possible.
- */
- export function fromFloat(floatVal: number, currency: string) {
- return {
- currency,
- fraction: Math.floor((floatVal - Math.floor(floatVal)) * fractionalBase),
- value: Math.floor(floatVal),
- };
- }
-}
-
-
-/**
- * Listener for notifications from the wallet.
- */
-export interface Notifier {
- /**
- * Called when a new notification arrives.
- */
- notify(): void;
-}
-
-/**
- * For terseness.
- */
-export function mkAmount(value: number, fraction: number, currency: string): AmountJson {
- return {value, fraction, currency};
-}
-
-/**
- * Possible results for checkPay.
- */
-export interface CheckPayResult {
- status: "paid" | "payment-possible" | "insufficient-balance";
- coinSelection?: CoinSelectionResult;
-}
-
-/**
- * Possible results for confirmPay.
- */
-export type ConfirmPayResult = "paid" | "insufficient-balance";
-
-
-/**
- * Activity history record.
- */
-export interface HistoryRecord {
- /**
- * Type of the history event.
- */
- type: string;
-
- /**
- * Time when the activity was recorded.
- */
- timestamp: number;
-
- /**
- * Subject of the entry. Used to group multiple history records together.
- * Only the latest history record with the same subjectId will be shown.
- */
- subjectId?: string;
-
- /**
- * Details used when rendering the history record.
- */
- detail: any;
-}
-
-
-/**
- * Payment body sent to the merchant's /pay.
- */
-export interface PayReq {
- /**
- * Coins with signature.
- */
- coins: CoinPaySig[];
-
- /**
- * The merchant public key, used to uniquely
- * identify the merchant instance.
- */
- merchant_pub: string;
-
- /**
- * Order ID that's being payed for.
- */
- order_id: string;
-
- /**
- * Exchange that the coins are from (base URL).
- */
- exchange: string;
-}
-
-
-/**
- * Response to a query payment request. Tagged union over the 'found' field.
- */
-export type QueryPaymentResult = QueryPaymentNotFound | QueryPaymentFound;
-
-/**
- * Query payment response when the payment was found.
- */
-export interface QueryPaymentNotFound {
- found: false;
-}
-
-/**
- * Query payment response when the payment wasn't found.
- */
-export interface QueryPaymentFound {
- found: true;
- contractTermsHash: string;
- contractTerms: ContractTerms;
- payReq: PayReq;
-}
-
-/**
- * Information about all sender wire details known to the wallet,
- * as well as exchanges that accept these wire types.
- */
-export interface SenderWireInfos {
- /**
- * Mapping from exchange base url to list of accepted
- * wire types.
- */
- exchangeWireTypes: { [exchangeBaseUrl: string]: string[] };
-
- /**
- * Sender wire types stored in the wallet.
- */
- senderWires: object[];
-}
-
-
-/**
- * Request to mark a reserve as confirmed.
- */
-@Checkable.Class()
-export class CreateReserveRequest {
- /**
- * The initial amount for the reserve.
- */
- @Checkable.Value(AmountJson)
- amount: AmountJson;
-
- /**
- * Exchange URL where the bank should create the reserve.
- */
- @Checkable.String
- exchange: string;
-
- /**
- * Wire details for the bank account that sent the funds to the exchange.
- */
- @Checkable.Optional(Checkable.Any)
- senderWire?: object;
-
- /**
- * Verify that a value matches the schema of this class and convert it into a
- * member.
- */
- static checked: (obj: any) => CreateReserveRequest;
-}
-
-
-/**
- * Request to mark a reserve as confirmed.
- */
-@Checkable.Class()
-export class ConfirmReserveRequest {
- /**
- * Public key of then reserve that should be marked
- * as confirmed.
- */
- @Checkable.String
- reservePub: string;
-
- /**
- * Verify that a value matches the schema of this class and convert it into a
- * member.
- */
- static checked: (obj: any) => ConfirmReserveRequest;
-}
-
-
-/**
- * Wire coins to the user's own bank account.
- */
-@Checkable.Class()
-export class ReturnCoinsRequest {
- /**
- * The amount to wire.
- */
- @Checkable.Value(AmountJson)
- amount: AmountJson;
-
- /**
- * The exchange to take the coins from.
- */
- @Checkable.String
- exchange: string;
-
- /**
- * Wire details for the bank account of the customer that will
- * receive the funds.
- */
- @Checkable.Any
- senderWire?: object;
-
- /**
- * Verify that a value matches the schema of this class and convert it into a
- * member.
- */
- static checked: (obj: any) => ReturnCoinsRequest;
-}
-
-
-/**
- * Refund permission in the format that the merchant gives it to us.
- */
-export interface RefundPermission {
- /**
- * Amount to be refunded.
- */
- refund_amount: AmountJson;
-
- /**
- * Fee for the refund.
- */
- refund_fee: AmountJson;
-
- /**
- * Contract terms hash to identify the contract that this
- * refund is for.
- */
- h_contract_terms: string;
-
- /**
- * Public key of the coin being refunded.
- */
- coin_pub: string;
-
- /**
- * Refund transaction ID between merchant and exchange.
- */
- rtransaction_id: number;
-
- /**
- * Public key of the merchant.
- */
- merchant_pub: string;
-
- /**
- * Signature made by the merchant over the refund permission.
- */
- merchant_sig: string;
-}
-
-
-/**
- * Record that stores status information about one purchase, starting from when
- * the customer accepts a proposal. Includes refund status if applicable.
- */
-export interface PurchaseRecord {
- contractTermsHash: string;
- contractTerms: ContractTerms;
- payReq: PayReq;
- merchantSig: string;
-
- /**
- * The purchase isn't active anymore, it's either successfully paid or
- * refunded/aborted.
- */
- finished: boolean;
-
- refundsPending: { [refundSig: string]: RefundPermission };
- refundsDone: { [refundSig: string]: RefundPermission };
-
- /**
- * When was the purchase made?
- * Refers to the time that the user accepted.
- */
- timestamp: number;
-
- /**
- * When was the last refund made?
- * Set to 0 if no refund was made on the purchase.
- */
- timestamp_refund: number;
-}
-
-
-/**
- * Result of selecting coins, contains the exchange, and selected
- * coins with their denomination.
- */
-export interface CoinSelectionResult {
- exchangeUrl: string;
- cds: CoinWithDenom[];
- totalFees: AmountJson;
-}
-
-
-/**
- * Named tuple of coin and denomination.
- */
-export interface CoinWithDenom {
- /**
- * A coin. Must have the same denomination public key as the associated
- * denomination.
- */
- coin: CoinRecord;
- /**
- * An associated denomination.
- */
- denom: DenominationRecord;
-}
-
-
-/**
- * Planchet detail sent to the merchant.
- */
-export interface TipPlanchetDetail {
- /**
- * Hashed denomination public key.
- */
- denom_pub_hash: string;
-
- /**
- * Coin's blinded public key.
- */
- coin_ev: string;
-}
-
-
-export interface TipPickupRequest {
- /**
- * Identifier of the tip.
- */
- tip_id: string;
-
- /**
- * List of planchets the wallet wants to use for the tip.
- */
- planchets: TipPlanchetDetail[];
-}
-
-@Checkable.Class()
-export class ReserveSigSingleton {
- @Checkable.String
- reserve_sig: string;
-
- static checked: (obj: any) => ReserveSigSingleton;
-}
-
-/**
- * Response of the merchant
- * to the TipPickupRequest.
- */
-@Checkable.Class()
-export class TipResponse {
- /**
- * Public key of the reserve
- */
- @Checkable.String
- reserve_pub: string;
-
- /**
- * The order of the signatures matches the planchets list.
- */
- @Checkable.List(Checkable.Value(ReserveSigSingleton))
- reserve_sigs: ReserveSigSingleton[];
-
- static checked: (obj: any) => TipResponse;
-}
-
-
-/**
- * Tipping planchet stored in the database.
- */
-export interface TipPlanchet {
- blindingKey: string;
- coinEv: string;
- coinPriv: string;
- coinPub: string;
- coinValue: AmountJson;
- denomPubHash: string;
- denomPub: string;
-}
-
-/**
- * Status of a tip we got from a merchant.
- */
-export interface TipRecord {
- /**
- * Has the user accepted the tip? Only after the tip has been accepted coins
- * withdrawn from the tip may be used.
- */
- accepted: boolean;
-
- /**
- * The tipped amount.
- */
- amount: AmountJson;
-
- /**
- * Coin public keys from the planchets.
- * This field is redundant and used for indexing the record via
- * a multi-entry index to look up tip records by coin public key.
- */
- coinPubs: string[];
-
- /**
- * Timestamp, the tip can't be picked up anymore after this deadline.
- */
- deadline: number;
-
- /**
- * The exchange that will sign our coins, chosen by the merchant.
- */
- exchangeUrl: string;
-
- /**
- * Domain of the merchant, necessary to uniquely identify the tip since
- * merchants can freely choose the ID and a malicious merchant might cause a
- * collision.
- */
- merchantDomain: string;
-
- /**
- * Planchets, the members included in TipPlanchetDetail will be sent to the
- * merchant.
- */
- planchets: TipPlanchet[];
-
- /**
- * Response if the merchant responded,
- * undefined otherwise.
- */
- response?: TipResponse[];
-
- /**
- * Identifier for the tip, chosen by the merchant.
- */
- tipId: string;
-
- /**
- * URL to go to once the tip has been accepted.
- */
- nextUrl: string;
-
- timestamp: number;
-}
-
-
-export interface TipStatus {
- tip: TipRecord;
- rci?: ReserveCreationInfo;
-}
-
-
-@Checkable.Class()
-export class TipStatusRequest {
- @Checkable.String
- tipId: string;
-
- @Checkable.String
- merchantDomain: string;
-
- static checked: (obj: any) => TipStatusRequest;
-}
-
-
-@Checkable.Class()
-export class AcceptTipRequest {
- @Checkable.String
- tipId: string;
-
- @Checkable.String
- merchantDomain: string;
-
- static checked: (obj: any) => AcceptTipRequest;
-}
-
-
-@Checkable.Class()
-export class ProcessTipResponseRequest {
- @Checkable.String
- tipId: string;
-
- @Checkable.String
- merchantDomain: string;
-
- @Checkable.Value(TipResponse)
- tipResponse: TipResponse;
-
- static checked: (obj: any) => ProcessTipResponseRequest;
-}
-
-@Checkable.Class()
-export class GetTipPlanchetsRequest {
- @Checkable.String
- tipId: string;
-
- @Checkable.String
- merchantDomain: string;
-
- @Checkable.Optional(Checkable.Value(AmountJson))
- amount: AmountJson;
-
- @Checkable.Number
- deadline: number;
-
- @Checkable.String
- exchangeUrl: string;
-
- @Checkable.String
- nextUrl: string;
-
- static checked: (obj: any) => GetTipPlanchetsRequest;
-}
-
-@Checkable.Class()
-export class TipToken {
- @Checkable.String
- expiration: string;
-
- @Checkable.String
- exchange_url: string;
-
- @Checkable.String
- pickup_url: string;
-
- @Checkable.String
- tip_id: string;
-
- @Checkable.Value(AmountJson)
- amount: AmountJson;
-
- @Checkable.String
- next_url: string;
-
- static checked: (obj: any) => TipToken;
-}
diff --git a/src/wallet-test.ts b/src/wallet-test.ts
index 037cc7592..6b06085c0 100644
--- a/src/wallet-test.ts
+++ b/src/wallet-test.ts
@@ -15,13 +15,19 @@
*/
-import {test} from "ava";
-import * as types from "./types";
+import { test } from "ava";
+
+import * as dbTypes from "./dbTypes";
+import * as types from "./walletTypes";
+
import * as wallet from "./wallet";
+import { AmountJson} from "./amounts";
+import * as Amounts from "./amounts";
+
-function a(x: string): types.AmountJson {
- const amt = types.Amounts.parse(x);
+function a(x: string): AmountJson {
+ const amt = Amounts.parse(x);
if (!amt) {
throw Error("invalid amount");
}
@@ -40,7 +46,7 @@ function fakeCwd(current: string, value: string, feeDeposit: string): types.Coin
denomSig: "(mock)",
exchangeBaseUrl: "(mock)",
reservePub: "(mock)",
- status: types.CoinStatus.Fresh,
+ status: dbTypes.CoinStatus.Fresh,
},
denom: {
denomPub: "(mock)",
@@ -56,7 +62,7 @@ function fakeCwd(current: string, value: string, feeDeposit: string): types.Coin
stampExpireLegal: "(mock)",
stampExpireWithdraw: "(mock)",
stampStart: "(mock)",
- status: types.DenominationStatus.VerifiedGood,
+ status: dbTypes.DenominationStatus.VerifiedGood,
value: a(value),
},
};
diff --git a/src/wallet.ts b/src/wallet.ts
index ca94e1d59..41f8e7276 100644
--- a/src/wallet.ts
+++ b/src/wallet.ts
@@ -43,56 +43,64 @@ import {
QueryRoot,
Store,
} from "./query";
-import {TimerGroup} from "./timer";
+import { TimerGroup } from "./timer";
+
+import { AmountJson } from "./amounts";
+import * as Amounts from "./amounts";
+
import {
- AmountJson,
- Amounts,
- Auditor,
- CheckPayResult,
- CoinPaySig,
CoinRecord,
- CoinSelectionResult,
CoinStatus,
- CoinWithDenom,
- ConfirmPayResult,
- ConfirmReserveRequest,
- ContractTerms,
- CreateReserveRequest,
- CreateReserveResponse,
CurrencyRecord,
- Denomination,
DenominationRecord,
DenominationStatus,
- ExchangeHandle,
ExchangeRecord,
ExchangeWireFeesRecord,
- HistoryRecord,
- Notifier,
- PayCoinInfo,
- PayReq,
- PaybackConfirmation,
PreCoinRecord,
ProposalRecord,
PurchaseRecord,
- QueryPaymentResult,
RefreshPreCoinRecord,
RefreshSessionRecord,
+ ReserveRecord,
+ TipRecord,
+ WireFee,
+} from "./dbTypes";
+
+import URI = require("urijs");
+
+import {
+ Auditor,
+ CoinPaySig,
+ ContractTerms,
+ Denomination,
+ ExchangeHandle,
+ PayReq,
+ PaybackConfirmation,
RefundPermission,
+ TipPlanchetDetail,
+ TipResponse,
+} from "./talerTypes";
+import {
+ CheckPayResult,
+ CoinSelectionResult,
+ CoinWithDenom,
+ ConfirmPayResult,
+ ConfirmReserveRequest,
+ CreateReserveRequest,
+ CreateReserveResponse,
+ HistoryRecord,
+ Notifier,
+ PayCoinInfo,
+ QueryPaymentResult,
ReserveCreationInfo,
- ReserveRecord,
ReturnCoinsRequest,
SenderWireInfos,
- TipPlanchetDetail,
- TipRecord,
- TipResponse,
TipStatus,
WalletBalance,
WalletBalanceEntry,
- WireFee,
WireInfo,
-} from "./types";
-import URI = require("urijs");
+} from "./walletTypes";
/**
@@ -561,7 +569,9 @@ export namespace Stores {
super("purchases", {keyPath: "contractTermsHash"});
}
- fulfillmentUrlIndex = new Index<string, PurchaseRecord>(this, "fulfillmentUrlIndex", "contractTerms.fulfillment_url");
+ fulfillmentUrlIndex = new Index<string, PurchaseRecord>(this,
+ "fulfillmentUrlIndex",
+ "contractTerms.fulfillment_url");
orderIdIndex = new Index<string, PurchaseRecord>(this, "orderIdIndex", "contractTerms.order_id");
timestampIndex = new Index<string, PurchaseRecord>(this, "timestampIndex", "timestamp");
}
@@ -1077,7 +1087,7 @@ export class Wallet {
if (!sp) {
return;
}
- if (sp.proposalId != proposalId) {
+ if (sp.proposalId !== proposalId) {
return;
}
const coinKeys = sp.payCoinInfo.updatedCoins.map(x => x.coinPub);
@@ -1090,8 +1100,8 @@ export class Wallet {
if (!currentCoin) {
return;
}
- if (Amounts.cmp(specCoin.currentAmount, currentCoin.currentAmount) != 0) {
- return
+ if (Amounts.cmp(specCoin.currentAmount, currentCoin.currentAmount) !== 0) {
+ return;
}
}
return sp;
@@ -1135,7 +1145,7 @@ export class Wallet {
}
// Only create speculative signature if we don't already have one for this proposal
- if ((!this.speculativePayData) || (this.speculativePayData && this.speculativePayData.proposalId != proposalId)) {
+ if ((!this.speculativePayData) || (this.speculativePayData && this.speculativePayData.proposalId !== proposalId)) {
const { exchangeUrl, cds } = res;
const payCoinInfo = await this.cryptoApi.signDeposit(proposal.contractTerms, cds);
this.speculativePayData = {
@@ -1250,7 +1260,7 @@ export class Wallet {
.finish();
if (coin.status === CoinStatus.TainedByTip) {
- let tip = await this.q().getIndexed(Stores.tips.coinPubIndex, coin.coinPub);
+ const tip = await this.q().getIndexed(Stores.tips.coinPubIndex, coin.coinPub);
if (!tip) {
throw Error(`inconsistent DB: tip for coin pub ${coin.coinPub} not found.`);
}
@@ -1263,8 +1273,8 @@ export class Wallet {
c.status = CoinStatus.Fresh;
}
return c;
- }
- await this.q().mutate(Stores.coins, coin.coinPub, mutateCoin)
+ };
+ await this.q().mutate(Stores.coins, coin.coinPub, mutateCoin);
// Show notifications only for accepted tips
this.badge.showNotification();
}
@@ -1724,6 +1734,7 @@ export class Wallet {
const ret: ReserveCreationInfo = {
earliestDepositExpiration,
exchangeInfo,
+ exchangeVersion: exchangeInfo.protocolVersion || "unknown",
isAudited,
isTrusted,
numOfferedDenoms: possibleDenoms.length,
@@ -1731,11 +1742,10 @@ export class Wallet {
selectedDenoms,
trustedAuditorPubs,
versionMatch,
+ walletVersion: WALLET_PROTOCOL_VERSION,
wireFees,
wireInfo,
withdrawFee: acc,
- exchangeVersion: exchangeInfo.protocolVersion || "unknown",
- walletVersion: WALLET_PROTOCOL_VERSION,
};
return ret;
}
@@ -1779,7 +1789,7 @@ export class Wallet {
.indexJoinLeft(Stores.denominations.exchangeBaseUrlIndex,
(e) => e.exchangeBaseUrl)
.fold((cd: JoinLeftResult<CoinRecord, DenominationRecord>,
- suspendedCoins: CoinRecord[]) => {
+ suspendedCoins: CoinRecord[]) => {
if ((!cd.right) || (!cd.right.isOffered)) {
return Array.prototype.concat(suspendedCoins, [cd.left]);
}
@@ -1922,8 +1932,7 @@ export class Wallet {
this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex,
exchangeInfo.baseUrl)
.fold((x: DenominationRecord,
- acc: typeof existingDenoms) => (acc[x.denomPub] = x, acc),
- {})
+ acc: typeof existingDenoms) => (acc[x.denomPub] = x, acc), {})
);
const newDenoms: typeof existingDenoms = {};
@@ -2432,9 +2441,9 @@ export class Wallet {
for (const tip of tips) {
history.push({
detail: {
- merchantDomain: tip.merchantDomain,
- amount: tip.amount,
accepted: tip.accepted,
+ amount: tip.amount,
+ merchantDomain: tip.merchantDomain,
tipId: tip.tipId,
},
timestamp: tip.timestamp,
@@ -2760,8 +2769,8 @@ export class Wallet {
H_wire: coinsReturnRecord.contractTerms.H_wire,
coin_pub: c.coinPaySig.coin_pub,
coin_sig: c.coinPaySig.coin_sig,
- denom_pub: c.coinPaySig.denom_pub,
contribution: c.coinPaySig.contribution,
+ denom_pub: c.coinPaySig.denom_pub,
h_contract_terms: coinsReturnRecord.contractTermsHash,
merchant_pub: coinsReturnRecord.contractTerms.merchant_pub,
pay_deadline: coinsReturnRecord.contractTerms.pay_deadline,
@@ -2950,7 +2959,12 @@ export class Wallet {
* Get planchets for a tip. Creates new planchets if they don't exist already
* for this tip. The tip is uniquely identified by the merchant's domain and the tip id.
*/
- async getTipPlanchets(merchantDomain: string, tipId: string, amount: AmountJson, deadline: number, exchangeUrl: string, nextUrl: string): Promise<TipPlanchetDetail[]> {
+ async getTipPlanchets(merchantDomain: string,
+ tipId: string,
+ amount: AmountJson,
+ deadline: number,
+ exchangeUrl: string,
+ nextUrl: string): Promise<TipPlanchetDetail[]> {
let tipRecord = await this.q().get(Stores.tips, [tipId, merchantDomain]);
if (!tipRecord) {
await this.updateExchangeFromUrl(exchangeUrl);
@@ -2973,9 +2987,9 @@ export class Wallet {
await this.q().put(Stores.tips, tipRecord).finish();
}
// Planchets in the form that the merchant expects
- const planchetDetail: TipPlanchetDetail[]= tipRecord.planchets.map((p) => ({
- denom_pub_hash: p.denomPubHash,
+ const planchetDetail: TipPlanchetDetail[] = tipRecord.planchets.map((p) => ({
coin_ev: p.coinEv,
+ denom_pub_hash: p.denomPubHash,
}));
return planchetDetail;
}
@@ -2985,7 +2999,7 @@ export class Wallet {
* These coins will not appear in the wallet yet.
*/
async processTipResponse(merchantDomain: string, tipId: string, response: TipResponse): Promise<void> {
- let tipRecord = await this.q().get(Stores.tips, [tipId, merchantDomain]);
+ const tipRecord = await this.q().get(Stores.tips, [tipId, merchantDomain]);
if (!tipRecord) {
throw Error("tip not found");
}
@@ -2995,18 +3009,18 @@ export class Wallet {
}
for (let i = 0; i < tipRecord.planchets.length; i++) {
- let planchet = tipRecord.planchets[i];
- let preCoin = {
- coinPub: planchet.coinPub,
- coinPriv: planchet.coinPriv,
+ const planchet = tipRecord.planchets[i];
+ const preCoin = {
+ blindingKey: planchet.blindingKey,
coinEv: planchet.coinEv,
+ coinPriv: planchet.coinPriv,
+ coinPub: planchet.coinPub,
coinValue: planchet.coinValue,
- reservePub: response.reserve_pub,
denomPub: planchet.denomPub,
- blindingKey: planchet.blindingKey,
- withdrawSig: response.reserve_sigs[i].reserve_sig,
exchangeBaseUrl: tipRecord.exchangeUrl,
isFromTip: true,
+ reservePub: response.reserve_pub,
+ withdrawSig: response.reserve_sigs[i].reserve_sig,
};
await this.q().put(Stores.precoins, preCoin);
this.processPreCoin(preCoin);
@@ -3082,8 +3096,8 @@ export class Wallet {
const gcProposal = (d: ProposalRecord, n: number) => {
// Delete proposal after 60 minutes or 5 minutes before pay deadline,
// whatever comes first.
- let deadlinePayMilli = getTalerStampSec(d.contractTerms.pay_deadline)! * 1000;
- let deadlineExpireMilli = nowMilli + (1000 * 60 * 60);
+ const deadlinePayMilli = getTalerStampSec(d.contractTerms.pay_deadline)! * 1000;
+ const deadlineExpireMilli = nowMilli + (1000 * 60 * 60);
return d.timestamp < Math.min(deadlinePayMilli, deadlineExpireMilli);
};
await this.q().deleteIf(Stores.proposals, gcProposal).finish();
@@ -3096,7 +3110,7 @@ export class Wallet {
}
activeExchanges.push(d.baseUrl);
return false;
- }
+ };
await this.q().deleteIf(Stores.exchanges, gcExchange).finish();
diff --git a/src/walletTypes.ts b/src/walletTypes.ts
new file mode 100644
index 000000000..cd0eee4cc
--- /dev/null
+++ b/src/walletTypes.ts
@@ -0,0 +1,572 @@
+/*
+ This file is part of TALER
+ (C) 2015-2017 GNUnet e.V. and INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Types used by clients of the wallet.
+ *
+ * These types are defined in a separate file make tree shaking easier, since
+ * some components use these types (via RPC) but do not depend on the wallet
+ * code directly.
+ */
+
+/**
+ * Imports.
+ */
+import { Checkable } from "./checkable";
+import * as LibtoolVersion from "./libtoolVersion";
+
+import { AmountJson } from "./amounts";
+
+import {
+ CoinRecord,
+ DenominationRecord,
+ ExchangeRecord,
+ ExchangeWireFeesRecord,
+ TipRecord,
+} from "./dbTypes";
+import {
+ CoinPaySig,
+ ContractTerms,
+ PayReq,
+ TipResponse,
+} from "./talerTypes";
+
+
+/**
+ * Response for the create reserve request to the wallet.
+ */
+@Checkable.Class()
+export class CreateReserveResponse {
+ /**
+ * Exchange URL where the bank should create the reserve.
+ * The URL is canonicalized in the response.
+ */
+ @Checkable.String
+ exchange: string;
+
+ /**
+ * Reserve public key of the newly created reserve.
+ */
+ @Checkable.String
+ reservePub: string;
+
+ /**
+ * Verify that a value matches the schema of this class and convert it into a
+ * member.
+ */
+ static checked: (obj: any) => CreateReserveResponse;
+}
+
+
+/**
+ * Wire info, sent to the bank when creating a reserve. Fee information will
+ * be filtered out. Only methods that the bank also supports should be sent.
+ */
+export interface WireInfo {
+ /**
+ * Mapping from wire method type to the exchange's wire info,
+ * excluding fees.
+ */
+ [type: string]: any;
+}
+
+
+/**
+ * Information about what will happen when creating a reserve.
+ *
+ * Sent to the wallet frontend to be rendered and shown to the user.
+ */
+export interface ReserveCreationInfo {
+ /**
+ * Exchange that the reserve will be created at.
+ */
+ exchangeInfo: ExchangeRecord;
+
+ /**
+ * Filtered wire info to send to the bank.
+ */
+ wireInfo: WireInfo;
+
+ /**
+ * Selected denominations for withdraw.
+ */
+ selectedDenoms: DenominationRecord[];
+
+ /**
+ * Fees for withdraw.
+ */
+ withdrawFee: AmountJson;
+
+ /**
+ * Remaining balance that is too small to be withdrawn.
+ */
+ overhead: AmountJson;
+
+ /**
+ * Wire fees from the exchange.
+ */
+ wireFees: ExchangeWireFeesRecord;
+
+ /**
+ * Does the wallet know about an auditor for
+ * the exchange that the reserve.
+ */
+ isAudited: boolean;
+
+ /**
+ * The exchange is trusted directly.
+ */
+ isTrusted: boolean;
+
+ /**
+ * The earliest deposit expiration of the selected coins.
+ */
+ earliestDepositExpiration: number;
+
+ /**
+ * Number of currently offered denominations.
+ */
+ numOfferedDenoms: number;
+ /**
+ * Public keys of trusted auditors for the currency we're withdrawing.
+ */
+ trustedAuditorPubs: string[];
+
+ /**
+ * Result of checking the wallet's version
+ * against the exchange's version.
+ *
+ * Older exchanges don't return version information.
+ */
+ versionMatch: LibtoolVersion.VersionMatchResult|undefined;
+
+ /**
+ * Libtool-style version string for the exchange or "unknown"
+ * for older exchanges.
+ */
+ exchangeVersion: string;
+
+ /**
+ * Libtool-style version string for the wallet.
+ */
+ walletVersion: string;
+}
+
+
+/**
+ * Mapping from currency/exchange to detailed balance
+ * information.
+ */
+export interface WalletBalance {
+ /**
+ * Mapping from currency name to detailed balance info.
+ */
+ byExchange: { [exchangeBaseUrl: string]: WalletBalanceEntry };
+
+ /**
+ * Mapping from currency name to detailed balance info.
+ */
+ byCurrency: { [currency: string]: WalletBalanceEntry };
+}
+
+
+/**
+ * Detailed wallet balance for a particular currency.
+ */
+export interface WalletBalanceEntry {
+ /**
+ * Directly available amount.
+ */
+ available: AmountJson;
+ /**
+ * Amount that we're waiting for (refresh, withdrawal).
+ */
+ pendingIncoming: AmountJson;
+ /**
+ * Amount that's marked for a pending payment.
+ */
+ pendingPayment: AmountJson;
+ /**
+ * Amount that was paid back and we could withdraw again.
+ */
+ paybackAmount: AmountJson;
+}
+
+
+/**
+ * Coins used for a payment, with signatures authorizing the payment and the
+ * coins with remaining value updated to accomodate for a payment.
+ */
+export interface PayCoinInfo {
+ originalCoins: CoinRecord[];
+ updatedCoins: CoinRecord[];
+ sigs: CoinPaySig[];
+}
+
+
+/**
+ * Listener for notifications from the wallet.
+ */
+export interface Notifier {
+ /**
+ * Called when a new notification arrives.
+ */
+ notify(): void;
+}
+
+
+/**
+ * For terseness.
+ */
+export function mkAmount(value: number, fraction: number, currency: string): AmountJson {
+ return {value, fraction, currency};
+}
+
+
+/**
+ * Possible results for checkPay.
+ */
+export interface CheckPayResult {
+ status: "paid" | "payment-possible" | "insufficient-balance";
+ coinSelection?: CoinSelectionResult;
+}
+
+
+/**
+ * Possible results for confirmPay.
+ */
+export type ConfirmPayResult = "paid" | "insufficient-balance";
+
+
+/**
+ * Activity history record.
+ */
+export interface HistoryRecord {
+ /**
+ * Type of the history event.
+ */
+ type: string;
+
+ /**
+ * Time when the activity was recorded.
+ */
+ timestamp: number;
+
+ /**
+ * Subject of the entry. Used to group multiple history records together.
+ * Only the latest history record with the same subjectId will be shown.
+ */
+ subjectId?: string;
+
+ /**
+ * Details used when rendering the history record.
+ */
+ detail: any;
+}
+
+
+/**
+ * Response to a query payment request. Tagged union over the 'found' field.
+ */
+export type QueryPaymentResult = QueryPaymentNotFound | QueryPaymentFound;
+
+
+/**
+ * Query payment response when the payment was found.
+ */
+export interface QueryPaymentNotFound {
+ found: false;
+}
+
+
+/**
+ * Query payment response when the payment wasn't found.
+ */
+export interface QueryPaymentFound {
+ found: true;
+ contractTermsHash: string;
+ contractTerms: ContractTerms;
+ payReq: PayReq;
+}
+
+
+/**
+ * Information about all sender wire details known to the wallet,
+ * as well as exchanges that accept these wire types.
+ */
+export interface SenderWireInfos {
+ /**
+ * Mapping from exchange base url to list of accepted
+ * wire types.
+ */
+ exchangeWireTypes: { [exchangeBaseUrl: string]: string[] };
+
+ /**
+ * Sender wire types stored in the wallet.
+ */
+ senderWires: object[];
+}
+
+
+/**
+ * Request to mark a reserve as confirmed.
+ */
+@Checkable.Class()
+export class CreateReserveRequest {
+ /**
+ * The initial amount for the reserve.
+ */
+ @Checkable.Value(AmountJson)
+ amount: AmountJson;
+
+ /**
+ * Exchange URL where the bank should create the reserve.
+ */
+ @Checkable.String
+ exchange: string;
+
+ /**
+ * Wire details for the bank account that sent the funds to the exchange.
+ */
+ @Checkable.Optional(Checkable.Any)
+ senderWire?: object;
+
+ /**
+ * Verify that a value matches the schema of this class and convert it into a
+ * member.
+ */
+ static checked: (obj: any) => CreateReserveRequest;
+}
+
+
+/**
+ * Request to mark a reserve as confirmed.
+ */
+@Checkable.Class()
+export class ConfirmReserveRequest {
+ /**
+ * Public key of then reserve that should be marked
+ * as confirmed.
+ */
+ @Checkable.String
+ reservePub: string;
+
+ /**
+ * Verify that a value matches the schema of this class and convert it into a
+ * member.
+ */
+ static checked: (obj: any) => ConfirmReserveRequest;
+}
+
+
+/**
+ * Wire coins to the user's own bank account.
+ */
+@Checkable.Class()
+export class ReturnCoinsRequest {
+ /**
+ * The amount to wire.
+ */
+ @Checkable.Value(AmountJson)
+ amount: AmountJson;
+
+ /**
+ * The exchange to take the coins from.
+ */
+ @Checkable.String
+ exchange: string;
+
+ /**
+ * Wire details for the bank account of the customer that will
+ * receive the funds.
+ */
+ @Checkable.Any
+ senderWire?: object;
+
+ /**
+ * Verify that a value matches the schema of this class and convert it into a
+ * member.
+ */
+ static checked: (obj: any) => ReturnCoinsRequest;
+}
+
+
+/**
+ * Result of selecting coins, contains the exchange, and selected
+ * coins with their denomination.
+ */
+export interface CoinSelectionResult {
+ exchangeUrl: string;
+ cds: CoinWithDenom[];
+ totalFees: AmountJson;
+}
+
+
+/**
+ * Named tuple of coin and denomination.
+ */
+export interface CoinWithDenom {
+ /**
+ * A coin. Must have the same denomination public key as the associated
+ * denomination.
+ */
+ coin: CoinRecord;
+ /**
+ * An associated denomination.
+ */
+ denom: DenominationRecord;
+}
+
+
+/**
+ * Status of processing a tip.
+ */
+export interface TipStatus {
+ tip: TipRecord;
+ rci?: ReserveCreationInfo;
+}
+
+
+/**
+ * Request to the wallet for the status of processing a tip.
+ */
+@Checkable.Class()
+export class TipStatusRequest {
+ /**
+ * Identifier of the tip.
+ */
+ @Checkable.String
+ tipId: string;
+
+ /**
+ * Merchant domain. Within each merchant domain, the tip identifier
+ * uniquely identifies a tip.
+ */
+ @Checkable.String
+ merchantDomain: string;
+
+ /**
+ * Create a TipStatusRequest from untyped JSON.
+ */
+ static checked: (obj: any) => TipStatusRequest;
+}
+
+/**
+ * Request to the wallet to accept a tip.
+ */
+@Checkable.Class()
+export class AcceptTipRequest {
+ /**
+ * Identifier of the tip.
+ */
+ @Checkable.String
+ tipId: string;
+
+ /**
+ * Merchant domain. Within each merchant domain, the tip identifier
+ * uniquely identifies a tip.
+ */
+ @Checkable.String
+ merchantDomain: string;
+
+ /**
+ * Create an AcceptTipRequest from untyped JSON.
+ * Validates the schema and throws on error.
+ */
+ static checked: (obj: any) => AcceptTipRequest;
+}
+
+
+/**
+ * Request for the wallet to process a tip response from a merchant.
+ */
+@Checkable.Class()
+export class ProcessTipResponseRequest {
+ /**
+ * Identifier of the tip.
+ */
+ @Checkable.String
+ tipId: string;
+
+ /**
+ * Merchant domain. Within each merchant domain, the tip identifier
+ * uniquely identifies a tip.
+ */
+ @Checkable.String
+ merchantDomain: string;
+
+ /**
+ * Tip response from the merchant.
+ */
+ @Checkable.Value(TipResponse)
+ tipResponse: TipResponse;
+
+ /**
+ * Create an AcceptTipRequest from untyped JSON.
+ * Validates the schema and throws on error.
+ */
+ static checked: (obj: any) => ProcessTipResponseRequest;
+}
+
+
+/**
+ * Request for the wallet to generate tip planchets.
+ */
+@Checkable.Class()
+export class GetTipPlanchetsRequest {
+ /**
+ * Identifier of the tip.
+ */
+ @Checkable.String
+ tipId: string;
+
+ /**
+ * Merchant domain. Within each merchant domain, the tip identifier
+ * uniquely identifies a tip.
+ */
+ @Checkable.String
+ merchantDomain: string;
+
+ /**
+ * Amount of the tip.
+ */
+ @Checkable.Optional(Checkable.Value(AmountJson))
+ amount: AmountJson;
+
+ /**
+ * Deadline for picking up the tip.
+ */
+ @Checkable.Number
+ deadline: number;
+
+ /**
+ * Exchange URL that must be used to pick up the tip.
+ */
+ @Checkable.String
+ exchangeUrl: string;
+
+ /**
+ * URL to nagivate to after processing the tip.
+ */
+ @Checkable.String
+ nextUrl: string;
+
+ /**
+ * Create an AcceptTipRequest from untyped JSON.
+ * Validates the schema and throws on error.
+ */
+ static checked: (obj: any) => GetTipPlanchetsRequest;
+}
diff --git a/src/webex/messages.ts b/src/webex/messages.ts
index 44c9f166c..0d0329808 100644
--- a/src/webex/messages.ts
+++ b/src/webex/messages.ts
@@ -21,7 +21,10 @@
// Messages are already documented in wxApi.
/* tslint:disable:completed-docs */
-import * as types from "../types";
+import { AmountJson } from "../amounts";
+import * as dbTypes from "../dbTypes";
+import * as talerTypes from "../talerTypes";
+import * as walletTypes from "../walletTypes";
/**
* Message type information.
@@ -29,7 +32,7 @@ import * as types from "../types";
export interface MessageMap {
"balances": {
request: { };
- response: types.WalletBalance;
+ response: walletTypes.WalletBalance;
};
"dump-db": {
request: { };
@@ -55,7 +58,7 @@ export interface MessageMap {
};
"create-reserve": {
request: {
- amount: types.AmountJson;
+ amount: AmountJson;
exchange: string
};
response: void;
@@ -70,11 +73,11 @@ export interface MessageMap {
};
"confirm-pay": {
request: { proposalId: number; };
- response: types.ConfirmPayResult;
+ response: walletTypes.ConfirmPayResult;
};
"check-pay": {
request: { proposalId: number; };
- response: types.CheckPayResult;
+ response: walletTypes.CheckPayResult;
};
"query-payment": {
request: { };
@@ -82,31 +85,31 @@ export interface MessageMap {
};
"exchange-info": {
request: { baseUrl: string };
- response: types.ExchangeRecord;
+ response: dbTypes.ExchangeRecord;
};
"currency-info": {
request: { name: string };
- response: types.CurrencyRecord;
+ response: dbTypes.CurrencyRecord;
};
"hash-contract": {
request: { contract: object };
response: string;
};
"save-proposal": {
- request: { proposal: types.ProposalRecord };
+ request: { proposal: dbTypes.ProposalRecord };
response: void;
};
"reserve-creation-info": {
- request: { baseUrl: string, amount: types.AmountJson };
- response: types.ReserveCreationInfo;
+ request: { baseUrl: string, amount: AmountJson };
+ response: walletTypes.ReserveCreationInfo;
};
"get-history": {
request: { };
- response: types.HistoryRecord[];
+ response: walletTypes.HistoryRecord[];
};
"get-proposal": {
request: { proposalId: number };
- response: types.ProposalRecord | undefined;
+ response: dbTypes.ProposalRecord | undefined;
};
"get-coins": {
request: { exchangeBaseUrl: string };
@@ -118,23 +121,23 @@ export interface MessageMap {
};
"get-currencies": {
request: { };
- response: types.CurrencyRecord[];
+ response: dbTypes.CurrencyRecord[];
};
"update-currency": {
- request: { currencyRecord: types.CurrencyRecord };
+ request: { currencyRecord: dbTypes.CurrencyRecord };
response: void;
};
"get-exchanges": {
request: { };
- response: types.ExchangeRecord[];
+ response: dbTypes.ExchangeRecord[];
};
"get-reserves": {
request: { exchangeBaseUrl: string };
- response: types.ReserveRecord[];
+ response: dbTypes.ReserveRecord[];
};
"get-payback-reserves": {
request: { };
- response: types.ReserveRecord[];
+ response: dbTypes.ReserveRecord[];
};
"withdraw-payback-reserve": {
request: { reservePub: string };
@@ -142,11 +145,11 @@ export interface MessageMap {
};
"get-precoins": {
request: { exchangeBaseUrl: string };
- response: types.PreCoinRecord[];
+ response: dbTypes.PreCoinRecord[];
};
"get-denoms": {
request: { exchangeBaseUrl: string };
- response: types.DenominationRecord[];
+ response: dbTypes.DenominationRecord[];
};
"payback-coin": {
request: { coinPub: string };
@@ -189,23 +192,23 @@ export interface MessageMap {
response: void;
};
"get-full-refund-fees": {
- request: { refundPermissions: types.RefundPermission[] };
+ request: { refundPermissions: talerTypes.RefundPermission[] };
response: void;
};
"get-tip-planchets": {
- request: types.GetTipPlanchetsRequest;
+ request: walletTypes.GetTipPlanchetsRequest;
response: void;
};
"process-tip-response": {
- request: types.ProcessTipResponseRequest;
+ request: walletTypes.ProcessTipResponseRequest;
response: void;
};
"accept-tip": {
- request: types.AcceptTipRequest;
+ request: walletTypes.AcceptTipRequest;
response: void;
};
"get-tip-status": {
- request: types.TipStatusRequest;
+ request: walletTypes.TipStatusRequest;
response: void;
};
"clear-notification": {
diff --git a/src/webex/notify.ts b/src/webex/notify.ts
index 05883e8bb..1a447c0ac 100644
--- a/src/webex/notify.ts
+++ b/src/webex/notify.ts
@@ -29,7 +29,8 @@ import URI = require("urijs");
import wxApi = require("./wxApi");
import { getTalerStampSec } from "../helpers";
-import { TipToken, QueryPaymentResult } from "../types";
+import { TipToken } from "../talerTypes";
+import { QueryPaymentResult } from "../walletTypes";
import axios from "axios";
@@ -272,7 +273,12 @@ function talerPay(msg: any): Promise<any> {
const merchantDomain = new URI(document.location.href).origin();
let walletResp;
try {
- walletResp = await wxApi.getTipPlanchets(merchantDomain, tipToken.tip_id, tipToken.amount, deadlineSec, tipToken.exchange_url, tipToken.next_url);
+ walletResp = await wxApi.getTipPlanchets(merchantDomain,
+ tipToken.tip_id,
+ tipToken.amount,
+ deadlineSec,
+ tipToken.exchange_url,
+ tipToken.next_url);
} catch (e) {
wxApi.logAndDisplayError({
message: e.message,
@@ -283,12 +289,12 @@ function talerPay(msg: any): Promise<any> {
throw e;
}
- let planchets = walletResp;
+ const planchets = walletResp;
if (!planchets) {
wxApi.logAndDisplayError({
- message: "processing tip failed",
detail: walletResp,
+ message: "processing tip failed",
name: "tipping-failed",
sameTab: true,
});
diff --git a/src/webex/pages/add-auditor.tsx b/src/webex/pages/add-auditor.tsx
index 4b898b13c..1ab6fdf9c 100644
--- a/src/webex/pages/add-auditor.tsx
+++ b/src/webex/pages/add-auditor.tsx
@@ -23,7 +23,7 @@
import {
CurrencyRecord,
-} from "../../types";
+} from "../../dbTypes";
import { ImplicitStateComponent, StateHolder } from "../components";
import {
diff --git a/src/webex/pages/auditors.tsx b/src/webex/pages/auditors.tsx
index 9d57218ad..276a7e8e1 100644
--- a/src/webex/pages/auditors.tsx
+++ b/src/webex/pages/auditors.tsx
@@ -25,7 +25,7 @@ import {
AuditorRecord,
CurrencyRecord,
ExchangeForCurrencyRecord,
-} from "../../types";
+} from "../../dbTypes";
import {
getCurrencies,
diff --git a/src/webex/pages/confirm-contract.tsx b/src/webex/pages/confirm-contract.tsx
index e41b0a1df..83de738b9 100644
--- a/src/webex/pages/confirm-contract.tsx
+++ b/src/webex/pages/confirm-contract.tsx
@@ -24,12 +24,15 @@
* Imports.
*/
import * as i18n from "../../i18n";
+
import {
- CheckPayResult,
- ContractTerms,
ExchangeRecord,
ProposalRecord,
-} from "../../types";
+} from "../../dbTypes";
+import { ContractTerms } from "../../talerTypes";
+import {
+ CheckPayResult,
+} from "../../walletTypes";
import { renderAmount } from "../renderHtml";
import * as wxApi from "../wxApi";
diff --git a/src/webex/pages/confirm-create-reserve.tsx b/src/webex/pages/confirm-create-reserve.tsx
index 48bcd97c9..903975c6e 100644
--- a/src/webex/pages/confirm-create-reserve.tsx
+++ b/src/webex/pages/confirm-create-reserve.tsx
@@ -24,13 +24,17 @@
import { canonicalizeBaseUrl } from "../../helpers";
import * as i18n from "../../i18n";
+
+import { AmountJson } from "../../amounts";
+import * as Amounts from "../../amounts";
+
import {
- AmountJson,
- Amounts,
- CreateReserveResponse,
CurrencyRecord,
+} from "../../dbTypes";
+import {
+ CreateReserveResponse,
ReserveCreationInfo,
-} from "../../types";
+} from "../../walletTypes";
import { ImplicitStateComponent, StateHolder } from "../components";
import {
@@ -40,7 +44,10 @@ import {
getReserveCreationInfo,
} from "../wxApi";
-import { renderAmount, WithdrawDetailView } from "../renderHtml";
+import {
+ WithdrawDetailView,
+ renderAmount,
+} from "../renderHtml";
import * as React from "react";
import * as ReactDOM from "react-dom";
@@ -78,8 +85,6 @@ class EventTrigger {
}
-
-
interface ExchangeSelectionProps {
suggestedExchangeUrl: string;
amount: AmountJson;
@@ -273,7 +278,8 @@ class ExchangeSelection extends ImplicitStateComponent<ExchangeSelectionProps> {
if (rci.versionMatch.currentCmp === -1) {
return (
<p className="errorbox">
- Your wallet (protocol version <span>{rci.walletVersion}</span>) might be outdated. The exchange has a higher, incompatible
+ Your wallet (protocol version <span>{rci.walletVersion}</span>) might be outdated.<span> </span>
+ The exchange has a higher, incompatible
protocol version (<span>{rci.exchangeVersion}</span>).
</p>
);
@@ -281,7 +287,8 @@ class ExchangeSelection extends ImplicitStateComponent<ExchangeSelectionProps> {
if (rci.versionMatch.currentCmp === 1) {
return (
<p className="errorbox">
- The chosen exchange (protocol version <span>{rci.exchangeVersion}</span> might be outdated. The exchange has a lower, incompatible
+ The chosen exchange (protocol version <span>{rci.exchangeVersion}</span> might be outdated.<span> </span>
+ The exchange has a lower, incompatible
protocol version than your wallet (protocol version <span>{rci.walletVersion}</span>).
</p>
);
@@ -429,8 +436,8 @@ class ExchangeSelection extends ImplicitStateComponent<ExchangeSelectionProps> {
amount_fraction: amount.fraction,
amount_value: amount.value,
exchange: resp.exchange,
- reserve_pub: resp.reservePub,
exchange_wire_details: JSON.stringify(filteredWireDetails),
+ reserve_pub: resp.reservePub,
};
const url = new URI(callback_url).addQuery(q);
if (!url.is("absolute")) {
diff --git a/src/webex/pages/payback.tsx b/src/webex/pages/payback.tsx
index a380a33d0..f69a33493 100644
--- a/src/webex/pages/payback.tsx
+++ b/src/webex/pages/payback.tsx
@@ -26,7 +26,7 @@
*/
import {
ReserveRecord,
-} from "../../types";
+} from "../../dbTypes";
import { ImplicitStateComponent, StateHolder } from "../components";
import { renderAmount } from "../renderHtml";
diff --git a/src/webex/pages/popup.tsx b/src/webex/pages/popup.tsx
index ded430d2b..134ee6dea 100644
--- a/src/webex/pages/popup.tsx
+++ b/src/webex/pages/popup.tsx
@@ -26,13 +26,15 @@
* Imports.
*/
import * as i18n from "../../i18n";
+
+import { AmountJson } from "../../amounts";
+import * as Amounts from "../../amounts";
+
import {
- AmountJson,
- Amounts,
HistoryRecord,
WalletBalance,
WalletBalanceEntry,
-} from "../../types";
+} from "../../walletTypes";
import { abbrev, renderAmount } from "../renderHtml";
import * as wxApi from "../wxApi";
@@ -407,7 +409,8 @@ function formatHistoryItem(historyItem: HistoryRecord) {
const url = tipPageUrl.query(params).href();
return (
<i18n.Translate wrap="p">
- Merchant <span>{d.merchantDomain}</span> gave a <a href={url} onClick={openTab(url)}> tip</a> of <span>{renderAmount(d.amount)}</span>.
+ Merchant <span>{d.merchantDomain}</span> gave
+ a <a href={url} onClick={openTab(url)}> tip</a> of <span>{renderAmount(d.amount)}</span>.
<span> </span>
{ d.accepted ? null : <span>You did not accept the tip yet.</span> }
</i18n.Translate>
diff --git a/src/webex/pages/refund.tsx b/src/webex/pages/refund.tsx
index e76fdfff3..3e82f3667 100644
--- a/src/webex/pages/refund.tsx
+++ b/src/webex/pages/refund.tsx
@@ -26,7 +26,10 @@ import * as React from "react";
import * as ReactDOM from "react-dom";
import URI = require("urijs");
-import * as types from "../../types";
+import * as dbTypes from "../../dbTypes";
+
+import { AmountJson } from "../../amounts";
+import * as Amounts from "../../amounts";
import { AmountDisplay } from "../renderHtml";
import * as wxApi from "../wxApi";
@@ -36,14 +39,14 @@ interface RefundStatusViewProps {
}
interface RefundStatusViewState {
- purchase?: types.PurchaseRecord;
- refundFees?: types.AmountJson;
+ purchase?: dbTypes.PurchaseRecord;
+ refundFees?: AmountJson;
gotResult: boolean;
}
interface RefundDetailProps {
- purchase: types.PurchaseRecord;
- fullRefundFees: types.AmountJson;
+ purchase: dbTypes.PurchaseRecord;
+ fullRefundFees: AmountJson;
}
const RefundDetail = ({purchase, fullRefundFees}: RefundDetailProps) => {
@@ -59,13 +62,13 @@ const RefundDetail = ({purchase, fullRefundFees}: RefundDetailProps) => {
throw Error("invariant");
}
- let amountPending = types.Amounts.getZero(currency);
+ let amountPending = Amounts.getZero(currency);
for (const k of pendingKeys) {
- amountPending = types.Amounts.add(amountPending, purchase.refundsPending[k].refund_amount).amount;
+ amountPending = Amounts.add(amountPending, purchase.refundsPending[k].refund_amount).amount;
}
- let amountDone = types.Amounts.getZero(currency);
+ let amountDone = Amounts.getZero(currency);
for (const k of doneKeys) {
- amountDone = types.Amounts.add(amountDone, purchase.refundsDone[k].refund_amount).amount;
+ amountDone = Amounts.add(amountDone, purchase.refundsDone[k].refund_amount).amount;
}
const hasPending = amountPending.fraction !== 0 || amountPending.value !== 0;
diff --git a/src/webex/pages/return-coins.tsx b/src/webex/pages/return-coins.tsx
index 5bcb2252a..26db52ef4 100644
--- a/src/webex/pages/return-coins.tsx
+++ b/src/webex/pages/return-coins.tsx
@@ -25,12 +25,13 @@
* Imports.
*/
+import { AmountJson } from "../../amounts";
+import * as Amounts from "../../amounts";
+
import {
- AmountJson,
- Amounts,
SenderWireInfos,
WalletBalance,
-} from "../../types";
+} from "../../walletTypes";
import * as i18n from "../../i18n";
diff --git a/src/webex/pages/tip.tsx b/src/webex/pages/tip.tsx
index 678c0dfdd..7f96401c5 100644
--- a/src/webex/pages/tip.tsx
+++ b/src/webex/pages/tip.tsx
@@ -33,9 +33,13 @@ import {
getTipStatus,
} from "../wxApi";
-import { renderAmount, WithdrawDetailView } from "../renderHtml";
+import {
+ WithdrawDetailView,
+ renderAmount,
+} from "../renderHtml";
-import { Amounts, TipStatus } from "../../types";
+import * as Amounts from "../../amounts";
+import { TipStatus } from "../../walletTypes";
interface TipDisplayProps {
merchantDomain: string;
@@ -54,7 +58,7 @@ class TipDisplay extends React.Component<TipDisplayProps, TipDisplayState> {
}
async update() {
- let tipStatus = await getTipStatus(this.props.merchantDomain, this.props.tipId);
+ const tipStatus = await getTipStatus(this.props.merchantDomain, this.props.tipId);
this.setState({ tipStatus });
}
@@ -73,7 +77,7 @@ class TipDisplay extends React.Component<TipDisplayProps, TipDisplayState> {
renderExchangeInfo(ts: TipStatus) {
const rci = ts.rci;
if (!rci) {
- return <p>Waiting for info about exchange ...</p>
+ return <p>Waiting for info about exchange ...</p>;
}
const totalCost = Amounts.add(rci.overhead, rci.withdrawFee).amount;
return (
@@ -102,7 +106,9 @@ class TipDisplay extends React.Component<TipDisplayProps, TipDisplayState> {
className="pure-button pure-button-primary"
type="button"
onClick={() => this.accept()}>
- { this.state.working ? <span><object className="svg-icon svg-baseline" data="/img/spinner-bars.svg" /> </span> : null }
+ { this.state.working
+ ? <span><object className="svg-icon svg-baseline" data="/img/spinner-bars.svg" /> </span>
+ : null }
Accept tip
</button>
{" "}
@@ -119,7 +125,8 @@ class TipDisplay extends React.Component<TipDisplayProps, TipDisplayState> {
return (
<div>
<h2>Tip Received!</h2>
- <p>You received a tip of <strong>{renderAmount(ts.tip.amount)}</strong> from <strong>{this.props.merchantDomain}</strong>.</p>
+ <p>You received a tip of <strong>{renderAmount(ts.tip.amount)}</strong> from <span> </span>
+ <strong>{this.props.merchantDomain}</strong>.</p>
{ts.tip.accepted
? <p>You've accepted this tip! <a href={ts.tip.nextUrl}>Go back to merchant</a></p>
: this.renderButtons()
@@ -134,10 +141,10 @@ async function main() {
try {
const url = new URI(document.location.href);
const query: any = URI.parseQuery(url.query());
-
- let merchantDomain = query.merchant_domain;
- let tipId = query.tip_id;
- let props: TipDisplayProps = { tipId, merchantDomain };
+
+ const merchantDomain = query.merchant_domain;
+ const tipId = query.tip_id;
+ const props: TipDisplayProps = { tipId, merchantDomain };
ReactDOM.render(<TipDisplay {...props} />,
document.getElementById("container")!);
diff --git a/src/webex/pages/tree.tsx b/src/webex/pages/tree.tsx
index 2ac0b8631..67e58a1df 100644
--- a/src/webex/pages/tree.tsx
+++ b/src/webex/pages/tree.tsx
@@ -22,6 +22,7 @@
import { getTalerStampDate } from "../../helpers";
+
import {
CoinRecord,
CoinStatus,
@@ -29,7 +30,7 @@ import {
ExchangeRecord,
PreCoinRecord,
ReserveRecord,
-} from "../../types";
+} from "../../dbTypes";
import { ImplicitStateComponent, StateHolder } from "../components";
import {
diff --git a/src/webex/renderHtml.tsx b/src/webex/renderHtml.tsx
index d225cef0c..2e21932b0 100644
--- a/src/webex/renderHtml.tsx
+++ b/src/webex/renderHtml.tsx
@@ -24,12 +24,16 @@
/**
* Imports.
*/
+import { AmountJson } from "../amounts";
+import * as Amounts from "../amounts";
+
import {
- AmountJson,
- Amounts,
DenominationRecord,
+} from "../dbTypes";
+import {
ReserveCreationInfo,
-} from "../types";
+} from "../walletTypes";
+
import { ImplicitStateComponent } from "./components";
@@ -239,7 +243,9 @@ function FeeDetailsView(props: {rci: ReserveCreationInfo|null}): JSX.Element {
);
}
-
+/**
+ * Shows details about a withdraw request.
+ */
export function WithdrawDetailView(props: {rci: ReserveCreationInfo | null}): JSX.Element {
const rci = props.rci;
return (
@@ -259,6 +265,9 @@ interface ExpanderTextProps {
text: string;
}
+/**
+ * Show a heading with a toggle to show/hide the expandable content.
+ */
export class ExpanderText extends ImplicitStateComponent<ExpanderTextProps> {
private expanded = this.makeState<boolean>(false);
private textArea: any = undefined;
diff --git a/src/webex/wxApi.ts b/src/webex/wxApi.ts
index 2575eec90..2f7a13c48 100644
--- a/src/webex/wxApi.ts
+++ b/src/webex/wxApi.ts
@@ -22,26 +22,31 @@
/**
* Imports.
*/
+import { AmountJson } from "../amounts";
import {
- AmountJson,
- CheckPayResult,
CoinRecord,
- ConfirmPayResult,
CurrencyRecord,
DenominationRecord,
ExchangeRecord,
PreCoinRecord,
PurchaseRecord,
+ ReserveRecord,
+} from "../dbTypes";
+import {
+ CheckPayResult,
+ ConfirmPayResult,
QueryPaymentResult,
- RefundPermission,
ReserveCreationInfo,
- ReserveRecord,
SenderWireInfos,
- TipResponse,
- TipPlanchetDetail,
TipStatus,
WalletBalance,
-} from "../types";
+} from "../walletTypes";
+
+import {
+ RefundPermission,
+ TipPlanchetDetail,
+ TipResponse,
+} from "../talerTypes";
import { MessageMap, MessageType } from "./messages";
@@ -366,22 +371,39 @@ export function getFullRefundFees(args: { refundPermissions: RefundPermission[]
/**
* Get or generate planchets to give the merchant that wants to tip us.
*/
-export function getTipPlanchets(merchantDomain: string, tipId: string, amount: AmountJson, deadline: number, exchangeUrl: string, nextUrl: string): Promise<TipPlanchetDetail[]> {
+export function getTipPlanchets(merchantDomain: string,
+ tipId: string,
+ amount: AmountJson,
+ deadline: number,
+ exchangeUrl: string,
+ nextUrl: string): Promise<TipPlanchetDetail[]> {
return callBackend("get-tip-planchets", { merchantDomain, tipId, amount, deadline, exchangeUrl, nextUrl });
}
+/**
+ * Get the status of processing a tip.
+ */
export function getTipStatus(merchantDomain: string, tipId: string): Promise<TipStatus> {
return callBackend("get-tip-status", { merchantDomain, tipId });
}
+/**
+ * Mark a tip as accepted by the user.
+ */
export function acceptTip(merchantDomain: string, tipId: string): Promise<TipStatus> {
return callBackend("accept-tip", { merchantDomain, tipId });
}
+/**
+ * Process a response from the merchant for a tip request.
+ */
export function processTipResponse(merchantDomain: string, tipId: string, tipResponse: TipResponse): Promise<void> {
return callBackend("process-tip-response", { merchantDomain, tipId, tipResponse });
}
+/**
+ * Clear notifications that the wallet shows to the user.
+ */
export function clearNotification(): Promise<void> {
return callBackend("clear-notification", { });
}
diff --git a/src/webex/wxBackend.ts b/src/webex/wxBackend.ts
index 213d234d4..a8ce5eebc 100644
--- a/src/webex/wxBackend.ts
+++ b/src/webex/wxBackend.ts
@@ -30,18 +30,21 @@ import {
Index,
Store,
} from "../query";
+
+import { AmountJson } from "../amounts";
+
+import { ProposalRecord } from "../dbTypes";
import {
AcceptTipRequest,
- AmountJson,
ConfirmReserveRequest,
CreateReserveRequest,
GetTipPlanchetsRequest,
Notifier,
ProcessTipResponseRequest,
- ProposalRecord,
ReturnCoinsRequest,
TipStatusRequest,
-} from "../types";
+} from "../walletTypes";
+
import {
Stores,
WALLET_DB_VERSION,
@@ -335,7 +338,12 @@ function handleMessage(sender: MessageSender,
}
case "get-tip-planchets": {
const req = GetTipPlanchetsRequest.checked(detail);
- return needsWallet().getTipPlanchets(req.merchantDomain, req.tipId, req.amount, req.deadline, req.exchangeUrl, req.nextUrl);
+ return needsWallet().getTipPlanchets(req.merchantDomain,
+ req.tipId,
+ req.amount,
+ req.deadline,
+ req.exchangeUrl,
+ req.nextUrl);
}
case "clear-notification": {
return needsWallet().clearNotification();
@@ -702,11 +710,10 @@ export async function wxMain() {
});
-
// Clear notifications both when the popop opens,
// as well when it closes.
chrome.runtime.onConnect.addListener((port) => {
- if (port.name == "popup") {
+ if (port.name === "popup") {
if (currentWallet) {
currentWallet.clearNotification();
}
diff --git a/tooling/pogen/dumpTree.ts b/tooling/pogen/dumpTree.ts
index 958c79416..af25caf32 100644
--- a/tooling/pogen/dumpTree.ts
+++ b/tooling/pogen/dumpTree.ts
@@ -21,12 +21,10 @@
* @author Florian Dold
*/
-/// <reference path="../decl/node.d.ts" />
-
"use strict";
-import {readFileSync} from "fs";
-import {execSync} from "child_process";
+import { readFileSync } from "fs";
+import { execSync } from "child_process";
import * as ts from "typescript";
diff --git a/tsconfig.json b/tsconfig.json
index d2a7f5526..ae77fb27c 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,4 +1,5 @@
{
+ "compileOnSave": true,
"compilerOptions": {
"target": "es6",
"jsx": "react",
@@ -7,8 +8,8 @@
"module": "commonjs",
"sourceMap": true,
"lib": [
- "ES6",
- "DOM"
+ "es6",
+ "dom"
],
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
@@ -23,6 +24,7 @@
"decl/chrome/chrome.d.ts",
"decl/jed.d.ts",
"decl/urijs.d.ts",
+ "src/amounts.ts",
"src/checkable.ts",
"src/crypto/cryptoApi-test.ts",
"src/crypto/cryptoApi.ts",
@@ -34,6 +36,7 @@
"src/crypto/nodeWorker.ts",
"src/crypto/nodeWorkerEntry.ts",
"src/crypto/startWorker.js",
+ "src/dbTypes.ts",
"src/helpers-test.ts",
"src/helpers.ts",
"src/http.ts",
@@ -42,18 +45,13 @@
"src/libtoolVersion-test.ts",
"src/libtoolVersion.ts",
"src/logging.ts",
- "src/memidb/aatree-test.ts",
- "src/memidb/aatree.ts",
- "src/memidb/memidb-test.ts",
- "src/memidb/memidb.ts",
- "src/memidb/w3c-wpt/abort-in-initial-upgradeneeded-test.ts",
- "src/memidb/w3c-wpt/support.ts",
"src/query.ts",
+ "src/talerTypes.ts",
"src/timer.ts",
"src/types-test.ts",
- "src/types.ts",
"src/wallet-test.ts",
"src/wallet.ts",
+ "src/walletTypes.ts",
"src/webex/background.ts",
"src/webex/chromeBadge.ts",
"src/webex/components.ts",
diff --git a/tslint.json b/tslint.json
index fbd1975b7..a56b56a94 100644
--- a/tslint.json
+++ b/tslint.json
@@ -5,6 +5,7 @@
],
"jsRules": {},
"rules": {
+ "arrow-parens": false,
"max-line-length": {
"options": [120]
},