From 43d338907e067a5e4757f40ba231e9e101a86e54 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Mon, 22 Jul 2024 15:29:20 +0200 Subject: harness: fix idempotency test --- .../integrationtests/test-withdrawal-idempotent.ts | 175 +++++++++++++-------- 1 file changed, 113 insertions(+), 62 deletions(-) (limited to 'packages/taler-harness/src/integrationtests/test-withdrawal-idempotent.ts') diff --git a/packages/taler-harness/src/integrationtests/test-withdrawal-idempotent.ts b/packages/taler-harness/src/integrationtests/test-withdrawal-idempotent.ts index a7f9f51ea..0daa53f64 100644 --- a/packages/taler-harness/src/integrationtests/test-withdrawal-idempotent.ts +++ b/packages/taler-harness/src/integrationtests/test-withdrawal-idempotent.ts @@ -17,18 +17,33 @@ /** * Imports. */ -import { AmountString, j2s, TalerError } from "@gnu-taler/taler-util"; +import { + AgeRestriction, + Amounts, + AmountString, + codecForExchangeWithdrawBatchResponse, + encodeCrock, + ExchangeBatchWithdrawRequest, + getRandomBytes, +} from "@gnu-taler/taler-util"; +import { + HttpRequestLibrary, + readSuccessResponseJsonOrThrow, +} from "@gnu-taler/taler-util/http"; import { CryptoDispatcher, SynchronousCryptoWorkerFactoryPlain, + TalerCryptoInterface, } from "@gnu-taler/taler-wallet-core"; import { checkReserve, + CoinInfo, downloadExchangeInfo, findDenomOrThrow, + ReserveKeypair, topupReserveWithBank, - withdrawCoin, } from "@gnu-taler/taler-wallet-core/dbless"; +import { DenominationRecord } from "../../../taler-wallet-core/src/db.js"; import { GlobalTestState, harnessHttpLib } from "../harness/harness.js"; import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js"; @@ -46,76 +61,112 @@ export async function runWithdrawalIdempotentTest(t: GlobalTestState) { ); const cryptoApi = cryptiDisp.cryptoApi; - try { - // Withdraw digital cash into the wallet. + const exchangeInfo = await downloadExchangeInfo(exchange.baseUrl, http); - const exchangeInfo = await downloadExchangeInfo(exchange.baseUrl, http); + const reserveKeyPair = await cryptoApi.createEddsaKeypair({}); - const reserveKeyPair = await cryptoApi.createEddsaKeypair({}); + let reserveUrl = new URL(`reserves/${reserveKeyPair.pub}`, exchange.baseUrl); + reserveUrl.searchParams.set("timeout_ms", "30000"); + const longpollReq = http.fetch(reserveUrl.href, { + method: "GET", + }); - let reserveUrl = new URL( - `reserves/${reserveKeyPair.pub}`, - exchange.baseUrl, - ); - reserveUrl.searchParams.set("timeout_ms", "30000"); - const longpollReq = http.fetch(reserveUrl.href, { - method: "GET", - }); - - await topupReserveWithBank({ - amount: "TESTKUDOS:10" as AmountString, - http, - reservePub: reserveKeyPair.pub, - corebankApiBaseUrl: bank.corebankApiBaseUrl, - exchangeInfo, - }); + await topupReserveWithBank({ + amount: "TESTKUDOS:10" as AmountString, + http, + reservePub: reserveKeyPair.pub, + corebankApiBaseUrl: bank.corebankApiBaseUrl, + exchangeInfo, + }); - console.log("waiting for longpoll request"); - const resp = await longpollReq; - console.log(`got response, status ${resp.status}`); + console.log("waiting for longpoll request"); + const resp = await longpollReq; + console.log(`got response, status ${resp.status}`); - console.log(exchangeInfo); + console.log(exchangeInfo); - await checkReserve(http, exchange.baseUrl, reserveKeyPair.pub); - const denomselAllowLate = false; + await checkReserve(http, exchange.baseUrl, reserveKeyPair.pub); + const denomselAllowLate = false; - const d1 = findDenomOrThrow(exchangeInfo, "TESTKUDOS:8" as AmountString, { - denomselAllowLate, - }); + const d1 = findDenomOrThrow(exchangeInfo, "TESTKUDOS:8" as AmountString, { + denomselAllowLate, + }); - const coin = await withdrawCoin({ - http, - cryptoApi, - reserveKeyPair: { - reservePriv: reserveKeyPair.priv, - reservePub: reserveKeyPair.pub, + await myWithdrawCoin({ + http, + cryptoApi, + reserveKeyPair: { + reservePriv: reserveKeyPair.priv, + reservePub: reserveKeyPair.pub, + }, + denom: d1, + exchangeBaseUrl: exchange.baseUrl, + }); +} + +async function myWithdrawCoin(args: { + http: HttpRequestLibrary; + cryptoApi: TalerCryptoInterface; + reserveKeyPair: ReserveKeypair; + denom: DenominationRecord; + exchangeBaseUrl: string; +}): Promise { + const { http, cryptoApi, reserveKeyPair, denom, exchangeBaseUrl } = args; + const planchet = await cryptoApi.createPlanchet({ + coinIndex: 0, + denomPub: denom.denomPub, + feeWithdraw: Amounts.parseOrThrow(denom.fees.feeWithdraw), + reservePriv: reserveKeyPair.reservePriv, + reservePub: reserveKeyPair.reservePub, + secretSeed: encodeCrock(getRandomBytes(32)), + value: Amounts.parseOrThrow(denom.value), + }); + + const reqBody: ExchangeBatchWithdrawRequest = { + planchets: [ + { + denom_pub_hash: planchet.denomPubHash, + reserve_sig: planchet.withdrawSig, + coin_ev: planchet.coinEv, }, - denom: d1, - exchangeBaseUrl: exchange.baseUrl, - }); - - { - // Do it again for idempotency! - const coin2 = await withdrawCoin({ - http, - cryptoApi, - reserveKeyPair: { - reservePriv: reserveKeyPair.priv, - reservePub: reserveKeyPair.pub, - }, - denom: d1, - exchangeBaseUrl: exchange.baseUrl, - }); - } - } catch (e) { - if (e instanceof TalerError) { - console.log(e); - console.log(j2s(e.errorDetail)); - } else { - console.log(e); - } - throw e; + ], + }; + const reqUrl = new URL( + `reserves/${planchet.reservePub}/batch-withdraw`, + exchangeBaseUrl, + ).href; + + const resp = await http.fetch(reqUrl, { method: "POST", body: reqBody }); + const rBatch = await readSuccessResponseJsonOrThrow( + resp, + codecForExchangeWithdrawBatchResponse(), + ); + + { + // Check for idempotency! + const resp2 = await http.fetch(reqUrl, { method: "POST", body: reqBody }); + await readSuccessResponseJsonOrThrow( + resp2, + codecForExchangeWithdrawBatchResponse(), + ); } + + const ubSig = await cryptoApi.unblindDenominationSignature({ + planchet, + evSig: rBatch.ev_sigs[0].ev_sig, + }); + + return { + coinPriv: planchet.coinPriv, + coinPub: planchet.coinPub, + denomSig: ubSig, + denomPub: denom.denomPub, + denomPubHash: denom.denomPubHash, + feeDeposit: Amounts.stringify(denom.fees.feeDeposit), + feeRefresh: Amounts.stringify(denom.fees.feeRefresh), + exchangeBaseUrl: args.exchangeBaseUrl, + maxAge: AgeRestriction.AGE_UNRESTRICTED, + }; } runWithdrawalIdempotentTest.suites = ["wallet"]; -- cgit v1.2.3