diff options
Diffstat (limited to 'src/headless')
-rw-r--r-- | src/headless/clk.ts | 2 | ||||
-rw-r--r-- | src/headless/helpers.ts | 16 | ||||
-rw-r--r-- | src/headless/integrationtest.ts | 1 | ||||
-rw-r--r-- | src/headless/taler-wallet-cli.ts | 380 |
4 files changed, 201 insertions, 198 deletions
diff --git a/src/headless/clk.ts b/src/headless/clk.ts index f66d609e8..51ee119c9 100644 --- a/src/headless/clk.ts +++ b/src/headless/clk.ts @@ -440,7 +440,7 @@ export class CommandGroup<GN extends keyof any, TG> { if (option.isFlag == false && option.required == true) { if (!foundOptions[option.name]) { if (option.args.default !== undefined) { - parsedArgs[this.argKey] = option.args.default; + myArgs[option.name] = option.args.default; } else { const name = option.flagspec.join(",") console.error(`error: missing option '${name}'`); diff --git a/src/headless/helpers.ts b/src/headless/helpers.ts index 49881d469..5e06a2f25 100644 --- a/src/headless/helpers.ts +++ b/src/headless/helpers.ts @@ -21,7 +21,7 @@ /** * Imports. */ -import { Wallet } from "../wallet"; +import { Wallet, OperationFailedAndReportedError } from "../wallet"; import { Notifier, Badge } from "../walletTypes"; import { MemoryBackend, BridgeIDBFactory, shimIndexedDB } from "idb-bridge"; import { SynchronousCryptoWorkerFactory } from "../crypto/synchronousWorker"; @@ -139,18 +139,16 @@ export async function getDefaultNodeWallet( const storagePath = args.persistentStoragePath; if (storagePath) { - console.log(`using storage path ${storagePath}`); - try { const dbContentStr: string = fs.readFileSync(storagePath, { encoding: "utf-8" }); const dbContent = JSON.parse(dbContentStr); myBackend.importDump(dbContent); - console.log("imported wallet"); } catch (e) { - console.log("could not read wallet file"); + console.error("could not read wallet file"); } myBackend.afterCommitCallback = async () => { + console.log("DATABASE COMMITTED"); // Allow caller to stop persisting the wallet. if (args.persistentStoragePath === undefined) { return; @@ -190,8 +188,6 @@ export async function getDefaultNodeWallet( myUnsupportedUpgrade, ); - console.log("opened db"); - return new Wallet( myDb, myHttpLib, @@ -214,6 +210,8 @@ export async function withdrawTestBalance( exchangeWire: "payto://unknown", }); + const reservePub = reserveResponse.reservePub; + const bank = new Bank(bankBaseUrl); const bankUser = await bank.registerRandomUser(); @@ -228,11 +226,11 @@ export async function withdrawTestBalance( await bank.createReserve( bankUser, amount, - reserveResponse.reservePub, + reservePub, exchangePaytoUri, ); await myWallet.confirmReserve({ reservePub: reserveResponse.reservePub }); - await myWallet.processReserve(reserveResponse.reservePub); + await myWallet.runUntilReserveDepleted(reservePub); } diff --git a/src/headless/integrationtest.ts b/src/headless/integrationtest.ts index 6b3286904..6f2139c9d 100644 --- a/src/headless/integrationtest.ts +++ b/src/headless/integrationtest.ts @@ -31,6 +31,7 @@ export async function runIntegrationTest(args: { amountToWithdraw: string; amountToSpend: string; }) { + console.log("running test with", args); const myWallet = await getDefaultNodeWallet(); await withdrawTestBalance(myWallet, args.amountToWithdraw, args.bankBaseUrl, args.exchangeBaseUrl); diff --git a/src/headless/taler-wallet-cli.ts b/src/headless/taler-wallet-cli.ts index 06235d0b4..0a6780808 100644 --- a/src/headless/taler-wallet-cli.ts +++ b/src/headless/taler-wallet-cli.ts @@ -18,9 +18,14 @@ import os = require("os"); import { getDefaultNodeWallet, withdrawTestBalance } from "./helpers"; import { MerchantBackendConnection } from "./merchant"; import { runIntegrationTest } from "./integrationtest"; -import { Wallet } from "../wallet"; +import { Wallet, OperationFailedAndReportedError } from "../wallet"; import qrcodeGenerator = require("qrcode-generator"); import * as clk from "./clk"; +import { BridgeIDBFactory, MemoryBackend } from "idb-bridge"; +import { Logger } from "../logging"; +import * as Amounts from "../amounts"; + +const logger = new Logger("taler-wallet-cli.ts"); const walletDbPath = os.homedir + "/" + ".talerwalletdb.json"; @@ -82,6 +87,7 @@ function applyVerbose(verbose: boolean) { if (verbose) { console.log("enabled verbose logging"); Wallet.enableTracing = true; + BridgeIDBFactory.enableTracing = true; } } @@ -103,16 +109,21 @@ async function withWallet<T>( walletCliArgs: WalletCliArgsType, f: (w: Wallet) => Promise<T>, ): Promise<T> { - applyVerbose(walletCliArgs.wallet.verbose); const wallet = await getDefaultNodeWallet({ persistentStoragePath: walletDbPath, }); + applyVerbose(walletCliArgs.wallet.verbose); try { await wallet.fillDefaults(); const ret = await f(wallet); return ret; } catch (e) { - console.error("caught exception:", e); + if (e instanceof OperationFailedAndReportedError) { + console.error("Operation failed: " + e.message); + console.log("Hint: check pending operations for details."); + } else { + console.error("caught exception:", e); + } process.exit(1); } finally { wallet.stop(); @@ -120,45 +131,10 @@ async function withWallet<T>( } walletCli - .subcommand("testPayCmd", "test-pay", { help: "create contract and pay" }) - .requiredOption("amount", ["-a", "--amount"], clk.STRING) - .requiredOption("summary", ["-s", "--summary"], clk.STRING, { - default: "Test Payment", - }) - .action(async args => { - const cmdArgs = args.testPayCmd; - console.log("creating order"); - const merchantBackend = new MerchantBackendConnection( - "https://backend.test.taler.net/", - "sandbox", - ); - const orderResp = await merchantBackend.createOrder( - cmdArgs.amount, - cmdArgs.summary, - "", - ); - console.log("created new order with order ID", orderResp.orderId); - const checkPayResp = await merchantBackend.checkPayment(orderResp.orderId); - const talerPayUri = checkPayResp.taler_pay_uri; - if (!talerPayUri) { - console.error("fatal: no taler pay URI received from backend"); - process.exit(1); - return; - } - console.log("taler pay URI:", talerPayUri); - - const wallet = await getDefaultNodeWallet({ - persistentStoragePath: walletDbPath, - }); - - await doPay(wallet, talerPayUri, { alwaysYes: true }); - }); - -walletCli .subcommand("", "balance", { help: "Show wallet balance." }) .action(async args => { console.log("balance command called"); - withWallet(args, async (wallet) => { + await withWallet(args, async wallet => { const balance = await wallet.getBalances(); console.log(JSON.stringify(balance, undefined, 2)); }); @@ -166,12 +142,12 @@ walletCli walletCli .subcommand("", "history", { help: "Show wallet event history." }) - .requiredOption("from", ["--from"], clk.STRING) - .requiredOption("to", ["--to"], clk.STRING) - .requiredOption("limit", ["--limit"], clk.STRING) - .requiredOption("contEvt", ["--continue-with"], clk.STRING) + .maybeOption("from", ["--from"], clk.STRING) + .maybeOption("to", ["--to"], clk.STRING) + .maybeOption("limit", ["--limit"], clk.STRING) + .maybeOption("contEvt", ["--continue-with"], clk.STRING) .action(async args => { - withWallet(args, async (wallet) => { + await withWallet(args, async wallet => { const history = await wallet.getHistory(); console.log(JSON.stringify(history, undefined, 2)); }); @@ -180,7 +156,7 @@ walletCli walletCli .subcommand("", "pending", { help: "Show pending operations." }) .action(async args => { - withWallet(args, async (wallet) => { + await withWallet(args, async wallet => { const pending = await wallet.getPendingOperations(); console.log(JSON.stringify(pending, undefined, 2)); }); @@ -194,25 +170,129 @@ async function asyncSleep(milliSeconds: number): Promise<void> { walletCli .subcommand("runPendingOpt", "run-pending", { - help: "Run pending operations." + help: "Run pending operations.", }) - .action(async (args) => { - withWallet(args, async (wallet) => { - await wallet.processPending(); + .action(async args => { + await withWallet(args, async wallet => { + await wallet.runPending(); }); }); walletCli - .subcommand("testMerchantQrcodeCmd", "test-merchant-qrcode") - .requiredOption("amount", ["-a", "--amount"], clk.STRING, { - default: "TESTKUDOS:1", + .subcommand("handleUri", "handle-uri", { + help: "Handle a taler:// URI.", }) + .requiredArgument("uri", clk.STRING) + .flag("autoYes", ["-y", "--yes"]) + .action(async args => { + await withWallet(args, async wallet => { + const uri: string = args.handleUri.uri; + if (uri.startsWith("taler://pay/")) { + await doPay(wallet, uri, { alwaysYes: args.handleUri.autoYes }); + } else if (uri.startsWith("taler://tip/")) { + const res = await wallet.getTipStatus(uri); + console.log("tip status", res); + await wallet.acceptTip(uri); + } else if (uri.startsWith("taler://refund/")) { + await wallet.applyRefund(uri); + } else if (uri.startsWith("taler://withdraw/")) { + const withdrawInfo = await wallet.getWithdrawalInfo(uri); + const selectedExchange = withdrawInfo.suggestedExchange; + if (!selectedExchange) { + console.error("no suggested exchange!"); + process.exit(1); + return; + } + const { confirmTransferUrl } = await wallet.acceptWithdrawal( + uri, + selectedExchange, + ); + if (confirmTransferUrl) { + console.log("please confirm the transfer at", confirmTransferUrl); + } + } else { + console.error("unrecognized URI"); + } + }); + }); + +const exchangesCli = walletCli.subcommand("exchangesCmd", "exchanges", { + help: "Manage exchanges.", +}); + +exchangesCli + .subcommand("exchangesListCmd", "list", { + help: "List known exchanges.", + }) + .action(async args => { + console.log("Listing exchanges ..."); + await withWallet(args, async wallet => { + const exchanges = await wallet.getExchanges(); + console.log("exchanges", exchanges); + }); + }); + +exchangesCli + .subcommand("exchangesUpdateCmd", "update", { + help: "Update or add an exchange by base URL.", + }) + .requiredArgument("url", clk.STRING, { + help: "Base URL of the exchange.", + }) + .flag("force", ["-f", "--force"]) + .action(async args => { + await withWallet(args, async wallet => { + const res = await wallet.updateExchangeFromUrl( + args.exchangesUpdateCmd.url, + args.exchangesUpdateCmd.force, + ); + }); + }); + +const advancedCli = walletCli.subcommand("advancedArgs", "advanced", { + help: + "Subcommands for advanced operations (only use if you know what you're doing!).", +}); + +advancedCli + .subcommand("refresh", "force-refresh", { + help: "Force a refresh on a coin.", + }) + .requiredArgument("coinPub", clk.STRING) + .action(async args => { + await withWallet(args, async wallet => { + await wallet.refresh(args.refresh.coinPub, true); + }); + }); + +advancedCli + .subcommand("coins", "list-coins", { + help: "List coins.", + }) + .action(async args => { + await withWallet(args, async wallet => { + const coins = await wallet.getCoins(); + for (const coin of coins) { + console.log(`coin ${coin.coinPub}`); + console.log(` status ${coin.status}`); + console.log(` exchange ${coin.exchangeBaseUrl}`); + console.log(` remaining amount ${Amounts.toString(coin.currentAmount)}`); + } + }); + }); + +const testCli = walletCli.subcommand("testingArgs", "testing", { + help: "Subcommands for testing GNU Taler deployments.", +}); + +testCli + .subcommand("testPayCmd", "test-pay", { help: "create contract and pay" }) + .requiredOption("amount", ["-a", "--amount"], clk.STRING) .requiredOption("summary", ["-s", "--summary"], clk.STRING, { default: "Test Payment", }) .action(async args => { - const cmdArgs = args.testMerchantQrcodeCmd; - applyVerbose(args.wallet.verbose); + const cmdArgs = args.testPayCmd; console.log("creating order"); const merchantBackend = new MerchantBackendConnection( "https://backend.test.taler.net/", @@ -225,7 +305,6 @@ walletCli ); console.log("created new order with order ID", orderResp.orderId); const checkPayResp = await merchantBackend.checkPayment(orderResp.orderId); - const qrcode = qrcodeGenerator(0, "M"); const talerPayUri = checkPayResp.taler_pay_uri; if (!talerPayUri) { console.error("fatal: no taler pay URI received from backend"); @@ -233,23 +312,13 @@ walletCli return; } console.log("taler pay URI:", talerPayUri); - qrcode.addData(talerPayUri); - qrcode.make(); - console.log(qrcode.createASCII()); - console.log("waiting for payment ..."); - while (1) { - await asyncSleep(500); - const checkPayResp2 = await merchantBackend.checkPayment( - orderResp.orderId, - ); - if (checkPayResp2.paid) { - console.log("payment successfully received!"); - break; - } - } + await withWallet(args, async (wallet) => { + await doPay(wallet, talerPayUri, { alwaysYes: true }); + }); }); -walletCli + +testCli .subcommand("integrationtestCmd", "integrationtest", { help: "Run integration test with bank, exchange and merchant.", }) @@ -265,13 +334,14 @@ walletCli .requiredOption("bank", ["-b", "--bank"], clk.STRING, { default: "https://bank.test.taler.net/", }) - .requiredOption("withdrawAmount", ["-b", "--bank"], clk.STRING, { + .requiredOption("withdrawAmount", ["-a", "--amount"], clk.STRING, { default: "TESTKUDOS:10", }) .requiredOption("spendAmount", ["-s", "--spend-amount"], clk.STRING, { default: "TESTKUDOS:4", }) .action(async args => { + console.log("parsed args", args); applyVerbose(args.wallet.verbose); let cmdObj = args.integrationtestCmd; @@ -295,128 +365,61 @@ walletCli } }); -walletCli - .subcommand("withdrawUriCmd", "withdraw-uri") - .requiredArgument("withdrawUri", clk.STRING) +testCli + .subcommand("testMerchantQrcodeCmd", "test-merchant-qrcode") + .requiredOption("amount", ["-a", "--amount"], clk.STRING, { + default: "TESTKUDOS:1", + }) + .requiredOption("summary", ["-s", "--summary"], clk.STRING, { + default: "Test Payment", + }) .action(async args => { + const cmdArgs = args.testMerchantQrcodeCmd; applyVerbose(args.wallet.verbose); - const cmdArgs = args.withdrawUriCmd; - const withdrawUrl = cmdArgs.withdrawUri; - console.log("withdrawing", withdrawUrl); - const wallet = await getDefaultNodeWallet({ - persistentStoragePath: walletDbPath, - }); - - const withdrawInfo = await wallet.getWithdrawalInfo(withdrawUrl); - - console.log("withdraw info", withdrawInfo); - - const selectedExchange = withdrawInfo.suggestedExchange; - if (!selectedExchange) { - console.error("no suggested exchange!"); + console.log("creating order"); + const merchantBackend = new MerchantBackendConnection( + "https://backend.test.taler.net/", + "sandbox", + ); + const orderResp = await merchantBackend.createOrder( + cmdArgs.amount, + cmdArgs.summary, + "", + ); + console.log("created new order with order ID", orderResp.orderId); + const checkPayResp = await merchantBackend.checkPayment(orderResp.orderId); + const qrcode = qrcodeGenerator(0, "M"); + const talerPayUri = checkPayResp.taler_pay_uri; + if (!talerPayUri) { + console.error("fatal: no taler pay URI received from backend"); process.exit(1); return; } - - const { reservePub, confirmTransferUrl } = await wallet.acceptWithdrawal( - withdrawUrl, - selectedExchange, - ); - - if (confirmTransferUrl) { - console.log("please confirm the transfer at", confirmTransferUrl); + console.log("taler pay URI:", talerPayUri); + qrcode.addData(talerPayUri); + qrcode.make(); + console.log(qrcode.createASCII()); + console.log("waiting for payment ..."); + while (1) { + await asyncSleep(500); + const checkPayResp2 = await merchantBackend.checkPayment( + orderResp.orderId, + ); + if (checkPayResp2.paid) { + console.log("payment successfully received!"); + break; + } } - - await wallet.processReserve(reservePub); - - console.log("finished withdrawing"); - - wallet.stop(); - }); - -walletCli - .subcommand("tipUriCmd", "tip-uri") - .requiredArgument("uri", clk.STRING) - .action(async args => { - applyVerbose(args.wallet.verbose); - const tipUri = args.tipUriCmd.uri; - console.log("getting tip", tipUri); - const wallet = await getDefaultNodeWallet({ - persistentStoragePath: walletDbPath, - }); - const res = await wallet.getTipStatus(tipUri); - console.log("tip status", res); - await wallet.acceptTip(tipUri); - wallet.stop(); }); -walletCli - .subcommand("refundUriCmd", "refund-uri") - .requiredArgument("uri", clk.STRING) - .action(async args => { - applyVerbose(args.wallet.verbose); - const refundUri = args.refundUriCmd.uri; - console.log("getting refund", refundUri); - const wallet = await getDefaultNodeWallet({ - persistentStoragePath: walletDbPath, - }); - await wallet.applyRefund(refundUri); - wallet.stop(); - }); - -const exchangesCli = walletCli.subcommand("exchangesCmd", "exchanges", { - help: "Manage exchanges.", -}); - -exchangesCli - .subcommand("exchangesListCmd", "list", { - help: "List known exchanges.", - }) - .action(async args => { - console.log("Listing exchanges ..."); - withWallet(args, async (wallet) => { - const exchanges = await wallet.getExchanges(); - console.log("exchanges", exchanges); - }); - }); - -exchangesCli - .subcommand("exchangesUpdateCmd", "update", { - help: "Update or add an exchange by base URL.", - }) - .requiredArgument("url", clk.STRING, { - help: "Base URL of the exchange.", - }) - .action(async args => { - withWallet(args, async (wallet) => { - const res = await wallet.updateExchangeFromUrl(args.exchangesUpdateCmd.url); - }); - }); - -walletCli - .subcommand("payUriCmd", "pay-uri") - .requiredArgument("url", clk.STRING) - .flag("autoYes", ["-y", "--yes"]) - .action(async args => { - applyVerbose(args.wallet.verbose); - const payUrl = args.payUriCmd.url; - console.log("paying for", payUrl); - const wallet = await getDefaultNodeWallet({ - persistentStoragePath: walletDbPath, - }); - - await doPay(wallet, payUrl, { alwaysYes: args.payUriCmd.autoYes }); - wallet.stop(); - }); - -const testCli = walletCli.subcommand("testingArgs", "testing", { - help: "Subcommands for testing GNU Taler deployments.", -}); - testCli .subcommand("withdrawArgs", "withdraw", { help: "Withdraw from a test bank (must support test registrations).", }) + .requiredOption("amount", ["-a", "--amount"], clk.STRING, { + default: "TESTKUDOS:10", + help: "Amount to withdraw.", + }) .requiredOption("exchange", ["-e", "--exchange"], clk.STRING, { default: "https://exchange.test.taler.net/", help: "Exchange base URL.", @@ -426,14 +429,15 @@ testCli help: "Bank base URL", }) .action(async args => { - applyVerbose(args.wallet.verbose); - console.log("balance command called"); - const wallet = await getDefaultNodeWallet({ - persistentStoragePath: walletDbPath, + await withWallet(args, async wallet => { + await withdrawTestBalance( + wallet, + args.withdrawArgs.amount, + args.withdrawArgs.bank, + args.withdrawArgs.exchange, + ); + logger.info("Withdraw done"); }); - console.log("got wallet"); - const balance = await wallet.getBalances(); - console.log(JSON.stringify(balance, undefined, 2)); }); walletCli.run(); |