WIP: simplification and error handling
This commit is contained in:
parent
e8f362ccfe
commit
c623309430
@ -63,7 +63,7 @@
|
|||||||
"@types/chrome": "^0.0.91",
|
"@types/chrome": "^0.0.91",
|
||||||
"@types/urijs": "^1.19.3",
|
"@types/urijs": "^1.19.3",
|
||||||
"axios": "^0.19.0",
|
"axios": "^0.19.0",
|
||||||
"idb-bridge": "^0.0.11",
|
"idb-bridge": "^0.0.14",
|
||||||
"qrcode-generator": "^1.4.3",
|
"qrcode-generator": "^1.4.3",
|
||||||
"source-map-support": "^0.5.12",
|
"source-map-support": "^0.5.12",
|
||||||
"urijs": "^1.18.10"
|
"urijs": "^1.18.10"
|
||||||
|
@ -22,6 +22,7 @@ import {
|
|||||||
DenominationRecord,
|
DenominationRecord,
|
||||||
DenominationStatus,
|
DenominationStatus,
|
||||||
ReserveRecord,
|
ReserveRecord,
|
||||||
|
ReserveRecordStatus,
|
||||||
} from "../dbTypes";
|
} from "../dbTypes";
|
||||||
|
|
||||||
import { CryptoApi } from "./cryptoApi";
|
import { CryptoApi } from "./cryptoApi";
|
||||||
@ -86,18 +87,18 @@ test("precoin creation", async t => {
|
|||||||
const crypto = new CryptoApi(new NodeCryptoWorkerFactory());
|
const crypto = new CryptoApi(new NodeCryptoWorkerFactory());
|
||||||
const { priv, pub } = await crypto.createEddsaKeypair();
|
const { priv, pub } = await crypto.createEddsaKeypair();
|
||||||
const r: ReserveRecord = {
|
const r: ReserveRecord = {
|
||||||
created: 0,
|
created: { t_ms: 0 },
|
||||||
current_amount: null,
|
currentAmount: null,
|
||||||
exchange_base_url: "https://example.com/exchange",
|
exchangeBaseUrl: "https://example.com/exchange",
|
||||||
hasPayback: false,
|
hasPayback: false,
|
||||||
precoin_amount: { currency: "PUDOS", value: 0, fraction: 0 },
|
precoinAmount: { currency: "PUDOS", value: 0, fraction: 0 },
|
||||||
requested_amount: { currency: "PUDOS", value: 0, fraction: 0 },
|
requestedAmount: { currency: "PUDOS", value: 0, fraction: 0 },
|
||||||
reserve_priv: priv,
|
reservePriv: priv,
|
||||||
reserve_pub: pub,
|
reservePub: pub,
|
||||||
timestamp_confirmed: 0,
|
timestampConfirmed: undefined,
|
||||||
timestamp_depleted: 0,
|
timestampReserveInfoPosted: undefined,
|
||||||
timestamp_reserve_info_posted: 0,
|
exchangeWire: "payto://foo",
|
||||||
exchangeWire: "payto://foo"
|
reserveStatus: ReserveRecordStatus.UNCONFIRMED,
|
||||||
};
|
};
|
||||||
|
|
||||||
const precoin = await crypto.createPreCoin(denomValid1, r);
|
const precoin = await crypto.createPreCoin(denomValid1, r);
|
||||||
|
@ -45,6 +45,7 @@ import * as native from "./emscInterface";
|
|||||||
import { AmountJson } from "../amounts";
|
import { AmountJson } from "../amounts";
|
||||||
import * as Amounts from "../amounts";
|
import * as Amounts from "../amounts";
|
||||||
import * as timer from "../timer";
|
import * as timer from "../timer";
|
||||||
|
import { getRandomBytes, encodeCrock } from "./nativeCrypto";
|
||||||
|
|
||||||
export class CryptoImplementation {
|
export class CryptoImplementation {
|
||||||
static enableTracing: boolean = false;
|
static enableTracing: boolean = false;
|
||||||
@ -60,9 +61,9 @@ export class CryptoImplementation {
|
|||||||
reserve: ReserveRecord,
|
reserve: ReserveRecord,
|
||||||
): PreCoinRecord {
|
): PreCoinRecord {
|
||||||
const reservePriv = new native.EddsaPrivateKey(this.emsc);
|
const reservePriv = new native.EddsaPrivateKey(this.emsc);
|
||||||
reservePriv.loadCrock(reserve.reserve_priv);
|
reservePriv.loadCrock(reserve.reservePriv);
|
||||||
const reservePub = new native.EddsaPublicKey(this.emsc);
|
const reservePub = new native.EddsaPublicKey(this.emsc);
|
||||||
reservePub.loadCrock(reserve.reserve_pub);
|
reservePub.loadCrock(reserve.reservePub);
|
||||||
const denomPub = native.RsaPublicKey.fromCrock(this.emsc, denom.denomPub);
|
const denomPub = native.RsaPublicKey.fromCrock(this.emsc, denom.denomPub);
|
||||||
const coinPriv = native.EddsaPrivateKey.create(this.emsc);
|
const coinPriv = native.EddsaPrivateKey.create(this.emsc);
|
||||||
const coinPub = coinPriv.getPublicKey();
|
const coinPub = coinPriv.getPublicKey();
|
||||||
@ -103,7 +104,7 @@ export class CryptoImplementation {
|
|||||||
coinValue: denom.value,
|
coinValue: denom.value,
|
||||||
denomPub: denomPub.toCrock(),
|
denomPub: denomPub.toCrock(),
|
||||||
denomPubHash: denomPubHash.toCrock(),
|
denomPubHash: denomPubHash.toCrock(),
|
||||||
exchangeBaseUrl: reserve.exchange_base_url,
|
exchangeBaseUrl: reserve.exchangeBaseUrl,
|
||||||
isFromTip: false,
|
isFromTip: false,
|
||||||
reservePub: reservePub.toCrock(),
|
reservePub: reservePub.toCrock(),
|
||||||
withdrawSig: sig.toCrock(),
|
withdrawSig: sig.toCrock(),
|
||||||
@ -199,14 +200,14 @@ export class CryptoImplementation {
|
|||||||
isValidWireFee(type: string, wf: WireFee, masterPub: string): boolean {
|
isValidWireFee(type: string, wf: WireFee, masterPub: string): boolean {
|
||||||
const p = new native.MasterWireFeePS(this.emsc, {
|
const p = new native.MasterWireFeePS(this.emsc, {
|
||||||
closing_fee: new native.Amount(this.emsc, wf.closingFee).toNbo(),
|
closing_fee: new native.Amount(this.emsc, wf.closingFee).toNbo(),
|
||||||
end_date: native.AbsoluteTimeNbo.fromStampSeconds(this.emsc, wf.endStamp),
|
end_date: native.AbsoluteTimeNbo.fromStampSeconds(this.emsc, (wf.endStamp.t_ms / 1000)),
|
||||||
h_wire_method: native.ByteArray.fromStringWithNull(
|
h_wire_method: native.ByteArray.fromStringWithNull(
|
||||||
this.emsc,
|
this.emsc,
|
||||||
type,
|
type,
|
||||||
).hash(),
|
).hash(),
|
||||||
start_date: native.AbsoluteTimeNbo.fromStampSeconds(
|
start_date: native.AbsoluteTimeNbo.fromStampSeconds(
|
||||||
this.emsc,
|
this.emsc,
|
||||||
wf.startStamp,
|
Math.floor(wf.startStamp.t_ms / 1000),
|
||||||
),
|
),
|
||||||
wire_fee: new native.Amount(this.emsc, wf.wireFee).toNbo(),
|
wire_fee: new native.Amount(this.emsc, wf.wireFee).toNbo(),
|
||||||
});
|
});
|
||||||
@ -354,7 +355,7 @@ export class CryptoImplementation {
|
|||||||
const newAmount = new native.Amount(this.emsc, cd.coin.currentAmount);
|
const newAmount = new native.Amount(this.emsc, cd.coin.currentAmount);
|
||||||
newAmount.sub(coinSpend);
|
newAmount.sub(coinSpend);
|
||||||
cd.coin.currentAmount = newAmount.toJson();
|
cd.coin.currentAmount = newAmount.toJson();
|
||||||
cd.coin.status = CoinStatus.PurchasePending;
|
cd.coin.status = CoinStatus.Dirty;
|
||||||
|
|
||||||
const d = new native.DepositRequestPS(this.emsc, {
|
const d = new native.DepositRequestPS(this.emsc, {
|
||||||
amount_with_fee: coinSpend.toNbo(),
|
amount_with_fee: coinSpend.toNbo(),
|
||||||
@ -505,7 +506,10 @@ export class CryptoImplementation {
|
|||||||
valueOutput = Amounts.add(valueOutput, denom.value).amount;
|
valueOutput = Amounts.add(valueOutput, denom.value).amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const refreshSessionId = encodeCrock(getRandomBytes(32));
|
||||||
|
|
||||||
const refreshSession: RefreshSessionRecord = {
|
const refreshSession: RefreshSessionRecord = {
|
||||||
|
refreshSessionId,
|
||||||
confirmSig,
|
confirmSig,
|
||||||
exchangeBaseUrl,
|
exchangeBaseUrl,
|
||||||
finished: false,
|
finished: false,
|
||||||
|
@ -12,7 +12,6 @@ export function openTalerDb(
|
|||||||
onVersionChange: () => void,
|
onVersionChange: () => void,
|
||||||
onUpgradeUnsupported: (oldVersion: number, newVersion: number) => void,
|
onUpgradeUnsupported: (oldVersion: number, newVersion: number) => void,
|
||||||
): Promise<IDBDatabase> {
|
): Promise<IDBDatabase> {
|
||||||
console.log("in openTalerDb");
|
|
||||||
return new Promise<IDBDatabase>((resolve, reject) => {
|
return new Promise<IDBDatabase>((resolve, reject) => {
|
||||||
const req = idbFactory.open(DB_NAME, WALLET_DB_VERSION);
|
const req = idbFactory.open(DB_NAME, WALLET_DB_VERSION);
|
||||||
req.onerror = e => {
|
req.onerror = e => {
|
||||||
|
171
src/dbTypes.ts
171
src/dbTypes.ts
@ -46,6 +46,36 @@ import { Timestamp, OperationError } from "./walletTypes";
|
|||||||
*/
|
*/
|
||||||
export const WALLET_DB_VERSION = 27;
|
export const WALLET_DB_VERSION = 27;
|
||||||
|
|
||||||
|
export enum ReserveRecordStatus {
|
||||||
|
/**
|
||||||
|
* Waiting for manual confirmation.
|
||||||
|
*/
|
||||||
|
UNCONFIRMED = "unconfirmed",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reserve must be registered with the bank.
|
||||||
|
*/
|
||||||
|
REGISTERING_BANK = "registering-bank",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Querying reserve status with the exchange.
|
||||||
|
*/
|
||||||
|
QUERYING_STATUS = "querying-status",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Status is queried, the wallet must now select coins
|
||||||
|
* and start withdrawing.
|
||||||
|
*/
|
||||||
|
WITHDRAWING = "withdrawing",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The corresponding withdraw record has been created.
|
||||||
|
* No further processing is done, unless explicitly requested
|
||||||
|
* by the user.
|
||||||
|
*/
|
||||||
|
DORMANT = "dormant",
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A reserve record as stored in the wallet's database.
|
* A reserve record as stored in the wallet's database.
|
||||||
*/
|
*/
|
||||||
@ -53,28 +83,22 @@ export interface ReserveRecord {
|
|||||||
/**
|
/**
|
||||||
* The reserve public key.
|
* The reserve public key.
|
||||||
*/
|
*/
|
||||||
reserve_pub: string;
|
reservePub: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The reserve private key.
|
* The reserve private key.
|
||||||
*/
|
*/
|
||||||
reserve_priv: string;
|
reservePriv: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The exchange base URL.
|
* The exchange base URL.
|
||||||
*/
|
*/
|
||||||
exchange_base_url: string;
|
exchangeBaseUrl: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Time when the reserve was created.
|
* Time when the reserve was created.
|
||||||
*/
|
*/
|
||||||
created: number;
|
created: Timestamp;
|
||||||
|
|
||||||
/**
|
|
||||||
* Time when the reserve was depleted.
|
|
||||||
* Set to 0 if not depleted yet.
|
|
||||||
*/
|
|
||||||
timestamp_depleted: number;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Time when the information about this reserve was posted to the bank.
|
* Time when the information about this reserve was posted to the bank.
|
||||||
@ -83,32 +107,32 @@ export interface ReserveRecord {
|
|||||||
*
|
*
|
||||||
* Set to 0 if that hasn't happened yet.
|
* Set to 0 if that hasn't happened yet.
|
||||||
*/
|
*/
|
||||||
timestamp_reserve_info_posted: number;
|
timestampReserveInfoPosted: Timestamp | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Time when the reserve was confirmed.
|
* Time when the reserve was confirmed.
|
||||||
*
|
*
|
||||||
* Set to 0 if not confirmed yet.
|
* Set to 0 if not confirmed yet.
|
||||||
*/
|
*/
|
||||||
timestamp_confirmed: number;
|
timestampConfirmed: Timestamp | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current amount left in the reserve
|
* Current amount left in the reserve
|
||||||
*/
|
*/
|
||||||
current_amount: AmountJson | null;
|
currentAmount: AmountJson | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Amount requested when the reserve was created.
|
* Amount requested when the reserve was created.
|
||||||
* When a reserve is re-used (rare!) the current_amount can
|
* When a reserve is re-used (rare!) the current_amount can
|
||||||
* be higher than the requested_amount
|
* be higher than the requested_amount
|
||||||
*/
|
*/
|
||||||
requested_amount: AmountJson;
|
requestedAmount: AmountJson;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* What's the current amount that sits
|
* What's the current amount that sits
|
||||||
* in precoins?
|
* in precoins?
|
||||||
*/
|
*/
|
||||||
precoin_amount: AmountJson;
|
precoinAmount: AmountJson;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We got some payback to this reserve. We'll cease to automatically
|
* We got some payback to this reserve. We'll cease to automatically
|
||||||
@ -129,6 +153,10 @@ export interface ReserveRecord {
|
|||||||
exchangeWire: string;
|
exchangeWire: string;
|
||||||
|
|
||||||
bankWithdrawStatusUrl?: string;
|
bankWithdrawStatusUrl?: string;
|
||||||
|
|
||||||
|
reserveStatus: ReserveRecordStatus;
|
||||||
|
|
||||||
|
lastError?: OperationError;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -341,9 +369,9 @@ export interface ExchangeDetails {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum ExchangeUpdateStatus {
|
export enum ExchangeUpdateStatus {
|
||||||
NONE = "none",
|
|
||||||
FETCH_KEYS = "fetch_keys",
|
FETCH_KEYS = "fetch_keys",
|
||||||
FETCH_WIRE = "fetch_wire",
|
FETCH_WIRE = "fetch_wire",
|
||||||
|
FINISHED = "finished",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExchangeBankAccount {
|
export interface ExchangeBankAccount {
|
||||||
@ -374,13 +402,18 @@ export interface ExchangeRecord {
|
|||||||
*/
|
*/
|
||||||
wireInfo: ExchangeWireInfo | undefined;
|
wireInfo: ExchangeWireInfo | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When was the exchange added to the wallet?
|
||||||
|
*/
|
||||||
|
timestampAdded: Timestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Time when the update to the exchange has been started or
|
* Time when the update to the exchange has been started or
|
||||||
* undefined if no update is in progress.
|
* undefined if no update is in progress.
|
||||||
*/
|
*/
|
||||||
updateStarted: Timestamp | undefined;
|
updateStarted: Timestamp | undefined;
|
||||||
|
|
||||||
updateStatus: ExchangeUpdateStatus;
|
updateStatus: ExchangeUpdateStatus;
|
||||||
|
updateReason?: "initial" | "forced";
|
||||||
|
|
||||||
lastError?: OperationError;
|
lastError?: OperationError;
|
||||||
}
|
}
|
||||||
@ -436,31 +469,15 @@ export enum CoinStatus {
|
|||||||
/**
|
/**
|
||||||
* Withdrawn and never shown to anybody.
|
* Withdrawn and never shown to anybody.
|
||||||
*/
|
*/
|
||||||
Fresh,
|
Fresh = "fresh",
|
||||||
/**
|
|
||||||
* Currently planned to be sent to a merchant for a purchase.
|
|
||||||
*/
|
|
||||||
PurchasePending,
|
|
||||||
/**
|
/**
|
||||||
* Used for a completed transaction and now dirty.
|
* Used for a completed transaction and now dirty.
|
||||||
*/
|
*/
|
||||||
Dirty,
|
Dirty = "dirty",
|
||||||
/**
|
/**
|
||||||
* A coin that was refreshed.
|
* A coin that has been spent and refreshed.
|
||||||
*/
|
*/
|
||||||
Refreshed,
|
Dormant = "dormant",
|
||||||
/**
|
|
||||||
* 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,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -569,7 +586,7 @@ export class ProposalDownloadRecord {
|
|||||||
* was created.
|
* was created.
|
||||||
*/
|
*/
|
||||||
@Checkable.Number()
|
@Checkable.Number()
|
||||||
timestamp: number;
|
timestamp: Timestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private key for the nonce.
|
* Private key for the nonce.
|
||||||
@ -658,7 +675,7 @@ export interface TipRecord {
|
|||||||
*/
|
*/
|
||||||
nextUrl?: string;
|
nextUrl?: string;
|
||||||
|
|
||||||
timestamp: number;
|
timestamp: Timestamp;
|
||||||
|
|
||||||
pickupUrl: string;
|
pickupUrl: string;
|
||||||
}
|
}
|
||||||
@ -735,9 +752,9 @@ export interface RefreshSessionRecord {
|
|||||||
finished: boolean;
|
finished: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Record ID when retrieved from the DB.
|
* A 32-byte base32-crockford encoded random identifier.
|
||||||
*/
|
*/
|
||||||
id?: number;
|
refreshSessionId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -771,12 +788,12 @@ export interface WireFee {
|
|||||||
/**
|
/**
|
||||||
* Start date of the fee.
|
* Start date of the fee.
|
||||||
*/
|
*/
|
||||||
startStamp: number;
|
startStamp: Timestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* End date of the fee.
|
* End date of the fee.
|
||||||
*/
|
*/
|
||||||
endStamp: number;
|
endStamp: Timestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signature made by the exchange master key.
|
* Signature made by the exchange master key.
|
||||||
@ -830,14 +847,13 @@ export interface PurchaseRecord {
|
|||||||
* When was the purchase made?
|
* When was the purchase made?
|
||||||
* Refers to the time that the user accepted.
|
* Refers to the time that the user accepted.
|
||||||
*/
|
*/
|
||||||
timestamp: number;
|
timestamp: Timestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When was the last refund made?
|
* When was the last refund made?
|
||||||
* Set to 0 if no refund was made on the purchase.
|
* Set to 0 if no refund was made on the purchase.
|
||||||
*/
|
*/
|
||||||
timestamp_refund: number;
|
timestamp_refund: Timestamp | undefined;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Last session signature that we submitted to /pay (if any).
|
* Last session signature that we submitted to /pay (if any).
|
||||||
@ -917,7 +933,6 @@ export interface CoinsReturnRecord {
|
|||||||
wire: any;
|
wire: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface WithdrawalRecord {
|
export interface WithdrawalRecord {
|
||||||
/**
|
/**
|
||||||
* Reserve that we're withdrawing from.
|
* Reserve that we're withdrawing from.
|
||||||
@ -928,18 +943,22 @@ export interface WithdrawalRecord {
|
|||||||
* When was the withdrawal operation started started?
|
* When was the withdrawal operation started started?
|
||||||
* Timestamp in milliseconds.
|
* Timestamp in milliseconds.
|
||||||
*/
|
*/
|
||||||
startTimestamp: number;
|
startTimestamp: Timestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When was the withdrawal operation completed?
|
* When was the withdrawal operation completed?
|
||||||
*/
|
*/
|
||||||
finishTimestamp?: number;
|
finishTimestamp?: Timestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Amount that is being withdrawn with this operation.
|
* Amount that is being withdrawn with this operation.
|
||||||
* This does not include fees.
|
* This does not include fees.
|
||||||
*/
|
*/
|
||||||
withdrawalAmount: string;
|
withdrawalAmount: string;
|
||||||
|
|
||||||
|
numCoinsTotal: number;
|
||||||
|
|
||||||
|
numCoinsWithdrawn: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* tslint:disable:completed-docs */
|
/* tslint:disable:completed-docs */
|
||||||
@ -983,11 +1002,6 @@ export namespace Stores {
|
|||||||
"urlIndex",
|
"urlIndex",
|
||||||
"url",
|
"url",
|
||||||
);
|
);
|
||||||
timestampIndex = new Index<string, ProposalDownloadRecord>(
|
|
||||||
this,
|
|
||||||
"timestampIndex",
|
|
||||||
"timestamp",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class PurchasesStore extends Store<PurchaseRecord> {
|
class PurchasesStore extends Store<PurchaseRecord> {
|
||||||
@ -1005,11 +1019,6 @@ export namespace Stores {
|
|||||||
"orderIdIndex",
|
"orderIdIndex",
|
||||||
"contractTerms.order_id",
|
"contractTerms.order_id",
|
||||||
);
|
);
|
||||||
timestampIndex = new Index<string, PurchaseRecord>(
|
|
||||||
this,
|
|
||||||
"timestampIndex",
|
|
||||||
"timestamp",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class DenominationsStore extends Store<DenominationRecord> {
|
class DenominationsStore extends Store<DenominationRecord> {
|
||||||
@ -1051,23 +1060,8 @@ export namespace Stores {
|
|||||||
|
|
||||||
class ReservesStore extends Store<ReserveRecord> {
|
class ReservesStore extends Store<ReserveRecord> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("reserves", { keyPath: "reserve_pub" });
|
super("reserves", { keyPath: "reservePub" });
|
||||||
}
|
}
|
||||||
timestampCreatedIndex = new Index<string, ReserveRecord>(
|
|
||||||
this,
|
|
||||||
"timestampCreatedIndex",
|
|
||||||
"created",
|
|
||||||
);
|
|
||||||
timestampConfirmedIndex = new Index<string, ReserveRecord>(
|
|
||||||
this,
|
|
||||||
"timestampConfirmedIndex",
|
|
||||||
"timestamp_confirmed",
|
|
||||||
);
|
|
||||||
timestampDepletedIndex = new Index<string, ReserveRecord>(
|
|
||||||
this,
|
|
||||||
"timestampDepletedIndex",
|
|
||||||
"timestamp_depleted",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class TipsStore extends Store<TipRecord> {
|
class TipsStore extends Store<TipRecord> {
|
||||||
@ -1092,8 +1086,26 @@ export namespace Stores {
|
|||||||
|
|
||||||
class WithdrawalsStore extends Store<WithdrawalRecord> {
|
class WithdrawalsStore extends Store<WithdrawalRecord> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("withdrawals", { keyPath: "id", autoIncrement: true })
|
super("withdrawals", { keyPath: "id", autoIncrement: true });
|
||||||
}
|
}
|
||||||
|
byReservePub = new Index<string, WithdrawalRecord>(
|
||||||
|
this,
|
||||||
|
"withdrawalsReservePubIndex",
|
||||||
|
"reservePub",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class PreCoinsStore extends Store<PreCoinRecord> {
|
||||||
|
constructor() {
|
||||||
|
super("precoins", {
|
||||||
|
keyPath: "coinPub",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
byReservePub = new Index<string, PreCoinRecord>(
|
||||||
|
this,
|
||||||
|
"precoinsReservePubIndex",
|
||||||
|
"reservePub",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const coins = new CoinsStore();
|
export const coins = new CoinsStore();
|
||||||
@ -1104,13 +1116,10 @@ export namespace Stores {
|
|||||||
export const currencies = new CurrenciesStore();
|
export const currencies = new CurrenciesStore();
|
||||||
export const denominations = new DenominationsStore();
|
export const denominations = new DenominationsStore();
|
||||||
export const exchanges = new ExchangeStore();
|
export const exchanges = new ExchangeStore();
|
||||||
export const precoins = new Store<PreCoinRecord>("precoins", {
|
export const precoins = new PreCoinsStore();
|
||||||
keyPath: "coinPub",
|
|
||||||
});
|
|
||||||
export const proposals = new ProposalsStore();
|
export const proposals = new ProposalsStore();
|
||||||
export const refresh = new Store<RefreshSessionRecord>("refresh", {
|
export const refresh = new Store<RefreshSessionRecord>("refresh", {
|
||||||
keyPath: "id",
|
keyPath: "refreshSessionId",
|
||||||
autoIncrement: true,
|
|
||||||
});
|
});
|
||||||
export const reserves = new ReservesStore();
|
export const reserves = new ReservesStore();
|
||||||
export const purchases = new PurchasesStore();
|
export const purchases = new PurchasesStore();
|
||||||
|
@ -440,7 +440,7 @@ export class CommandGroup<GN extends keyof any, TG> {
|
|||||||
if (option.isFlag == false && option.required == true) {
|
if (option.isFlag == false && option.required == true) {
|
||||||
if (!foundOptions[option.name]) {
|
if (!foundOptions[option.name]) {
|
||||||
if (option.args.default !== undefined) {
|
if (option.args.default !== undefined) {
|
||||||
parsedArgs[this.argKey] = option.args.default;
|
myArgs[option.name] = option.args.default;
|
||||||
} else {
|
} else {
|
||||||
const name = option.flagspec.join(",")
|
const name = option.flagspec.join(",")
|
||||||
console.error(`error: missing option '${name}'`);
|
console.error(`error: missing option '${name}'`);
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
/**
|
/**
|
||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import { Wallet } from "../wallet";
|
import { Wallet, OperationFailedAndReportedError } from "../wallet";
|
||||||
import { Notifier, Badge } from "../walletTypes";
|
import { Notifier, Badge } from "../walletTypes";
|
||||||
import { MemoryBackend, BridgeIDBFactory, shimIndexedDB } from "idb-bridge";
|
import { MemoryBackend, BridgeIDBFactory, shimIndexedDB } from "idb-bridge";
|
||||||
import { SynchronousCryptoWorkerFactory } from "../crypto/synchronousWorker";
|
import { SynchronousCryptoWorkerFactory } from "../crypto/synchronousWorker";
|
||||||
@ -139,18 +139,16 @@ export async function getDefaultNodeWallet(
|
|||||||
|
|
||||||
const storagePath = args.persistentStoragePath;
|
const storagePath = args.persistentStoragePath;
|
||||||
if (storagePath) {
|
if (storagePath) {
|
||||||
console.log(`using storage path ${storagePath}`);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const dbContentStr: string = fs.readFileSync(storagePath, { encoding: "utf-8" });
|
const dbContentStr: string = fs.readFileSync(storagePath, { encoding: "utf-8" });
|
||||||
const dbContent = JSON.parse(dbContentStr);
|
const dbContent = JSON.parse(dbContentStr);
|
||||||
myBackend.importDump(dbContent);
|
myBackend.importDump(dbContent);
|
||||||
console.log("imported wallet");
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("could not read wallet file");
|
console.error("could not read wallet file");
|
||||||
}
|
}
|
||||||
|
|
||||||
myBackend.afterCommitCallback = async () => {
|
myBackend.afterCommitCallback = async () => {
|
||||||
|
console.log("DATABASE COMMITTED");
|
||||||
// Allow caller to stop persisting the wallet.
|
// Allow caller to stop persisting the wallet.
|
||||||
if (args.persistentStoragePath === undefined) {
|
if (args.persistentStoragePath === undefined) {
|
||||||
return;
|
return;
|
||||||
@ -190,8 +188,6 @@ export async function getDefaultNodeWallet(
|
|||||||
myUnsupportedUpgrade,
|
myUnsupportedUpgrade,
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log("opened db");
|
|
||||||
|
|
||||||
return new Wallet(
|
return new Wallet(
|
||||||
myDb,
|
myDb,
|
||||||
myHttpLib,
|
myHttpLib,
|
||||||
@ -214,6 +210,8 @@ export async function withdrawTestBalance(
|
|||||||
exchangeWire: "payto://unknown",
|
exchangeWire: "payto://unknown",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const reservePub = reserveResponse.reservePub;
|
||||||
|
|
||||||
const bank = new Bank(bankBaseUrl);
|
const bank = new Bank(bankBaseUrl);
|
||||||
|
|
||||||
const bankUser = await bank.registerRandomUser();
|
const bankUser = await bank.registerRandomUser();
|
||||||
@ -228,11 +226,11 @@ export async function withdrawTestBalance(
|
|||||||
await bank.createReserve(
|
await bank.createReserve(
|
||||||
bankUser,
|
bankUser,
|
||||||
amount,
|
amount,
|
||||||
reserveResponse.reservePub,
|
reservePub,
|
||||||
exchangePaytoUri,
|
exchangePaytoUri,
|
||||||
);
|
);
|
||||||
|
|
||||||
await myWallet.confirmReserve({ reservePub: reserveResponse.reservePub });
|
await myWallet.confirmReserve({ reservePub: reserveResponse.reservePub });
|
||||||
|
|
||||||
await myWallet.processReserve(reserveResponse.reservePub);
|
await myWallet.runUntilReserveDepleted(reservePub);
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ export async function runIntegrationTest(args: {
|
|||||||
amountToWithdraw: string;
|
amountToWithdraw: string;
|
||||||
amountToSpend: string;
|
amountToSpend: string;
|
||||||
}) {
|
}) {
|
||||||
|
console.log("running test with", args);
|
||||||
const myWallet = await getDefaultNodeWallet();
|
const myWallet = await getDefaultNodeWallet();
|
||||||
|
|
||||||
await withdrawTestBalance(myWallet, args.amountToWithdraw, args.bankBaseUrl, args.exchangeBaseUrl);
|
await withdrawTestBalance(myWallet, args.amountToWithdraw, args.bankBaseUrl, args.exchangeBaseUrl);
|
||||||
|
@ -18,9 +18,14 @@ import os = require("os");
|
|||||||
import { getDefaultNodeWallet, withdrawTestBalance } from "./helpers";
|
import { getDefaultNodeWallet, withdrawTestBalance } from "./helpers";
|
||||||
import { MerchantBackendConnection } from "./merchant";
|
import { MerchantBackendConnection } from "./merchant";
|
||||||
import { runIntegrationTest } from "./integrationtest";
|
import { runIntegrationTest } from "./integrationtest";
|
||||||
import { Wallet } from "../wallet";
|
import { Wallet, OperationFailedAndReportedError } from "../wallet";
|
||||||
import qrcodeGenerator = require("qrcode-generator");
|
import qrcodeGenerator = require("qrcode-generator");
|
||||||
import * as clk from "./clk";
|
import * as clk from "./clk";
|
||||||
|
import { BridgeIDBFactory, MemoryBackend } from "idb-bridge";
|
||||||
|
import { Logger } from "../logging";
|
||||||
|
import * as Amounts from "../amounts";
|
||||||
|
|
||||||
|
const logger = new Logger("taler-wallet-cli.ts");
|
||||||
|
|
||||||
const walletDbPath = os.homedir + "/" + ".talerwalletdb.json";
|
const walletDbPath = os.homedir + "/" + ".talerwalletdb.json";
|
||||||
|
|
||||||
@ -82,6 +87,7 @@ function applyVerbose(verbose: boolean) {
|
|||||||
if (verbose) {
|
if (verbose) {
|
||||||
console.log("enabled verbose logging");
|
console.log("enabled verbose logging");
|
||||||
Wallet.enableTracing = true;
|
Wallet.enableTracing = true;
|
||||||
|
BridgeIDBFactory.enableTracing = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,16 +109,21 @@ async function withWallet<T>(
|
|||||||
walletCliArgs: WalletCliArgsType,
|
walletCliArgs: WalletCliArgsType,
|
||||||
f: (w: Wallet) => Promise<T>,
|
f: (w: Wallet) => Promise<T>,
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
applyVerbose(walletCliArgs.wallet.verbose);
|
|
||||||
const wallet = await getDefaultNodeWallet({
|
const wallet = await getDefaultNodeWallet({
|
||||||
persistentStoragePath: walletDbPath,
|
persistentStoragePath: walletDbPath,
|
||||||
});
|
});
|
||||||
|
applyVerbose(walletCliArgs.wallet.verbose);
|
||||||
try {
|
try {
|
||||||
await wallet.fillDefaults();
|
await wallet.fillDefaults();
|
||||||
const ret = await f(wallet);
|
const ret = await f(wallet);
|
||||||
return ret;
|
return ret;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("caught exception:", e);
|
if (e instanceof OperationFailedAndReportedError) {
|
||||||
|
console.error("Operation failed: " + e.message);
|
||||||
|
console.log("Hint: check pending operations for details.");
|
||||||
|
} else {
|
||||||
|
console.error("caught exception:", e);
|
||||||
|
}
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
} finally {
|
} finally {
|
||||||
wallet.stop();
|
wallet.stop();
|
||||||
@ -120,6 +131,161 @@ async function withWallet<T>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
walletCli
|
walletCli
|
||||||
|
.subcommand("", "balance", { help: "Show wallet balance." })
|
||||||
|
.action(async args => {
|
||||||
|
console.log("balance command called");
|
||||||
|
await withWallet(args, async wallet => {
|
||||||
|
const balance = await wallet.getBalances();
|
||||||
|
console.log(JSON.stringify(balance, undefined, 2));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
walletCli
|
||||||
|
.subcommand("", "history", { help: "Show wallet event history." })
|
||||||
|
.maybeOption("from", ["--from"], clk.STRING)
|
||||||
|
.maybeOption("to", ["--to"], clk.STRING)
|
||||||
|
.maybeOption("limit", ["--limit"], clk.STRING)
|
||||||
|
.maybeOption("contEvt", ["--continue-with"], clk.STRING)
|
||||||
|
.action(async args => {
|
||||||
|
await withWallet(args, async wallet => {
|
||||||
|
const history = await wallet.getHistory();
|
||||||
|
console.log(JSON.stringify(history, undefined, 2));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
walletCli
|
||||||
|
.subcommand("", "pending", { help: "Show pending operations." })
|
||||||
|
.action(async args => {
|
||||||
|
await withWallet(args, async wallet => {
|
||||||
|
const pending = await wallet.getPendingOperations();
|
||||||
|
console.log(JSON.stringify(pending, undefined, 2));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
async function asyncSleep(milliSeconds: number): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
setTimeout(() => resolve(), milliSeconds);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
walletCli
|
||||||
|
.subcommand("runPendingOpt", "run-pending", {
|
||||||
|
help: "Run pending operations.",
|
||||||
|
})
|
||||||
|
.action(async args => {
|
||||||
|
await withWallet(args, async wallet => {
|
||||||
|
await wallet.runPending();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
walletCli
|
||||||
|
.subcommand("handleUri", "handle-uri", {
|
||||||
|
help: "Handle a taler:// URI.",
|
||||||
|
})
|
||||||
|
.requiredArgument("uri", clk.STRING)
|
||||||
|
.flag("autoYes", ["-y", "--yes"])
|
||||||
|
.action(async args => {
|
||||||
|
await withWallet(args, async wallet => {
|
||||||
|
const uri: string = args.handleUri.uri;
|
||||||
|
if (uri.startsWith("taler://pay/")) {
|
||||||
|
await doPay(wallet, uri, { alwaysYes: args.handleUri.autoYes });
|
||||||
|
} else if (uri.startsWith("taler://tip/")) {
|
||||||
|
const res = await wallet.getTipStatus(uri);
|
||||||
|
console.log("tip status", res);
|
||||||
|
await wallet.acceptTip(uri);
|
||||||
|
} else if (uri.startsWith("taler://refund/")) {
|
||||||
|
await wallet.applyRefund(uri);
|
||||||
|
} else if (uri.startsWith("taler://withdraw/")) {
|
||||||
|
const withdrawInfo = await wallet.getWithdrawalInfo(uri);
|
||||||
|
const selectedExchange = withdrawInfo.suggestedExchange;
|
||||||
|
if (!selectedExchange) {
|
||||||
|
console.error("no suggested exchange!");
|
||||||
|
process.exit(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { confirmTransferUrl } = await wallet.acceptWithdrawal(
|
||||||
|
uri,
|
||||||
|
selectedExchange,
|
||||||
|
);
|
||||||
|
if (confirmTransferUrl) {
|
||||||
|
console.log("please confirm the transfer at", confirmTransferUrl);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error("unrecognized URI");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const exchangesCli = walletCli.subcommand("exchangesCmd", "exchanges", {
|
||||||
|
help: "Manage exchanges.",
|
||||||
|
});
|
||||||
|
|
||||||
|
exchangesCli
|
||||||
|
.subcommand("exchangesListCmd", "list", {
|
||||||
|
help: "List known exchanges.",
|
||||||
|
})
|
||||||
|
.action(async args => {
|
||||||
|
console.log("Listing exchanges ...");
|
||||||
|
await withWallet(args, async wallet => {
|
||||||
|
const exchanges = await wallet.getExchanges();
|
||||||
|
console.log("exchanges", exchanges);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
exchangesCli
|
||||||
|
.subcommand("exchangesUpdateCmd", "update", {
|
||||||
|
help: "Update or add an exchange by base URL.",
|
||||||
|
})
|
||||||
|
.requiredArgument("url", clk.STRING, {
|
||||||
|
help: "Base URL of the exchange.",
|
||||||
|
})
|
||||||
|
.flag("force", ["-f", "--force"])
|
||||||
|
.action(async args => {
|
||||||
|
await withWallet(args, async wallet => {
|
||||||
|
const res = await wallet.updateExchangeFromUrl(
|
||||||
|
args.exchangesUpdateCmd.url,
|
||||||
|
args.exchangesUpdateCmd.force,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const advancedCli = walletCli.subcommand("advancedArgs", "advanced", {
|
||||||
|
help:
|
||||||
|
"Subcommands for advanced operations (only use if you know what you're doing!).",
|
||||||
|
});
|
||||||
|
|
||||||
|
advancedCli
|
||||||
|
.subcommand("refresh", "force-refresh", {
|
||||||
|
help: "Force a refresh on a coin.",
|
||||||
|
})
|
||||||
|
.requiredArgument("coinPub", clk.STRING)
|
||||||
|
.action(async args => {
|
||||||
|
await withWallet(args, async wallet => {
|
||||||
|
await wallet.refresh(args.refresh.coinPub, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
advancedCli
|
||||||
|
.subcommand("coins", "list-coins", {
|
||||||
|
help: "List coins.",
|
||||||
|
})
|
||||||
|
.action(async args => {
|
||||||
|
await withWallet(args, async wallet => {
|
||||||
|
const coins = await wallet.getCoins();
|
||||||
|
for (const coin of coins) {
|
||||||
|
console.log(`coin ${coin.coinPub}`);
|
||||||
|
console.log(` status ${coin.status}`);
|
||||||
|
console.log(` exchange ${coin.exchangeBaseUrl}`);
|
||||||
|
console.log(` remaining amount ${Amounts.toString(coin.currentAmount)}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const testCli = walletCli.subcommand("testingArgs", "testing", {
|
||||||
|
help: "Subcommands for testing GNU Taler deployments.",
|
||||||
|
});
|
||||||
|
|
||||||
|
testCli
|
||||||
.subcommand("testPayCmd", "test-pay", { help: "create contract and pay" })
|
.subcommand("testPayCmd", "test-pay", { help: "create contract and pay" })
|
||||||
.requiredOption("amount", ["-a", "--amount"], clk.STRING)
|
.requiredOption("amount", ["-a", "--amount"], clk.STRING)
|
||||||
.requiredOption("summary", ["-s", "--summary"], clk.STRING, {
|
.requiredOption("summary", ["-s", "--summary"], clk.STRING, {
|
||||||
@ -146,63 +312,60 @@ walletCli
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log("taler pay URI:", talerPayUri);
|
console.log("taler pay URI:", talerPayUri);
|
||||||
|
await withWallet(args, async (wallet) => {
|
||||||
const wallet = await getDefaultNodeWallet({
|
await doPay(wallet, talerPayUri, { alwaysYes: true });
|
||||||
persistentStoragePath: walletDbPath,
|
|
||||||
});
|
|
||||||
|
|
||||||
await doPay(wallet, talerPayUri, { alwaysYes: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
walletCli
|
|
||||||
.subcommand("", "balance", { help: "Show wallet balance." })
|
|
||||||
.action(async args => {
|
|
||||||
console.log("balance command called");
|
|
||||||
withWallet(args, async (wallet) => {
|
|
||||||
const balance = await wallet.getBalances();
|
|
||||||
console.log(JSON.stringify(balance, undefined, 2));
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
walletCli
|
|
||||||
.subcommand("", "history", { help: "Show wallet event history." })
|
|
||||||
.requiredOption("from", ["--from"], clk.STRING)
|
|
||||||
.requiredOption("to", ["--to"], clk.STRING)
|
|
||||||
.requiredOption("limit", ["--limit"], clk.STRING)
|
|
||||||
.requiredOption("contEvt", ["--continue-with"], clk.STRING)
|
|
||||||
.action(async args => {
|
|
||||||
withWallet(args, async (wallet) => {
|
|
||||||
const history = await wallet.getHistory();
|
|
||||||
console.log(JSON.stringify(history, undefined, 2));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
walletCli
|
testCli
|
||||||
.subcommand("", "pending", { help: "Show pending operations." })
|
.subcommand("integrationtestCmd", "integrationtest", {
|
||||||
.action(async args => {
|
help: "Run integration test with bank, exchange and merchant.",
|
||||||
withWallet(args, async (wallet) => {
|
|
||||||
const pending = await wallet.getPendingOperations();
|
|
||||||
console.log(JSON.stringify(pending, undefined, 2));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
async function asyncSleep(milliSeconds: number): Promise<void> {
|
|
||||||
return new Promise<void>((resolve, reject) => {
|
|
||||||
setTimeout(() => resolve(), milliSeconds);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
walletCli
|
|
||||||
.subcommand("runPendingOpt", "run-pending", {
|
|
||||||
help: "Run pending operations."
|
|
||||||
})
|
})
|
||||||
.action(async (args) => {
|
.requiredOption("exchange", ["-e", "--exchange"], clk.STRING, {
|
||||||
withWallet(args, async (wallet) => {
|
default: "https://exchange.test.taler.net/",
|
||||||
await wallet.processPending();
|
})
|
||||||
});
|
.requiredOption("merchant", ["-m", "--merchant"], clk.STRING, {
|
||||||
|
default: "https://backend.test.taler.net/",
|
||||||
|
})
|
||||||
|
.requiredOption("merchantApiKey", ["-k", "--merchant-api-key"], clk.STRING, {
|
||||||
|
default: "sandbox",
|
||||||
|
})
|
||||||
|
.requiredOption("bank", ["-b", "--bank"], clk.STRING, {
|
||||||
|
default: "https://bank.test.taler.net/",
|
||||||
|
})
|
||||||
|
.requiredOption("withdrawAmount", ["-a", "--amount"], clk.STRING, {
|
||||||
|
default: "TESTKUDOS:10",
|
||||||
|
})
|
||||||
|
.requiredOption("spendAmount", ["-s", "--spend-amount"], clk.STRING, {
|
||||||
|
default: "TESTKUDOS:4",
|
||||||
|
})
|
||||||
|
.action(async args => {
|
||||||
|
console.log("parsed args", args);
|
||||||
|
applyVerbose(args.wallet.verbose);
|
||||||
|
let cmdObj = args.integrationtestCmd;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await runIntegrationTest({
|
||||||
|
amountToSpend: cmdObj.spendAmount,
|
||||||
|
amountToWithdraw: cmdObj.withdrawAmount,
|
||||||
|
bankBaseUrl: cmdObj.bank,
|
||||||
|
exchangeBaseUrl: cmdObj.exchange,
|
||||||
|
merchantApiKey: cmdObj.merchantApiKey,
|
||||||
|
merchantBaseUrl: cmdObj.merchant,
|
||||||
|
}).catch(err => {
|
||||||
|
console.error("Failed with exception:");
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
process.exit(0);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
walletCli
|
testCli
|
||||||
.subcommand("testMerchantQrcodeCmd", "test-merchant-qrcode")
|
.subcommand("testMerchantQrcodeCmd", "test-merchant-qrcode")
|
||||||
.requiredOption("amount", ["-a", "--amount"], clk.STRING, {
|
.requiredOption("amount", ["-a", "--amount"], clk.STRING, {
|
||||||
default: "TESTKUDOS:1",
|
default: "TESTKUDOS:1",
|
||||||
@ -249,174 +412,14 @@ walletCli
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
walletCli
|
|
||||||
.subcommand("integrationtestCmd", "integrationtest", {
|
|
||||||
help: "Run integration test with bank, exchange and merchant.",
|
|
||||||
})
|
|
||||||
.requiredOption("exchange", ["-e", "--exchange"], clk.STRING, {
|
|
||||||
default: "https://exchange.test.taler.net/",
|
|
||||||
})
|
|
||||||
.requiredOption("merchant", ["-m", "--merchant"], clk.STRING, {
|
|
||||||
default: "https://backend.test.taler.net/",
|
|
||||||
})
|
|
||||||
.requiredOption("merchantApiKey", ["-k", "--merchant-api-key"], clk.STRING, {
|
|
||||||
default: "sandbox",
|
|
||||||
})
|
|
||||||
.requiredOption("bank", ["-b", "--bank"], clk.STRING, {
|
|
||||||
default: "https://bank.test.taler.net/",
|
|
||||||
})
|
|
||||||
.requiredOption("withdrawAmount", ["-b", "--bank"], clk.STRING, {
|
|
||||||
default: "TESTKUDOS:10",
|
|
||||||
})
|
|
||||||
.requiredOption("spendAmount", ["-s", "--spend-amount"], clk.STRING, {
|
|
||||||
default: "TESTKUDOS:4",
|
|
||||||
})
|
|
||||||
.action(async args => {
|
|
||||||
applyVerbose(args.wallet.verbose);
|
|
||||||
let cmdObj = args.integrationtestCmd;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await runIntegrationTest({
|
|
||||||
amountToSpend: cmdObj.spendAmount,
|
|
||||||
amountToWithdraw: cmdObj.withdrawAmount,
|
|
||||||
bankBaseUrl: cmdObj.bank,
|
|
||||||
exchangeBaseUrl: cmdObj.exchange,
|
|
||||||
merchantApiKey: cmdObj.merchantApiKey,
|
|
||||||
merchantBaseUrl: cmdObj.merchant,
|
|
||||||
}).catch(err => {
|
|
||||||
console.error("Failed with exception:");
|
|
||||||
console.error(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
process.exit(0);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
walletCli
|
|
||||||
.subcommand("withdrawUriCmd", "withdraw-uri")
|
|
||||||
.requiredArgument("withdrawUri", clk.STRING)
|
|
||||||
.action(async args => {
|
|
||||||
applyVerbose(args.wallet.verbose);
|
|
||||||
const cmdArgs = args.withdrawUriCmd;
|
|
||||||
const withdrawUrl = cmdArgs.withdrawUri;
|
|
||||||
console.log("withdrawing", withdrawUrl);
|
|
||||||
const wallet = await getDefaultNodeWallet({
|
|
||||||
persistentStoragePath: walletDbPath,
|
|
||||||
});
|
|
||||||
|
|
||||||
const withdrawInfo = await wallet.getWithdrawalInfo(withdrawUrl);
|
|
||||||
|
|
||||||
console.log("withdraw info", withdrawInfo);
|
|
||||||
|
|
||||||
const selectedExchange = withdrawInfo.suggestedExchange;
|
|
||||||
if (!selectedExchange) {
|
|
||||||
console.error("no suggested exchange!");
|
|
||||||
process.exit(1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { reservePub, confirmTransferUrl } = await wallet.acceptWithdrawal(
|
|
||||||
withdrawUrl,
|
|
||||||
selectedExchange,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (confirmTransferUrl) {
|
|
||||||
console.log("please confirm the transfer at", confirmTransferUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
await wallet.processReserve(reservePub);
|
|
||||||
|
|
||||||
console.log("finished withdrawing");
|
|
||||||
|
|
||||||
wallet.stop();
|
|
||||||
});
|
|
||||||
|
|
||||||
walletCli
|
|
||||||
.subcommand("tipUriCmd", "tip-uri")
|
|
||||||
.requiredArgument("uri", clk.STRING)
|
|
||||||
.action(async args => {
|
|
||||||
applyVerbose(args.wallet.verbose);
|
|
||||||
const tipUri = args.tipUriCmd.uri;
|
|
||||||
console.log("getting tip", tipUri);
|
|
||||||
const wallet = await getDefaultNodeWallet({
|
|
||||||
persistentStoragePath: walletDbPath,
|
|
||||||
});
|
|
||||||
const res = await wallet.getTipStatus(tipUri);
|
|
||||||
console.log("tip status", res);
|
|
||||||
await wallet.acceptTip(tipUri);
|
|
||||||
wallet.stop();
|
|
||||||
});
|
|
||||||
|
|
||||||
walletCli
|
|
||||||
.subcommand("refundUriCmd", "refund-uri")
|
|
||||||
.requiredArgument("uri", clk.STRING)
|
|
||||||
.action(async args => {
|
|
||||||
applyVerbose(args.wallet.verbose);
|
|
||||||
const refundUri = args.refundUriCmd.uri;
|
|
||||||
console.log("getting refund", refundUri);
|
|
||||||
const wallet = await getDefaultNodeWallet({
|
|
||||||
persistentStoragePath: walletDbPath,
|
|
||||||
});
|
|
||||||
await wallet.applyRefund(refundUri);
|
|
||||||
wallet.stop();
|
|
||||||
});
|
|
||||||
|
|
||||||
const exchangesCli = walletCli.subcommand("exchangesCmd", "exchanges", {
|
|
||||||
help: "Manage exchanges.",
|
|
||||||
});
|
|
||||||
|
|
||||||
exchangesCli
|
|
||||||
.subcommand("exchangesListCmd", "list", {
|
|
||||||
help: "List known exchanges.",
|
|
||||||
})
|
|
||||||
.action(async args => {
|
|
||||||
console.log("Listing exchanges ...");
|
|
||||||
withWallet(args, async (wallet) => {
|
|
||||||
const exchanges = await wallet.getExchanges();
|
|
||||||
console.log("exchanges", exchanges);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
exchangesCli
|
|
||||||
.subcommand("exchangesUpdateCmd", "update", {
|
|
||||||
help: "Update or add an exchange by base URL.",
|
|
||||||
})
|
|
||||||
.requiredArgument("url", clk.STRING, {
|
|
||||||
help: "Base URL of the exchange.",
|
|
||||||
})
|
|
||||||
.action(async args => {
|
|
||||||
withWallet(args, async (wallet) => {
|
|
||||||
const res = await wallet.updateExchangeFromUrl(args.exchangesUpdateCmd.url);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
walletCli
|
|
||||||
.subcommand("payUriCmd", "pay-uri")
|
|
||||||
.requiredArgument("url", clk.STRING)
|
|
||||||
.flag("autoYes", ["-y", "--yes"])
|
|
||||||
.action(async args => {
|
|
||||||
applyVerbose(args.wallet.verbose);
|
|
||||||
const payUrl = args.payUriCmd.url;
|
|
||||||
console.log("paying for", payUrl);
|
|
||||||
const wallet = await getDefaultNodeWallet({
|
|
||||||
persistentStoragePath: walletDbPath,
|
|
||||||
});
|
|
||||||
|
|
||||||
await doPay(wallet, payUrl, { alwaysYes: args.payUriCmd.autoYes });
|
|
||||||
wallet.stop();
|
|
||||||
});
|
|
||||||
|
|
||||||
const testCli = walletCli.subcommand("testingArgs", "testing", {
|
|
||||||
help: "Subcommands for testing GNU Taler deployments.",
|
|
||||||
});
|
|
||||||
|
|
||||||
testCli
|
testCli
|
||||||
.subcommand("withdrawArgs", "withdraw", {
|
.subcommand("withdrawArgs", "withdraw", {
|
||||||
help: "Withdraw from a test bank (must support test registrations).",
|
help: "Withdraw from a test bank (must support test registrations).",
|
||||||
})
|
})
|
||||||
|
.requiredOption("amount", ["-a", "--amount"], clk.STRING, {
|
||||||
|
default: "TESTKUDOS:10",
|
||||||
|
help: "Amount to withdraw.",
|
||||||
|
})
|
||||||
.requiredOption("exchange", ["-e", "--exchange"], clk.STRING, {
|
.requiredOption("exchange", ["-e", "--exchange"], clk.STRING, {
|
||||||
default: "https://exchange.test.taler.net/",
|
default: "https://exchange.test.taler.net/",
|
||||||
help: "Exchange base URL.",
|
help: "Exchange base URL.",
|
||||||
@ -426,14 +429,15 @@ testCli
|
|||||||
help: "Bank base URL",
|
help: "Bank base URL",
|
||||||
})
|
})
|
||||||
.action(async args => {
|
.action(async args => {
|
||||||
applyVerbose(args.wallet.verbose);
|
await withWallet(args, async wallet => {
|
||||||
console.log("balance command called");
|
await withdrawTestBalance(
|
||||||
const wallet = await getDefaultNodeWallet({
|
wallet,
|
||||||
persistentStoragePath: walletDbPath,
|
args.withdrawArgs.amount,
|
||||||
|
args.withdrawArgs.bank,
|
||||||
|
args.withdrawArgs.exchange,
|
||||||
|
);
|
||||||
|
logger.info("Withdraw done");
|
||||||
});
|
});
|
||||||
console.log("got wallet");
|
|
||||||
const balance = await wallet.getBalances();
|
|
||||||
console.log(JSON.stringify(balance, undefined, 2));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
walletCli.run();
|
walletCli.run();
|
||||||
|
@ -107,10 +107,3 @@ export class BrowserHttpLib implements HttpRequestLibrary {
|
|||||||
return this.req("post", url, { req: form });
|
return this.req("post", url, { req: form });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Exception thrown on request errors.
|
|
||||||
*/
|
|
||||||
export class RequestException {
|
|
||||||
constructor(public detail: any) {}
|
|
||||||
}
|
|
||||||
|
78
src/query.ts
78
src/query.ts
@ -1,5 +1,3 @@
|
|||||||
import { openPromise } from "./promiseUtils";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This file is part of TALER
|
This file is part of TALER
|
||||||
(C) 2016 GNUnet e.V.
|
(C) 2016 GNUnet e.V.
|
||||||
@ -22,6 +20,12 @@ import { openPromise } from "./promiseUtils";
|
|||||||
* @author Florian Dold
|
* @author Florian Dold
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports.
|
||||||
|
*/
|
||||||
|
import { openPromise } from "./promiseUtils";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Result of an inner join.
|
* Result of an inner join.
|
||||||
*/
|
*/
|
||||||
@ -63,27 +67,48 @@ export interface IndexOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function requestToPromise(req: IDBRequest): Promise<any> {
|
function requestToPromise(req: IDBRequest): Promise<any> {
|
||||||
|
const stack = Error("Failed request was started here.")
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
req.onsuccess = () => {
|
req.onsuccess = () => {
|
||||||
resolve(req.result);
|
resolve(req.result);
|
||||||
};
|
};
|
||||||
req.onerror = () => {
|
req.onerror = () => {
|
||||||
|
console.log("error in DB request", req.error);
|
||||||
reject(req.error);
|
reject(req.error);
|
||||||
|
console.log("Request failed:", stack);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function oneShotGet<T>(
|
function transactionToPromise(tx: IDBTransaction): Promise<void> {
|
||||||
|
const stack = Error("Failed transaction was started here.");
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
tx.onabort = () => {
|
||||||
|
reject(TransactionAbort);
|
||||||
|
};
|
||||||
|
tx.oncomplete = () => {
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
tx.onerror = () => {
|
||||||
|
console.error("Transaction failed:", stack);
|
||||||
|
reject(tx.error);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function oneShotGet<T>(
|
||||||
db: IDBDatabase,
|
db: IDBDatabase,
|
||||||
store: Store<T>,
|
store: Store<T>,
|
||||||
key: any,
|
key: any,
|
||||||
): Promise<T | undefined> {
|
): Promise<T | undefined> {
|
||||||
const tx = db.transaction([store.name], "readonly");
|
const tx = db.transaction([store.name], "readonly");
|
||||||
const req = tx.objectStore(store.name).get(key);
|
const req = tx.objectStore(store.name).get(key);
|
||||||
return requestToPromise(req);
|
const v = await requestToPromise(req)
|
||||||
|
await transactionToPromise(tx);
|
||||||
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function oneShotGetIndexed<S extends IDBValidKey, T>(
|
export async function oneShotGetIndexed<S extends IDBValidKey, T>(
|
||||||
db: IDBDatabase,
|
db: IDBDatabase,
|
||||||
index: Index<S, T>,
|
index: Index<S, T>,
|
||||||
key: any,
|
key: any,
|
||||||
@ -93,10 +118,12 @@ export function oneShotGetIndexed<S extends IDBValidKey, T>(
|
|||||||
.objectStore(index.storeName)
|
.objectStore(index.storeName)
|
||||||
.index(index.indexName)
|
.index(index.indexName)
|
||||||
.get(key);
|
.get(key);
|
||||||
return requestToPromise(req);
|
const v = await requestToPromise(req);
|
||||||
|
await transactionToPromise(tx);
|
||||||
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function oneShotPut<T>(
|
export async function oneShotPut<T>(
|
||||||
db: IDBDatabase,
|
db: IDBDatabase,
|
||||||
store: Store<T>,
|
store: Store<T>,
|
||||||
value: T,
|
value: T,
|
||||||
@ -104,7 +131,9 @@ export function oneShotPut<T>(
|
|||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
const tx = db.transaction([store.name], "readwrite");
|
const tx = db.transaction([store.name], "readwrite");
|
||||||
const req = tx.objectStore(store.name).put(value, key);
|
const req = tx.objectStore(store.name).put(value, key);
|
||||||
return requestToPromise(req);
|
const v = await requestToPromise(req);
|
||||||
|
await transactionToPromise(tx);
|
||||||
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyMutation<T>(
|
function applyMutation<T>(
|
||||||
@ -115,7 +144,7 @@ function applyMutation<T>(
|
|||||||
req.onsuccess = () => {
|
req.onsuccess = () => {
|
||||||
const cursor = req.result;
|
const cursor = req.result;
|
||||||
if (cursor) {
|
if (cursor) {
|
||||||
const val = cursor.value();
|
const val = cursor.value;
|
||||||
const modVal = f(val);
|
const modVal = f(val);
|
||||||
if (modVal !== undefined && modVal !== null) {
|
if (modVal !== undefined && modVal !== null) {
|
||||||
const req2: IDBRequest = cursor.update(modVal);
|
const req2: IDBRequest = cursor.update(modVal);
|
||||||
@ -138,7 +167,7 @@ function applyMutation<T>(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function oneShotMutate<T>(
|
export async function oneShotMutate<T>(
|
||||||
db: IDBDatabase,
|
db: IDBDatabase,
|
||||||
store: Store<T>,
|
store: Store<T>,
|
||||||
key: any,
|
key: any,
|
||||||
@ -146,7 +175,8 @@ export function oneShotMutate<T>(
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const tx = db.transaction([store.name], "readwrite");
|
const tx = db.transaction([store.name], "readwrite");
|
||||||
const req = tx.objectStore(store.name).openCursor(key);
|
const req = tx.objectStore(store.name).openCursor(key);
|
||||||
return applyMutation(req, f);
|
await applyMutation(req, f);
|
||||||
|
await transactionToPromise(tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
type CursorResult<T> = CursorEmptyResult<T> | CursorValueResult<T>;
|
type CursorResult<T> = CursorEmptyResult<T> | CursorValueResult<T>;
|
||||||
@ -326,15 +356,12 @@ export function runWithWriteTransaction<T>(
|
|||||||
stores: Store<any>[],
|
stores: Store<any>[],
|
||||||
f: (t: TransactionHandle) => Promise<T>,
|
f: (t: TransactionHandle) => Promise<T>,
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
|
const stack = Error("Failed transaction was started here.");
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const storeName = stores.map(x => x.name);
|
const storeName = stores.map(x => x.name);
|
||||||
const tx = db.transaction(storeName, "readwrite");
|
const tx = db.transaction(storeName, "readwrite");
|
||||||
let funResult: any = undefined;
|
let funResult: any = undefined;
|
||||||
let gotFunResult: boolean = false;
|
let gotFunResult: boolean = false;
|
||||||
tx.onerror = () => {
|
|
||||||
console.error("error in transaction:", tx.error);
|
|
||||||
reject(tx.error);
|
|
||||||
};
|
|
||||||
tx.oncomplete = () => {
|
tx.oncomplete = () => {
|
||||||
// This is a fatal error: The transaction completed *before*
|
// This is a fatal error: The transaction completed *before*
|
||||||
// the transaction function returned. Likely, the transaction
|
// the transaction function returned. Likely, the transaction
|
||||||
@ -350,15 +377,30 @@ export function runWithWriteTransaction<T>(
|
|||||||
}
|
}
|
||||||
resolve(funResult);
|
resolve(funResult);
|
||||||
};
|
};
|
||||||
|
tx.onerror = () => {
|
||||||
|
console.error("error in transaction");
|
||||||
|
};
|
||||||
tx.onabort = () => {
|
tx.onabort = () => {
|
||||||
console.error("aborted transaction");
|
if (tx.error) {
|
||||||
reject(AbortTransaction);
|
console.error("Transaction aborted with error:", tx.error);
|
||||||
|
} else {
|
||||||
|
console.log("Trasaction aborted (no error)");
|
||||||
|
}
|
||||||
|
reject(TransactionAbort);
|
||||||
};
|
};
|
||||||
const th = new TransactionHandle(tx);
|
const th = new TransactionHandle(tx);
|
||||||
const resP = f(th);
|
const resP = f(th);
|
||||||
resP.then(result => {
|
resP.then(result => {
|
||||||
gotFunResult = true;
|
gotFunResult = true;
|
||||||
funResult = result;
|
funResult = result;
|
||||||
|
}).catch((e) => {
|
||||||
|
if (e == TransactionAbort) {
|
||||||
|
console.info("aborting transaction");
|
||||||
|
} else {
|
||||||
|
tx.abort();
|
||||||
|
console.error("Transaction failed:", e);
|
||||||
|
console.error(stack);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -401,4 +443,4 @@ export class Index<S extends IDBValidKey, T> {
|
|||||||
/**
|
/**
|
||||||
* Exception that should be thrown by client code to abort a transaction.
|
* Exception that should be thrown by client code to abort a transaction.
|
||||||
*/
|
*/
|
||||||
export const AbortTransaction = Symbol("abort_transaction");
|
export const TransactionAbort = Symbol("transaction_abort");
|
||||||
|
1251
src/wallet.ts
1251
src/wallet.ts
File diff suppressed because it is too large
Load Diff
@ -233,7 +233,7 @@ export interface ConfirmPayResult {
|
|||||||
/**
|
/**
|
||||||
* Activity history record.
|
* Activity history record.
|
||||||
*/
|
*/
|
||||||
export interface HistoryRecord {
|
export interface HistoryEvent {
|
||||||
/**
|
/**
|
||||||
* Type of the history event.
|
* Type of the history event.
|
||||||
*/
|
*/
|
||||||
@ -242,7 +242,7 @@ export interface HistoryRecord {
|
|||||||
/**
|
/**
|
||||||
* Time when the activity was recorded.
|
* Time when the activity was recorded.
|
||||||
*/
|
*/
|
||||||
timestamp: number;
|
timestamp: Timestamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subject of the entry. Used to group multiple history records together.
|
* Subject of the entry. Used to group multiple history records together.
|
||||||
@ -254,6 +254,13 @@ export interface HistoryRecord {
|
|||||||
* Details used when rendering the history record.
|
* Details used when rendering the history record.
|
||||||
*/
|
*/
|
||||||
detail: any;
|
detail: any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to 'true' if the event has been explicitly created,
|
||||||
|
* and set to 'false' if the event has been derived from the
|
||||||
|
* state of the database.
|
||||||
|
*/
|
||||||
|
explicit: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -516,6 +523,8 @@ export interface WalletDiagnostics {
|
|||||||
|
|
||||||
export interface PendingWithdrawOperation {
|
export interface PendingWithdrawOperation {
|
||||||
type: "withdraw";
|
type: "withdraw";
|
||||||
|
stage: string;
|
||||||
|
reservePub: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PendingRefreshOperation {
|
export interface PendingRefreshOperation {
|
||||||
@ -535,6 +544,7 @@ export interface OperationError {
|
|||||||
export interface PendingExchangeUpdateOperation {
|
export interface PendingExchangeUpdateOperation {
|
||||||
type: "exchange-update";
|
type: "exchange-update";
|
||||||
stage: string;
|
stage: string;
|
||||||
|
reason: string;
|
||||||
exchangeBaseUrl: string;
|
exchangeBaseUrl: string;
|
||||||
lastError?: OperationError;
|
lastError?: OperationError;
|
||||||
}
|
}
|
||||||
@ -545,10 +555,28 @@ export interface PendingBugOperation {
|
|||||||
details: any;
|
details: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PendingReserveOperation {
|
||||||
|
type: "reserve";
|
||||||
|
lastError?: OperationError;
|
||||||
|
stage: string;
|
||||||
|
timestampCreated: Timestamp;
|
||||||
|
reserveType: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PendingRefreshOperation {
|
||||||
|
type: "refresh";
|
||||||
|
lastError?: OperationError;
|
||||||
|
oldCoinPub: string;
|
||||||
|
refreshStatus: string;
|
||||||
|
refreshOutputSize: number;
|
||||||
|
}
|
||||||
|
|
||||||
export type PendingOperationInfo =
|
export type PendingOperationInfo =
|
||||||
| PendingWithdrawOperation
|
| PendingWithdrawOperation
|
||||||
|
| PendingReserveOperation
|
||||||
| PendingBugOperation
|
| PendingBugOperation
|
||||||
| PendingExchangeUpdateOperation;
|
| PendingExchangeUpdateOperation
|
||||||
|
| PendingRefreshOperation;
|
||||||
|
|
||||||
export interface PendingOperationsResponse {
|
export interface PendingOperationsResponse {
|
||||||
pendingOperations: PendingOperationInfo[];
|
pendingOperations: PendingOperationInfo[];
|
||||||
|
@ -79,7 +79,7 @@ export interface MessageMap {
|
|||||||
};
|
};
|
||||||
"get-history": {
|
"get-history": {
|
||||||
request: {};
|
request: {};
|
||||||
response: walletTypes.HistoryRecord[];
|
response: walletTypes.HistoryEvent[];
|
||||||
};
|
};
|
||||||
"get-coins": {
|
"get-coins": {
|
||||||
request: { exchangeBaseUrl: string };
|
request: { exchangeBaseUrl: string };
|
||||||
|
@ -57,11 +57,11 @@ function Payback() {
|
|||||||
<div>
|
<div>
|
||||||
{reserves.map(r => (
|
{reserves.map(r => (
|
||||||
<div>
|
<div>
|
||||||
<h2>Reserve for ${renderAmount(r.current_amount!)}</h2>
|
<h2>Reserve for ${renderAmount(r.currentAmount!)}</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Exchange: ${r.exchange_base_url}</li>
|
<li>Exchange: ${r.exchangeBaseUrl}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<button onClick={() => withdrawPaybackReserve(r.reserve_pub)}>
|
<button onClick={() => withdrawPaybackReserve(r.reservePub)}>
|
||||||
Withdraw again
|
Withdraw again
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -30,7 +30,7 @@ import { AmountJson } from "../../amounts";
|
|||||||
import * as Amounts from "../../amounts";
|
import * as Amounts from "../../amounts";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
HistoryRecord,
|
HistoryEvent,
|
||||||
WalletBalance,
|
WalletBalance,
|
||||||
WalletBalanceEntry,
|
WalletBalanceEntry,
|
||||||
} from "../../walletTypes";
|
} from "../../walletTypes";
|
||||||
@ -327,7 +327,7 @@ class WalletBalanceView extends React.Component<any, any> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatHistoryItem(historyItem: HistoryRecord) {
|
function formatHistoryItem(historyItem: HistoryEvent) {
|
||||||
const d = historyItem.detail;
|
const d = historyItem.detail;
|
||||||
console.log("hist item", historyItem);
|
console.log("hist item", historyItem);
|
||||||
switch (historyItem.type) {
|
switch (historyItem.type) {
|
||||||
@ -459,7 +459,7 @@ class WalletHistory extends React.Component<any, any> {
|
|||||||
|
|
||||||
render(): JSX.Element {
|
render(): JSX.Element {
|
||||||
console.log("rendering history");
|
console.log("rendering history");
|
||||||
const history: HistoryRecord[] = this.myHistory;
|
const history: HistoryEvent[] = this.myHistory;
|
||||||
if (this.gotError) {
|
if (this.gotError) {
|
||||||
return i18n.str`Error: could not retrieve event history`;
|
return i18n.str`Error: could not retrieve event history`;
|
||||||
}
|
}
|
||||||
@ -474,7 +474,7 @@ class WalletHistory extends React.Component<any, any> {
|
|||||||
const item = (
|
const item = (
|
||||||
<div className="historyItem">
|
<div className="historyItem">
|
||||||
<div className="historyDate">
|
<div className="historyDate">
|
||||||
{new Date(record.timestamp).toString()}
|
{new Date(record.timestamp.t_ms).toString()}
|
||||||
</div>
|
</div>
|
||||||
{formatHistoryItem(record)}
|
{formatHistoryItem(record)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -215,7 +215,7 @@ function FeeDetailsView(props: {
|
|||||||
<tbody>
|
<tbody>
|
||||||
{rci!.wireFees.feesForType[s].map(f => (
|
{rci!.wireFees.feesForType[s].map(f => (
|
||||||
<tr>
|
<tr>
|
||||||
<td>{moment.unix(f.endStamp).format("llll")}</td>
|
<td>{moment.unix(Math.floor(f.endStamp.t_ms / 1000)).format("llll")}</td>
|
||||||
<td>{renderAmount(f.wireFee)}</td>
|
<td>{renderAmount(f.wireFee)}</td>
|
||||||
<td>{renderAmount(f.closingFee)}</td>
|
<td>{renderAmount(f.closingFee)}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -176,7 +176,7 @@ async function handleMessage(
|
|||||||
if (typeof detail.exchangeBaseUrl !== "string") {
|
if (typeof detail.exchangeBaseUrl !== "string") {
|
||||||
return Promise.reject(Error("exchangBaseUrl missing"));
|
return Promise.reject(Error("exchangBaseUrl missing"));
|
||||||
}
|
}
|
||||||
return needsWallet().getCoins(detail.exchangeBaseUrl);
|
return needsWallet().getCoinsForExchange(detail.exchangeBaseUrl);
|
||||||
}
|
}
|
||||||
case "get-precoins": {
|
case "get-precoins": {
|
||||||
if (typeof detail.exchangeBaseUrl !== "string") {
|
if (typeof detail.exchangeBaseUrl !== "string") {
|
||||||
|
@ -33,6 +33,8 @@
|
|||||||
"src/crypto/cryptoWorker.ts",
|
"src/crypto/cryptoWorker.ts",
|
||||||
"src/crypto/emscInterface-test.ts",
|
"src/crypto/emscInterface-test.ts",
|
||||||
"src/crypto/emscInterface.ts",
|
"src/crypto/emscInterface.ts",
|
||||||
|
"src/crypto/nativeCrypto-test.ts",
|
||||||
|
"src/crypto/nativeCrypto.ts",
|
||||||
"src/crypto/nodeEmscriptenLoader.ts",
|
"src/crypto/nodeEmscriptenLoader.ts",
|
||||||
"src/crypto/nodeProcessWorker.ts",
|
"src/crypto/nodeProcessWorker.ts",
|
||||||
"src/crypto/nodeWorkerEntry.ts",
|
"src/crypto/nodeWorkerEntry.ts",
|
||||||
@ -53,6 +55,7 @@
|
|||||||
"src/index.ts",
|
"src/index.ts",
|
||||||
"src/libtoolVersion-test.ts",
|
"src/libtoolVersion-test.ts",
|
||||||
"src/libtoolVersion.ts",
|
"src/libtoolVersion.ts",
|
||||||
|
"src/logging.ts",
|
||||||
"src/promiseUtils.ts",
|
"src/promiseUtils.ts",
|
||||||
"src/query.ts",
|
"src/query.ts",
|
||||||
"src/talerTypes.ts",
|
"src/talerTypes.ts",
|
||||||
|
@ -3412,10 +3412,10 @@ iconv-lite@0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13:
|
|||||||
dependencies:
|
dependencies:
|
||||||
safer-buffer ">= 2.1.2 < 3"
|
safer-buffer ">= 2.1.2 < 3"
|
||||||
|
|
||||||
idb-bridge@^0.0.11:
|
idb-bridge@^0.0.14:
|
||||||
version "0.0.11"
|
version "0.0.14"
|
||||||
resolved "https://registry.yarnpkg.com/idb-bridge/-/idb-bridge-0.0.11.tgz#ba2fbd24b7e6f7f4de8333ed12b0912e64dda308"
|
resolved "https://registry.yarnpkg.com/idb-bridge/-/idb-bridge-0.0.14.tgz#5fd50cd68b574df0eb6b1a960cef0cb984a21ded"
|
||||||
integrity sha512-fLlHce/WwT6eD3sc54gsfvM5fZqrhAPwBNH4uU/y6D0C1+0higH7OgC5/wploMhkmNYkQID3BMNZvSUBr0leSQ==
|
integrity sha512-jc9ZYGhhIrW6nh/pWyycGWzCmsLTFQ0iMY61lN+y9YcIOCxREpAkZxdfmhwNL7H0RvsYp7iJv0GH7ujs7HPC+g==
|
||||||
|
|
||||||
ieee754@^1.1.4:
|
ieee754@^1.1.4:
|
||||||
version "1.1.13"
|
version "1.1.13"
|
||||||
|
Loading…
Reference in New Issue
Block a user