respond to failed payments
This commit is contained in:
parent
ce7f7b8321
commit
27a42f9257
@ -165,9 +165,22 @@ namespace TalerNotify {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addHandler("taler-payment-failed", (e: CustomEvent) => {
|
||||||
|
const msg = {
|
||||||
|
type: "payment-failed",
|
||||||
|
detail: {},
|
||||||
|
};
|
||||||
|
chrome.runtime.sendMessage(msg, (resp) => {
|
||||||
|
let evt = new CustomEvent("taler-payment-failed-ok", {
|
||||||
|
detail: {}
|
||||||
|
});
|
||||||
|
document.dispatchEvent(evt);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Should be: taler-request-payment, taler-result-payment
|
// Should be: taler-request-payment, taler-result-payment
|
||||||
|
|
||||||
addHandler("taler-execute-contract", function(e: CustomEvent) {
|
addHandler("taler-execute-contract", (e: CustomEvent) => {
|
||||||
console.log("got taler-execute-contract in content page");
|
console.log("got taler-execute-contract in content page");
|
||||||
const msg = {
|
const msg = {
|
||||||
type: "execute-payment",
|
type: "execute-payment",
|
||||||
@ -195,6 +208,9 @@ namespace TalerNotify {
|
|||||||
throw Error("contract missing");
|
throw Error("contract missing");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We have the details for then payment, the merchant page
|
||||||
|
// is responsible to give it to the merchant.
|
||||||
|
|
||||||
let evt = new CustomEvent("taler-notify-payment", {
|
let evt = new CustomEvent("taler-notify-payment", {
|
||||||
detail: {
|
detail: {
|
||||||
H_contract: e.detail.H_contract,
|
H_contract: e.detail.H_contract,
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const DB_NAME = "taler";
|
const DB_NAME = "taler";
|
||||||
const DB_VERSION = 5;
|
const DB_VERSION = 6;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a promise that resolves
|
* Return a promise that resolves
|
||||||
@ -41,7 +41,7 @@ export function openTalerDb(): Promise<IDBDatabase> {
|
|||||||
resolve(req.result);
|
resolve(req.result);
|
||||||
};
|
};
|
||||||
req.onupgradeneeded = (e) => {
|
req.onupgradeneeded = (e) => {
|
||||||
let db = req.result;
|
const db = req.result;
|
||||||
console.log("DB: upgrade needed: oldVersion = " + e.oldVersion);
|
console.log("DB: upgrade needed: oldVersion = " + e.oldVersion);
|
||||||
switch (e.oldVersion) {
|
switch (e.oldVersion) {
|
||||||
case 0: // DB does not exist yet
|
case 0: // DB does not exist yet
|
||||||
@ -49,7 +49,6 @@ export function openTalerDb(): Promise<IDBDatabase> {
|
|||||||
{keyPath: "baseUrl"});
|
{keyPath: "baseUrl"});
|
||||||
exchanges.createIndex("pubKey", "masterPublicKey");
|
exchanges.createIndex("pubKey", "masterPublicKey");
|
||||||
db.createObjectStore("reserves", {keyPath: "reserve_pub"});
|
db.createObjectStore("reserves", {keyPath: "reserve_pub"});
|
||||||
db.createObjectStore("denoms", {keyPath: "denomPub"});
|
|
||||||
const coins = db.createObjectStore("coins", {keyPath: "coinPub"});
|
const coins = db.createObjectStore("coins", {keyPath: "coinPub"});
|
||||||
coins.createIndex("exchangeBaseUrl", "exchangeBaseUrl");
|
coins.createIndex("exchangeBaseUrl", "exchangeBaseUrl");
|
||||||
const transactions = db.createObjectStore("transactions",
|
const transactions = db.createObjectStore("transactions",
|
||||||
|
@ -93,9 +93,6 @@ export class Denomination {
|
|||||||
@Checkable.String
|
@Checkable.String
|
||||||
master_sig: string;
|
master_sig: string;
|
||||||
|
|
||||||
@Checkable.Optional(Checkable.String)
|
|
||||||
pub_hash: string;
|
|
||||||
|
|
||||||
static checked: (obj: any) => Denomination;
|
static checked: (obj: any) => Denomination;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +100,23 @@ export class Denomination {
|
|||||||
export interface IExchangeInfo {
|
export interface IExchangeInfo {
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
masterPublicKey: string;
|
masterPublicKey: string;
|
||||||
denoms: Denomination[];
|
|
||||||
|
/**
|
||||||
|
* All denominations we ever received from the exchange.
|
||||||
|
* Expired denominations may be garbage collected.
|
||||||
|
*/
|
||||||
|
all_denoms: Denomination[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Denominations we received with the last update.
|
||||||
|
* Subset of "denoms".
|
||||||
|
*/
|
||||||
|
active_denoms: Denomination[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timestamp for last update.
|
||||||
|
*/
|
||||||
|
last_update_time: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WireInfo {
|
export interface WireInfo {
|
||||||
@ -151,13 +164,48 @@ export interface CoinPaySig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Coin as stored in the "coins" data store
|
||||||
|
* of the wallet database.
|
||||||
|
*/
|
||||||
export interface Coin {
|
export interface Coin {
|
||||||
|
/**
|
||||||
|
* Public key of the coin.
|
||||||
|
*/
|
||||||
coinPub: string;
|
coinPub: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private key to authorize operations on the coin.
|
||||||
|
*/
|
||||||
coinPriv: string;
|
coinPriv: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key used by the exchange used to sign the coin.
|
||||||
|
*/
|
||||||
denomPub: string;
|
denomPub: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unblinded signature by the exchange.
|
||||||
|
*/
|
||||||
denomSig: string;
|
denomSig: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amount that's left on the coin.
|
||||||
|
*/
|
||||||
currentAmount: AmountJson;
|
currentAmount: AmountJson;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base URL that identifies the exchange from which we got the
|
||||||
|
* coin.
|
||||||
|
*/
|
||||||
exchangeBaseUrl: string;
|
exchangeBaseUrl: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We have withdrawn the coin, but it's not accepted by the exchange anymore.
|
||||||
|
* We have to tell an auditor and wait for compensation or for the exchange
|
||||||
|
* to fix it.
|
||||||
|
*/
|
||||||
|
suspended?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -79,91 +79,6 @@ export class KeysJson {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ExchangeInfo implements IExchangeInfo {
|
|
||||||
baseUrl: string;
|
|
||||||
masterPublicKey: string;
|
|
||||||
denoms: Denomination[];
|
|
||||||
|
|
||||||
constructor(obj: {baseUrl: string} & any) {
|
|
||||||
this.baseUrl = obj.baseUrl;
|
|
||||||
|
|
||||||
if (obj.denoms) {
|
|
||||||
this.denoms = Array.from(<Denomination[]>obj.denoms);
|
|
||||||
} else {
|
|
||||||
this.denoms = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof obj.masterPublicKey === "string") {
|
|
||||||
this.masterPublicKey = obj.masterPublicKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static fresh(baseUrl: string): ExchangeInfo {
|
|
||||||
return new ExchangeInfo({baseUrl});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Merge new key information into the exchange info.
|
|
||||||
* If the new key information is invalid (missing fields,
|
|
||||||
* invalid signatures), an exception is thrown, but the
|
|
||||||
* exchange info is updated with the new information up until
|
|
||||||
* the first error.
|
|
||||||
*/
|
|
||||||
mergeKeys(newKeys: KeysJson, cryptoApi: CryptoApi): Promise<void> {
|
|
||||||
if (!this.masterPublicKey) {
|
|
||||||
this.masterPublicKey = newKeys.master_public_key;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.masterPublicKey != newKeys.master_public_key) {
|
|
||||||
throw Error("public keys do not match");
|
|
||||||
}
|
|
||||||
|
|
||||||
let ps = newKeys.denoms.map((newDenom) => {
|
|
||||||
let found = false;
|
|
||||||
for (let oldDenom of this.denoms) {
|
|
||||||
if (oldDenom.denom_pub === newDenom.denom_pub) {
|
|
||||||
let a = Object.assign({}, oldDenom);
|
|
||||||
let b = Object.assign({}, newDenom);
|
|
||||||
// pub hash is only there for convenience in the wallet
|
|
||||||
delete a["pub_hash"];
|
|
||||||
delete b["pub_hash"];
|
|
||||||
if (!deepEquals(a, b)) {
|
|
||||||
console.log("old/new:");
|
|
||||||
console.dir(a);
|
|
||||||
console.dir(b);
|
|
||||||
throw Error("denomination modified");
|
|
||||||
}
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
return cryptoApi
|
|
||||||
.isValidDenom(newDenom, this.masterPublicKey)
|
|
||||||
.then((valid) => {
|
|
||||||
if (!valid) {
|
|
||||||
console.error("invalid denomination",
|
|
||||||
newDenom,
|
|
||||||
"with key",
|
|
||||||
this.masterPublicKey);
|
|
||||||
throw Error("signature on denomination invalid");
|
|
||||||
}
|
|
||||||
return cryptoApi.hashRsaPub(newDenom.denom_pub);
|
|
||||||
})
|
|
||||||
.then((h) => {
|
|
||||||
this.denoms.push(Object.assign({}, newDenom, {pub_hash: h}));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return Promise.all(ps).then(() => void 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Checkable.Class
|
@Checkable.Class
|
||||||
export class CreateReserveRequest {
|
export class CreateReserveRequest {
|
||||||
/**
|
/**
|
||||||
@ -264,7 +179,7 @@ function flatMap<T, U>(xs: T[], f: (x: T) => U[]): U[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getTalerStampSec(stamp: string) {
|
function getTalerStampSec(stamp: string): number {
|
||||||
const m = stamp.match(/\/?Date\(([0-9]*)\)\/?/);
|
const m = stamp.match(/\/?Date\(([0-9]*)\)\/?/);
|
||||||
if (!m) {
|
if (!m) {
|
||||||
return null;
|
return null;
|
||||||
@ -306,6 +221,16 @@ function copy(o) {
|
|||||||
return JSON.parse(JSON.stringify(o));
|
return JSON.parse(JSON.stringify(o));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of updating exisiting information
|
||||||
|
* about an exchange with a new '/keys' response.
|
||||||
|
*/
|
||||||
|
interface KeyUpdateInfo {
|
||||||
|
updatedExchangeInfo: IExchangeInfo;
|
||||||
|
addedDenominations: Denomination[];
|
||||||
|
removedDenominations: Denomination[];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of denominations (with repetitions possible)
|
* Get a list of denominations (with repetitions possible)
|
||||||
@ -381,6 +306,19 @@ export class Wallet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateExchanges(): void {
|
||||||
|
console.log("updating exchanges");
|
||||||
|
|
||||||
|
Query(this.db)
|
||||||
|
.iter("exchanges")
|
||||||
|
.reduce((exchange: IExchangeInfo) => {
|
||||||
|
this.updateExchangeFromUrl(exchange.baseUrl)
|
||||||
|
.catch((e) => {
|
||||||
|
console.error("updating exchange failed", e);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resume various pending operations that are pending
|
* Resume various pending operations that are pending
|
||||||
* by looking at the database.
|
* by looking at the database.
|
||||||
@ -421,12 +359,20 @@ export class Wallet {
|
|||||||
let exchange: IExchangeInfo = mc[0];
|
let exchange: IExchangeInfo = mc[0];
|
||||||
console.log("got coin for exchange", url);
|
console.log("got coin for exchange", url);
|
||||||
let coin: Coin = mc[1];
|
let coin: Coin = mc[1];
|
||||||
|
if (coin.suspended) {
|
||||||
|
console.log("skipping suspended coin",
|
||||||
|
coin.denomPub,
|
||||||
|
"from exchange",
|
||||||
|
exchange.baseUrl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
let cd = {
|
let cd = {
|
||||||
coin: coin,
|
coin: coin,
|
||||||
denom: exchange.denoms.find((e) => e.denom_pub === coin.denomPub)
|
denom: exchange.active_denoms.find((e) => e.denom_pub === coin.denomPub)
|
||||||
};
|
};
|
||||||
if (!cd.denom) {
|
if (!cd.denom) {
|
||||||
throw Error("denom not found (database inconsistent)");
|
console.warn("denom not found (database inconsistent)");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (cd.denom.value.currency !== paymentAmount.currency) {
|
if (cd.denom.value.currency !== paymentAmount.currency) {
|
||||||
console.warn("same pubkey for different currencies");
|
console.warn("same pubkey for different currencies");
|
||||||
@ -588,7 +534,9 @@ export class Wallet {
|
|||||||
let exchangeUrl = Object.keys(mcs)[0];
|
let exchangeUrl = Object.keys(mcs)[0];
|
||||||
|
|
||||||
return this.cryptoApi.signDeposit(offer, mcs[exchangeUrl])
|
return this.cryptoApi.signDeposit(offer, mcs[exchangeUrl])
|
||||||
.then((ds) => this.recordConfirmPay(offer, ds, exchangeUrl))
|
.then((ds) => this.recordConfirmPay(offer,
|
||||||
|
ds,
|
||||||
|
exchangeUrl))
|
||||||
.then(() => ({}));
|
.then(() => ({}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -856,8 +804,8 @@ export class Wallet {
|
|||||||
/**
|
/**
|
||||||
* Withdraw coins from a reserve until it is empty.
|
* Withdraw coins from a reserve until it is empty.
|
||||||
*/
|
*/
|
||||||
private depleteReserve(reserve, exchange: ExchangeInfo): Promise<void> {
|
private depleteReserve(reserve, exchange: IExchangeInfo): Promise<void> {
|
||||||
let denomsAvailable: Denomination[] = copy(exchange.denoms);
|
let denomsAvailable: Denomination[] = copy(exchange.active_denoms);
|
||||||
let denomsForWithdraw = getWithdrawDenomList(reserve.current_amount,
|
let denomsForWithdraw = getWithdrawDenomList(reserve.current_amount,
|
||||||
denomsAvailable);
|
denomsAvailable);
|
||||||
|
|
||||||
@ -877,7 +825,7 @@ export class Wallet {
|
|||||||
* by quering the reserve's exchange.
|
* by quering the reserve's exchange.
|
||||||
*/
|
*/
|
||||||
private updateReserve(reservePub: string,
|
private updateReserve(reservePub: string,
|
||||||
exchange: ExchangeInfo): Promise<Reserve> {
|
exchange: IExchangeInfo): Promise<Reserve> {
|
||||||
return Query(this.db)
|
return Query(this.db)
|
||||||
.get("reserves", reservePub)
|
.get("reserves", reservePub)
|
||||||
.then((reserve) => {
|
.then((reserve) => {
|
||||||
@ -935,7 +883,8 @@ export class Wallet {
|
|||||||
amount: AmountJson): Promise<ReserveCreationInfo> {
|
amount: AmountJson): Promise<ReserveCreationInfo> {
|
||||||
let p = this.updateExchangeFromUrl(baseUrl);
|
let p = this.updateExchangeFromUrl(baseUrl);
|
||||||
return p.then((exchangeInfo: IExchangeInfo) => {
|
return p.then((exchangeInfo: IExchangeInfo) => {
|
||||||
let selectedDenoms = getWithdrawDenomList(amount, exchangeInfo.denoms);
|
let selectedDenoms = getWithdrawDenomList(amount,
|
||||||
|
exchangeInfo.active_denoms);
|
||||||
let acc = Amounts.getZero(amount.currency);
|
let acc = Amounts.getZero(amount.currency);
|
||||||
for (let d of selectedDenoms) {
|
for (let d of selectedDenoms) {
|
||||||
acc = Amounts.add(acc, d.fee_withdraw).amount;
|
acc = Amounts.add(acc, d.fee_withdraw).amount;
|
||||||
@ -964,7 +913,7 @@ export class Wallet {
|
|||||||
* Optionally link the reserve entry to the new or existing
|
* Optionally link the reserve entry to the new or existing
|
||||||
* exchange entry in then DB.
|
* exchange entry in then DB.
|
||||||
*/
|
*/
|
||||||
updateExchangeFromUrl(baseUrl): Promise<ExchangeInfo> {
|
updateExchangeFromUrl(baseUrl): Promise<IExchangeInfo> {
|
||||||
baseUrl = canonicalizeBaseUrl(baseUrl);
|
baseUrl = canonicalizeBaseUrl(baseUrl);
|
||||||
let reqUrl = URI("keys").absoluteTo(baseUrl);
|
let reqUrl = URI("keys").absoluteTo(baseUrl);
|
||||||
return this.http.get(reqUrl).then((resp) => {
|
return this.http.get(reqUrl).then((resp) => {
|
||||||
@ -972,38 +921,138 @@ export class Wallet {
|
|||||||
throw Error("/keys request failed");
|
throw Error("/keys request failed");
|
||||||
}
|
}
|
||||||
let exchangeKeysJson = KeysJson.checked(JSON.parse(resp.responseText));
|
let exchangeKeysJson = KeysJson.checked(JSON.parse(resp.responseText));
|
||||||
|
return this.updateExchangeFromJson(baseUrl, exchangeKeysJson);
|
||||||
return Query(this.db).get("exchanges", baseUrl).then((r) => {
|
|
||||||
let exchangeInfo;
|
|
||||||
console.dir(r);
|
|
||||||
|
|
||||||
if (!r) {
|
|
||||||
exchangeInfo = ExchangeInfo.fresh(baseUrl);
|
|
||||||
console.log("making fresh exchange");
|
|
||||||
} else {
|
|
||||||
exchangeInfo = new ExchangeInfo(r);
|
|
||||||
console.log("using old exchange");
|
|
||||||
}
|
|
||||||
|
|
||||||
return exchangeInfo.mergeKeys(exchangeKeysJson, this.cryptoApi)
|
|
||||||
.then(() => {
|
|
||||||
return Query(this.db)
|
|
||||||
.put("exchanges", exchangeInfo)
|
|
||||||
.finish()
|
|
||||||
.then(() => exchangeInfo);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private updateExchangeFromJson(baseUrl: string,
|
||||||
|
exchangeKeysJson: KeysJson): Promise<IExchangeInfo> {
|
||||||
|
let updateTimeSec = getTalerStampSec(exchangeKeysJson.list_issue_date);
|
||||||
|
if (!updateTimeSec) {
|
||||||
|
throw Error("invalid update time");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Query(this.db).get("exchanges", baseUrl).then((r) => {
|
||||||
|
let exchangeInfo: IExchangeInfo;
|
||||||
|
console.dir(r);
|
||||||
|
|
||||||
|
if (!r) {
|
||||||
|
exchangeInfo = {
|
||||||
|
baseUrl,
|
||||||
|
all_denoms: [],
|
||||||
|
active_denoms: [],
|
||||||
|
last_update_time: updateTimeSec,
|
||||||
|
masterPublicKey: exchangeKeysJson.master_public_key,
|
||||||
|
};
|
||||||
|
console.log("making fresh exchange");
|
||||||
|
} else {
|
||||||
|
if (updateTimeSec < r.last_update_time) {
|
||||||
|
console.log("outdated /keys, not updating")
|
||||||
|
return Promise.resolve(r);
|
||||||
|
}
|
||||||
|
exchangeInfo = r;
|
||||||
|
console.log("updating old exchange");
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.updateExchangeInfo(exchangeInfo, exchangeKeysJson)
|
||||||
|
.then((updatedExchangeInfo: IExchangeInfo) => {
|
||||||
|
let q1 = Query(this.db)
|
||||||
|
.put("exchanges", updatedExchangeInfo)
|
||||||
|
.finish()
|
||||||
|
.then(() => updatedExchangeInfo);
|
||||||
|
|
||||||
|
let q2 = Query(this.db)
|
||||||
|
.iter("coins",
|
||||||
|
{indexName: "exchangeBaseUrl", only: baseUrl})
|
||||||
|
.reduce((coin: Coin, suspendedCoins: Coin[]) => {
|
||||||
|
if (!updatedExchangeInfo.active_denoms.find((c) => c.denom_pub == coin.denomPub)) {
|
||||||
|
return [].concat(suspendedCoins, [coin]);
|
||||||
|
}
|
||||||
|
return [].concat(suspendedCoins);
|
||||||
|
}, [])
|
||||||
|
.then((suspendedCoins: Coin[]) => {
|
||||||
|
let q = Query(this.db);
|
||||||
|
suspendedCoins.map((c) => {
|
||||||
|
console.log("suspending coin", c);
|
||||||
|
c.suspended = true;
|
||||||
|
q.put("coins", c);
|
||||||
|
});
|
||||||
|
return q.finish();
|
||||||
|
});
|
||||||
|
return Promise.all([q1, q2]).then(() => updatedExchangeInfo);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private updateExchangeInfo(exchangeInfo: IExchangeInfo,
|
||||||
|
newKeys: KeysJson): Promise<IExchangeInfo> {
|
||||||
|
|
||||||
|
if (exchangeInfo.masterPublicKey != newKeys.master_public_key) {
|
||||||
|
throw Error("public keys do not match");
|
||||||
|
}
|
||||||
|
|
||||||
|
exchangeInfo.active_denoms = [];
|
||||||
|
|
||||||
|
let ps = newKeys.denoms.map((newDenom) => {
|
||||||
|
// did we find the new denom in the list of all (old) denoms?
|
||||||
|
let found = false;
|
||||||
|
for (let oldDenom of exchangeInfo.all_denoms) {
|
||||||
|
if (oldDenom.denom_pub === newDenom.denom_pub) {
|
||||||
|
let a = Object.assign({}, oldDenom);
|
||||||
|
let b = Object.assign({}, newDenom);
|
||||||
|
// pub hash is only there for convenience in the wallet
|
||||||
|
delete a["pub_hash"];
|
||||||
|
delete b["pub_hash"];
|
||||||
|
if (!deepEquals(a, b)) {
|
||||||
|
console.error("denomination parameters were modified, old/new:");
|
||||||
|
console.dir(a);
|
||||||
|
console.dir(b);
|
||||||
|
// FIXME: report to auditors
|
||||||
|
}
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
exchangeInfo.active_denoms.push(newDenom);
|
||||||
|
// No need to check signatures
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.cryptoApi
|
||||||
|
.isValidDenom(newDenom, exchangeInfo.masterPublicKey)
|
||||||
|
.then((valid) => {
|
||||||
|
if (!valid) {
|
||||||
|
console.error("invalid denomination",
|
||||||
|
newDenom,
|
||||||
|
"with key",
|
||||||
|
exchangeInfo.masterPublicKey);
|
||||||
|
// FIXME: report to auditors
|
||||||
|
}
|
||||||
|
return this.cryptoApi.hashRsaPub(newDenom.denom_pub);
|
||||||
|
})
|
||||||
|
.then((h) => {
|
||||||
|
exchangeInfo.active_denoms.push(newDenom);
|
||||||
|
exchangeInfo.all_denoms.push(newDenom);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all(ps).then(() => exchangeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a mapping from currency name to the amount
|
* Retrieve a mapping from currency name to the amount
|
||||||
* that is currenctly available for spending in the wallet.
|
* that is currenctly available for spending in the wallet.
|
||||||
*/
|
*/
|
||||||
getBalances(): Promise<any> {
|
getBalances(): Promise<any> {
|
||||||
function collectBalances(c: Coin, byCurrency) {
|
function collectBalances(c: Coin, byCurrency) {
|
||||||
|
if (c.suspended) {
|
||||||
|
return byCurrency;
|
||||||
|
}
|
||||||
let acc: AmountJson = byCurrency[c.currentAmount.currency];
|
let acc: AmountJson = byCurrency[c.currentAmount.currency];
|
||||||
if (!acc) {
|
if (!acc) {
|
||||||
acc = Amounts.getZero(c.currentAmount.currency);
|
acc = Amounts.getZero(c.currentAmount.currency);
|
||||||
|
@ -141,6 +141,13 @@ function makeHandlers(db: IDBDatabase,
|
|||||||
// TODO: limit history length
|
// TODO: limit history length
|
||||||
return wallet.getHistory();
|
return wallet.getHistory();
|
||||||
},
|
},
|
||||||
|
["payment-failed"]: function(detail, sender) {
|
||||||
|
// For now we just update exchanges (maybe the exchange did something
|
||||||
|
// wrong and the keys were messed up).
|
||||||
|
// FIXME: in the future we should look at what actually went wrong.
|
||||||
|
wallet.updateExchanges();
|
||||||
|
return Promise.resolve();
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,6 +162,7 @@ class ChromeBadge implements Badge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
startBusy() {
|
startBusy() {
|
||||||
|
this.setColor("#00F");
|
||||||
this.setText("...");
|
this.setText("...");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user