diff options
author | Florian Dold <florian.dold@gmail.com> | 2019-12-16 16:59:09 +0100 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2019-12-16 17:00:46 +0100 |
commit | c2ee8fd9ab6754275d7423152681236a46cf36a9 (patch) | |
tree | c7306f6575f89e3b66226fdaf7af342b0dc973c2 /src | |
parent | 35a7b76a7d935dc2c749fd39ac80c6af1096b795 (diff) |
cleanup, avoid some circular deps
Diffstat (limited to 'src')
-rw-r--r-- | src/operations/exchanges.ts | 2 | ||||
-rw-r--r-- | src/operations/reserves.ts | 34 | ||||
-rw-r--r-- | src/operations/tip.ts | 78 | ||||
-rw-r--r-- | src/operations/versions.ts | 32 | ||||
-rw-r--r-- | src/operations/withdraw.ts | 42 | ||||
-rw-r--r-- | src/types/ReserveStatus.ts | 61 | ||||
-rw-r--r-- | src/types/ReserveTransaction.ts | 273 | ||||
-rw-r--r-- | src/wallet.ts | 14 |
8 files changed, 457 insertions, 79 deletions
diff --git a/src/operations/exchanges.ts b/src/operations/exchanges.ts index 871ee1138..d9adc7c52 100644 --- a/src/operations/exchanges.ts +++ b/src/operations/exchanges.ts @@ -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, diff --git a/src/operations/reserves.ts b/src/operations/reserves.ts index 7b0a6886e..649bf75f2 100644 --- a/src/operations/reserves.ts +++ b/src/operations/reserves.ts @@ -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, + }; +}
\ No newline at end of file diff --git a/src/operations/tip.ts b/src/operations/tip.ts index df4b7990e..76d0df22d 100644 --- a/src/operations/tip.ts +++ b/src/operations/tip.ts @@ -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); diff --git a/src/operations/versions.ts b/src/operations/versions.ts new file mode 100644 index 000000000..393bdd887 --- /dev/null +++ b/src/operations/versions.ts @@ -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";
\ No newline at end of file diff --git a/src/operations/withdraw.ts b/src/operations/withdraw.ts index 6e7cdf512..38baffa8d 100644 --- a/src/operations/withdraw.ts +++ b/src/operations/withdraw.ts @@ -39,15 +39,10 @@ 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, diff --git a/src/types/ReserveStatus.ts b/src/types/ReserveStatus.ts new file mode 100644 index 000000000..d9b5d9496 --- /dev/null +++ b/src/types/ReserveStatus.ts @@ -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") + ) +));
\ No newline at end of file diff --git a/src/types/ReserveTransaction.ts b/src/types/ReserveTransaction.ts new file mode 100644 index 000000000..2ec859498 --- /dev/null +++ b/src/types/ReserveTransaction.ts @@ -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"), + ), +); diff --git a/src/wallet.ts b/src/wallet.ts index 8decbc00a..1ea8c2fd2 100644 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -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(); } |