commit bb89eca3f423d7365a409d56bbbdadd265e74bf4 parent 6acdbc3f0fb4537a5307defc83047578d0a411ea Author: Florian Dold <florian@dold.me> Date: Sat, 18 Jul 2026 17:54:13 +0200 fix payto helper deprecations (WIP) Diffstat:
19 files changed, 50 insertions(+), 160 deletions(-)
diff --git a/packages/taler-exchange-aml-webui/src/pages/Search.tsx b/packages/taler-exchange-aml-webui/src/pages/Search.tsx @@ -139,7 +139,7 @@ function ShowResult({ onNewDecision: (account: string, payto: string) => void; }): VNode { const paytoStr = Paytos.toNormalizedString(payto); - const account = encodeCrock(Paytos.hash(paytoStr)); + const account = encodeCrock(Paytos.hashFull(paytoStr)); const { i18n } = useTranslationContext(); const history = useAccountDecisions(account); diff --git a/packages/taler-exchange-aml-webui/src/pages/Transfers.tsx b/packages/taler-exchange-aml-webui/src/pages/Transfers.tsx @@ -19,7 +19,7 @@ import { Amounts, assertUnreachable, encodeCrock, - hashNormalizedPaytoUri, + Paytos, HttpStatusCode, PaytoHash, } from "@gnu-taler/taler-util"; @@ -287,7 +287,7 @@ export function Transfers({ </tr> {txs.map((item) => { const hashPayto = encodeCrock( - hashNormalizedPaytoUri(item.payto_uri), + Paytos.hashNormalized(item.payto_uri), ); return ( diff --git a/packages/taler-harness/src/harness/tops.ts b/packages/taler-harness/src/harness/tops.ts @@ -25,7 +25,6 @@ import { Duration, encodeCrock, getRandomBytes, - hashNormalizedPaytoUri, j2s, KycRule, LimitOperationType, @@ -916,7 +915,7 @@ export async function doTopsKycAuth( t.assertTrue(typeof kycStatus.body.kyc_data[0].access_token === "string"); accessToken = kycStatus.body.kyc_data[0].access_token as AccessToken; merchantPaytoHash = encodeCrock( - hashNormalizedPaytoUri(kycStatus.body.kyc_data[0].payto_uri), + Paytos.hashNormalized(kycStatus.body.kyc_data[0].payto_uri), ); return { accessToken, merchantPaytoHash }; } diff --git a/packages/taler-harness/src/index.ts b/packages/taler-harness/src/index.ts @@ -33,6 +33,7 @@ import { LoginTokenScope, MerchantAuthMethod, PaytoString, + Paytos, TalerBankConversionHttpClient, TalerCoreBankHttpClient, TalerExchangeHttpClient, @@ -52,8 +53,6 @@ import { encodeCrock, generateIban, getRandomBytes, - hashFullPaytoUri, - hashNormalizedPaytoUri, j2s, randomBytes, rsaBlind, @@ -270,20 +269,18 @@ advancedCli .action((args) => { console.log( `normalized base64: ${encodeCrock( - hashNormalizedPaytoUri(args.hashPayto.payto), + Paytos.hashNormalized(args.hashPayto.payto), )}`, ); console.log( `normalized hex: ${toHexString( - hashNormalizedPaytoUri(args.hashPayto.payto), + Paytos.hashNormalized(args.hashPayto.payto), )}`, ); console.log( - `full base64: ${encodeCrock(hashFullPaytoUri(args.hashPayto.payto))}`, - ); - console.log( - `full hex: ${toHexString(hashFullPaytoUri(args.hashPayto.payto))}`, + `full base64: ${encodeCrock(Paytos.hashFull(args.hashPayto.payto))}`, ); + console.log(`full hex: ${toHexString(Paytos.hashFull(args.hashPayto.payto))}`); }); advancedCli @@ -2113,9 +2110,7 @@ merchantCli const kyc_status = await exchangeApi.checkKycStatus({ accountPub: encodeCrock(merchantPub), accountSig: accountOwnerSig, - paytoHash: encodeCrock( - hashNormalizedPaytoUri(payto_uri), - ), + paytoHash: encodeCrock(Paytos.hashNormalized(payto_uri)), }); return { payto_uri, diff --git a/packages/taler-harness/src/integrationtests/test-exchange-kyc-auth.ts b/packages/taler-harness/src/integrationtests/test-exchange-kyc-auth.ts @@ -22,7 +22,7 @@ import { createEddsaKeyPair, encodeCrock, getRandomBytes, - hashNormalizedPaytoUri, + Paytos, HttpStatusCode, j2s, TalerWireGatewayHttpClient, @@ -134,7 +134,7 @@ export async function runExchangeKycAuthTest(t: GlobalTestState) { amount: "TESTKUDOS:20", }); - const kycPaytoHash = encodeCrock(hashNormalizedPaytoUri(merchantPayto)); + const kycPaytoHash = encodeCrock(Paytos.hashNormalized(merchantPayto)); await wres.withdrawalFinishedCond; diff --git a/packages/taler-harness/src/integrationtests/test-exchange-merchant-kyc-auth.ts b/packages/taler-harness/src/integrationtests/test-exchange-merchant-kyc-auth.ts @@ -21,7 +21,7 @@ import { Configuration, encodeCrock, getRandomBytes, - hashNormalizedPaytoUri, + Paytos, j2s, succeedOrThrow, TalerWireGatewayHttpClient, @@ -105,7 +105,7 @@ export async function runExchangeMerchantKycAuthTest(t: GlobalTestState) { exchangeBankAccount.wireGatewayApiBaseUrl, ); - const kycPaytoHash = encodeCrock(hashNormalizedPaytoUri(merchantPayto)); + const kycPaytoHash = encodeCrock(Paytos.hashNormalized(merchantPayto)); const merchantPair1 = await cryptoApi.createEddsaKeypair({}); const merchantPair2 = await cryptoApi.createEddsaKeypair({}); diff --git a/packages/taler-harness/src/integrationtests/test-kyc-balance-withdrawal.ts b/packages/taler-harness/src/integrationtests/test-kyc-balance-withdrawal.ts @@ -21,9 +21,9 @@ import { Configuration, encodeCrock, ExchangeWalletKycStatus, - hashFullPaytoUri, j2s, LimitOperationType, + Paytos, TalerProtocolDuration, TalerProtocolTimestamp, TalerWireGatewayHttpClient, @@ -143,7 +143,7 @@ export async function runKycBalanceWithdrawalTest(t: GlobalTestState) { t.assertTrue(!!kycReservePub); // FIXME: Create/use helper function for this! - const hPayto = hashFullPaytoUri( + const hPayto = Paytos.hashFull( `payto://taler-reserve-http/localhost:${exchange.port}/${kycReservePub}`, ); diff --git a/packages/taler-harness/src/integrationtests/test-kyc-decision-attr.ts b/packages/taler-harness/src/integrationtests/test-kyc-decision-attr.ts @@ -23,7 +23,7 @@ import { decodeCrock, Duration, encodeCrock, - hashNormalizedPaytoUri, + Paytos, j2s, LimitOperationType, signAmlQuery, @@ -75,7 +75,7 @@ export async function runKycDecisionAttrTest(t: GlobalTestState) { const cryptoApi = createSyncCryptoApi(); - const kycPaytoHash = encodeCrock(hashNormalizedPaytoUri(merchantPayto)); + const kycPaytoHash = encodeCrock(Paytos.hashNormalized(merchantPayto)); await postAmlDecision(t, { amlPriv: amlKeypair.priv, diff --git a/packages/taler-harness/src/integrationtests/test-kyc-decision-events.ts b/packages/taler-harness/src/integrationtests/test-kyc-decision-events.ts @@ -23,7 +23,7 @@ import { decodeCrock, Duration, encodeCrock, - hashNormalizedPaytoUri, + Paytos, j2s, LimitOperationType, signAmlQuery, @@ -68,7 +68,7 @@ export async function runKycDecisionEventsTest(t: GlobalTestState) { const merchantPayto = getTestHarnessPaytoForLabel("merchant-default"); - const kycPaytoHash = encodeCrock(hashNormalizedPaytoUri(merchantPayto)); + const kycPaytoHash = encodeCrock(Paytos.hashNormalized(merchantPayto)); await postAmlDecision(t, { amlPriv: amlKeypair.priv, diff --git a/packages/taler-harness/src/integrationtests/test-kyc-decisions.ts b/packages/taler-harness/src/integrationtests/test-kyc-decisions.ts @@ -25,7 +25,7 @@ import { Duration, encodeCrock, getRandomBytes, - hashNormalizedPaytoUri, + Paytos, HttpStatusCode, j2s, LimitOperationType, @@ -108,7 +108,7 @@ export async function runKycDecisionsTest(t: GlobalTestState) { accountPub: merchantPair.pub, }); - const kycPaytoHash = encodeCrock(hashNormalizedPaytoUri(merchantPayto)); + const kycPaytoHash = encodeCrock(Paytos.hashNormalized(merchantPayto)); const wres = await withdrawViaBankV3(t, { walletClient, diff --git a/packages/taler-harness/src/integrationtests/test-kyc-exchange-wallet.ts b/packages/taler-harness/src/integrationtests/test-kyc-exchange-wallet.ts @@ -20,8 +20,8 @@ import { encodeCrock, ExchangeWalletKycStatus, - hashFullPaytoUri, j2s, + Paytos, } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { @@ -90,8 +90,7 @@ export async function runKycExchangeWalletTest(t: GlobalTestState) { t.assertTrue(!!kycReservePub); - // FIXME: Create/user helper function for this! - const hPayto = hashFullPaytoUri( + const hPayto = Paytos.hashFull( `payto://taler-reserve-http/localhost:${exchange.port}/${kycReservePub}`, ); diff --git a/packages/taler-harness/src/integrationtests/test-kyc-merchant-deposit-rewrite.ts b/packages/taler-harness/src/integrationtests/test-kyc-merchant-deposit-rewrite.ts @@ -20,7 +20,7 @@ import { Configuration, encodeCrock, - hashNormalizedPaytoUri, + Paytos, j2s, Logger, MerchantAccountKycStatus, @@ -236,7 +236,7 @@ export async function runKycMerchantDepositRewriteTest(t: GlobalTestState) { ); merchantAmlAccount = encodeCrock( - hashNormalizedPaytoUri(firstBankAccount.payto_uri), + Paytos.hashNormalized(firstBankAccount.payto_uri), ); } logger.info( diff --git a/packages/taler-harness/src/integrationtests/test-kyc-merchant-deposit.ts b/packages/taler-harness/src/integrationtests/test-kyc-merchant-deposit.ts @@ -23,7 +23,7 @@ import { codecForQueryInstancesResponse, Configuration, encodeCrock, - hashNormalizedPaytoUri, + Paytos, j2s, Logger, MerchantAccountKycRedirectsResponse, @@ -221,7 +221,7 @@ export async function runKycMerchantDepositTest(t: GlobalTestState) { amlPub: amlKeypair.pub, exchangeBaseUrl: exchange.baseUrl, paytoHash: encodeCrock( - hashNormalizedPaytoUri(kycRespTwo.kyc_data[0].payto_uri), + Paytos.hashNormalized(kycRespTwo.kyc_data[0].payto_uri), ), }); diff --git a/packages/taler-harness/src/integrationtests/test-tops-aml-basic.ts b/packages/taler-harness/src/integrationtests/test-tops-aml-basic.ts @@ -20,7 +20,6 @@ import { AccessToken, encodeCrock, - hashNormalizedPaytoUri, j2s, Logger, MerchantAccountKycStatus, @@ -127,7 +126,7 @@ export async function runTopsAmlBasicTest(t: GlobalTestState) { t.assertTrue(typeof kycStatus.body.kyc_data[0].access_token === "string"); accessToken = kycStatus.body.kyc_data[0].access_token as AccessToken; merchantPaytoHash = encodeCrock( - hashNormalizedPaytoUri(kycStatus.body.kyc_data[0].payto_uri), + Paytos.hashNormalized(kycStatus.body.kyc_data[0].payto_uri), ); } diff --git a/packages/taler-harness/src/integrationtests/test-tops-aml-custom-addr-postal.ts b/packages/taler-harness/src/integrationtests/test-tops-aml-custom-addr-postal.ts @@ -20,7 +20,6 @@ import { AccessToken, encodeCrock, - hashNormalizedPaytoUri, j2s, Logger, MerchantAccountKycStatus, @@ -120,7 +119,7 @@ export async function runTopsAmlCustomAddrPostalTest(t: GlobalTestState) { t.assertTrue(typeof kycStatus.body.kyc_data[0].access_token === "string"); accessToken = kycStatus.body.kyc_data[0].access_token as AccessToken; merchantPaytoHash = encodeCrock( - hashNormalizedPaytoUri(kycStatus.body.kyc_data[0].payto_uri), + Paytos.hashNormalized(kycStatus.body.kyc_data[0].payto_uri), ); } diff --git a/packages/taler-harness/src/integrationtests/test-tops-aml-custom-addr-sms.ts b/packages/taler-harness/src/integrationtests/test-tops-aml-custom-addr-sms.ts @@ -21,7 +21,6 @@ import { AccessToken, decodeCrock, encodeCrock, - hashNormalizedPaytoUri, j2s, Logger, MerchantAccountKycStatus, @@ -124,7 +123,7 @@ export async function runTopsAmlCustomAddrSmsTest(t: GlobalTestState) { t.assertTrue(typeof kycStatus.body.kyc_data[0].access_token === "string"); accessToken = kycStatus.body.kyc_data[0].access_token as AccessToken; merchantPaytoHash = encodeCrock( - hashNormalizedPaytoUri(kycStatus.body.kyc_data[0].payto_uri), + Paytos.hashNormalized(kycStatus.body.kyc_data[0].payto_uri), ); } diff --git a/packages/taler-util/src/payto.ts b/packages/taler-util/src/payto.ts @@ -190,11 +190,23 @@ export namespace Paytos { cyclos: true, }; - export function hash(p: NormalizedPaytoString | FullPaytoString): Uint8Array { + export function hashFull( + p: NormalizedPaytoString | FullPaytoString | string, + ): Uint8Array { return hashTruncate32(stringToBytes(p + "\0")); } /** + * Parse, normalize and hash a full payto-URI into its canonical H_PAYTO. + * + * Shorthand for `hash(toNormalizedString(asString(p)))`. Throws if `p` is + * not a valid, supported payto-URI. + */ + export function hashNormalized(p: FullPaytoString | string): Uint8Array { + return hashFull(toNormalizedString(asString(p as FullPaytoString))); + } + + /** * A **normalized** payto-URI uniquely identifies a bank account (or * wallet) and must be able to serve as a canonical representation of such a * bank account. Thus, optional arguments such as the *receiver-name* or @@ -1042,116 +1054,6 @@ export function stringifyPaytoUri(p: PaytoUri): PaytoString { } /** - * @deprecated use paytos namespace - */ -export function hashFullPaytoUri(p: PaytoUri | string): Uint8Array { - const paytoUri = typeof p === "string" ? p : stringifyPaytoUri(p); - return hashTruncate32(stringToBytes(paytoUri + "\0")); -} - -/** - * Normalize and then hash a payto URI. - * @deprecated use paytos namespace - */ -export function hashNormalizedPaytoUri(p: PaytoUri | string): Uint8Array { - const paytoUri = typeof p === "string" ? p : stringifyPaytoUri(p); - if (typeof p === "string") { - const parseRes = parsePaytoUri(p); - if (!parseRes) { - throw Error("invalid payto URI"); - } - p = parseRes; - } - let paytoStr: string; - if (!p.isKnown) { - const normalizedPayto: PaytoUri = { - targetType: p.targetType, - targetPath: p.targetPath, - isKnown: false, - params: {}, - }; - paytoStr = stringifyPaytoUri(normalizedPayto); - } else { - switch (p.targetType) { - case "iban": - // FIXME: Strip BIC? - paytoStr = `payto://iban/${p.targetPath}`; - break; - case "x-taler-bank": - paytoStr = `payto://x-taler-bank/${p.host}/${p.account}`; - break; - case "bitcoin": - paytoStr = `payto://bitcoin/${p.address}`; - break; - case "ethereum": - paytoStr = `payto://ethereum/${p.address}`; - break; - case "cyclos": - paytoStr = `payto://cyclos/${p.host}/${p.account}`; - break; - case "taler-reserve": - paytoStr = `payto://taler-reserve/${p.exchange}/${p.reservePub}`; - break; - case "taler-reserve-http": - paytoStr = `payto://taler-reserve-http/${p.exchange}/${p.reservePub}`; - break; - } - } - return hashTruncate32(stringToBytes(paytoStr + "\0")); -} - -/** - * @deprecated do not use this, create a taler-reserve payto and use - * stringify - * - * @param exchangeBaseUrl - * @param reservePub - * @returns - */ -export function stringifyReservePaytoUri( - exchangeBaseUrl: string, - reservePub: string, -): string { - const url = new URL(exchangeBaseUrl); - let target: string; - let domainWithOptPort: string; - if (url.protocol === "https:") { - target = "taler-reserve"; - if (url.port != "443" && url.port !== "") { - domainWithOptPort = `${url.hostname}:${url.port}`; - } else { - domainWithOptPort = `${url.hostname}`; - } - } else { - target = "taler-reserve-http"; - if (url.port != "80" && url.port !== "") { - domainWithOptPort = `${url.hostname}:${url.port}`; - } else { - domainWithOptPort = `${url.hostname}`; - } - } - let optPath = ""; - if (url.pathname !== "/" && url.pathname !== "") { - optPath = url.pathname; - } - return `payto://${target}/${domainWithOptPort}${optPath}/${reservePub}`; -} - -/** - * @deprecated use new Payto namespace functions - * - * @param s - * @returns - */ -export function parsePaytoUriOrThrow(s: string): PaytoUri { - const ret = parsePaytoUri(s); - if (!ret) { - throw Error("invalid payto URI"); - } - return ret; -} - -/** * Parse a valid payto:// uri into a PaytoUri object * RFC 8905 * @deprecated use new Payto namespace functions diff --git a/packages/taler-wallet-core/src/deposits.ts b/packages/taler-wallet-core/src/deposits.ts @@ -75,7 +75,6 @@ import { codecForTackTransactionWired, encodeCrock, getRandomBytes, - hashNormalizedPaytoUri, hashWire, j2s, Paytos, @@ -1486,7 +1485,7 @@ async function processDepositGroupTrack( if (!track.kyc_ok && track.requirement_row !== undefined) { // FIXME: Take this from the response. const paytoHash = encodeCrock( - hashNormalizedPaytoUri(depositGroup.wire.payto_uri), + Paytos.hashNormalized(depositGroup.wire.payto_uri), ); return transitionToKycRequired(wex, depositGroup, { exchangeUrl: exchangeBaseUrl, diff --git a/packages/taler-wallet-core/src/exchanges.ts b/packages/taler-wallet-core/src/exchanges.ts @@ -65,8 +65,10 @@ import { ListExchangesRequest, Logger, NotificationType, + Paytos, Recoup, RefreshReason, + Result, ScopeInfo, ScopeType, StartExchangeWalletKycRequest, @@ -103,14 +105,11 @@ import { encodeCrock, getRandomBytes, hashDenomPub, - hashFullPaytoUri, j2s, makeErrorDetail, makeTalerErrorDetail, - Paytos, - Result, - stringifyReservePaytoUri, stringifyScopeInfo, + talerPaytoFromExchangeReserve, } from "@gnu-taler/taler-util"; import { HttpRequestLibrary, @@ -3972,12 +3971,12 @@ async function handleExchangeKycPendingLegitimization( accountPub: reserve.reservePub, }); - const reservePayto = stringifyReservePaytoUri( + const reservePayto = talerPaytoFromExchangeReserve( exchange.baseUrl, reserve.reservePub, ); - const paytoHash = encodeCrock(hashFullPaytoUri(reservePayto)); + const paytoHash = encodeCrock(Paytos.hashFull(reservePayto)); const reqUrl = new URL(`kyc-check/${paytoHash}`, exchange.baseUrl); // We already have the access token.