From e951075d2ef52fa8e9e7489c62031777c3a7e66b Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Mon, 19 Feb 2024 18:05:48 +0100 Subject: wallet-core: flatten directory structure --- packages/taler-wallet-core/src/pay-peer-common.ts | 172 ++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 packages/taler-wallet-core/src/pay-peer-common.ts (limited to 'packages/taler-wallet-core/src/pay-peer-common.ts') diff --git a/packages/taler-wallet-core/src/pay-peer-common.ts b/packages/taler-wallet-core/src/pay-peer-common.ts new file mode 100644 index 000000000..5d3aac915 --- /dev/null +++ b/packages/taler-wallet-core/src/pay-peer-common.ts @@ -0,0 +1,172 @@ +/* + This file is part of GNU Taler + (C) 2022 GNUnet e.V. + + 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 + */ + +/** + * Imports. + */ +import { + AmountJson, + AmountString, + Amounts, + Codec, + Logger, + TalerProtocolTimestamp, + buildCodecForObject, + codecForAmountString, + codecForTimestamp, + codecOptional, +} from "@gnu-taler/taler-util"; +import { SpendCoinDetails } from "./crypto/cryptoImplementation.js"; +import { PeerPushPaymentCoinSelection, ReserveRecord } from "./db.js"; +import { InternalWalletState } from "./internal-wallet-state.js"; +import type { SelectedPeerCoin } from "./util/coinSelection.js"; +import { checkDbInvariant } from "./util/invariants.js"; +import { getTotalRefreshCost } from "./refresh.js"; +import { getCandidateWithdrawalDenomsTx } from "./withdraw.js"; + +const logger = new Logger("operations/peer-to-peer.ts"); + +/** + * Get information about the coin selected for signatures. + */ +export async function queryCoinInfosForSelection( + ws: InternalWalletState, + csel: PeerPushPaymentCoinSelection, +): Promise { + let infos: SpendCoinDetails[] = []; + await ws.db.runReadOnlyTx(["coins", "denominations"], async (tx) => { + for (let i = 0; i < csel.coinPubs.length; i++) { + const coin = await tx.coins.get(csel.coinPubs[i]); + if (!coin) { + throw Error("coin not found anymore"); + } + const denom = await ws.getDenomInfo( + ws, + tx, + coin.exchangeBaseUrl, + coin.denomPubHash, + ); + if (!denom) { + throw Error("denom for coin not found anymore"); + } + infos.push({ + coinPriv: coin.coinPriv, + coinPub: coin.coinPub, + denomPubHash: coin.denomPubHash, + denomSig: coin.denomSig, + ageCommitmentProof: coin.ageCommitmentProof, + contribution: csel.contributions[i], + }); + } + }); + return infos; +} + +export async function getTotalPeerPaymentCost( + ws: InternalWalletState, + pcs: SelectedPeerCoin[], +): Promise { + const currency = Amounts.currencyOf(pcs[0].contribution); + return ws.db.runReadOnlyTx(["coins", "denominations"], async (tx) => { + const costs: AmountJson[] = []; + for (let i = 0; i < pcs.length; i++) { + const coin = await tx.coins.get(pcs[i].coinPub); + if (!coin) { + throw Error("can't calculate payment cost, coin not found"); + } + const denomInfo = await ws.getDenomInfo( + ws, + tx, + coin.exchangeBaseUrl, + coin.denomPubHash, + ); + if (!denomInfo) { + throw Error( + "can't calculate payment cost, denomination for coin not found", + ); + } + const allDenoms = await getCandidateWithdrawalDenomsTx( + ws, + tx, + coin.exchangeBaseUrl, + currency, + ); + const amountLeft = Amounts.sub( + denomInfo.value, + pcs[i].contribution, + ).amount; + const refreshCost = getTotalRefreshCost( + allDenoms, + denomInfo, + amountLeft, + ws.config.testing.denomselAllowLate, + ); + costs.push(Amounts.parseOrThrow(pcs[i].contribution)); + costs.push(refreshCost); + } + const zero = Amounts.zeroOfAmount(pcs[0].contribution); + return Amounts.sum([zero, ...costs]).amount; + }); +} + +interface ExchangePurseStatus { + balance: AmountString; + deposit_timestamp?: TalerProtocolTimestamp; + merge_timestamp?: TalerProtocolTimestamp; +} + +export const codecForExchangePurseStatus = (): Codec => + buildCodecForObject() + .property("balance", codecForAmountString()) + .property("deposit_timestamp", codecOptional(codecForTimestamp)) + .property("merge_timestamp", codecOptional(codecForTimestamp)) + .build("ExchangePurseStatus"); + +export async function getMergeReserveInfo( + ws: InternalWalletState, + req: { + exchangeBaseUrl: string; + }, +): Promise { + // We have to eagerly create the key pair outside of the transaction, + // due to the async crypto API. + const newReservePair = await ws.cryptoApi.createEddsaKeypair({}); + + const mergeReserveRecord: ReserveRecord = await ws.db.runReadWriteTx( + ["exchanges", "reserves"], + async (tx) => { + const ex = await tx.exchanges.get(req.exchangeBaseUrl); + checkDbInvariant(!!ex); + if (ex.currentMergeReserveRowId != null) { + const reserve = await tx.reserves.get(ex.currentMergeReserveRowId); + checkDbInvariant(!!reserve); + return reserve; + } + const reserve: ReserveRecord = { + reservePriv: newReservePair.priv, + reservePub: newReservePair.pub, + }; + const insertResp = await tx.reserves.put(reserve); + checkDbInvariant(typeof insertResp.key === "number"); + reserve.rowId = insertResp.key; + ex.currentMergeReserveRowId = reserve.rowId; + await tx.exchanges.put(ex); + return reserve; + }, + ); + + return mergeReserveRecord; +} -- cgit v1.2.3