automatic refresh
This commit is contained in:
parent
6262af4ad7
commit
8c0c4b5331
@ -282,7 +282,9 @@ namespace TalerNotify {
|
||||
addHandler("taler-payment-failed", (msg: any, sendResponse: any) => {
|
||||
const walletMsg = {
|
||||
type: "payment-failed",
|
||||
detail: {},
|
||||
detail: {
|
||||
contractHash: msg.H_contract
|
||||
},
|
||||
};
|
||||
chrome.runtime.sendMessage(walletMsg, (resp) => {
|
||||
sendResponse();
|
||||
@ -290,8 +292,20 @@ namespace TalerNotify {
|
||||
});
|
||||
|
||||
addHandler("taler-payment-succeeded", (msg: any, sendResponse: any) => {
|
||||
if (!msg.H_contract) {
|
||||
console.error("H_contract missing in taler-payment-succeeded");
|
||||
return;
|
||||
}
|
||||
console.log("got taler-payment-succeeded");
|
||||
sendResponse();
|
||||
const walletMsg = {
|
||||
type: "payment-succeeded",
|
||||
detail: {
|
||||
contractHash: msg.H_contract,
|
||||
},
|
||||
};
|
||||
chrome.runtime.sendMessage(walletMsg, (resp) => {
|
||||
sendResponse();
|
||||
})
|
||||
});
|
||||
|
||||
addHandler("taler-get-payment", (msg: any, sendResponse: any) => {
|
||||
|
23
gulpfile.js
23
gulpfile.js
@ -58,6 +58,9 @@ const paths = {
|
||||
"pages/*.{ts,tsx}",
|
||||
"!**/*.d.ts",
|
||||
],
|
||||
decl: [
|
||||
"lib/refs.d.ts",
|
||||
],
|
||||
dev: [
|
||||
"test/tests/*.{ts,tsx}",
|
||||
],
|
||||
@ -73,10 +76,10 @@ const paths = {
|
||||
"lib/module-trampoline.js",
|
||||
"popup/**/*.{html,css}",
|
||||
"pages/**/*.{html,css}",
|
||||
"lib/**/*.d.ts",
|
||||
"background/*.html",
|
||||
],
|
||||
extra: [
|
||||
"lib/**/*.d.ts",
|
||||
"AUTHORS",
|
||||
"README",
|
||||
"COPYING",
|
||||
@ -220,7 +223,12 @@ gulp.task("compile-prod", ["clean"], function () {
|
||||
tsArgs.outDir = ".";
|
||||
// We don't want source maps for production
|
||||
tsArgs.sourceMap = undefined;
|
||||
return gulp.src(paths.ts.release, {base: "."})
|
||||
let opts = {base: "."};
|
||||
const files = concatStreams(
|
||||
gulp.src(paths.ts.release, opts),
|
||||
gulp.src(paths.ts.decl, opts));
|
||||
|
||||
return files
|
||||
.pipe(ts(tsArgs))
|
||||
.pipe(gulp.dest("build/ext/"));
|
||||
});
|
||||
@ -274,6 +282,7 @@ gulp.task("srcdist", [], function () {
|
||||
// We can't just concat patterns due to exclude patterns
|
||||
const files = concatStreams(
|
||||
gulp.src(paths.ts.release, opts),
|
||||
gulp.src(paths.ts.decl, opts),
|
||||
gulp.src(paths.ts.dev, opts),
|
||||
gulp.src(paths.dist, opts),
|
||||
gulp.src(paths.extra, opts));
|
||||
@ -346,9 +355,13 @@ function tsconfig(confBase) {
|
||||
// Generate the tsconfig file
|
||||
// that should be used during development.
|
||||
gulp.task("tsconfig", function() {
|
||||
return gulp.src(Array.prototype.concat(paths.ts.release, paths.ts.dev), {base: "."})
|
||||
.pipe(tsconfig(tsBaseArgs))
|
||||
.pipe(gulp.dest("."));
|
||||
let opts = {base: "."};
|
||||
const files = concatStreams(
|
||||
gulp.src(paths.ts.release, opts),
|
||||
gulp.src(paths.ts.dev, opts),
|
||||
gulp.src(paths.ts.decl, opts));
|
||||
return files.pipe(tsconfig(tsBaseArgs))
|
||||
.pipe(gulp.dest("."));
|
||||
});
|
||||
|
||||
|
||||
|
@ -1,6 +0,0 @@
|
||||
|
||||
// Help the TypeScript compiler find declarations.
|
||||
|
||||
/// <reference path="decl/lib.es6.d.ts" />
|
||||
/// <reference path="decl/urijs/URIjs.d.ts" />
|
||||
/// <reference path="decl/systemjs/systemjs.d.ts" />
|
@ -203,6 +203,8 @@ namespace RpcFunctions {
|
||||
let newAmount = new native.Amount(cd.coin.currentAmount);
|
||||
newAmount.sub(coinSpend);
|
||||
cd.coin.currentAmount = newAmount.toJson();
|
||||
cd.coin.dirty = true;
|
||||
cd.coin.transactionPending = true;
|
||||
|
||||
let d = new native.DepositRequestPS({
|
||||
h_contract: native.HashCode.fromCrock(offer.H_contract),
|
||||
@ -338,4 +340,8 @@ namespace RpcFunctions {
|
||||
return refreshSession;
|
||||
}
|
||||
|
||||
export function hashString(str: string): string {
|
||||
const b = native.ByteArray.fromStringWithNull(str);
|
||||
return b.hash().toCrock();
|
||||
}
|
||||
}
|
@ -25,7 +25,7 @@
|
||||
*/
|
||||
|
||||
const DB_NAME = "taler";
|
||||
const DB_VERSION = 8;
|
||||
const DB_VERSION = 10;
|
||||
|
||||
/**
|
||||
* Return a promise that resolves
|
||||
@ -59,14 +59,15 @@ export function openTalerDb(): Promise<IDBDatabase> {
|
||||
"contract.repurchase_correlation_id"
|
||||
]);
|
||||
|
||||
db.createObjectStore("precoins",
|
||||
{keyPath: "coinPub", autoIncrement: true});
|
||||
db.createObjectStore("precoins", {keyPath: "coinPub"});
|
||||
const history = db.createObjectStore("history",
|
||||
{
|
||||
keyPath: "id",
|
||||
autoIncrement: true
|
||||
});
|
||||
history.createIndex("timestamp", "timestamp");
|
||||
db.createObjectStore("refresh",
|
||||
{keyPath: "meltCoinPub"});
|
||||
break;
|
||||
default:
|
||||
if (e.oldVersion != DB_VERSION) {
|
||||
|
@ -279,6 +279,19 @@ export interface Coin {
|
||||
* to fix it.
|
||||
*/
|
||||
suspended?: boolean;
|
||||
|
||||
/**
|
||||
* Was the coin revealed in a transaction?
|
||||
*/
|
||||
dirty: boolean;
|
||||
|
||||
/**
|
||||
* Is the coin currently involved in a transaction?
|
||||
*
|
||||
* This delays refreshing until the transaction is finished or
|
||||
* aborted.
|
||||
*/
|
||||
transactionPending: boolean;
|
||||
}
|
||||
|
||||
|
||||
|
@ -27,7 +27,7 @@ import {
|
||||
IExchangeInfo,
|
||||
Denomination,
|
||||
Notifier,
|
||||
WireInfo, RefreshSession, ReserveRecord
|
||||
WireInfo, RefreshSession, ReserveRecord, CoinPaySig
|
||||
} from "./types";
|
||||
import {HttpResponse, RequestException} from "./http";
|
||||
import {QueryRoot} from "./query";
|
||||
@ -135,11 +135,22 @@ interface ExchangeCoins {
|
||||
[exchangeUrl: string]: CoinWithDenom[];
|
||||
}
|
||||
|
||||
interface PayReq {
|
||||
amount: AmountJson;
|
||||
coins: CoinPaySig[];
|
||||
H_contract: string;
|
||||
max_fee: AmountJson;
|
||||
merchant_sig: string;
|
||||
exchange: string;
|
||||
refund_deadline: string;
|
||||
timestamp: string;
|
||||
transaction_id: number;
|
||||
}
|
||||
|
||||
interface Transaction {
|
||||
contractHash: string;
|
||||
contract: Contract;
|
||||
payReq: any;
|
||||
payReq: PayReq;
|
||||
merchantSig: string;
|
||||
}
|
||||
|
||||
@ -492,16 +503,17 @@ export class Wallet {
|
||||
private async recordConfirmPay(offer: Offer,
|
||||
payCoinInfo: PayCoinInfo,
|
||||
chosenExchange: string): Promise<void> {
|
||||
let payReq: any = {};
|
||||
payReq["amount"] = offer.contract.amount;
|
||||
payReq["coins"] = payCoinInfo.map((x) => x.sig);
|
||||
payReq["H_contract"] = offer.H_contract;
|
||||
payReq["max_fee"] = offer.contract.max_fee;
|
||||
payReq["merchant_sig"] = offer.merchant_sig;
|
||||
payReq["exchange"] = URI(chosenExchange).href();
|
||||
payReq["refund_deadline"] = offer.contract.refund_deadline;
|
||||
payReq["timestamp"] = offer.contract.timestamp;
|
||||
payReq["transaction_id"] = offer.contract.transaction_id;
|
||||
let payReq: PayReq = {
|
||||
amount: offer.contract.amount,
|
||||
coins: payCoinInfo.map((x) => x.sig),
|
||||
H_contract: offer.H_contract,
|
||||
max_fee: offer.contract.max_fee,
|
||||
merchant_sig: offer.merchant_sig,
|
||||
exchange: URI(chosenExchange).href(),
|
||||
refund_deadline: offer.contract.refund_deadline,
|
||||
timestamp: offer.contract.timestamp,
|
||||
transaction_id: offer.contract.transaction_id,
|
||||
};
|
||||
let t: Transaction = {
|
||||
contractHash: offer.H_contract,
|
||||
contract: offer.contract,
|
||||
@ -792,6 +804,8 @@ export class Wallet {
|
||||
denomSig: denomSig,
|
||||
currentAmount: pc.coinValue,
|
||||
exchangeBaseUrl: pc.exchangeBaseUrl,
|
||||
dirty: false,
|
||||
transactionPending: false,
|
||||
};
|
||||
return coin;
|
||||
}
|
||||
@ -1127,18 +1141,53 @@ export class Wallet {
|
||||
let newCoinDenoms = getWithdrawDenomList(availableAmount,
|
||||
availableDenoms);
|
||||
|
||||
newCoinDenoms = [newCoinDenoms[0]];
|
||||
console.log("refreshing into", newCoinDenoms);
|
||||
|
||||
if (newCoinDenoms.length == 0) {
|
||||
console.log("not refreshing, value too small");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let refreshSession: RefreshSession = await (
|
||||
this.cryptoApi.createRefreshSession(exchange.baseUrl,
|
||||
3,
|
||||
coin,
|
||||
newCoinDenoms,
|
||||
oldDenom.fee_refresh));
|
||||
3,
|
||||
coin,
|
||||
newCoinDenoms,
|
||||
oldDenom.fee_refresh));
|
||||
|
||||
let reqUrl = URI("refresh/melt").absoluteTo(exchange!.baseUrl);
|
||||
coin.currentAmount = Amounts.sub(coin.currentAmount,
|
||||
refreshSession.valueWithFee).amount;
|
||||
|
||||
// FIXME: we should check whether the amount still matches!
|
||||
await this.q()
|
||||
.put("refresh", refreshSession)
|
||||
.put("coins", coin)
|
||||
.finish();
|
||||
|
||||
await this.refreshMelt(refreshSession);
|
||||
|
||||
let r = await this.q().get<RefreshSession>("refresh", oldCoinPub);
|
||||
if (!r) {
|
||||
throw Error("refresh session does not exist anymore");
|
||||
}
|
||||
await this.refreshReveal(r);
|
||||
}
|
||||
|
||||
|
||||
async refreshMelt(refreshSession: RefreshSession): Promise<void> {
|
||||
if (refreshSession.norevealIndex != undefined) {
|
||||
console.error("won't melt again");
|
||||
return;
|
||||
}
|
||||
|
||||
let coin = await this.q().get<Coin>("coins", refreshSession.meltCoinPub);
|
||||
if (!coin) {
|
||||
console.error("can't melt coin, it does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
let reqUrl = URI("refresh/melt").absoluteTo(refreshSession.exchangeBaseUrl);
|
||||
let meltCoin = {
|
||||
coin_pub: coin.coinPub,
|
||||
denom_pub: coin.denomPub,
|
||||
@ -1148,7 +1197,7 @@ export class Wallet {
|
||||
};
|
||||
let coinEvs = refreshSession.preCoinsForGammas.map((x) => x.map((y) => y.coinEv));
|
||||
let req = {
|
||||
"new_denoms": newCoinDenoms.map((d) => d.denom_pub),
|
||||
"new_denoms": refreshSession.newDenoms,
|
||||
"melt_coin": meltCoin,
|
||||
"transfer_pubs": refreshSession.transferPubs,
|
||||
"coin_evs": coinEvs,
|
||||
@ -1178,11 +1227,10 @@ export class Wallet {
|
||||
|
||||
refreshSession.norevealIndex = norevealIndex;
|
||||
|
||||
this.refreshReveal(refreshSession);
|
||||
|
||||
// FIXME: implement rest
|
||||
await this.q().put("refresh", refreshSession).finish();
|
||||
}
|
||||
|
||||
|
||||
async refreshReveal(refreshSession: RefreshSession): Promise<void> {
|
||||
let norevealIndex = refreshSession.norevealIndex;
|
||||
if (norevealIndex == undefined) {
|
||||
@ -1196,12 +1244,54 @@ export class Wallet {
|
||||
"transfer_privs": privs,
|
||||
};
|
||||
|
||||
let reqUrl = URI("refresh/reveal").absoluteTo(refreshSession.exchangeBaseUrl);
|
||||
let reqUrl = URI("refresh/reveal")
|
||||
.absoluteTo(refreshSession.exchangeBaseUrl);
|
||||
console.log("reveal request:", req);
|
||||
let resp = await this.http.postJson(reqUrl, req);
|
||||
|
||||
console.log("session:", refreshSession);
|
||||
console.log("reveal response:", resp);
|
||||
|
||||
if (resp.status != 200) {
|
||||
console.log("error: /refresh/reveal returned status " + resp.status);
|
||||
return;
|
||||
}
|
||||
|
||||
let respJson = JSON.parse(resp.responseText);
|
||||
|
||||
if (!respJson.ev_sigs || !Array.isArray(respJson.ev_sigs)) {
|
||||
console.log("/refresh/reveal did not contain ev_sigs");
|
||||
}
|
||||
|
||||
let exchange = await this.q().get<IExchangeInfo>("exchanges", refreshSession.exchangeBaseUrl);
|
||||
if (!exchange) {
|
||||
console.error(`exchange ${refreshSession.exchangeBaseUrl} not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < respJson.ev_sigs.length; i++) {
|
||||
let denom = exchange.all_denoms.find((d) => d.denom_pub == refreshSession.newDenoms[i]);
|
||||
if (!denom) {
|
||||
console.error("denom not found");
|
||||
continue;
|
||||
}
|
||||
let pc = refreshSession.preCoinsForGammas[refreshSession.norevealIndex!][i];
|
||||
let denomSig = await this.cryptoApi.rsaUnblind(respJson.ev_sigs[i].ev_sig,
|
||||
pc.blindingKey,
|
||||
denom.denom_pub);
|
||||
let coin: Coin = {
|
||||
coinPub: pc.publicKey,
|
||||
coinPriv: pc.privateKey,
|
||||
denomPub: denom.denom_pub,
|
||||
denomSig: denomSig,
|
||||
currentAmount: denom.value,
|
||||
exchangeBaseUrl: refreshSession.exchangeBaseUrl,
|
||||
dirty: false,
|
||||
transactionPending: false,
|
||||
};
|
||||
|
||||
await this.q().put("coins", coin).finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1283,4 +1373,29 @@ export class Wallet {
|
||||
return {isRepurchase: false};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async paymentSucceeded(contractHash: string): Promise<any> {
|
||||
const doPaymentSucceeded = async () => {
|
||||
let t = await this.q().get<Transaction>("transactions", contractHash);
|
||||
if (!t) {
|
||||
console.error("contract not found");
|
||||
return;
|
||||
}
|
||||
for (let pc of t.payReq.coins) {
|
||||
let c = await this.q().get<Coin>("coins", pc.coin_pub);
|
||||
if (!c) {
|
||||
console.error("coin not found");
|
||||
return;
|
||||
}
|
||||
c.transactionPending = false;
|
||||
await this.q().put("coins", c).finish();
|
||||
}
|
||||
for (let c of t.payReq.coins) {
|
||||
this.refresh(c.coin_pub);
|
||||
}
|
||||
};
|
||||
doPaymentSucceeded();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -218,6 +218,13 @@ function makeHandlers(db: IDBDatabase,
|
||||
wallet.updateExchanges();
|
||||
return Promise.resolve();
|
||||
},
|
||||
["payment-succeeded"]: function (detail, sender) {
|
||||
let contractHash = detail.contractHash;
|
||||
if (!contractHash) {
|
||||
return Promise.reject(Error("contractHash missing"));
|
||||
}
|
||||
return wallet.paymentSucceeded(contractHash);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -13,8 +13,10 @@
|
||||
"noImplicitAny": true
|
||||
},
|
||||
"files": [
|
||||
"lib/components.ts",
|
||||
"test/tests/taler.ts",
|
||||
"lib/refs.d.ts",
|
||||
"lib/i18n.ts",
|
||||
"lib/refs.ts",
|
||||
"lib/shopApi.ts",
|
||||
"lib/taler-wallet-lib.ts",
|
||||
"lib/wallet/checkable.ts",
|
||||
@ -38,7 +40,6 @@
|
||||
"pages/show-db.ts",
|
||||
"pages/confirm-contract.tsx",
|
||||
"pages/confirm-create-reserve.tsx",
|
||||
"pages/tree.tsx",
|
||||
"test/tests/taler.ts"
|
||||
"pages/tree.tsx"
|
||||
]
|
||||
}
|
@ -1 +1 @@
|
||||
Subproject commit e9eb34dfb56c8e5a18fa7d555aa651a4532e8f3c
|
||||
Subproject commit 02271055cbe9e62cffb3713d109a77015df625d1
|
Loading…
Reference in New Issue
Block a user