cleanup, avoid some circular deps

This commit is contained in:
Florian Dold 2019-12-16 16:59:09 +01:00
parent 35a7b76a7d
commit c2ee8fd9ab
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
12 changed files with 465 additions and 95 deletions

View File

@ -22,12 +22,10 @@ import BridgeIDBRequest from "./BridgeIDBRequest";
import compareKeys from "./util/cmp";
import {
DataError,
InvalidAccessError,
InvalidStateError,
ReadOnlyError,
TransactionInactiveError,
} from "./util/errors";
import extractKey from "./util/extractKey";
import structuredClone from "./util/structuredClone";
import {
CursorRange,
@ -41,7 +39,6 @@ import {
RecordGetRequest,
ResultLevel,
Backend,
DatabaseTransaction,
RecordStoreRequest,
StoreLevel,
} from "./backend-interface";

View File

@ -24,16 +24,11 @@ import BridgeIDBTransaction from "./BridgeIDBTransaction";
import {
ConstraintError,
DataError,
InvalidAccessError,
InvalidStateError,
NotFoundError,
ReadOnlyError,
TransactionInactiveError,
} from "./util/errors";
import extractKey from "./util/extractKey";
import fakeDOMStringList from "./util/fakeDOMStringList";
import structuredClone from "./util/structuredClone";
import {
FakeDOMStringList,
BridgeIDBCursorDirection,

View File

@ -4,28 +4,27 @@ import nodeResolve from 'rollup-plugin-node-resolve';
import json from '@rollup/plugin-json';
import builtins from 'builtin-modules'
const walletCli = {
input: 'dist/node/headless/taler-wallet-cli.js',
output: {
file: 'dist/standalone/taler-wallet-cli.js',
format: 'cjs'
},
external: builtins,
plugins: [
json(),
nodeResolve({
external: builtins,
preferBuiltins: true
preferBuiltins: true,
}),
commonjs({
include: ['node_modules/**', 'dist/node/**'],
extensions: [ '.js' ],
ignoreGlobal: false, // Default: false
sourceMap: false,
ignore: [ 'taler-wallet' ]
})
}),
json(),
]
};
@ -36,11 +35,11 @@ const walletAndroid = {
format: 'cjs',
exports: 'named',
},
external: builtins,
plugins: [
json(),
nodeResolve({
external: builtins,
preferBuiltins: true
}),

View File

@ -15,7 +15,6 @@
*/
import { InternalWalletState } from "./state";
import { WALLET_CACHE_BREAKER_CLIENT_VERSION } from "../wallet";
import { KeysJson, Denomination, ExchangeWireJson } from "../types/talerTypes";
import { getTimestampNow, OperationError } from "../types/walletTypes";
import {
@ -40,6 +39,7 @@ import {
OperationFailedAndReportedError,
guardOperationException,
} from "./errors";
import { WALLET_CACHE_BREAKER_CLIENT_VERSION } from "./versions";
async function denominationRecordFromKeys(
ws: InternalWalletState,

View File

@ -20,6 +20,7 @@ import {
getTimestampNow,
ConfirmReserveRequest,
OperationError,
AcceptWithdrawalResponse,
} from "../types/walletTypes";
import { canonicalizeBaseUrl } from "../util/helpers";
import { InternalWalletState } from "./state";
@ -38,7 +39,7 @@ import {
} from "../util/query";
import { Logger } from "../util/logging";
import * as Amounts from "../util/amounts";
import { updateExchangeFromUrl, getExchangeTrust } from "./exchanges";
import { updateExchangeFromUrl, getExchangeTrust, getExchangePaytoUri } from "./exchanges";
import { WithdrawOperationStatusResponse } from "../types/talerTypes";
import { assertUnreachable } from "../util/assertUnreachable";
import { encodeCrock, getRandomBytes } from "../crypto/talerCrypto";
@ -46,6 +47,7 @@ import { randomBytes } from "../crypto/primitives/nacl-fast";
import {
getVerifiedWithdrawDenomList,
processWithdrawSession,
getBankWithdrawalInfo,
} from "./withdraw";
import { guardOperationException, OperationFailedAndReportedError } from "./errors";
import { NotificationType } from "../types/notifications";
@ -652,3 +654,33 @@ async function depleteReserve(
console.trace("withdraw session already existed");
}
}
export async function createTalerWithdrawReserve(
ws: InternalWalletState,
talerWithdrawUri: string,
selectedExchange: string,
): Promise<AcceptWithdrawalResponse> {
const withdrawInfo = await getBankWithdrawalInfo(ws, talerWithdrawUri);
const exchangeWire = await getExchangePaytoUri(
ws,
selectedExchange,
withdrawInfo.wireTypes,
);
const reserve = await createReserve(ws, {
amount: withdrawInfo.amount,
bankWithdrawStatusUrl: withdrawInfo.extractedStatusUrl,
exchange: selectedExchange,
senderWire: withdrawInfo.senderWire,
exchangeWire: exchangeWire,
});
// We do this here, as the reserve should be registered before we return,
// so that we can redirect the user to the bank's status page.
await processReserveBankStatus(ws, reserve.reservePub);
console.log("acceptWithdrawal: returning");
return {
reservePub: reserve.reservePub,
confirmTransferUrl: withdrawInfo.confirmTransferUrl,
};
}

View File

@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
(C) 2019 GNUnet e.V.
(C) 2019 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@ -14,24 +14,41 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { InternalWalletState } from "./state";
import { parseTipUri } from "../util/taleruri";
import { TipStatus, getTimestampNow, OperationError } from "../types/walletTypes";
import { TipPickupGetResponse, TipPlanchetDetail, TipResponse } from "../types/talerTypes";
import {
TipStatus,
getTimestampNow,
OperationError,
} from "../types/walletTypes";
import {
TipPickupGetResponse,
TipPlanchetDetail,
TipResponse,
} from "../types/talerTypes";
import * as Amounts from "../util/amounts";
import { Stores, PlanchetRecord, WithdrawalSessionRecord, initRetryInfo, updateRetryInfoTimeout } from "../types/dbTypes";
import { getExchangeWithdrawalInfo, getVerifiedWithdrawDenomList, processWithdrawSession } from "./withdraw";
import {
Stores,
PlanchetRecord,
WithdrawalSessionRecord,
initRetryInfo,
updateRetryInfoTimeout,
} from "../types/dbTypes";
import {
getExchangeWithdrawalInfo,
getVerifiedWithdrawDenomList,
processWithdrawSession,
} from "./withdraw";
import { getTalerStampSec, extractTalerStampOrThrow } from "../util/helpers";
import { updateExchangeFromUrl } from "./exchanges";
import { getRandomBytes, encodeCrock } from "../crypto/talerCrypto";
import { guardOperationException } from "./errors";
import { NotificationType } from "../types/notifications";
export async function getTipStatus(
ws: InternalWalletState,
talerTipUri: string): Promise<TipStatus> {
talerTipUri: string,
): Promise<TipStatus> {
const res = parseTipUri(talerTipUri);
if (!res) {
throw Error("invalid taler://tip URI");
@ -134,19 +151,22 @@ export async function processTip(
forceNow: boolean = false,
): Promise<void> {
const onOpErr = (e: OperationError) => incrementTipRetry(ws, tipId, e);
await guardOperationException(() => processTipImpl(ws, tipId, forceNow), onOpErr);
await guardOperationException(
() => processTipImpl(ws, tipId, forceNow),
onOpErr,
);
}
async function resetTipRetry(
ws: InternalWalletState,
tipId: string,
): Promise<void> {
await ws.db.mutate(Stores.tips, tipId, (x) => {
await ws.db.mutate(Stores.tips, tipId, x => {
if (x.retryInfo.active) {
x.retryInfo = initRetryInfo();
}
return x;
})
});
}
async function processTipImpl(
@ -248,7 +268,7 @@ async function processTipImpl(
const withdrawalSessionId = encodeCrock(getRandomBytes(32));
const withdrawalSession: WithdrawalSessionRecord = {
denoms: planchets.map((x) => x.denomPub),
denoms: planchets.map(x => x.denomPub),
exchangeBaseUrl: tipRecord.exchangeUrl,
planchets: planchets,
source: {
@ -258,29 +278,31 @@ async function processTipImpl(
timestampStart: getTimestampNow(),
withdrawSessionId: withdrawalSessionId,
rawWithdrawalAmount: tipRecord.amount,
withdrawn: planchets.map((x) => false),
totalCoinValue: Amounts.sum(planchets.map((p) => p.coinValue)).amount,
withdrawn: planchets.map(x => false),
totalCoinValue: Amounts.sum(planchets.map(p => p.coinValue)).amount,
lastErrorPerCoin: {},
retryInfo: initRetryInfo(),
timestampFinish: undefined,
lastError: undefined,
};
await ws.db.runWithWriteTransaction(
[Stores.tips, Stores.withdrawalSession],
async tx => {
const tr = await tx.get(Stores.tips, tipId);
if (!tr) {
return;
}
if (tr.pickedUp) {
return;
}
tr.pickedUp = true;
tr.retryInfo = initRetryInfo(false);
await ws.db.runWithWriteTransaction([Stores.tips, Stores.withdrawalSession], async (tx) => {
const tr = await tx.get(Stores.tips, tipId);
if (!tr) {
return;
}
if (tr.pickedUp) {
return;
}
tr.pickedUp = true;
tr.retryInfo = initRetryInfo(false);
await tx.put(Stores.tips, tr);
await tx.put(Stores.withdrawalSession, withdrawalSession);
});
await tx.put(Stores.tips, tr);
await tx.put(Stores.withdrawalSession, withdrawalSession);
},
);
await processWithdrawSession(ws, withdrawalSessionId);

View File

@ -0,0 +1,32 @@
/*
This file is part of GNU Taler
(C) 2019 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* Wallet protocol version spoken with the exchange
* and merchant.
*
* Uses libtool's current:revision:age versioning.
*/
export const WALLET_EXCHANGE_PROTOCOL_VERSION = "6";
/**
* Cache breaker that is appended to queries such as /keys and /wire
* to break through caching, if it has been accidentally/badly configured
* by the exchange.
*
* This is only a temporary measure.
*/
export const WALLET_CACHE_BREAKER_CLIENT_VERSION = "3";

View File

@ -38,16 +38,11 @@ import { WithdrawOperationStatusResponse } from "../types/talerTypes";
import { InternalWalletState } from "./state";
import { parseWithdrawUri } from "../util/taleruri";
import { Logger } from "../util/logging";
import {
Database
} from "../util/query";
import {
updateExchangeFromUrl,
getExchangePaytoUri,
getExchangeTrust,
} from "./exchanges";
import { createReserve, processReserveBankStatus } from "./reserves";
import { WALLET_PROTOCOL_VERSION } from "../wallet";
import { WALLET_EXCHANGE_PROTOCOL_VERSION } from "./versions";
import * as LibtoolVersion from "../util/libtoolVersion";
import { guardOperationException } from "./errors";
@ -103,7 +98,7 @@ export function getWithdrawDenomList(
* Get information about a withdrawal from
* a taler://withdraw URI by asking the bank.
*/
async function getBankWithdrawalInfo(
export async function getBankWithdrawalInfo(
ws: InternalWalletState,
talerWithdrawUri: string,
): Promise<BankWithdrawDetails> {
@ -130,33 +125,6 @@ async function getBankWithdrawalInfo(
};
}
export async function acceptWithdrawal(
ws: InternalWalletState,
talerWithdrawUri: string,
selectedExchange: string,
): Promise<AcceptWithdrawalResponse> {
const withdrawInfo = await getBankWithdrawalInfo(ws, talerWithdrawUri);
const exchangeWire = await getExchangePaytoUri(
ws,
selectedExchange,
withdrawInfo.wireTypes,
);
const reserve = await createReserve(ws, {
amount: withdrawInfo.amount,
bankWithdrawStatusUrl: withdrawInfo.extractedStatusUrl,
exchange: selectedExchange,
senderWire: withdrawInfo.senderWire,
exchangeWire: exchangeWire,
});
// We do this here, as the reserve should be registered before we return,
// so that we can redirect the user to the bank's status page.
await processReserveBankStatus(ws, reserve.reservePub);
console.log("acceptWithdrawal: returning");
return {
reservePub: reserve.reservePub,
confirmTransferUrl: withdrawInfo.confirmTransferUrl,
};
}
async function getPossibleDenoms(
ws: InternalWalletState,
@ -619,7 +587,7 @@ export async function getExchangeWithdrawalInfo(
let versionMatch;
if (exchangeDetails.protocolVersion) {
versionMatch = LibtoolVersion.compare(
WALLET_PROTOCOL_VERSION,
WALLET_EXCHANGE_PROTOCOL_VERSION,
exchangeDetails.protocolVersion,
);
@ -629,7 +597,7 @@ export async function getExchangeWithdrawalInfo(
versionMatch.currentCmp === -1
) {
console.warn(
`wallet version ${WALLET_PROTOCOL_VERSION} might be outdated ` +
`wallet's support for exchange protocol version ${WALLET_EXCHANGE_PROTOCOL_VERSION} might be outdated ` +
`(exchange has ${exchangeDetails.protocolVersion}), checking for updates`,
);
}
@ -655,7 +623,7 @@ export async function getExchangeWithdrawalInfo(
selectedDenoms,
trustedAuditorPubs,
versionMatch,
walletVersion: WALLET_PROTOCOL_VERSION,
walletVersion: WALLET_EXCHANGE_PROTOCOL_VERSION,
wireFees: exchangeWireInfo,
withdrawFee: acc,
termsOfServiceAccepted: tosAccepted,

View File

@ -0,0 +1,61 @@
/*
This file is part of GNU Taler
(C) 2019 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* @author Florian Dold <dold@taler.net>
*/
/**
* Imports.
*/
import {
codecForString,
typecheckedCodec,
makeCodecForObject,
makeCodecForConstString,
makeCodecForUnion,
makeCodecForList,
} from "../util/codec";
import { runBlock } from "../util/helpers";
import { AmountString } from "./talerTypes";
import { ReserveTransaction, codecForReserveTransaction } from "./ReserveTransaction";
/**
* Status of a reserve.
*
* Schema type for the exchange's response to "/reserve/status".
*/
export interface ReserveStatus {
/**
* Balance left in the reserve.
*/
balance: AmountString;
/**
* Transaction history for the reserve.
*/
history: ReserveTransaction[];
}
export const codecForReserveStatus = runBlock(() => (
typecheckedCodec<ReserveStatus>(
makeCodecForObject<ReserveStatus>()
.property("balance", codecForString)
.property("history", makeCodecForList(codecForReserveTransaction))
.build("ReserveStatus")
)
));

View File

@ -0,0 +1,273 @@
/*
This file is part of GNU Taler
(C) 2019 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* @author Florian Dold <dold@taler.net>
*/
/**
* Imports.
*/
import {
codecForString,
typecheckedCodec,
makeCodecForObject,
makeCodecForConstString,
makeCodecForUnion,
} from "../util/codec";
import { runBlock } from "../util/helpers";
import {
AmountString,
Base32String,
EddsaSignatureString,
TimestampString,
EddsaPublicKeyString,
CoinPublicKeyString,
} from "./talerTypes";
export const enum ReserveTransactionType {
Withdraw = "WITHDRAW",
Deposit = "DEPOSIT",
Payback = "PAYBACK",
Closing = "CLOSING",
}
export interface ReserveWithdrawTransaction {
type: ReserveTransactionType.Withdraw;
/**
* Amount withdrawn.
*/
amount: AmountString;
/**
* Hash of the denomination public key of the coin.
*/
h_denom_pub: Base32String;
/**
* Hash of the blinded coin to be signed
*/
h_coin_envelope: Base32String;
/**
* Signature of 'TALER_WithdrawRequestPS' created with the reserves's
* private key.
*/
reserve_sig: EddsaSignatureString;
/**
* Fee that is charged for withdraw.
*/
withdraw_fee: AmountString;
}
export interface ReserveDepositTransaction {
type: ReserveTransactionType.Deposit;
/**
* Amount withdrawn.
*/
amount: AmountString;
/**
* Sender account payto://-URL
*/
sender_account_url: string;
/**
* Transfer details uniquely identifying the transfer.
*/
wire_reference: string;
/**
* Timestamp of the incoming wire transfer.
*/
timestamp: TimestampString;
}
export interface ReserveClosingTransaction {
type: ReserveTransactionType.Closing;
/**
* Closing balance.
*/
amount: AmountString;
/**
* Closing fee charged by the exchange.
*/
closing_fee: AmountString;
/**
* Wire transfer subject.
*/
wtid: string;
/**
* Hash of the wire account into which the funds were returned to.
*/
h_wire: string;
/**
* This is a signature over a
* struct TALER_ReserveCloseConfirmationPS with purpose
* TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED.
*/
exchange_sig: EddsaSignatureString;
/**
* Public key used to create exchange_sig.
*/
exchange_pub: EddsaPublicKeyString;
/**
* Time when the reserve was closed.
*/
timestamp: TimestampString;
}
export interface ReservePaybackTransaction {
type: ReserveTransactionType.Payback;
/**
* Amount paid back.
*/
amount: AmountString;
/**
* Receiver account details.
*/
receiver_account_details: any;
/**
* Wire transfer identifier.
*/
wire_transfer: any;
/**
* This is a signature over
* a struct TALER_PaybackConfirmationPS with purpose
* TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK.
*/
exchange_sig: EddsaSignatureString;
/**
* Public key used to create exchange_sig.
*/
exchange_pub: EddsaPublicKeyString;
/**
* Time when the funds were paid back into the reserve.
*/
timestamp: TimestampString;
/**
* Public key of the coin that was paid back.
*/
coin_pub: CoinPublicKeyString;
}
/**
* Format of the exchange's transaction history for a reserve.
*/
export type ReserveTransaction =
| ReserveWithdrawTransaction
| ReserveDepositTransaction
| ReserveClosingTransaction
| ReservePaybackTransaction;
export const codecForReserveWithdrawTransaction = runBlock(() =>
typecheckedCodec<ReserveWithdrawTransaction>(
makeCodecForObject<ReserveWithdrawTransaction>()
.property("amount", codecForString)
.property("h_coin_envelope", codecForString)
.property("h_denom_pub", codecForString)
.property("reserve_sig", codecForString)
.property(
"type",
makeCodecForConstString(ReserveTransactionType.Withdraw),
)
.property("withdraw_fee", codecForString)
.build("ReserveWithdrawTransaction"),
),
);
export const codecForReserveDepositTransaction = runBlock(() =>
typecheckedCodec<ReserveDepositTransaction>(
makeCodecForObject<ReserveDepositTransaction>()
.property("amount", codecForString)
.property("sender_account_url", codecForString)
.property("timestamp", codecForString)
.property("wire_reference", codecForString)
.property("type", makeCodecForConstString(ReserveTransactionType.Deposit))
.build("ReserveDepositTransaction"),
),
);
export const codecForReserveClosingTransaction = runBlock(() =>
typecheckedCodec<ReserveClosingTransaction>(
makeCodecForObject<ReserveClosingTransaction>()
.property("amount", codecForString)
.property("closing_fee", codecForString)
.property("exchange_pub", codecForString)
.property("exchange_sig", codecForString)
.property("h_wire", codecForString)
.property("timestamp", codecForString)
.property("type", makeCodecForConstString(ReserveTransactionType.Closing))
.property("wtid", codecForString)
.build("ReserveClosingTransaction"),
),
);
export const codecForReservePaybackTransaction = runBlock(() =>
typecheckedCodec<ReservePaybackTransaction>(
makeCodecForObject<ReservePaybackTransaction>()
.property("amount", codecForString)
.property("coin_pub", codecForString)
.property("exchange_pub", codecForString)
.property("exchange_sig", codecForString)
.property("receiver_account_details", codecForString)
.property("timestamp", codecForString)
.property("type", makeCodecForConstString(ReserveTransactionType.Payback))
.property("wire_transfer", codecForString)
.build("ReservePaybackTransaction"),
),
);
export const codecForReserveTransaction = runBlock(() =>
typecheckedCodec<ReserveTransaction>(
makeCodecForUnion<ReserveTransaction>()
.discriminateOn("type")
.alternative(
ReserveTransactionType.Withdraw,
codecForReserveWithdrawTransaction,
)
.alternative(
ReserveTransactionType.Closing,
codecForReserveClosingTransaction,
)
.alternative(
ReserveTransactionType.Payback,
codecForReservePaybackTransaction,
)
.alternative(
ReserveTransactionType.Deposit,
codecForReserveDepositTransaction,
)
.build<ReserveTransaction>("ReserveTransaction"),
),
);

View File

@ -30,7 +30,6 @@ import { AmountJson } from "./util/amounts";
import * as Amounts from "./util/amounts";
import {
acceptWithdrawal,
getWithdrawDetailsForUri,
getExchangeWithdrawalInfo,
} from "./operations/withdraw";
@ -82,7 +81,7 @@ import {
getExchangePaytoUri,
acceptExchangeTermsOfService,
} from "./operations/exchanges";
import { processReserve } from "./operations/reserves";
import { processReserve, createTalerWithdrawReserve } from "./operations/reserves";
import { InternalWalletState } from "./operations/state";
import { createReserve, confirmReserve } from "./operations/reserves";
@ -111,15 +110,6 @@ import {
applyRefund,
} from "./operations/refund";
/**
* Wallet protocol version spoken with the exchange
* and merchant.
*
* Uses libtool's current:revision:age versioning.
*/
export const WALLET_PROTOCOL_VERSION = "3:0:0";
export const WALLET_CACHE_BREAKER_CLIENT_VERSION = "3";
const builtinCurrencies: CurrencyRecord[] = [
{
@ -690,7 +680,7 @@ export class Wallet {
selectedExchange: string,
): Promise<AcceptWithdrawalResponse> {
try {
return acceptWithdrawal(this.ws, talerWithdrawUri, selectedExchange);
return createTalerWithdrawReserve(this.ws, talerWithdrawUri, selectedExchange);
} finally {
this.latch.trigger();
}

View File

@ -59,6 +59,7 @@
"src/operations/return.ts",
"src/operations/state.ts",
"src/operations/tip.ts",
"src/operations/versions.ts",
"src/operations/withdraw.ts",
"src/types/ReserveStatus.ts",
"src/types/ReserveTransaction.ts",