version upgrade and formatting

This commit is contained in:
Florian Dold 2019-11-20 20:02:48 +01:00
parent 7974a76228
commit 035b3fdae2
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
4 changed files with 1142 additions and 751 deletions

View File

@ -46,7 +46,7 @@
"react": "^16.8.5", "react": "^16.8.5",
"react-dom": "^16.8.5", "react-dom": "^16.8.5",
"structured-clone": "^0.2.2", "structured-clone": "^0.2.2",
"terser-webpack-plugin": "^1.2.3", "terser-webpack-plugin": "^2.2.1",
"through2": "3.0.1", "through2": "3.0.1",
"tslint": "^5.19.0", "tslint": "^5.19.0",
"typedoc": "^0.15.0", "typedoc": "^0.15.0",
@ -60,7 +60,7 @@
"webpack-merge": "^4.2.2" "webpack-merge": "^4.2.2"
}, },
"dependencies": { "dependencies": {
"@types/chrome": "^0.0.88", "@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.11",

View File

@ -89,7 +89,10 @@ export function oneShotGetIndexed<S extends IDBValidKey, T>(
key: any, key: any,
): Promise<T | undefined> { ): Promise<T | undefined> {
const tx = db.transaction([index.storeName], "readonly"); const tx = db.transaction([index.storeName], "readonly");
const req = tx.objectStore(index.storeName).index(index.indexName).get(key); const req = tx
.objectStore(index.storeName)
.index(index.indexName)
.get(key);
return requestToPromise(req); return requestToPromise(req);
} }
@ -104,7 +107,10 @@ export function oneShotPut<T>(
return requestToPromise(req); return requestToPromise(req);
} }
function applyMutation<T>(req: IDBRequest, f: (x: T) => T | undefined): Promise<void> { function applyMutation<T>(
req: IDBRequest,
f: (x: T) => T | undefined,
): Promise<void> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
req.onsuccess = () => { req.onsuccess = () => {
const cursor = req.result; const cursor = req.result;
@ -226,7 +232,7 @@ class ResultStream<T> {
const x = await this.next(); const x = await this.next();
if (x.hasValue) { if (x.hasValue) {
if (f(x.value)) { if (f(x.value)) {
arr.push(x.value) arr.push(x.value);
} }
} else { } else {
break; break;
@ -261,7 +267,7 @@ class ResultStream<T> {
export function oneShotIter<T>( export function oneShotIter<T>(
db: IDBDatabase, db: IDBDatabase,
store: Store<T> store: Store<T>,
): ResultStream<T> { ): ResultStream<T> {
const tx = db.transaction([store.name], "readonly"); const tx = db.transaction([store.name], "readonly");
const req = tx.objectStore(store.name).openCursor(); const req = tx.objectStore(store.name).openCursor();
@ -274,7 +280,10 @@ export function oneShotIterIndex<S extends IDBValidKey, T>(
query?: any, query?: any,
): ResultStream<T> { ): ResultStream<T> {
const tx = db.transaction([index.storeName], "readonly"); const tx = db.transaction([index.storeName], "readonly");
const req = tx.objectStore(index.storeName).index(index.indexName).openCursor(query); const req = tx
.objectStore(index.storeName)
.index(index.indexName)
.openCursor(query);
return new ResultStream<T>(req); return new ResultStream<T>(req);
} }
@ -389,7 +398,6 @@ export class Index<S extends IDBValidKey, T> {
protected _dummyKey: S | undefined; protected _dummyKey: S | undefined;
} }
/** /**
* Exception that should be thrown by client code to abort a transaction. * Exception that should be thrown by client code to abort a transaction.
*/ */

View File

@ -409,8 +409,10 @@ export class Wallet {
} }
public async processPending(): Promise<void> { public async processPending(): Promise<void> {
const exchangeBaseUrlList = await oneShotIter(
const exchangeBaseUrlList = await oneShotIter(this.db, Stores.exchanges).map((x) => x.baseUrl); this.db,
Stores.exchanges,
).map(x => x.baseUrl);
for (let exchangeBaseUrl of exchangeBaseUrlList) { for (let exchangeBaseUrl of exchangeBaseUrlList) {
await this.updateExchangeFromUrl(exchangeBaseUrl); await this.updateExchangeFromUrl(exchangeBaseUrl);
@ -436,9 +438,12 @@ export class Wallet {
* already been applied. * already been applied.
*/ */
async fillDefaults() { async fillDefaults() {
await runWithWriteTransaction(this.db, [Stores.config, Stores.currencies], async (tx) => { await runWithWriteTransaction(
this.db,
[Stores.config, Stores.currencies],
async tx => {
let applied = false; let applied = false;
await tx.iter(Stores.config).forEach((x) => { await tx.iter(Stores.config).forEach(x => {
if (x.key == "currencyDefaultsApplied" && x.value == true) { if (x.key == "currencyDefaultsApplied" && x.value == true) {
applied = true; applied = true;
} }
@ -448,7 +453,8 @@ export class Wallet {
await tx.put(Stores.currencies, c); await tx.put(Stores.currencies, c);
} }
} }
}); },
);
} }
private startOperation(operationId: string) { private startOperation(operationId: string) {
@ -464,7 +470,9 @@ export class Wallet {
} }
async updateExchanges(): Promise<void> { async updateExchanges(): Promise<void> {
const exchangeUrls = await oneShotIter(this.db, Stores.exchanges).map((e) => e.baseUrl); const exchangeUrls = await oneShotIter(this.db, Stores.exchanges).map(
e => e.baseUrl,
);
for (const url of exchangeUrls) { for (const url of exchangeUrls) {
this.updateExchangeFromUrl(url).catch(e => { this.updateExchangeFromUrl(url).catch(e => {
@ -481,9 +489,9 @@ export class Wallet {
Wallet.enableTracing && console.log("resuming pending operations from db"); Wallet.enableTracing && console.log("resuming pending operations from db");
oneShotIter(this.db, Stores.reserves).forEach(reserve => { oneShotIter(this.db, Stores.reserves).forEach(reserve => {
Wallet.enableTracing && Wallet.enableTracing &&
console.log("resuming reserve", reserve.reserve_pub); console.log("resuming reserve", reserve.reserve_pub);
this.processReserve(reserve.reserve_pub); this.processReserve(reserve.reserve_pub);
}); });
oneShotIter(this.db, Stores.precoins).forEach(preCoin => { oneShotIter(this.db, Stores.precoins).forEach(preCoin => {
@ -495,27 +503,41 @@ export class Wallet {
this.continueRefreshSession(r); this.continueRefreshSession(r);
}); });
oneShotIter(this.db, Stores.coinsReturns).forEach((r: CoinsReturnRecord) => { oneShotIter(this.db, Stores.coinsReturns).forEach(
this.depositReturnedCoins(r); (r: CoinsReturnRecord) => {
}); this.depositReturnedCoins(r);
},
);
} }
private async getCoinsForReturn( private async getCoinsForReturn(
exchangeBaseUrl: string, exchangeBaseUrl: string,
amount: AmountJson, amount: AmountJson,
): Promise<CoinWithDenom[] | undefined> { ): Promise<CoinWithDenom[] | undefined> {
const exchange = await oneShotGet(this.db, Stores.exchanges, exchangeBaseUrl); const exchange = await oneShotGet(
this.db,
Stores.exchanges,
exchangeBaseUrl,
);
if (!exchange) { if (!exchange) {
throw Error(`Exchange ${exchangeBaseUrl} not known to the wallet`); throw Error(`Exchange ${exchangeBaseUrl} not known to the wallet`);
} }
const coins: CoinRecord[] = await oneShotIterIndex(this.db, Stores.coins.exchangeBaseUrlIndex, exchange.baseUrl).toArray(); const coins: CoinRecord[] = await oneShotIterIndex(
this.db,
Stores.coins.exchangeBaseUrlIndex,
exchange.baseUrl,
).toArray();
if (!coins || !coins.length) { if (!coins || !coins.length) {
return []; return [];
} }
const denoms = await oneShotIterIndex(this.db, Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl).toArray(); const denoms = await oneShotIterIndex(
this.db,
Stores.denominations.exchangeBaseUrlIndex,
exchange.baseUrl,
).toArray();
// Denomination of the first coin, we assume that all other // Denomination of the first coin, we assume that all other
// coins have the same currency // coins have the same currency
@ -621,9 +643,17 @@ export class Wallet {
continue; continue;
} }
const coins = await oneShotIterIndex(this.db, Stores.coins.exchangeBaseUrlIndex, exchange.baseUrl).toArray(); const coins = await oneShotIterIndex(
this.db,
Stores.coins.exchangeBaseUrlIndex,
exchange.baseUrl,
).toArray();
const denoms = await oneShotIterIndex(this.db, Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl).toArray(); const denoms = await oneShotIterIndex(
this.db,
Stores.denominations.exchangeBaseUrlIndex,
exchange.baseUrl,
).toArray();
if (!coins || coins.length === 0) { if (!coins || coins.length === 0) {
continue; continue;
@ -726,12 +756,16 @@ export class Wallet {
timestamp_refund: 0, timestamp_refund: 0,
}; };
await runWithWriteTransaction(this.db, [Stores.coins, Stores.purchases], async (tx) => { await runWithWriteTransaction(
await tx.put(Stores.purchases, t); this.db,
for (let c of payCoinInfo.updatedCoins) { [Stores.coins, Stores.purchases],
await tx.put(Stores.coins, c); async tx => {
} await tx.put(Stores.purchases, t);
}); for (let c of payCoinInfo.updatedCoins) {
await tx.put(Stores.coins, c);
}
},
);
this.badge.showNotification(); this.badge.showNotification();
this.notifier.notify(); this.notifier.notify();
@ -812,7 +846,11 @@ export class Wallet {
} }
// First check if we already payed for it. // First check if we already payed for it.
const purchase = await oneShotGet(this.db, Stores.purchases, proposal.contractTermsHash); const purchase = await oneShotGet(
this.db,
Stores.purchases,
proposal.contractTermsHash,
);
if (!purchase) { if (!purchase) {
const paymentAmount = Amounts.parseOrThrow(proposal.contractTerms.amount); const paymentAmount = Amounts.parseOrThrow(proposal.contractTerms.amount);
@ -894,7 +932,11 @@ export class Wallet {
* downloaded in the context of a session ID. * downloaded in the context of a session ID.
*/ */
async downloadProposal(url: string, sessionId?: string): Promise<number> { async downloadProposal(url: string, sessionId?: string): Promise<number> {
const oldProposal = await oneShotGetIndexed(this.db, Stores.proposals.urlIndex, url); const oldProposal = await oneShotGetIndexed(
this.db,
Stores.proposals.urlIndex,
url,
);
if (oldProposal) { if (oldProposal) {
return oldProposal.id!; return oldProposal.id!;
} }
@ -940,9 +982,11 @@ export class Wallet {
throw Error(`proposal with id ${proposalId} not found`); throw Error(`proposal with id ${proposalId} not found`);
} }
const purchase = await oneShotGet(this.db, const purchase = await oneShotGet(
this.db,
Stores.purchases, Stores.purchases,
proposal.contractTermsHash); proposal.contractTermsHash,
);
if (!purchase) { if (!purchase) {
throw Error("purchase not found for proposal"); throw Error("purchase not found for proposal");
@ -957,7 +1001,11 @@ export class Wallet {
contractTermsHash: string, contractTermsHash: string,
sessionId: string | undefined, sessionId: string | undefined,
): Promise<ConfirmPayResult> { ): Promise<ConfirmPayResult> {
const purchase = await oneShotGet(this.db, Stores.purchases, contractTermsHash); const purchase = await oneShotGet(
this.db,
Stores.purchases,
contractTermsHash,
);
if (!purchase) { if (!purchase) {
throw Error("Purchase not found: " + contractTermsHash); throw Error("Purchase not found: " + contractTermsHash);
} }
@ -1004,12 +1052,16 @@ export class Wallet {
modifiedCoins.push(c); modifiedCoins.push(c);
} }
await runWithWriteTransaction(this.db, [Stores.coins, Stores.purchases], async (tx) => { await runWithWriteTransaction(
for (let c of modifiedCoins) { this.db,
tx.put(Stores.coins, c); [Stores.coins, Stores.purchases],
} async tx => {
tx.put(Stores.purchases, purchase); for (let c of modifiedCoins) {
}); tx.put(Stores.coins, c);
}
tx.put(Stores.purchases, purchase);
},
);
for (const c of purchase.payReq.coins) { for (const c of purchase.payReq.coins) {
this.refresh(c.coin_pub); this.refresh(c.coin_pub);
@ -1065,9 +1117,11 @@ export class Wallet {
const sessionId = sessionIdOverride || proposal.downloadSessionId; const sessionId = sessionIdOverride || proposal.downloadSessionId;
let purchase = await oneShotGet(this.db, let purchase = await oneShotGet(
this.db,
Stores.purchases, Stores.purchases,
proposal.contractTermsHash,); proposal.contractTermsHash,
);
if (purchase) { if (purchase) {
return this.submitPay(purchase.contractTermsHash, sessionId); return this.submitPay(purchase.contractTermsHash, sessionId);
@ -1167,9 +1221,7 @@ export class Wallet {
* Send reserve details * Send reserve details
*/ */
private async sendReserveInfoToBank(reservePub: string) { private async sendReserveInfoToBank(reservePub: string) {
const reserve = await oneShotGet(this.db, const reserve = await oneShotGet(this.db, Stores.reserves, reservePub);
Stores.reserves,
reservePub);
if (!reserve) { if (!reserve) {
throw Error("reserve not in db"); throw Error("reserve not in db");
} }
@ -1190,7 +1242,7 @@ export class Wallet {
} }
if (status.transfer_done) { if (status.transfer_done) {
await oneShotMutate(this.db, Stores.reserves, reservePub, (r) => { await oneShotMutate(this.db, Stores.reserves, reservePub, r => {
r.timestamp_confirmed = now; r.timestamp_confirmed = now;
return r; return r;
}); });
@ -1206,7 +1258,7 @@ export class Wallet {
console.log("bank error response", e); console.log("bank error response", e);
throw e; throw e;
} }
await oneShotMutate(this.db, Stores.reserves, reservePub, (r) => { await oneShotMutate(this.db, Stores.reserves, reservePub, r => {
r.timestamp_reserve_info_posted = now; r.timestamp_reserve_info_posted = now;
return r; return r;
}); });
@ -1321,9 +1373,11 @@ export class Wallet {
this.processPreCoinConcurrent++; this.processPreCoinConcurrent++;
try { try {
const exchange = await oneShotGet(this.db, const exchange = await oneShotGet(
this.db,
Stores.exchanges, Stores.exchanges,
preCoin.exchangeBaseUrl,); preCoin.exchangeBaseUrl,
);
if (!exchange) { if (!exchange) {
console.error("db inconsistent: exchange for precoin not found"); console.error("db inconsistent: exchange for precoin not found");
return; return;
@ -1353,11 +1407,15 @@ export class Wallet {
return r; return r;
}; };
await runWithWriteTransaction(this.db, [Stores.reserves, Stores.precoins, Stores.coins], async (tx) => { await runWithWriteTransaction(
await tx.mutate(Stores.reserves, preCoin.reservePub, mutateReserve); this.db,
await tx.delete(Stores.precoins, coin.coinPub); [Stores.reserves, Stores.precoins, Stores.coins],
await tx.add(Stores.coins, coin); async tx => {
}); await tx.mutate(Stores.reserves, preCoin.reservePub, mutateReserve);
await tx.delete(Stores.precoins, coin.coinPub);
await tx.add(Stores.coins, coin);
},
);
this.badge.showNotification(); this.badge.showNotification();
@ -1441,7 +1499,11 @@ export class Wallet {
throw Error("exchange not updated"); throw Error("exchange not updated");
} }
const { isAudited, isTrusted } = await this.getExchangeTrust(exchangeInfo); const { isAudited, isTrusted } = await this.getExchangeTrust(exchangeInfo);
let currencyRecord = await oneShotGet(this.db, Stores.currencies, exchangeDetails.currency); let currencyRecord = await oneShotGet(
this.db,
Stores.currencies,
exchangeDetails.currency,
);
if (!currencyRecord) { if (!currencyRecord) {
currencyRecord = { currencyRecord = {
auditors: [], auditors: [],
@ -1460,10 +1522,14 @@ export class Wallet {
const cr: CurrencyRecord = currencyRecord; const cr: CurrencyRecord = currencyRecord;
runWithWriteTransaction(this.db, [Stores.currencies, Stores.reserves], async (tx) => { runWithWriteTransaction(
await tx.put(Stores.currencies, cr); this.db,
await tx.put(Stores.reserves, reserveRecord); [Stores.currencies, Stores.reserves],
}); async tx => {
await tx.put(Stores.currencies, cr);
await tx.put(Stores.reserves, reserveRecord);
},
);
if (req.bankWithdrawStatusUrl) { if (req.bankWithdrawStatusUrl) {
this.processReserve(keypair.pub); this.processReserve(keypair.pub);
@ -1611,13 +1677,17 @@ export class Wallet {
// This will fail and throw an exception if the remaining amount in the // This will fail and throw an exception if the remaining amount in the
// reserve is too low to create a pre-coin. // reserve is too low to create a pre-coin.
try { try {
await runWithWriteTransaction(this.db, [Stores.precoins, Stores.withdrawals, Stores.reserves], async (tx) => { await runWithWriteTransaction(
for (let pcr of preCoinRecords) { this.db,
await tx.put(Stores.precoins, pcr); [Stores.precoins, Stores.withdrawals, Stores.reserves],
} async tx => {
await tx.mutate(Stores.reserves, reserve.reserve_pub, mutateReserve); for (let pcr of preCoinRecords) {
await tx.put(Stores.withdrawals, withdrawalRecord); await tx.put(Stores.precoins, pcr);
}); }
await tx.mutate(Stores.reserves, reserve.reserve_pub, mutateReserve);
await tx.put(Stores.withdrawals, withdrawalRecord);
},
);
} catch (e) { } catch (e) {
return; return;
} }
@ -1661,10 +1731,18 @@ export class Wallet {
return reserve; return reserve;
} }
async getPossibleDenoms(exchangeBaseUrl: string): Promise<DenominationRecord[]> { async getPossibleDenoms(
return await oneShotIterIndex(this.db, Stores.denominations.exchangeBaseUrlIndex, exchangeBaseUrl).filter((d) => { exchangeBaseUrl: string,
return d.status === DenominationStatus.Unverified || ): Promise<DenominationRecord[]> {
d.status === DenominationStatus.VerifiedGood; return await oneShotIterIndex(
this.db,
Stores.denominations.exchangeBaseUrlIndex,
exchangeBaseUrl,
).filter(d => {
return (
d.status === DenominationStatus.Unverified ||
d.status === DenominationStatus.VerifiedGood
);
}); });
} }
@ -1677,7 +1755,11 @@ export class Wallet {
async getVerifiedSmallestWithdrawAmount( async getVerifiedSmallestWithdrawAmount(
exchangeBaseUrl: string, exchangeBaseUrl: string,
): Promise<AmountJson> { ): Promise<AmountJson> {
const exchange = await oneShotGet(this.db, Stores.exchanges, exchangeBaseUrl); const exchange = await oneShotGet(
this.db,
Stores.exchanges,
exchangeBaseUrl,
);
if (!exchange) { if (!exchange) {
throw Error(`exchange ${exchangeBaseUrl} not found`); throw Error(`exchange ${exchangeBaseUrl} not found`);
} }
@ -1726,7 +1808,11 @@ export class Wallet {
exchangeBaseUrl: string, exchangeBaseUrl: string,
amount: AmountJson, amount: AmountJson,
): Promise<DenominationRecord[]> { ): Promise<DenominationRecord[]> {
const exchange = await oneShotGet(this.db, Stores.exchanges, exchangeBaseUrl); const exchange = await oneShotGet(
this.db,
Stores.exchanges,
exchangeBaseUrl,
);
if (!exchange) { if (!exchange) {
throw Error(`exchange ${exchangeBaseUrl} not found`); throw Error(`exchange ${exchangeBaseUrl} not found`);
} }
@ -1780,9 +1866,11 @@ export class Wallet {
if (!exchangeDetails) { if (!exchangeDetails) {
throw Error(`exchange ${exchangeInfo.baseUrl} details not available`); throw Error(`exchange ${exchangeInfo.baseUrl} details not available`);
} }
const currencyRecord = await oneShotGet(this.db, const currencyRecord = await oneShotGet(
this.db,
Stores.currencies, Stores.currencies,
exchangeDetails.currency); exchangeDetails.currency,
);
if (currencyRecord) { if (currencyRecord) {
for (const trustedExchange of currencyRecord.exchanges) { for (const trustedExchange of currencyRecord.exchanges) {
if (trustedExchange.exchangePub === exchangeDetails.masterPublicKey) { if (trustedExchange.exchangePub === exchangeDetails.masterPublicKey) {
@ -1868,11 +1956,15 @@ export class Wallet {
const possibleDenoms = await oneShotIterIndex( const possibleDenoms = await oneShotIterIndex(
this.db, this.db,
Stores.denominations.exchangeBaseUrlIndex, Stores.denominations.exchangeBaseUrlIndex,
baseUrl) baseUrl,
.filter((d) => d.isOffered); ).filter(d => d.isOffered);
const trustedAuditorPubs = []; const trustedAuditorPubs = [];
const currencyRecord = await oneShotGet(this.db, Stores.currencies, amount.currency); const currencyRecord = await oneShotGet(
this.db,
Stores.currencies,
amount.currency,
);
if (currencyRecord) { if (currencyRecord) {
trustedAuditorPubs.push( trustedAuditorPubs.push(
...currencyRecord.auditors.map(a => a.auditorPub), ...currencyRecord.auditors.map(a => a.auditorPub),
@ -1927,7 +2019,11 @@ export class Wallet {
exchangeBaseUrl: string, exchangeBaseUrl: string,
supportedTargetTypes: string[], supportedTargetTypes: string[],
): Promise<string> { ): Promise<string> {
const exchangeRecord = await oneShotGet(this.db, Stores.exchanges, exchangeBaseUrl); const exchangeRecord = await oneShotGet(
this.db,
Stores.exchanges,
exchangeBaseUrl,
);
if (!exchangeRecord) { if (!exchangeRecord) {
throw Error(`Exchange '${exchangeBaseUrl}' not found.`); throw Error(`Exchange '${exchangeBaseUrl}' not found.`);
} }
@ -1967,7 +2063,7 @@ export class Wallet {
}; };
await oneShotPut(this.db, Stores.exchanges, newExchangeRecord); await oneShotPut(this.db, Stores.exchanges, newExchangeRecord);
} else { } else {
runWithWriteTransaction(this.db, [Stores.exchanges], async (t) => { runWithWriteTransaction(this.db, [Stores.exchanges], async t => {
const rec = await t.get(Stores.exchanges, baseUrl); const rec = await t.get(Stores.exchanges, baseUrl);
if (!rec) { if (!rec) {
return; return;
@ -1984,7 +2080,11 @@ export class Wallet {
await this.updateExchangeWithKeys(baseUrl); await this.updateExchangeWithKeys(baseUrl);
await this.updateExchangeWithWireInfo(baseUrl); await this.updateExchangeWithWireInfo(baseUrl);
const updatedExchange = await oneShotGet(this.db, Stores.exchanges, baseUrl); const updatedExchange = await oneShotGet(
this.db,
Stores.exchanges,
baseUrl,
);
if (!updatedExchange) { if (!updatedExchange) {
// This should practically never happen // This should practically never happen
@ -2011,9 +2111,15 @@ export class Wallet {
* in the pending operations. * in the pending operations.
*/ */
private async updateExchangeWithKeys(baseUrl: string): Promise<void> { private async updateExchangeWithKeys(baseUrl: string): Promise<void> {
const existingExchangeRecord = await oneShotGet(this.db, Stores.exchanges, baseUrl); const existingExchangeRecord = await oneShotGet(
this.db,
Stores.exchanges,
baseUrl,
);
if (existingExchangeRecord?.updateStatus != ExchangeUpdateStatus.FETCH_KEYS) { if (
existingExchangeRecord?.updateStatus != ExchangeUpdateStatus.FETCH_KEYS
) {
return; return;
} }
const keysUrl = new URI("keys") const keysUrl = new URI("keys")
@ -2111,7 +2217,6 @@ export class Wallet {
const wireInfo = ExchangeWireJson.checked(wiJson); const wireInfo = ExchangeWireJson.checked(wiJson);
} }
/** /**
* Get detailed balance information, sliced by exchange and by currency. * Get detailed balance information, sliced by exchange and by currency.
*/ */
@ -2155,71 +2260,100 @@ export class Wallet {
byExchange: {}, byExchange: {},
}; };
await runWithWriteTransaction(this.db, [Stores.coins, Stores.refresh, Stores.reserves, Stores.purchases], async (tx) => { await runWithWriteTransaction(
await tx.iter(Stores.coins).forEach((c) => { this.db,
if (c.suspended) { [Stores.coins, Stores.refresh, Stores.reserves, Stores.purchases],
return; async tx => {
} await tx.iter(Stores.coins).forEach(c => {
if (c.status === CoinStatus.Fresh) { if (c.suspended) {
addTo(balanceStore, "available", c.currentAmount, c.exchangeBaseUrl); return;
} }
if (c.status === CoinStatus.Dirty) { if (c.status === CoinStatus.Fresh) {
addTo(balanceStore, "pendingIncoming", c.currentAmount, c.exchangeBaseUrl); addTo(
balanceStore,
"available",
c.currentAmount,
c.exchangeBaseUrl,
);
}
if (c.status === CoinStatus.Dirty) {
addTo(
balanceStore,
"pendingIncoming",
c.currentAmount,
c.exchangeBaseUrl,
);
addTo(
balanceStore,
"pendingIncomingDirty",
c.currentAmount,
c.exchangeBaseUrl,
);
}
});
await tx.iter(Stores.refresh).forEach(r => {
// Don't count finished refreshes, since the refresh already resulted
// in coins being added to the wallet.
if (r.finished) {
return;
}
addTo( addTo(
balanceStore, balanceStore,
"pendingIncomingDirty", "pendingIncoming",
c.currentAmount, r.valueOutput,
c.exchangeBaseUrl, r.exchangeBaseUrl,
); );
}
});
await tx.iter(Stores.refresh).forEach((r) => {
// Don't count finished refreshes, since the refresh already resulted
// in coins being added to the wallet.
if (r.finished) {
return;
}
addTo(balanceStore, "pendingIncoming", r.valueOutput, r.exchangeBaseUrl);
addTo(
balanceStore,
"pendingIncomingRefresh",
r.valueOutput,
r.exchangeBaseUrl,
);
});
await tx.iter(Stores.reserves).forEach((r) => {
if (!r.timestamp_confirmed) {
return;
}
let amount = Amounts.getZero(r.requested_amount.currency);
amount = Amounts.add(amount, r.precoin_amount).amount;
addTo(balanceStore, "pendingIncoming", amount, r.exchange_base_url);
addTo(balanceStore, "pendingIncomingWithdraw", amount, r.exchange_base_url);
});
await tx.iter(Stores.reserves).forEach((r) => {
if (!r.hasPayback) {
return;
}
addTo(balanceStore, "paybackAmount", r.current_amount!, r.exchange_base_url);
return balanceStore;
});
await tx.iter(Stores.purchases).forEach((t) => {
if (t.finished) {
return;
}
for (const c of t.payReq.coins) {
addTo( addTo(
balanceStore, balanceStore,
"pendingPayment", "pendingIncomingRefresh",
Amounts.parseOrThrow(c.contribution), r.valueOutput,
c.exchange_url, r.exchangeBaseUrl,
); );
} });
});
}); await tx.iter(Stores.reserves).forEach(r => {
if (!r.timestamp_confirmed) {
return;
}
let amount = Amounts.getZero(r.requested_amount.currency);
amount = Amounts.add(amount, r.precoin_amount).amount;
addTo(balanceStore, "pendingIncoming", amount, r.exchange_base_url);
addTo(
balanceStore,
"pendingIncomingWithdraw",
amount,
r.exchange_base_url,
);
});
await tx.iter(Stores.reserves).forEach(r => {
if (!r.hasPayback) {
return;
}
addTo(
balanceStore,
"paybackAmount",
r.current_amount!,
r.exchange_base_url,
);
return balanceStore;
});
await tx.iter(Stores.purchases).forEach(t => {
if (t.finished) {
return;
}
for (const c of t.payReq.coins) {
addTo(
balanceStore,
"pendingPayment",
Amounts.parseOrThrow(c.contribution),
c.exchange_url,
);
}
});
},
);
Wallet.enableTracing && console.log("computed balances:", balanceStore); Wallet.enableTracing && console.log("computed balances:", balanceStore);
return balanceStore; return balanceStore;
@ -2253,7 +2387,11 @@ export class Wallet {
throw Error("db inconsistent"); throw Error("db inconsistent");
} }
const availableDenoms: DenominationRecord[] = await oneShotIterIndex(this.db, Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl).toArray(); const availableDenoms: DenominationRecord[] = await oneShotIterIndex(
this.db,
Stores.denominations.exchangeBaseUrlIndex,
exchange.baseUrl,
).toArray();
const availableAmount = Amounts.sub(coin.currentAmount, oldDenom.feeRefresh) const availableAmount = Amounts.sub(coin.currentAmount, oldDenom.feeRefresh)
.amount; .amount;
@ -2302,10 +2440,14 @@ export class Wallet {
// Store refresh session and subtract refreshed amount from // Store refresh session and subtract refreshed amount from
// coin in the same transaction. // coin in the same transaction.
await runWithWriteTransaction(this.db, [Stores.refresh, Stores.coins], async (tx) => { await runWithWriteTransaction(
key = await tx.put(Stores.refresh, refreshSession); this.db,
await tx.mutate(Stores.coins, coin.coinPub, mutateCoin); [Stores.refresh, Stores.coins],
}); async tx => {
key = await tx.put(Stores.refresh, refreshSession);
await tx.mutate(Stores.coins, coin.coinPub, mutateCoin);
},
);
this.notifier.notify(); this.notifier.notify();
if (!key || typeof key !== "number") { if (!key || typeof key !== "number") {
@ -2319,7 +2461,10 @@ export class Wallet {
async refresh(oldCoinPub: string): Promise<void> { async refresh(oldCoinPub: string): Promise<void> {
const refreshImpl = async () => { const refreshImpl = async () => {
const oldRefreshSessions = await oneShotIter(this.db, Stores.refresh).toArray(); const oldRefreshSessions = await oneShotIter(
this.db,
Stores.refresh,
).toArray();
for (const session of oldRefreshSessions) { for (const session of oldRefreshSessions) {
if (session.finished) { if (session.finished) {
continue; continue;
@ -2395,7 +2540,11 @@ export class Wallet {
return; return;
} }
const coin = await oneShotGet(this.db, Stores.coins, refreshSession.meltCoinPub); const coin = await oneShotGet(
this.db,
Stores.coins,
refreshSession.meltCoinPub,
);
if (!coin) { if (!coin) {
console.error("can't melt coin, it does not exist"); console.error("can't melt coin, it does not exist");
@ -2451,7 +2600,11 @@ export class Wallet {
throw Error("refresh index error"); throw Error("refresh index error");
} }
const meltCoinRecord = await oneShotGet(this.db, Stores.coins, refreshSession.meltCoinPub); const meltCoinRecord = await oneShotGet(
this.db,
Stores.coins,
refreshSession.meltCoinPub,
);
if (!meltCoinRecord) { if (!meltCoinRecord) {
throw Error("inconsistent database"); throw Error("inconsistent database");
} }
@ -2549,16 +2702,22 @@ export class Wallet {
refreshSession.finished = true; refreshSession.finished = true;
await runWithWriteTransaction(this.db, [Stores.coins, Stores.refresh], async (tx) => { await runWithWriteTransaction(
for (let coin of coins) { this.db,
await tx.put(Stores.coins, coin); [Stores.coins, Stores.refresh],
} async tx => {
await tx.put(Stores.refresh, refreshSession); for (let coin of coins) {
}); await tx.put(Stores.coins, coin);
}
await tx.put(Stores.refresh, refreshSession);
},
);
this.notifier.notify(); this.notifier.notify();
} }
async findExchange(exchangeBaseUrl: string): Promise<ExchangeRecord | undefined> { async findExchange(
exchangeBaseUrl: string,
): Promise<ExchangeRecord | undefined> {
return await oneShotGet(this.db, Stores.exchanges, exchangeBaseUrl); return await oneShotGet(this.db, Stores.exchanges, exchangeBaseUrl);
} }
@ -2588,7 +2747,10 @@ export class Wallet {
}); });
} }
const withdrawals = await oneShotIter(this.db, Stores.withdrawals).toArray(); const withdrawals = await oneShotIter(
this.db,
Stores.withdrawals,
).toArray();
for (const w of withdrawals) { for (const w of withdrawals) {
history.push({ history.push({
detail: { detail: {
@ -2721,7 +2883,11 @@ export class Wallet {
} }
async getDenoms(exchangeUrl: string): Promise<DenominationRecord[]> { async getDenoms(exchangeUrl: string): Promise<DenominationRecord[]> {
const denoms = await oneShotIterIndex(this.db, Stores.denominations.exchangeBaseUrlIndex, exchangeUrl).toArray(); const denoms = await oneShotIterIndex(
this.db,
Stores.denominations.exchangeBaseUrlIndex,
exchangeUrl,
).toArray();
return denoms; return denoms;
} }
@ -2747,15 +2913,21 @@ export class Wallet {
} }
async getReserves(exchangeBaseUrl: string): Promise<ReserveRecord[]> { async getReserves(exchangeBaseUrl: string): Promise<ReserveRecord[]> {
return await oneShotIter(this.db, Stores.reserves).filter((r) => r.exchange_base_url === exchangeBaseUrl); return await oneShotIter(this.db, Stores.reserves).filter(
r => r.exchange_base_url === exchangeBaseUrl,
);
} }
async getCoins(exchangeBaseUrl: string): Promise<CoinRecord[]> { async getCoins(exchangeBaseUrl: string): Promise<CoinRecord[]> {
return await oneShotIter(this.db, Stores.coins).filter((c) => c.exchangeBaseUrl === exchangeBaseUrl); return await oneShotIter(this.db, Stores.coins).filter(
c => c.exchangeBaseUrl === exchangeBaseUrl,
);
} }
async getPreCoins(exchangeBaseUrl: string): Promise<PreCoinRecord[]> { async getPreCoins(exchangeBaseUrl: string): Promise<PreCoinRecord[]> {
return await oneShotIter(this.db, Stores.precoins).filter((c) => c.exchangeBaseUrl === exchangeBaseUrl); return await oneShotIter(this.db, Stores.precoins).filter(
c => c.exchangeBaseUrl === exchangeBaseUrl,
);
} }
private async hashContract(contract: ContractTerms): Promise<string> { private async hashContract(contract: ContractTerms): Promise<string> {
@ -2789,10 +2961,14 @@ export class Wallet {
// technically we might update reserve status before we get the response // technically we might update reserve status before we get the response
// from the reserve for the payback request. // from the reserve for the payback request.
reserve.hasPayback = true; reserve.hasPayback = true;
await runWithWriteTransaction(this.db, [Stores.coins, Stores.reserves], async (tx) => { await runWithWriteTransaction(
await tx.put(Stores.coins, coin!!); this.db,
await tx.put(Stores.reserves, reserve); [Stores.coins, Stores.reserves],
}); async tx => {
await tx.put(Stores.coins, coin!!);
await tx.put(Stores.reserves, reserve);
},
);
this.notifier.notify(); this.notifier.notify();
const paybackRequest = await this.cryptoApi.createPaybackRequest(coin); const paybackRequest = await this.cryptoApi.createPaybackRequest(coin);
@ -2851,7 +3027,9 @@ export class Wallet {
} }
async getPaybackReserves(): Promise<ReserveRecord[]> { async getPaybackReserves(): Promise<ReserveRecord[]> {
return await oneShotIter(this.db, Stores.reserves).filter(r => r.hasPayback); return await oneShotIter(this.db, Stores.reserves).filter(
r => r.hasPayback,
);
} }
/** /**
@ -2865,7 +3043,7 @@ export class Wallet {
async getSenderWireInfos(): Promise<SenderWireInfos> { async getSenderWireInfos(): Promise<SenderWireInfos> {
const m: { [url: string]: Set<string> } = {}; const m: { [url: string]: Set<string> } = {};
await oneShotIter(this.db, Stores.exchanges).forEach((x) => { await oneShotIter(this.db, Stores.exchanges).forEach(x => {
const wi = x.wireInfo; const wi = x.wireInfo;
if (!wi) { if (!wi) {
return; return;
@ -2881,7 +3059,7 @@ export class Wallet {
}); });
const senderWiresSet: Set<string> = new Set(); const senderWiresSet: Set<string> = new Set();
await oneShotIter(this.db, Stores.senderWires).forEach((x) => { await oneShotIter(this.db, Stores.senderWires).forEach(x => {
senderWiresSet.add(x.paytoUri); senderWiresSet.add(x.paytoUri);
}); });
@ -2974,12 +3152,16 @@ export class Wallet {
wire: req.senderWire, wire: req.senderWire,
}; };
await runWithWriteTransaction(this.db, [Stores.coinsReturns, Stores.coins], async (tx) => { await runWithWriteTransaction(
await tx.put(Stores.coinsReturns, coinsReturnRecord); this.db,
for (let c of payCoinInfo.updatedCoins) { [Stores.coinsReturns, Stores.coins],
await tx.put(Stores.coins, c); async tx => {
} await tx.put(Stores.coinsReturns, coinsReturnRecord);
}); for (let c of payCoinInfo.updatedCoins) {
await tx.put(Stores.coins, c);
}
},
);
this.badge.showNotification(); this.badge.showNotification();
this.notifier.notify(); this.notifier.notify();
@ -3029,7 +3211,11 @@ export class Wallet {
// FIXME: verify signature // FIXME: verify signature
// For every successful deposit, we replace the old record with an updated one // For every successful deposit, we replace the old record with an updated one
const currentCrr = await oneShotGet(this.db, Stores.coinsReturns, coinsReturnRecord.contractTermsHash); const currentCrr = await oneShotGet(
this.db,
Stores.coinsReturns,
coinsReturnRecord.contractTermsHash,
);
if (!currentCrr) { if (!currentCrr) {
console.error("database inconsistent"); console.error("database inconsistent");
continue; continue;
@ -3114,7 +3300,11 @@ export class Wallet {
} }
private async submitRefunds(contractTermsHash: string): Promise<void> { private async submitRefunds(contractTermsHash: string): Promise<void> {
const purchase = await oneShotGet(this.db, Stores.purchases, contractTermsHash); const purchase = await oneShotGet(
this.db,
Stores.purchases,
contractTermsHash,
);
if (!purchase) { if (!purchase) {
console.error( console.error(
"not submitting refunds, contract terms not found:", "not submitting refunds, contract terms not found:",
@ -3177,10 +3367,18 @@ export class Wallet {
return c; return c;
}; };
await runWithWriteTransaction(this.db, [Stores.purchases, Stores.coins], async (tx) => { await runWithWriteTransaction(
await tx.mutate(Stores.purchases, contractTermsHash, transformPurchase); this.db,
await tx.mutate(Stores.coins, perm.coin_pub, transformCoin); [Stores.purchases, Stores.coins],
}); async tx => {
await tx.mutate(
Stores.purchases,
contractTermsHash,
transformPurchase,
);
await tx.mutate(Stores.coins, perm.coin_pub, transformCoin);
},
);
this.refresh(perm.coin_pub); this.refresh(perm.coin_pub);
} }
@ -3200,7 +3398,11 @@ export class Wallet {
if (refundPermissions.length === 0) { if (refundPermissions.length === 0) {
throw Error("no refunds given"); throw Error("no refunds given");
} }
const coin0 = await oneShotGet(this.db, Stores.coins, refundPermissions[0].coin_pub); const coin0 = await oneShotGet(
this.db,
Stores.coins,
refundPermissions[0].coin_pub,
);
if (!coin0) { if (!coin0) {
throw Error("coin not found"); throw Error("coin not found");
} }
@ -3208,8 +3410,11 @@ export class Wallet {
Amounts.parseOrThrow(refundPermissions[0].refund_amount).currency, Amounts.parseOrThrow(refundPermissions[0].refund_amount).currency,
); );
const denoms = await oneShotIterIndex(this.db, Stores.denominations.exchangeBaseUrlIndex, const denoms = await oneShotIterIndex(
coin0.exchangeBaseUrl).toArray() this.db,
Stores.denominations.exchangeBaseUrlIndex,
coin0.exchangeBaseUrl,
).toArray();
for (const rp of refundPermissions) { for (const rp of refundPermissions) {
const coin = await oneShotGet(this.db, Stores.coins, rp.coin_pub); const coin = await oneShotGet(this.db, Stores.coins, rp.coin_pub);
@ -3258,7 +3463,10 @@ export class Wallet {
tipId: string, tipId: string,
merchantOrigin: string, merchantOrigin: string,
): Promise<void> { ): Promise<void> {
let tipRecord = await oneShotGet(this.db, Stores.tips, [tipId, merchantOrigin]); let tipRecord = await oneShotGet(this.db, Stores.tips, [
tipId,
merchantOrigin,
]);
if (!tipRecord) { if (!tipRecord) {
throw Error("tip not in database"); throw Error("tip not in database");
} }
@ -3282,7 +3490,7 @@ export class Wallet {
); );
const coinPubs: string[] = planchets.map(x => x.coinPub); const coinPubs: string[] = planchets.map(x => x.coinPub);
await oneShotMutate(this.db, Stores.tips, [tipId, merchantOrigin], (r) => { await oneShotMutate(this.db, Stores.tips, [tipId, merchantOrigin], r => {
if (!r.planchets) { if (!r.planchets) {
r.planchets = planchets; r.planchets = planchets;
r.coinPubs = coinPubs; r.coinPubs = coinPubs;
@ -3376,7 +3584,7 @@ export class Wallet {
let tipRecord = await oneShotGet(this.db, Stores.tips, [ let tipRecord = await oneShotGet(this.db, Stores.tips, [
res.tipId, res.tipId,
res.merchantOrigin, res.merchantOrigin,
]) ]);
if (!tipRecord) { if (!tipRecord) {
const withdrawDetails = await this.getWithdrawDetailsForAmount( const withdrawDetails = await this.getWithdrawDetailsForAmount(
@ -3423,7 +3631,11 @@ export class Wallet {
} }
async abortFailedPayment(contractTermsHash: string): Promise<void> { async abortFailedPayment(contractTermsHash: string): Promise<void> {
const purchase = await oneShotGet(this.db, Stores.purchases, contractTermsHash); const purchase = await oneShotGet(
this.db,
Stores.purchases,
contractTermsHash,
);
if (!purchase) { if (!purchase) {
throw Error("Purchase not found, unable to abort with refund"); throw Error("Purchase not found, unable to abort with refund");
} }
@ -3461,7 +3673,7 @@ export class Wallet {
const refundResponse = MerchantRefundResponse.checked(resp.responseJson); const refundResponse = MerchantRefundResponse.checked(resp.responseJson);
await this.acceptRefundResponse(refundResponse); await this.acceptRefundResponse(refundResponse);
await runWithWriteTransaction(this.db, [Stores.purchases], async (tx) => { await runWithWriteTransaction(this.db, [Stores.purchases], async tx => {
const p = await tx.get(Stores.purchases, purchase.contractTermsHash); const p = await tx.get(Stores.purchases, purchase.contractTermsHash);
if (!p) { if (!p) {
return; return;
@ -3535,9 +3747,9 @@ export class Wallet {
const refundsDoneAmounts = Object.values(purchase.refundsDone).map(x => const refundsDoneAmounts = Object.values(purchase.refundsDone).map(x =>
Amounts.parseOrThrow(x.refund_amount), Amounts.parseOrThrow(x.refund_amount),
); );
const refundsPendingAmounts = Object.values(purchase.refundsPending).map( const refundsPendingAmounts = Object.values(
x => Amounts.parseOrThrow(x.refund_amount), purchase.refundsPending,
); ).map(x => Amounts.parseOrThrow(x.refund_amount));
const totalRefundAmount = Amounts.sum([ const totalRefundAmount = Amounts.sum([
...refundsDoneAmounts, ...refundsDoneAmounts,
...refundsPendingAmounts, ...refundsPendingAmounts,

1283
yarn.lock

File diff suppressed because it is too large Load Diff