summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2017-05-28 16:27:34 +0200
committerFlorian Dold <florian.dold@gmail.com>2017-05-28 21:55:16 +0200
commite7fa87bcc0052e1e99c6894e7e27a122374956b3 (patch)
tree56c243d08ae357533ebdb4fbf41211aa0fc914ce /src
parent08bd3dc0e8a3c2370e4e8abbaa241eaafc144f4c (diff)
downloadwallet-core-e7fa87bcc0052e1e99c6894e7e27a122374956b3.tar.gz
wallet-core-e7fa87bcc0052e1e99c6894e7e27a122374956b3.tar.bz2
wallet-core-e7fa87bcc0052e1e99c6894e7e27a122374956b3.zip
documentation and tslint settings to check for docs
Diffstat (limited to 'src')
-rw-r--r--src/chromeBadge.ts24
-rw-r--r--src/components.ts2
-rw-r--r--src/crypto/cryptoWorker.ts25
-rw-r--r--src/crypto/emscInterface.ts4
-rw-r--r--src/crypto/nodeWorker.ts23
-rw-r--r--src/helpers.ts8
-rw-r--r--src/logging.ts71
-rw-r--r--src/query.ts95
-rw-r--r--src/types.ts523
-rw-r--r--src/wallet-test.ts12
-rw-r--r--src/wallet.ts183
-rw-r--r--src/wxApi.ts70
-rw-r--r--src/wxBackend.ts8
13 files changed, 915 insertions, 133 deletions
diff --git a/src/chromeBadge.ts b/src/chromeBadge.ts
index 13716a64a..702cefea8 100644
--- a/src/chromeBadge.ts
+++ b/src/chromeBadge.ts
@@ -30,33 +30,37 @@ function rAF(cb: (ts: number) => void) {
}
+/**
+ * Badge for Chrome that renders a Taler logo with a rotating ring if some
+ * background activity is happening.
+ */
export class ChromeBadge implements Badge {
- canvas: HTMLCanvasElement;
- ctx: CanvasRenderingContext2D;
+ private canvas: HTMLCanvasElement;
+ private ctx: CanvasRenderingContext2D;
/**
* True if animation running. The animation
* might still be running even if we're not busy anymore,
* just to transition to the "normal" state in a animated way.
*/
- animationRunning: boolean = false;
+ private animationRunning: boolean = false;
/**
* Is the wallet still busy? Note that we do not stop the
* animation immediately when the wallet goes idle, but
* instead slowly close the gap.
*/
- isBusy: boolean = false;
+ private isBusy: boolean = false;
/**
* Current rotation angle, ranges from 0 to rotationAngleMax.
*/
- rotationAngle: number = 0;
+ private rotationAngle: number = 0;
/**
* While animating, how wide is the current gap in the circle?
* Ranges from 0 to openMax.
*/
- gapWidth: number = 0;
+ private gapWidth: number = 0;
/**
* Maximum value for our rotationAngle, corresponds to 2 Pi.
@@ -207,14 +211,6 @@ export class ChromeBadge implements Badge {
rAF(step);
}
- setText(s: string) {
- chrome.browserAction.setBadgeText({text: s});
- }
-
- setColor(c: string) {
- chrome.browserAction.setBadgeBackgroundColor({color: c});
- }
-
startBusy() {
if (this.isBusy) {
return;
diff --git a/src/components.ts b/src/components.ts
index 9d1127e99..633438766 100644
--- a/src/components.ts
+++ b/src/components.ts
@@ -37,7 +37,7 @@ export interface StateHolder<T> {
* but has multiple state holders.
*/
export abstract class ImplicitStateComponent<PropType> extends React.Component<PropType, any> {
- _implicit = {needsUpdate: false, didMount: false};
+ private _implicit = {needsUpdate: false, didMount: false};
componentDidMount() {
this._implicit.didMount = true;
if (this._implicit.needsUpdate) {
diff --git a/src/crypto/cryptoWorker.ts b/src/crypto/cryptoWorker.ts
index 9541b7442..1a337446d 100644
--- a/src/crypto/cryptoWorker.ts
+++ b/src/crypto/cryptoWorker.ts
@@ -108,6 +108,10 @@ namespace RpcFunctions {
return preCoin;
}
+
+ /**
+ * Create and sign a message to request payback for a coin.
+ */
export function createPaybackRequest(coin: CoinRecord): PaybackRequest {
const p = new native.PaybackRequestPS({
coin_blind: native.RsaBlindingKeySecret.fromCrock(coin.blindingKey),
@@ -127,6 +131,9 @@ namespace RpcFunctions {
}
+ /**
+ * Check if a payment signature is valid.
+ */
export function isValidPaymentSignature(sig: string, contractHash: string, merchantPub: string): boolean {
const p = new native.PaymentSignaturePS({
contract_hash: native.HashCode.fromCrock(contractHash),
@@ -140,6 +147,9 @@ namespace RpcFunctions {
nativePub);
}
+ /**
+ * Check if a wire fee is correctly signed.
+ */
export function isValidWireFee(type: string, wf: WireFee, masterPub: string): boolean {
const p = new native.MasterWireFeePS({
closing_fee: (new native.Amount(wf.closingFee)).toNbo(),
@@ -160,6 +170,9 @@ namespace RpcFunctions {
}
+ /**
+ * Check if the signature of a denomination is valid.
+ */
export function isValidDenom(denom: DenominationRecord,
masterPub: string): boolean {
const p = new native.DenominationKeyValidityPS({
@@ -189,6 +202,9 @@ namespace RpcFunctions {
}
+ /**
+ * Create a new EdDSA key pair.
+ */
export function createEddsaKeypair(): {priv: string, pub: string} {
const priv = native.EddsaPrivateKey.create();
const pub = priv.getPublicKey();
@@ -196,6 +212,9 @@ namespace RpcFunctions {
}
+ /**
+ * Unblind a blindly signed value.
+ */
export function rsaUnblind(sig: string, bk: string, pk: string): string {
const denomSig = native.rsaUnblind(native.RsaSignature.fromCrock(sig),
native.RsaBlindingKeySecret.fromCrock(bk),
@@ -278,6 +297,9 @@ namespace RpcFunctions {
}
+ /**
+ * Create a new refresh session.
+ */
export function createRefreshSession(exchangeBaseUrl: string,
kappa: number,
meltCoin: CoinRecord,
@@ -398,6 +420,9 @@ namespace RpcFunctions {
return b.hash().toCrock();
}
+ /**
+ * Hash a denomination public key.
+ */
export function hashDenomPub(denomPub: string): string {
return native.RsaPublicKey.fromCrock(denomPub).encode().hash().toCrock();
}
diff --git a/src/crypto/emscInterface.ts b/src/crypto/emscInterface.ts
index e00e67a84..f3aeb8272 100644
--- a/src/crypto/emscInterface.ts
+++ b/src/crypto/emscInterface.ts
@@ -259,7 +259,7 @@ interface Arena {
* Arena that must be manually destroyed.
*/
class SimpleArena implements Arena {
- heap: ArenaObject[];
+ protected heap: ArenaObject[];
constructor() {
this.heap = [];
@@ -774,7 +774,7 @@ export class EccSignaturePurpose extends PackedArenaObject {
return this.payloadSize + 8;
}
- payloadSize: number;
+ private payloadSize: number;
constructor(purpose: SignaturePurpose,
payload: PackedArenaObject,
diff --git a/src/crypto/nodeWorker.ts b/src/crypto/nodeWorker.ts
index 4352b66c2..fa942387a 100644
--- a/src/crypto/nodeWorker.ts
+++ b/src/crypto/nodeWorker.ts
@@ -22,10 +22,22 @@ const fork = require("child_process").fork;
const nodeWorkerEntry = path.join(__dirname, "nodeWorkerEntry.js");
+/**
+ * Worker implementation that uses node subprocesses.
+ */
export class Worker {
- child: any;
+ private child: any;
+
+ /**
+ * Function to be called when we receive a message from the worker thread.
+ */
onmessage: undefined | ((m: any) => void);
+
+ /**
+ * Function to be called when we receive an error from the worker thread.
+ */
onerror: undefined | ((m: any) => void);
+
constructor(scriptFilename: string) {
this.child = fork(nodeWorkerEntry);
this.onerror = undefined;
@@ -55,6 +67,9 @@ export class Worker {
this.child.send({scriptFilename, cwd: process.cwd()});
}
+ /**
+ * Add an event listener for either an "error" or "message" event.
+ */
addEventListener(event: "message" | "error", fn: (x: any) => void): void {
switch (event) {
case "message":
@@ -66,10 +81,16 @@ export class Worker {
}
}
+ /**
+ * Send a message to the worker thread.
+ */
postMessage (msg: any) {
this.child.send(JSON.stringify({data: msg}));
}
+ /**
+ * Forcibly terminate the worker thread.
+ */
terminate () {
this.child.kill("SIGINT");
}
diff --git a/src/helpers.ts b/src/helpers.ts
index e5eb40211..eff5fa731 100644
--- a/src/helpers.ts
+++ b/src/helpers.ts
@@ -83,6 +83,10 @@ export function canonicalJson(obj: any): string {
}
+/**
+ * Check for deep equality of two objects.
+ * Only arrays, objects and primitives are supported.
+ */
export function deepEquals(x: any, y: any): boolean {
if (x === y) {
return true;
@@ -98,6 +102,10 @@ export function deepEquals(x: any, y: any): boolean {
}
+/**
+ * Map from a collection to a list or results and then
+ * concatenate the results.
+ */
export function flatMap<T, U>(xs: T[], f: (x: T) => U[]): U[] {
return xs.reduce((acc: U[], next: T) => [...f(next), ...acc], []);
}
diff --git a/src/logging.ts b/src/logging.ts
index 19dd2f76c..a589c8091 100644
--- a/src/logging.ts
+++ b/src/logging.ts
@@ -15,9 +15,7 @@
*/
/**
- * Configurable logging.
- *
- * @author Florian Dold
+ * Configurable logging. Allows to log persistently to a database.
*/
import {
@@ -26,8 +24,14 @@ import {
openPromise,
} from "./query";
+/**
+ * Supported log levels.
+ */
export type Level = "error" | "debug" | "info" | "warn";
+// Right now, our debug/info/warn/debug loggers just use the console based
+// loggers. This might change in the future.
+
function makeInfo() {
return console.info.bind(console, "%o");
}
@@ -44,6 +48,9 @@ function makeDebug() {
return console.log.bind(console, "%o");
}
+/**
+ * Log a message using the configurable logger.
+ */
export async function log(msg: string, level: Level = "info"): Promise<void> {
const ci = getCallInfo(2);
return record(level, msg, undefined, ci.file, ci.line, ci.column);
@@ -122,17 +129,50 @@ function parseStackLine(stackLine: string): Frame {
let db: IDBDatabase|undefined;
+/**
+ * A structured log entry as stored in the database.
+ */
export interface LogEntry {
+ /**
+ * Soure code column where the error occured.
+ */
col?: number;
+ /**
+ * Additional detail for the log statement.
+ */
detail?: string;
+ /**
+ * Id of the log entry, used as primary
+ * key for the database.
+ */
id?: number;
+ /**
+ * Log level, see [[Level}}.
+ */
level: string;
+ /**
+ * Line where the log was created from.
+ */
line?: number;
+ /**
+ * The actual log message.
+ */
msg: string;
+ /**
+ * The source file where the log enctry
+ * was created from.
+ */
source?: string;
+ /**
+ * Time when the log entry was created.
+ */
timestamp: number;
}
+/**
+ * Get all logs. Only use for debugging, since this returns all logs ever made
+ * at once without pagination.
+ */
export async function getLogs(): Promise<LogEntry[]> {
if (!db) {
db = await openLoggingDb();
@@ -147,6 +187,9 @@ export async function getLogs(): Promise<LogEntry[]> {
*/
let barrier: any;
+/**
+ * Record an exeption in the log.
+ */
export async function recordException(msg: string, e: any): Promise<void> {
let stack: string|undefined;
let frame: Frame|undefined;
@@ -165,6 +208,9 @@ export async function recordException(msg: string, e: any): Promise<void> {
return record("error", e.toString(), stack, frame.file, frame.line, frame.column);
}
+/**
+ * Record a log entry in the database.
+ */
export async function record(level: Level,
msg: string,
detail?: string,
@@ -215,6 +261,10 @@ const loggingDbVersion = 1;
const logsStore: Store<LogEntry> = new Store<LogEntry>("logs");
+/**
+ * Get a handle to the IndexedDB used to store
+ * logs.
+ */
export function openLoggingDb(): Promise<IDBDatabase> {
return new Promise<IDBDatabase>((resolve, reject) => {
const req = indexedDB.open("taler-logging", loggingDbVersion);
@@ -238,7 +288,22 @@ export function openLoggingDb(): Promise<IDBDatabase> {
});
}
+/**
+ * Log a message at severity info.
+ */
export const info = makeInfo();
+
+/**
+ * Log a message at severity debug.
+ */
export const debug = makeDebug();
+
+/**
+ * Log a message at severity warn.
+ */
export const warn = makeWarn();
+
+/**
+ * Log a message at severity error.
+ */
export const error = makeError();
diff --git a/src/query.ts b/src/query.ts
index 78b810371..cb033df4c 100644
--- a/src/query.ts
+++ b/src/query.ts
@@ -53,6 +53,9 @@ export class Store<T> {
* Definition of an index.
*/
export class Index<S extends IDBValidKey, T> {
+ /**
+ * Name of the store that this index is associated with.
+ */
storeName: string;
constructor(s: Store<T>, public indexName: string, public keyPath: string | string[]) {
@@ -127,16 +130,26 @@ export interface QueryStream<T> {
* Query result that consists of at most one value.
*/
export interface QueryValue<T> {
+ /**
+ * Apply a function to a query value.
+ */
map<S>(f: (x: T) => S): QueryValue<S>;
+ /**
+ * Conditionally execute either of two queries based
+ * on a property of this query value.
+ *
+ * Useful to properly implement complex queries within a transaction (as
+ * opposed to just computing the conditional and then executing either
+ * branch). This is necessary since IndexedDB does not allow long-lived
+ * transactions.
+ */
cond<R>(f: (x: T) => boolean, onTrue: (r: QueryRoot) => R, onFalse: (r: QueryRoot) => R): Promise<void>;
}
abstract class BaseQueryValue<T> implements QueryValue<T> {
- root: QueryRoot;
- constructor(root: QueryRoot) {
- this.root = root;
+ constructor(public root: QueryRoot) {
}
map<S>(f: (x: T) => S): QueryValue<S> {
@@ -160,8 +173,9 @@ abstract class BaseQueryValue<T> implements QueryValue<T> {
}
class FirstQueryValue<T> extends BaseQueryValue<T> {
- gotValue = false;
- s: QueryStreamBase<T>;
+ private gotValue = false;
+ private s: QueryStreamBase<T>;
+
constructor(stream: QueryStreamBase<T>) {
super(stream.root);
this.s = stream;
@@ -183,13 +197,8 @@ class FirstQueryValue<T> extends BaseQueryValue<T> {
}
class MapQueryValue<T, S> extends BaseQueryValue<S> {
- mapFn: (x: T) => S;
- v: BaseQueryValue<T>;
-
- constructor(v: BaseQueryValue<T>, mapFn: (x: T) => S) {
+ constructor(private v: BaseQueryValue<T>, private mapFn: (x: T) => S) {
super(v.root);
- this.v = v;
- this.mapFn = mapFn;
}
subscribeOne(f: SubscribeOneFn): void {
@@ -226,11 +235,7 @@ abstract class QueryStreamBase<T> implements QueryStream<T>, PromiseLike<void> {
abstract subscribe(f: (isDone: boolean,
value: any,
tx: IDBTransaction) => void): void;
-
- root: QueryRoot;
-
- constructor(root: QueryRoot) {
- this.root = root;
+ constructor(public root: QueryRoot) {
}
first(): QueryValue<T> {
@@ -313,13 +318,8 @@ type SubscribeOneFn = (value: any, tx: IDBTransaction) => void;
type FlatMapFn<T> = (v: T) => T[];
class QueryStreamFilter<T> extends QueryStreamBase<T> {
- s: QueryStreamBase<T>;
- filterFn: FilterFn;
-
- constructor(s: QueryStreamBase<T>, filterFn: FilterFn) {
+ constructor(public s: QueryStreamBase<T>, public filterFn: FilterFn) {
super(s.root);
- this.s = s;
- this.filterFn = filterFn;
}
subscribe(f: SubscribeFn) {
@@ -337,13 +337,8 @@ class QueryStreamFilter<T> extends QueryStreamBase<T> {
class QueryStreamFlatMap<T, S> extends QueryStreamBase<S> {
- s: QueryStreamBase<T>;
- flatMapFn: (v: T) => S[];
-
- constructor(s: QueryStreamBase<T>, flatMapFn: (v: T) => S[]) {
+ constructor(public s: QueryStreamBase<T>, public flatMapFn: (v: T) => S[]) {
super(s.root);
- this.s = s;
- this.flatMapFn = flatMapFn;
}
subscribe(f: SubscribeFn) {
@@ -362,13 +357,8 @@ class QueryStreamFlatMap<T, S> extends QueryStreamBase<S> {
class QueryStreamMap<S, T> extends QueryStreamBase<T> {
- s: QueryStreamBase<S>;
- mapFn: (v: S) => T;
-
- constructor(s: QueryStreamBase<S>, mapFn: (v: S) => T) {
+ constructor(public s: QueryStreamBase<S>, public mapFn: (v: S) => T) {
super(s.root);
- this.s = s;
- this.mapFn = mapFn;
}
subscribe(f: SubscribeFn) {
@@ -385,18 +375,9 @@ class QueryStreamMap<S, T> extends QueryStreamBase<T> {
class QueryStreamIndexJoin<T, S> extends QueryStreamBase<JoinResult<T, S>> {
- s: QueryStreamBase<T>;
- storeName: string;
- key: any;
- indexName: string;
-
- constructor(s: QueryStreamBase<T>, storeName: string, indexName: string,
- key: any) {
+ constructor(public s: QueryStreamBase<T>, public storeName: string, public indexName: string,
+ public key: any) {
super(s.root);
- this.s = s;
- this.storeName = storeName;
- this.key = key;
- this.indexName = indexName;
}
subscribe(f: SubscribeFn) {
@@ -420,18 +401,9 @@ class QueryStreamIndexJoin<T, S> extends QueryStreamBase<JoinResult<T, S>> {
class QueryStreamIndexJoinLeft<T, S> extends QueryStreamBase<JoinLeftResult<T, S>> {
- s: QueryStreamBase<T>;
- storeName: string;
- key: any;
- indexName: string;
-
- constructor(s: QueryStreamBase<T>, storeName: string, indexName: string,
- key: any) {
+ constructor(public s: QueryStreamBase<T>, public storeName: string, public indexName: string,
+ public key: any) {
super(s.root);
- this.s = s;
- this.storeName = storeName;
- this.key = key;
- this.indexName = indexName;
}
subscribe(f: SubscribeFn) {
@@ -461,16 +433,9 @@ class QueryStreamIndexJoinLeft<T, S> extends QueryStreamBase<JoinLeftResult<T, S
class QueryStreamKeyJoin<T, S> extends QueryStreamBase<JoinResult<T, S>> {
- s: QueryStreamBase<T>;
- storeName: string;
- key: any;
-
- constructor(s: QueryStreamBase<T>, storeName: string,
- key: any) {
+ constructor(public s: QueryStreamBase<T>, public storeName: string,
+ public key: any) {
super(s.root);
- this.s = s;
- this.storeName = storeName;
- this.key = key;
}
subscribe(f: SubscribeFn) {
diff --git a/src/types.ts b/src/types.ts
index 91a61bc4b..805a0c061 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -28,32 +28,77 @@
*/
import { Checkable } from "./checkable";
+/**
+ * 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
- value: number;
+ readonly value: number;
+ /**
+ * Fraction, must be an integer. Represent 1/1e8 of a unit.
+ */
@Checkable.Number
- fraction: number;
+ readonly fraction: number;
+ /**
+ * Currency of the amount.
+ */
@Checkable.String
- currency: 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 last queried,
+ * or 'null' if it was never queried.
+ */
last_query: number | null;
/**
* Current amount left in the reserve
@@ -65,17 +110,16 @@ export interface ReserveRecord {
* be higher than the requested_amount
*/
requested_amount: AmountJson;
-
-
/**
* What's the current amount that sits
* in precoins?
*/
precoin_amount: AmountJson;
-
-
+ /**
+ * The bank conformed that the reserve will eventually
+ * be filled with money.
+ */
confirmed: boolean;
-
/**
* We got some payback to this reserve. We'll cease to automatically
* withdraw money from it.
@@ -106,6 +150,9 @@ export interface CurrencyRecord {
}
+/**
+ * Response for the create reserve request to the wallet.
+ */
@Checkable.Class()
export class CreateReserveResponse {
/**
@@ -115,52 +162,114 @@ export class CreateReserveResponse {
@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;
@@ -178,9 +287,16 @@ export class DenominationRecord {
@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;
}
@@ -189,59 +305,124 @@ export class DenominationRecord {
*/
@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;
}
+/**
+ * Auditor information.
+ */
export interface Auditor {
- // official name
+ /**
+ * Official name.
+ */
name: string;
- // Auditor's public key
+ /**
+ * Auditor's public key.
+ */
auditor_pub: string;
- // Base URL of the auditor
+ /**
+ * Base URL of the auditor.
+ */
url: string;
}
+/**
+ * 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;
/**
@@ -282,14 +463,36 @@ export interface PreCoinRecord {
coinValue: AmountJson;
}
+/**
+ * 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;
/**
@@ -297,13 +500,26 @@ export interface PaybackRequest {
*/
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 {
/**
@@ -344,6 +560,10 @@ export class PaybackConfirmation {
@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;
}
@@ -378,15 +598,19 @@ export interface RefreshSessionRecord {
*/
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[];
/**
@@ -399,23 +623,73 @@ export interface RefreshSessionRecord {
*/
hash: string;
+ /**
+ * Base URL for the exchange we're doing the refresh with.
+ */
exchangeBaseUrl: string;
+ /**
+ * Is this session finished?
+ */
finished: boolean;
}
+/**
+ * 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.
+ */
f: AmountJson;
}
+/**
+ * Status of a coin.
+ */
export enum CoinStatus {
- Fresh, TransactionPending, Dirty, Refreshed, PaybackPending, PaybackDone,
+ /**
+ * Withdrawn and never shown to anybody.
+ */
+ Fresh,
+ /**
+ * Currently planned to be sent to a merchant for a transaction.
+ */
+ TransactionPending,
+ /**
+ * 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,
}
@@ -462,6 +736,10 @@ export interface CoinRecord {
*/
suspended?: boolean;
+ /**
+ * Blinding key used when withdrawing the coin.
+ * Potentionally sed again during payback.
+ */
blindingKey: string;
/**
@@ -477,29 +755,70 @@ export interface CoinRecord {
}
+/**
+ * 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 names to detailed balance
+ * information for that particular currency.
+ */
export interface WalletBalance {
+ /**
+ * Mapping from currency name to defailed balance info.
+ */
[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;
}
+/**
+ * Information about a merchant.
+ */
interface Merchant {
/**
* label for a location with the business address of the merchant
@@ -524,108 +843,235 @@ interface Merchant {
instance?: string;
}
+
+/**
+ * Contract terms from a merchant.
+ */
@Checkable.Class({validate: true})
export class Contract {
-
- validate() {
+ private validate() {
if (this.exchanges.length === 0) {
throw Error("no exchanges in contract");
}
}
+ /**
+ * 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) => Contract;
}
+/**
+ * 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;
- feesForType: { [type: string]: WireFee[] };
+
+ /**
+ * 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 type PayCoinInfo = Array<{ updatedCoin: CoinRecord, sig: 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?
+ /**
+ * Was there an over-/underflow?
+ */
saturated: boolean;
}
+ /**
+ * Get the largest amount that is safely representable.
+ */
export function getMaxAmount(currency: string): AmountJson {
return {
currency,
@@ -634,6 +1080,9 @@ export namespace Amounts {
};
}
+ /**
+ * Get an amount that represents zero units of a currency.
+ */
export function getZero(currency: string): AmountJson {
return {
currency,
@@ -642,6 +1091,13 @@ export namespace Amounts {
};
}
+ /**
+ * 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);
@@ -663,7 +1119,13 @@ export namespace Amounts {
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;
@@ -691,6 +1153,10 @@ export namespace Amounts {
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}`);
@@ -715,6 +1181,9 @@ export namespace Amounts {
}
}
+ /**
+ * Create a copy of an amount.
+ */
export function copy(a: AmountJson): AmountJson {
return {
currency: a.currency,
@@ -723,6 +1192,9 @@ export namespace Amounts {
};
}
+ /**
+ * Divide an amount. Throws on division by zero.
+ */
export function divide(a: AmountJson, n: number): AmountJson {
if (n === 0) {
throw Error(`Division by 0`);
@@ -738,7 +1210,10 @@ export namespace Amounts {
};
}
- export function isNonZero(a: AmountJson) {
+ /**
+ * Check if an amount is non-zero.
+ */
+ export function isNonZero(a: AmountJson): boolean {
return a.value > 0 || a.fraction > 0;
}
@@ -759,7 +1234,13 @@ export namespace Amounts {
}
+/**
+ * Listener for notifications from the wallet.
+ */
export interface Notifier {
+ /**
+ * Called when a new notification arrives.
+ */
notify(): void;
}
diff --git a/src/wallet-test.ts b/src/wallet-test.ts
index 51a8497b7..acd776d67 100644
--- a/src/wallet-test.ts
+++ b/src/wallet-test.ts
@@ -69,7 +69,7 @@ test("coin selection 1", (t) => {
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.0"),
];
- const res = wallet.selectCoins(cds, a("EUR:2.0"), a("EUR:0.1"));
+ const res = wallet.selectPayCoins(cds, a("EUR:2.0"), a("EUR:0.1"));
if (!res) {
t.fail();
return;
@@ -86,7 +86,7 @@ test("coin selection 2", (t) => {
// Merchant covers the fee, this one shouldn't be used
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.0"),
];
- const res = wallet.selectCoins(cds, a("EUR:2.0"), a("EUR:0.5"));
+ const res = wallet.selectPayCoins(cds, a("EUR:2.0"), a("EUR:0.5"));
if (!res) {
t.fail();
return;
@@ -103,7 +103,7 @@ test("coin selection 3", (t) => {
// this coin should be selected instead of previous one with fee
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.0"),
];
- const res = wallet.selectCoins(cds, a("EUR:2.0"), a("EUR:0.5"));
+ const res = wallet.selectPayCoins(cds, a("EUR:2.0"), a("EUR:0.5"));
if (!res) {
t.fail();
return;
@@ -119,7 +119,7 @@ test("coin selection 4", (t) => {
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
];
- const res = wallet.selectCoins(cds, a("EUR:2.0"), a("EUR:0.2"));
+ const res = wallet.selectPayCoins(cds, a("EUR:2.0"), a("EUR:0.2"));
if (!res) {
t.fail();
return;
@@ -135,7 +135,7 @@ test("coin selection 5", (t) => {
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
];
- const res = wallet.selectCoins(cds, a("EUR:4.0"), a("EUR:0.2"));
+ const res = wallet.selectPayCoins(cds, a("EUR:4.0"), a("EUR:0.2"));
t.true(!res);
t.pass();
});
@@ -146,7 +146,7 @@ test("coin selection 6", (t) => {
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
];
- const res = wallet.selectCoins(cds, a("EUR:2.0"), a("EUR:0.2"));
+ const res = wallet.selectPayCoins(cds, a("EUR:2.0"), a("EUR:0.2"));
t.true(!res);
t.pass();
});
diff --git a/src/wallet.ts b/src/wallet.ts
index b21cdbd96..743042b97 100644
--- a/src/wallet.ts
+++ b/src/wallet.ts
@@ -81,7 +81,14 @@ import URI = require("urijs");
* 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;
}
@@ -92,6 +99,9 @@ export interface CoinWithDenom {
*/
@Checkable.Class()
export class Payback {
+ /**
+ * The hash of the denomination public key for which the payback is offered.
+ */
@Checkable.String
h_denom_pub: string;
}
@@ -102,67 +112,123 @@ export class Payback {
*/
@Checkable.Class({extra: true})
export class KeysJson {
+ /**
+ * List of offered denominations.
+ */
@Checkable.List(Checkable.Value(Denomination))
denoms: Denomination[];
+ /**
+ * The exchange's master public key.
+ */
@Checkable.String
master_public_key: string;
+ /**
+ * The list of auditors (partially) auditing the exchange.
+ */
@Checkable.Any
auditors: any[];
+ /**
+ * Timestamp when this response was issued.
+ */
@Checkable.String
list_issue_date: string;
+ /**
+ * List of paybacks for compromised denominations.
+ */
@Checkable.List(Checkable.Value(Payback))
payback?: Payback[];
+ /**
+ * Short-lived signing keys used to sign online
+ * responses.
+ */
@Checkable.Any
signkeys: any;
- @Checkable.String
- eddsa_pub: string;
-
- @Checkable.String
- eddsa_sig: string;
-
+ /**
+ * Verify that a value matches the schema of this class and convert it into a
+ * member.
+ */
static checked: (obj: any) => KeysJson;
}
+/**
+ * Wire fees as anounced by the exchange.
+ */
@Checkable.Class()
class WireFeesJson {
+ /**
+ * Cost of a wire transfer.
+ */
@Checkable.Value(AmountJson)
wire_fee: AmountJson;
+ /**
+ * Cost of clising a reserve.
+ */
@Checkable.Value(AmountJson)
closing_fee: AmountJson;
+ /**
+ * Signature made with the exchange's master key.
+ */
@Checkable.String
sig: string;
+ /**
+ * Date from which the fee applies.
+ */
@Checkable.String
start_date: string;
+ /**
+ * Data after which the fee doesn't apply anymore.
+ */
@Checkable.String
end_date: string;
+ /**
+ * Verify that a value matches the schema of this class and convert it into a
+ * member.
+ */
static checked: (obj: any) => WireFeesJson;
}
+/**
+ * Information about wire transfer methods supported
+ * by the exchange.
+ */
@Checkable.Class({extra: true})
class WireDetailJson {
+ /**
+ * Name of the wire transfer method.
+ */
@Checkable.String
type: string;
+ /**
+ * Fees associated with the wire transfer method.
+ */
@Checkable.List(Checkable.Value(WireFeesJson))
fees: WireFeesJson[];
+ /**
+ * Verify that a value matches the schema of this class and convert it into a
+ * member.
+ */
static checked: (obj: any) => WireDetailJson;
}
+/**
+ * Request to mark a reserve as confirmed.
+ */
@Checkable.Class()
export class CreateReserveRequest {
/**
@@ -177,10 +243,17 @@ export class CreateReserveRequest {
@Checkable.String
exchange: string;
+ /**
+ * 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 {
/**
@@ -190,21 +263,40 @@ export class ConfirmReserveRequest {
@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;
}
+/**
+ * Offer record, stored in the wallet's database.
+ */
@Checkable.Class()
export class OfferRecord {
+ /**
+ * The contract that was offered by the merchant.
+ */
@Checkable.Value(Contract)
contract: Contract;
+ /**
+ * Signature by the merchant over the contract details.
+ */
@Checkable.String
merchant_sig: string;
+ /**
+ * Hash of the contract terms.
+ */
@Checkable.String
H_contract: string;
+ /**
+ * Time when the offer was made.
+ */
@Checkable.Number
offer_time: number;
@@ -214,14 +306,41 @@ export class OfferRecord {
@Checkable.Optional(Checkable.Number)
id?: number;
+ /**
+ * Verify that a value matches the schema of this class and convert it into a
+ * member.
+ */
static checked: (obj: any) => OfferRecord;
}
+/**
+ * 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;
+
+ /**
+ * Level of detail of the history entry.
+ */
level: HistoryLevel;
}
@@ -246,6 +365,11 @@ interface TransactionRecord {
finished: boolean;
}
+
+/**
+ * Level of detail at which a history
+ * entry should be shown.
+ */
export enum HistoryLevel {
Trace = 1,
Developer = 2,
@@ -254,19 +378,34 @@ export enum HistoryLevel {
}
+/**
+ * Badge that shows activity for the wallet.
+ */
export interface Badge {
- setText(s: string): void;
- setColor(c: string): void;
+ /**
+ * Start indicating background activity.
+ */
startBusy(): void;
+
+ /**
+ * Stop indicating background activity.
+ */
stopBusy(): void;
}
+
+/**
+ * Nonce record as stored in the wallet's database.
+ */
export interface NonceRecord {
priv: string;
pub: string;
}
-
+/**
+ * Configuration key/value entries to configure
+ * the wallet.
+ */
export interface ConfigRecord {
key: string;
value: any;
@@ -328,10 +467,17 @@ function isWithdrawableDenom(d: DenominationRecord) {
}
+/**
+ * Result of selecting coins, contains the exchange, and selected
+ * coins with their denomination.
+ */
export type CoinSelectionResult = {exchangeUrl: string, cds: CoinWithDenom[]}|undefined;
-export function selectCoins(cds: CoinWithDenom[], paymentAmount: AmountJson,
- depositFeeLimit: AmountJson): CoinWithDenom[]|undefined {
+/**
+ * Select coins for a payment under the merchant's constraints.
+ */
+export function selectPayCoins(cds: CoinWithDenom[], paymentAmount: AmountJson,
+ depositFeeLimit: AmountJson): CoinWithDenom[]|undefined {
if (cds.length === 0) {
return undefined;
}
@@ -406,7 +552,11 @@ function getWithdrawDenomList(amountAvailable: AmountJson,
return ds;
}
+/* tslint:disable:completed-docs */
+/**
+ * The stores and indices for the wallet database.
+ */
export namespace Stores {
class ExchangeStore extends Store<ExchangeRecord> {
constructor() {
@@ -489,6 +639,7 @@ export namespace Stores {
super("exchangeWireFees", {keyPath: "exchangeBaseUrl"});
}
}
+
export const exchanges = new ExchangeStore();
export const exchangeWireFees = new ExchangeWireFeesStore();
export const nonces = new NonceStore();
@@ -504,6 +655,8 @@ export namespace Stores {
export const config = new ConfigStore();
}
+/* tslint:enable:completed-docs */
+
interface CoinsForPaymentArgs {
allowedAuditors: Auditor[];
@@ -517,13 +670,15 @@ interface CoinsForPaymentArgs {
}
+/**
+ * The platform-independent wallet implementation.
+ */
export class Wallet {
private db: IDBDatabase;
private http: HttpRequestLibrary;
private badge: Badge;
private notifier: Notifier;
- public cryptoApi: CryptoApi;
-
+ private cryptoApi: CryptoApi;
private processPreCoinConcurrent = 0;
private processPreCoinThrottle: {[url: string]: number} = {};
@@ -748,7 +903,7 @@ export class Wallet {
}
}
- const res = selectCoins(cds, remainingAmount, depositFeeLimit);
+ const res = selectPayCoins(cds, remainingAmount, depositFeeLimit);
if (res) {
return {
cds: res,
diff --git a/src/wxApi.ts b/src/wxApi.ts
index 1f7d08fb3..8a95e75f5 100644
--- a/src/wxApi.ts
+++ b/src/wxApi.ts
@@ -14,6 +14,14 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
+/**
+ * Interface to the wallet through WebExtension messaging.
+ */
+
+
+/**
+ * Imports.
+ */
import {
AmountJson,
CoinRecord,
@@ -25,12 +33,11 @@ import {
ReserveRecord,
} from "./types";
+
/**
- * Interface to the wallet through WebExtension messaging.
- * @author Florian Dold
+ * Query the wallet for the coins that would be used to withdraw
+ * from a given reserve.
*/
-
-
export function getReserveCreationInfo(baseUrl: string,
amount: AmountJson): Promise<ReserveCreationInfo> {
const m = { type: "reserve-creation-info", detail: { baseUrl, amount } };
@@ -48,7 +55,8 @@ export function getReserveCreationInfo(baseUrl: string,
});
}
-export async function callBackend(type: string, detail?: any): Promise<any> {
+
+async function callBackend(type: string, detail?: any): Promise<any> {
return new Promise<any>((resolve, reject) => {
chrome.runtime.sendMessage({ type, detail }, (resp) => {
if (resp && resp.error) {
@@ -60,55 +68,107 @@ export async function callBackend(type: string, detail?: any): Promise<any> {
});
}
+
+/**
+ * Get all exchanges the wallet knows about.
+ */
export async function getExchanges(): Promise<ExchangeRecord[]> {
return await callBackend("get-exchanges");
}
+
+/**
+ * Get all currencies the exchange knows about.
+ */
export async function getCurrencies(): Promise<CurrencyRecord[]> {
return await callBackend("get-currencies");
}
+/**
+ * Get information about a specific currency.
+ */
export async function getCurrency(name: string): Promise<CurrencyRecord|null> {
return await callBackend("currency-info", {name});
}
+
+/**
+ * Get information about a specific exchange.
+ */
export async function getExchangeInfo(baseUrl: string): Promise<ExchangeRecord> {
return await callBackend("exchange-info", {baseUrl});
}
+
+/**
+ * Replace an existing currency record with the one given. The currency to
+ * replace is specified inside the currency record.
+ */
export async function updateCurrency(currencyRecord: CurrencyRecord): Promise<void> {
return await callBackend("update-currency", { currencyRecord });
}
+
+/**
+ * Get all reserves the wallet has at an exchange.
+ */
export async function getReserves(exchangeBaseUrl: string): Promise<ReserveRecord[]> {
return await callBackend("get-reserves", { exchangeBaseUrl });
}
+
+/**
+ * Get all reserves for which a payback is available.
+ */
export async function getPaybackReserves(): Promise<ReserveRecord[]> {
return await callBackend("get-payback-reserves");
}
+
+/**
+ * Withdraw the payback that is available for a reserve.
+ */
export async function withdrawPaybackReserve(reservePub: string): Promise<ReserveRecord[]> {
return await callBackend("withdraw-payback-reserve", { reservePub });
}
+
+/**
+ * Get all coins withdrawn from the given exchange.
+ */
export async function getCoins(exchangeBaseUrl: string): Promise<CoinRecord[]> {
return await callBackend("get-coins", { exchangeBaseUrl });
}
+
+/**
+ * Get all precoins withdrawn from the given exchange.
+ */
export async function getPreCoins(exchangeBaseUrl: string): Promise<PreCoinRecord[]> {
return await callBackend("get-precoins", { exchangeBaseUrl });
}
+
+/**
+ * Get all denoms offered by the given exchange.
+ */
export async function getDenoms(exchangeBaseUrl: string): Promise<DenominationRecord[]> {
return await callBackend("get-denoms", { exchangeBaseUrl });
}
+
+/**
+ * Start refreshing a coin.
+ */
export async function refresh(coinPub: string): Promise<void> {
return await callBackend("refresh-coin", { coinPub });
}
+
+/**
+ * Request payback for a coin. Only works for non-refreshed coins.
+ */
export async function payback(coinPub: string): Promise<void> {
return await callBackend("payback-coin", { coinPub });
}
diff --git a/src/wxBackend.ts b/src/wxBackend.ts
index 6b9601572..a9a208dcd 100644
--- a/src/wxBackend.ts
+++ b/src/wxBackend.ts
@@ -340,8 +340,9 @@ async function dispatch(handlers: any, req: any, sender: any, sendResponse: any)
}
}
+
class ChromeNotifier implements Notifier {
- ports: Port[] = [];
+ private ports: Port[] = [];
constructor() {
chrome.runtime.onConnect.addListener((port) => {
@@ -483,6 +484,11 @@ function clearRateLimitCache() {
rateLimitCache = {};
}
+/**
+ * Main function to run for the WebExtension backend.
+ *
+ * Sets up all event handlers and other machinery.
+ */
export async function wxMain() {
window.onerror = (m, source, lineno, colno, error) => {
logging.record("error", m + error, undefined, source || "(unknown)", lineno || 0, colno || 0);