From 8d4a7d6103032a85c81240d9fb0de32dd44ec435 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Sun, 16 Oct 2022 20:15:55 +0200 Subject: wallet-core: CLI improvements, ToS fetching fixes --- packages/taler-wallet-cli/src/index.ts | 109 ++++++++++++--------- packages/taler-wallet-core/src/db.ts | 4 +- .../taler-wallet-core/src/operations/exchanges.ts | 58 ++++++++--- packages/taler-wallet-core/src/wallet.ts | 2 +- 4 files changed, 105 insertions(+), 68 deletions(-) (limited to 'packages') diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts index 249198e3c..b50e11883 100644 --- a/packages/taler-wallet-cli/src/index.ts +++ b/packages/taler-wallet-cli/src/index.ts @@ -314,48 +314,42 @@ walletCli logger.info("finished handling API request"); }); -walletCli - .subcommand("", "pending", { help: "Show pending operations." }) - .action(async (args) => { - await withWallet(args, async (wallet) => { - const pending = await wallet.client.call( - WalletApiOperation.GetPendingOperations, - {}, - ); - console.log(JSON.stringify(pending, undefined, 2)); - }); - }); - -walletCli - .subcommand("transactions", "transactions", { help: "Show transactions." }) +const transactionsCli = walletCli + .subcommand("transactions", "transactions", { help: "Manage transactions." }) .maybeOption("currency", ["--currency"], clk.STRING) - .maybeOption("search", ["--search"], clk.STRING) - .action(async (args) => { - await withWallet(args, async (wallet) => { - const pending = await wallet.client.call( - WalletApiOperation.GetTransactions, - { - currency: args.transactions.currency, - search: args.transactions.search, - }, - ); - console.log(JSON.stringify(pending, undefined, 2)); - }); + .maybeOption("search", ["--search"], clk.STRING); + +// Default action +transactionsCli.action(async (args) => { + await withWallet(args, async (wallet) => { + const pending = await wallet.client.call( + WalletApiOperation.GetTransactions, + { + currency: args.transactions.currency, + search: args.transactions.search, + }, + ); + console.log(JSON.stringify(pending, undefined, 2)); }); +}); -walletCli - .subcommand("runPendingOpt", "run-pending", { - help: "Run pending operations.", +transactionsCli + .subcommand("deleteTransaction", "delete", { + help: "Permanently delete a transaction from the transaction list.", + }) + .requiredArgument("transactionId", clk.STRING, { + help: "Identifier of the transaction to delete", }) - .flag("forceNow", ["-f", "--force-now"]) .action(async (args) => { await withWallet(args, async (wallet) => { - await wallet.ws.runPending(args.runPendingOpt.forceNow); + await wallet.client.call(WalletApiOperation.DeleteTransaction, { + transactionId: args.deleteTransaction.transactionId, + }); }); }); -walletCli - .subcommand("retryTransaction", "retry-transaction", { +transactionsCli + .subcommand("retryTransaction", "retry", { help: "Retry a transaction.", }) .requiredArgument("transactionId", clk.STRING) @@ -387,21 +381,6 @@ walletCli }); }); -walletCli - .subcommand("deleteTransaction", "delete-transaction", { - help: "Permanently delete a transaction from the transaction list.", - }) - .requiredArgument("transactionId", clk.STRING, { - help: "Identifier of the transaction to delete", - }) - .action(async (args) => { - await withWallet(args, async (wallet) => { - await wallet.client.call(WalletApiOperation.DeleteTransaction, { - transactionId: args.deleteTransaction.transactionId, - }); - }); - }); - walletCli .subcommand("withdraw", "withdraw", { help: "Withdraw with a taler://withdraw/ URI", @@ -604,17 +583,26 @@ exchangesCli exchangesCli .subcommand("exchangesTosCmd", "tos", { - help: "Show terms of service.", + help: "Show/request terms of service.", }) .requiredArgument("url", clk.STRING, { help: "Base URL of the exchange.", }) + .maybeOption("contentTypes", ["--content-type"], clk.STRING) .action(async (args) => { + let acceptedFormat: string[] | undefined = undefined; + if (args.exchangesTosCmd.contentTypes) { + const split = args.exchangesTosCmd.contentTypes + .split(",") + .map((x) => x.trim()); + acceptedFormat = split; + } await withWallet(args, async (wallet) => { const tosResult = await wallet.client.call( WalletApiOperation.GetExchangeTos, { exchangeBaseUrl: args.exchangesTosCmd.url, + acceptedFormat, }, ); console.log(JSON.stringify(tosResult, undefined, 2)); @@ -764,6 +752,29 @@ advancedCli await withWallet(args, async () => {}); }); +advancedCli + .subcommand("runPendingOpt", "run-pending", { + help: "Run pending operations.", + }) + .flag("forceNow", ["-f", "--force-now"]) + .action(async (args) => { + await withWallet(args, async (wallet) => { + await wallet.ws.runPending(args.runPendingOpt.forceNow); + }); + }); + +advancedCli + .subcommand("", "pending", { help: "Show pending operations." }) + .action(async (args) => { + await withWallet(args, async (wallet) => { + const pending = await wallet.client.call( + WalletApiOperation.GetPendingOperations, + {}, + ); + console.log(JSON.stringify(pending, undefined, 2)); + }); + }); + advancedCli .subcommand("bench1", "bench1", { help: "Run the 'bench1' benchmark", diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index 59980621f..aa74e327f 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -521,7 +521,7 @@ export interface ExchangeRecord { * Should usually not change. Only changes when the * exchange advertises a different master public key and/or * currency. - * + * * FIXME: Use a rowId here? */ detailsPointer: ExchangeDetailsPointer | undefined; @@ -1364,7 +1364,7 @@ export interface WithdrawalGroupRecord { /** * Wire information (as payto URI) for the bank account that * transferred funds for this reserve. - * + * * FIXME: Doesn't this belong to the bankAccounts object store? */ senderWire?: string; diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts index 142bedd83..d0da2e948 100644 --- a/packages/taler-wallet-core/src/operations/exchanges.ts +++ b/packages/taler-wallet-core/src/operations/exchanges.ts @@ -134,6 +134,7 @@ export async function downloadExchangeWithTermsOfService( timeout: Duration, contentType: string, ): Promise { + logger.info(`downloading exchange tos (type ${contentType})`); const reqUrl = new URL("terms", exchangeBaseUrl); const headers = { Accept: contentType, @@ -524,7 +525,9 @@ export async function downloadTosFromAcceptedFormat( break; } } - if (tosFound !== undefined) return tosFound; + if (tosFound !== undefined) { + return tosFound; + } // If none of the specified format was found try text/plain return await downloadExchangeWithTermsOfService( baseUrl, @@ -557,7 +560,7 @@ export async function updateExchangeFromUrl( */ export async function updateExchangeFromUrlHandler( ws: InternalWalletState, - baseUrl: string, + exchangeBaseUrl: string, options: { forceNow?: boolean; cancellationToken?: CancellationToken; @@ -569,19 +572,21 @@ export async function updateExchangeFromUrlHandler( }> > { const forceNow = options.forceNow ?? false; - logger.info(`updating exchange info for ${baseUrl}, forced: ${forceNow}`); + logger.info( + `updating exchange info for ${exchangeBaseUrl}, forced: ${forceNow}`, + ); const now = AbsoluteTime.now(); - baseUrl = canonicalizeBaseUrl(baseUrl); + exchangeBaseUrl = canonicalizeBaseUrl(exchangeBaseUrl); let isNewExchange = true; const { exchange, exchangeDetails } = await ws.db .mktx((x) => [x.exchanges, x.exchangeDetails]) .runReadWrite(async (tx) => { - let oldExch = await tx.exchanges.get(baseUrl); + let oldExch = await tx.exchanges.get(exchangeBaseUrl); if (oldExch) { isNewExchange = false; } - return provideExchangeRecordInTx(ws, tx, baseUrl, now); + return provideExchangeRecordInTx(ws, tx, exchangeBaseUrl, now); }); if ( @@ -600,11 +605,15 @@ export async function updateExchangeFromUrlHandler( const timeout = getExchangeRequestTimeout(); - const keysInfo = await downloadExchangeKeysInfo(baseUrl, ws.http, timeout); + const keysInfo = await downloadExchangeKeysInfo( + exchangeBaseUrl, + ws.http, + timeout, + ); logger.info("updating exchange /wire info"); const wireInfoDownload = await downloadExchangeWireInfo( - baseUrl, + exchangeBaseUrl, ws.http, timeout, ); @@ -632,15 +641,15 @@ export async function updateExchangeFromUrlHandler( logger.info("finished validating exchange /wire info"); + // We download the text/plain version here, + // because that one needs to exist, and we + // will get the current etag from the response. const tosDownload = await downloadTosFromAcceptedFormat( ws, - baseUrl, + exchangeBaseUrl, timeout, ["text/plain"], ); - const tosHasBeenAccepted = - exchangeDetails?.tosAccepted && - exchangeDetails.tosAccepted.etag === tosDownload.tosEtag; let recoupGroupId: string | undefined; @@ -651,6 +660,7 @@ export async function updateExchangeFromUrlHandler( const updated = await ws.db .mktx((x) => [ x.exchanges, + x.exchangeTos, x.exchangeDetails, x.exchangeSignkeys, x.denominations, @@ -659,13 +669,13 @@ export async function updateExchangeFromUrlHandler( x.recoupGroups, ]) .runReadWrite(async (tx) => { - const r = await tx.exchanges.get(baseUrl); + const r = await tx.exchanges.get(exchangeBaseUrl); if (!r) { - logger.warn(`exchange ${baseUrl} no longer present`); + logger.warn(`exchange ${exchangeBaseUrl} no longer present`); return; } let existingDetails = await getExchangeDetails(tx, r.baseUrl); - let acceptedTosEtag = undefined; + let acceptedTosEtag: string | undefined = undefined; if (!existingDetails) { detailsPointerChanged = true; } @@ -708,6 +718,21 @@ export async function updateExchangeFromUrlHandler( const drRowId = await tx.exchangeDetails.put(newDetails); checkDbInvariant(typeof drRowId.key === "number"); + let tosRecord = await tx.exchangeTos.get([ + exchangeBaseUrl, + tosDownload.tosEtag, + ]); + + if (!tosRecord || tosRecord.etag !== existingTosAccepted?.etag) { + tosRecord = { + etag: tosDownload.tosEtag, + exchangeBaseUrl, + termsOfServiceContentType: tosDownload.tosContentType, + termsOfServiceText: tosDownload.tosText, + }; + await tx.exchangeTos.put(tosRecord); + } + for (const sk of keysInfo.signingKeys) { // FIXME: validate signing keys before inserting them await tx.exchangeSignKeys.put({ @@ -726,7 +751,7 @@ export async function updateExchangeFromUrlHandler( ); for (const currentDenom of keysInfo.currentDenominations) { const oldDenom = await tx.denominations.get([ - baseUrl, + exchangeBaseUrl, currentDenom.denomPubHash, ]); if (oldDenom) { @@ -802,6 +827,7 @@ export async function updateExchangeFromUrlHandler( newlyRevokedCoinPubs, ); } + return { exchange: r, exchangeDetails: newDetails, diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index 7839f3dab..d97e5ba80 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -631,7 +631,7 @@ async function getExchangeTosStatusDetails( return { acceptedVersion: exchangeDetails.tosAccepted?.etag, - content: exchangeTos.termsOfServiceContentType, + content: exchangeTos.termsOfServiceText, contentType: exchangeTos.termsOfServiceContentType, currentVersion: exchangeTos.etag, }; -- cgit v1.2.3