taler-wallet-developer.rst (20803B)
1 .. 2 This file is part of GNU TALER. 3 Copyright (C) 2014-2024 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero General Public License as published by the Free Software 7 Foundation; either version 2.1, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 16 17 Wallet Developer Manual 18 ####################### 19 20 .. note:: 21 22 This manual contains information for developers working on the wallet 23 component of GNU Taler. It is not intended for a general audience. 24 25 26 The GNU Taler wallet allows customers to withdraw and spend digital cash. 27 28 29 WebExtension Wallet 30 =================== 31 32 Building from source 33 -------------------- 34 35 .. code-block:: console 36 37 $ git clone https://git.taler.net/wallet-core.git 38 $ cd wallet-core 39 $ ./configure 40 $ make webex-stable 41 # Packaged extension now available as: 42 # dist/taler-wallet-$VERSION.zip 43 44 45 Android Wallet 46 ============== 47 48 Please see :ref:`Build-apps-from-source` in the :doc:`taler-developer-manual`. 49 50 51 iOS Wallet 52 ========== 53 54 Please see :ref:`Build-iOS-from-source` in the :doc:`taler-developer-manual`. 55 56 .. _command-line-wallet: 57 58 Command-line Wallet 59 =================== 60 61 This section describes how to use the GNU Taler wallet command line 62 interface (CLI). 63 64 The the wallet CLI is targeted at developers and operators, but not meant to be 65 used by customers. It exposes all functionality that the more user-friendly 66 interfaces (Android app, browser extension) offer. However, it provides more 67 diagnostics and advanced features as well. 68 69 Building from source 70 -------------------- 71 72 The easiest way to install the wallet is via NPM. Note that a recent version of 73 Node.JS (``>=12.20.1``) is required. 74 75 We recommend to install the wallet package on a per-user basis, 76 thus setting ``$INSTALL_PREFIX`` to a directory in ``$HOME``. 77 78 .. code-block:: console 79 80 $ git clone https://git.taler.net/wallet-core.git 81 $ cd wallet-core 82 $ ./bootstrap 83 $ ./configure --prefix=$INSTALL_PREFIX 84 $ make && make install 85 86 The wallet command-line interface should then be available as ``taler-wallet-cli`` under ``$INSTALL_PREFIX/bin``. 87 88 Installation via NPM 89 -------------------- 90 91 The wallet can also obtained via NPM, the Node Package Manager. 92 93 To install the wallet as a global package, run: 94 95 .. code-block:: console 96 97 $ npm install -g taler-wallet 98 # check if installation was successful 99 $ taler-wallet-cli --version 100 101 To install the wallet only for your user, run: 102 103 .. code-block:: console 104 105 $ npm install -g --prefix=$HOME/local taler-wallet 106 # check if installation was successful 107 $ taler-wallet-cli --version 108 # If this fails, make sure that $HOME/local/bin is in your $PATH 109 110 To use the wallet as a library in your own project, run: 111 112 .. code-block:: console 113 114 $ npm install taler-wallet 115 116 117 Getting Help 118 ------------ 119 120 The wallet CLI comes with built-in help. Invoke the wallet CLI (or any subcommand) with the ``--help`` flag to get help: 121 122 .. code-block:: console 123 124 $ taler-wallet-cli --help 125 Usage: taler-wallet-cli COMMAND 126 127 Command line interface for the GNU Taler wallet. 128 129 Options: 130 -h, --help Show this message and exit. 131 --wallet-db=VALUE location of the wallet database file 132 --timetravel=VALUE modify system time by given offset in microseconds 133 --inhibit=VALUE Inhibit running certain operations, useful for debugging and testing. 134 --no-throttle Don't do any request throttling. 135 -v, --version 136 -V, --verbose Enable verbose output. 137 138 Commands: 139 advanced Subcommands for advanced operations (only use if you know what you're doing!). 140 api Call the wallet-core API directly. 141 backup Subcommands for backups 142 balance Show wallet balance. 143 deposit Subcommands for depositing money to payto:// accounts 144 exchanges Manage exchanges. 145 handle-uri Handle a taler:// URI. 146 pending Show pending operations. 147 run-pending Run pending operations. 148 run-until-done Run until no more work is left. 149 testing Subcommands for testing GNU Taler deployments. 150 transactions Show transactions. 151 152 Completing operations 153 --------------------- 154 155 Note that the CLI does not run as a background daemon. When starting 156 operations that don't immediately finish, the wallet needs to be run explicitly 157 to finish any pending tasks: 158 159 160 .. code-block:: console 161 162 # Do one attempt to finish all pending operations 163 $ taler-wallet-cli run-pending 164 165 # Run until all work is done 166 $ taler-wallet-cli run-until-done 167 168 Resetting the wallet 169 -------------------- 170 171 The wallet can be reset by deleting its database file. By default, the database file 172 is ``$HOME/.talerwalletdb.sqlite3``. 173 174 175 Handling taler:// URIs 176 ---------------------- 177 178 Many interactions with the Taler wallet happen by scanning QR codes or special 179 headers on Websites. To emulate this with the command line interface, run the following 180 command: 181 182 .. code-block:: console 183 184 $ taler-wallet-cli handle-uri $URI 185 186 187 Manual withdrawing 188 ------------------ 189 190 .. code-block:: console 191 192 $ taler-wallet-cli advanced withdraw-manually \ 193 --exchange https://exchange.eurint.taler.net/ \ 194 --amount EUR:5 195 196 197 P2P push payments 198 ----------------- 199 200 The following code generates a P2P push transaction over 1 CHF 201 with an expiration time of 30 days (assuming the wallet has a 202 sufficient balance): 203 204 .. code-block:: console 205 206 $ taler-wallet-cli p2p initiate-push-debit \ 207 --purse-expiration="30 d" \ 208 --summary="The summary" \ 209 CHF:1 210 211 The final URL can then be found in the transaction list: 212 213 .. code-block:: console 214 215 $ taler-wallet-cli transactions 216 217 Background wallet 218 ----------------- 219 220 A wallet can be launched in the background: 221 222 .. code-block:: console 223 224 $ taler-wallet-cli advanced serve & 225 226 You can then run various Taler operations faster against 227 this one persistent instance: 228 229 .. code-block:: console 230 231 $ taler-wallet-cli --wallet-connection=wallet-core.sock ... 232 233 Here ``...`` needs to be changed to the commando to run. 234 Make sure to run 235 236 .. code-block:: console 237 238 $ taler-wallet-cli --wallet-connection=wallet-core.sock \ 239 run-until-done 240 241 to wait for pending transactions to complete. 242 243 244 Testing an exchange deployment 245 ------------------------------ 246 247 The following series of commands can be used to check that an exchange deployment 248 is functional: 249 250 .. code-block:: console 251 252 # This will now output a payto URI that money needs to be sent to in order to allow withdrawal 253 # of taler coins 254 $ taler-wallet-cli advanced withdraw-manually --exchange $EXCHANGE_URL --amount EUR:10.50 255 256 # Show the status of the manual withdrawal operation 257 $ taler-wallet-cli transactions 258 259 # Once the transfer has been made, try completing the withdrawal 260 $ taler-wallet-cli run-pending 261 262 # Check status of transactions and show balance 263 $ taler-wallet-cli transactions 264 $ taler-wallet-cli balance 265 266 # Now, directly deposit coins with the exchange into a target account 267 # (Usually, a payment is made via a merchant. The wallet provides 268 # this functionality for testing.) 269 $ taler-wallet-cli deposit create EUR:5 payto://iban/$IBAN 270 271 # Check if transaction was successful. 272 # (If not, fix issue with exchange and run "run-pending" command again) 273 $ taler-wallet-cli transactions 274 275 # The wallet can also track if the exchange wired the money to the merchant account. 276 # The "deposit group id" can be found in the output of the transactions list. 277 $ taler-wallet-cli deposit track $DEPOSIT_GROUP_ID 278 279 280 APIs and Data Formats 281 ===================== 282 283 Envelope Format 284 --------------- 285 286 All API responses and notifications are returned in the 287 following envelope: 288 289 .. ts:def:: WalletResponseEnvelope 290 291 type WalletResponseEnvelope = 292 | WalletSuccess 293 | WalletError 294 | WalletNotification 295 296 .. ts:def:: WalletSuccess 297 298 export interface WalletSuccess { 299 type: "response"; 300 operation: string; 301 // ID to correlate success response to request 302 id: string; 303 // Result type depends on operation 304 result: unknown; 305 } 306 307 .. ts:def:: WalletError 308 309 export interface WalletError { 310 type: "error"; 311 operation: string; 312 // ID to correlate error response to request 313 id: string; 314 error: WalletErrorInfo; 315 } 316 317 .. ts:def:: WalletNotification 318 319 export interface WalletSuccess { 320 type: "notification"; 321 322 // actual type is WalletNotification, 323 // to be documented here 324 payload: any; 325 } 326 327 .. ts:def:: WalletErrorInfo 328 329 export interface WalletErrorInfo { 330 // Numeric error code defined defined in the 331 // GANA gnu-taler-error-codes registry. 332 talerErrorCode: number; 333 334 // English description of the error code. 335 talerErrorHint: string; 336 337 // English diagnostic message that can give details 338 // for the instance of the error. 339 message: string; 340 341 // Error details, type depends 342 // on talerErrorCode 343 details: unknown; 344 } 345 346 Withdrawal 347 ---------- 348 349 A typical API sequence for *bank-integrated* withdrawals can for example look like this: 350 351 #. ``"getWithdrawalDetailsForUri"`` returns an amount and default exchange 352 #. ``"getWithdrawalDetailsForAmount"`` returns fee information and that ToS are not accepted 353 354 #. ``"getExchangeTos"`` are shown to the user and return currentEtag 355 #. ``"setExchangeTosAccepted"`` called with currentEtag after user accepted 356 357 #. ``"acceptWithdrawal"`` after the user confirmed withdrawal with associated fees 358 359 A typical API sequence for *manual* withdrawals can for example look like this: 360 361 #. ``"listExchanges"`` shows a list of exchanges to the user who picks one and an amount 362 #. ``"getWithdrawalDetailsForAmount"`` returns fee information and that ToS are not accepted 363 364 #. ``"getExchangeTos"`` are shown to the user and return currentEtag 365 #. ``"setExchangeTosAccepted"`` called with currentEtag after user accepted 366 367 #. ``"acceptManualWithdrawal"`` after the user confirmed withdrawal with associated fees 368 369 Integration Tests 370 ================= 371 372 Integration Test Example 373 ------------------------ 374 375 Integration tests can be done with the low-level wallet commands. To select which coins and denominations 376 to use, the wallet can dump the coins in an easy-to-process format (`CoinDumpJson <https://git.taler.net/taler-typescript-core.git/tree/packages/taler-util/src/types-taler-wallet.ts#n613>`__). 377 378 The database file for the wallet can be selected with the ``--wallet-db`` 379 option. This option must be passed to the ``taler-wallet-cli`` command and not 380 the subcommands. If the database file doesn't exist, it will be created. 381 382 The following example does a simple withdrawal recoup: 383 384 .. code-block:: console 385 386 # Withdraw digital cash 387 $ taler-wallet-cli --wallet-db=mydb.sqlite3 testing withdraw \ 388 -b https://bank.int.taler.net/ \ 389 -e https://exchange.int.taler.net/ \ 390 -a INTKUDOS:10 391 392 $ coins=$(taler-wallet-cli --wallet-db=mydb.sqlite3 advanced dump-coins) 393 394 # Find coin we want to revoke 395 $ rc=$(echo "$coins" | \ 396 jq -r '[.coins[] | select((.denom_value == "INTKUDOS:5"))][0] | .coin_pub') 397 398 # Find the denom 399 $ rd=$(echo "$coins" | \ 400 jq -r '[.coins[] | select((.denom_value == "INTKUDOS:5"))][0] | .denom_pub_hash') 401 402 # Find all other coins, which will be suspended 403 $ susp=$(echo "$coins" | \ 404 jq --arg rc "$rc" '[.coins[] | select(.coin_pub != $rc) | .coin_pub]') 405 406 # The exchange revokes the denom 407 $ taler-exchange-keyup -r $rd 408 $ taler-deployment-restart 409 410 # Now we suspend the other coins, so later we will pay with the recouped coin 411 $ taler-wallet-cli --wallet-db=mydb.sqlite3 advanced suspend-coins "$susp" 412 413 # Update exchange /keys so recoup gets scheduled 414 $ taler-wallet-cli --wallet-db=mydb.sqlite3 exchanges update -f https://exchange.int.taler.net/ 415 416 # Block until scheduled operations are done 417 $ taler-wallet-cli --wallet-db=mydb.sqlite3 run-until-done 418 419 # Now we buy something, only the coins resulting from recouped will be 420 # used, as other ones are suspended 421 $ taler-wallet-cli --wallet-db=mydb.sqlite3 testing test-pay \ 422 -m https://backend.int.taler.net/ \ 423 -k sandbox \ 424 -a "INTKUDOS:1" \ 425 -s "foo" 426 $ taler-wallet-cli --wallet-db=mydb.sqlite3 run-until-done 427 428 429 To test refreshing, force a refresh: 430 431 .. code-block:: console 432 433 $ taler-wallet-cli --wallet-db=mydb.sqlite3 advanced force-refresh "$coin_pub" 434 435 436 To test zombie coins, use the timetravel option. It **must** be passed to the 437 top-level command and not the subcommand: 438 439 .. code-block:: console 440 441 # Update exchange /keys with time travel, value in microseconds 442 $ taler-wallet-cli --timetravel=1000000 --wallet-db=mydb.sqlite3 \ 443 exchanges update -f https://exchange.int.taler.net/ 444 445 446 Integration Test and Fault Injection Framework 447 ---------------------------------------------- 448 449 This section describes the current approach to integration testing in the wallet. 450 451 It's all based on a TypeScript harness process, which itself implements 452 the fault injection proxy (async and in-process)! 453 454 The new approach consists of the following parts: 455 456 1. A strongly typed, convenient helper library to easily set up and run 457 arbitrary Taler deployments and run test cases. These components plug 458 together as easily as lego bricks, even with multiple 459 exchanges/merchants/banks/etc. Logs and clean shutdown (even on SIGINT 460 or errors) are handled properly. (Support for auditors is still pending 461 but needed to fully test the wallet.) 462 463 This is how a simple withdrawal and payment test case looks like: 464 `<https://git.taler.net/taler-typescript-core.git/tree/packages/taler-harness/src/integrationtests/test-payment.ts>`__ 465 466 (What's particularly nice is that all our docs contain TypeScript 467 definitions for all API request bodies. So just copying them into the 468 test harness gives us auto-completion and compile-time checks to avoid 469 typos. The wallet's JSON validation machinery is also re-used.) 470 471 2. A fault injection proxy that can be plugged between the services 472 and/or the wallet. It runs alongside the test harness, and can thus can 473 use arbitrary custom logic. There's no dependency for it other than 474 built-in Node.JS libraries. Simple fault injections are just as easy to 475 set up as with the twister. 476 477 The following test case (a) logs all requests and responses to the test 478 harness stdout and (b) at a certain point, starts dropping the next 10 479 requests to the exchange (testing the wallet's retry logic): 480 481 `<https://git.taler.net/taler-typescript-core.git/tree/packages/taler-harness/src/integrationtests/test-payment-fault.ts#n165>`__ 482 483 3. All util functionality from JS wallet-core, such as the Taler crypto, 484 amount/date/etc. handling and JSON parsing/validation (the wallet is now 485 more modular and easier to use as a library) can be used in the 486 integration tests, even if a different wallet (Kotlin, whatever) is 487 tested via the CLI. 488 489 4. A bunch of test cases that use (1)-(3). These are *significantly* 490 more readable and hackable than other test approaches we had, while 491 allowing for more complex scenarios. There are still way too few tests 492 though! 493 494 5. A test runner (written in bash) that runs test cases based on a glob 495 pattern and reports the results. 496 497 Injecting a fault is as easy as: 498 499 .. code:: ts 500 501 // Set up test case 502 [...] 503 504 exchangeProxy.addFault({ 505 beforeResponse(ctx: FaultInjectionResponseContext) { 506 if (cond1) { // Drop some responses 507 ctx.dropResponse = true; 508 return; 509 } else if (cond2) { // modify some others 510 ctx.responseBody = Buffer.from(`{"oops": true}`, "utf-8"); 511 return; 512 } 513 // Other things that can be modified: 514 // - drop/modify the request, not just the response 515 // - modify headers 516 // - modify status codes 517 } 518 }); 519 520 await doSomethingWithTheWallet(); 521 522 exchangeProxy.clearFault(); 523 524 await doMoreWithTheWallet(); 525 526 527 To make the configuration easy, an ``ExchangeService`` (or ``MerchantService``, 528 ``BankService`` etc.) can be wrapped in a ``FaultInjectedExchangeService``, 529 which implements the ``ExchangeServiceInterface``: 530 531 .. code:: ts 532 533 // create exchange and two merchants 534 const exchange = await setupExchange(...); 535 const merchant1 = ...; 536 const merchant2 = ...; 537 538 // Add exchange to merchant-accepted exchanges. 539 // This will adjust the config. 540 merchant1.addExchange(exchange); 541 542 // Wrap exchange in fault injection proxy 543 const faultInjectedExchange: ExchangeServiceInterface 544 = new FaultInjectedExchangeService(t, exchange1, 8085); 545 546 // Merchant 2 talks to the exchange over fault injection, 547 // and thus must use the "twisted" base URL. 548 merchant2.addExchange(faultInjectedExchange); 549 550 551 The package for the integration tests is here: 552 553 `<https://git.taler.net/wallet-core.git/tree/packages/taler-harness>`__ 554 555 The integration tests are run via the ``taler-harness`` tool. 556 557 .. code:: sh 558 559 ./bootstrap && ./configure --prefix=... && make install 560 taler-harness run-integrationtests 561 562 563 Dev Experiments 564 =============== 565 566 Dev experiments allow simulating certain scenarios that are difficult to 567 reproduce otherwise. This allows more comprehensive (manual) testing of the 568 UIs. 569 570 You can enable dev experiments by putting the wallet into dev mode and then 571 scanning the QR code for a ``taler://dev-experiment`` URI that specifies the 572 desired dev experiment. 573 574 Faking Protocol Versions 575 ------------------------ 576 577 The ``start-fakeprotover`` dev experiment can be used to fake the protocol 578 version reported by Taler components. It mocks the ``version`` field in the 579 response to ``/config`` or ``/keys``. 580 581 Usage: 582 583 .. code:: none 584 585 taler://dev-experiment/start-fakeprotover?base_url=...&fake_ver=... 586 587 Example: 588 589 .. code:: none 590 591 # Fake version 10:0:0 for https://exchange.demo.taler.net/ 592 taler://dev-experiment/start-fakeprotover?base_url=https%3A%2F%2Fexchange.demo.taler.net%2F&fake_ver=10%3A0%3A0 593 594 595 Faking Transactions 596 ------------------- 597 598 599 **Withdrawal Transaction:** 600 601 .. code:: none 602 603 taler://dev-experiment/add-fake-tx?txType=withdrawal&amountEffective=KUDOS:5 604 605 Options: 606 607 * ``amountEffective``: Mandatory. Effective amount of the withdrawal. 608 * ``tRel``: Optional (defaults to ``0s``). Relative time that indicates 609 when in the past the transaction was started (and finished). 610 * ``exchangeBaseUrl``. Optional (defaults to ``https://exchange.demo.taler.net``). 611 Exchange base URL used for the withdrwal. 612 613 614 **Payment Transaction:** 615 616 .. code:: none 617 618 taler://dev-experiment/add-fake-tx?txType=payment&amountEffective=KUDOS:5 619 620 Options: 621 622 * ``amountEffective``: Mandatory. Effective amount of the withdrawal. 623 * ``tRel``: Optional (defaults to ``0s``). Relative time that indicates 624 when in the past the transaction was started (and finished). 625 * ``exchangeBaseUrl``. Optional (defaults to ``https://exchange.demo.taler.net``). 626 Exchange base URL used for the payment. 627 * ``merchantBaseUrl``. Optional (defaults to ``https://backend.demo.taler.net/``). 628 * ``merchantName``. Optional (defaults to ``Test Merchant``). Display name 629 of the merchant in the contract terms of the transaction. 630 * ``summary``. Optional (defaults to ``Test``). Summary in the contract terms of the 631 transaction. 632 633 **Peer Push Credit Transaction:** 634 635 .. code:: none 636 637 taler://dev-experiment/add-fake-tx?txType=peer-push-credit&amountEffective=KUDOS:5 638 639 Options: 640 641 * ``amountEffective``: Mandatory. Effective amount of the withdrawal. 642 * ``tRel``: Optional (defaults to ``0s``). Relative time that indicates 643 when in the past the transaction was started (and finished). 644 * ``exchangeBaseUrl``. Optional (defaults to ``https://exchange.demo.taler.net``). 645 Exchange base URL used for the payment. 646 * ``summary``. Optional (defaults to ``Test``). Summary in the contract terms of the 647 transaction. 648 649 **Peer Push Debit Transaction:** 650 651 .. code:: none 652 653 taler://dev-experiment/add-fake-tx?txType=peer-push-debit&amountEffective=KUDOS:5 654 655 Options: 656 657 * ``amountEffective``: Mandatory. Effective amount of the withdrawal. 658 * ``tRel``: Optional (defaults to ``0s``). Relative time that indicates 659 when in the past the transaction was started (and finished). 660 * ``exchangeBaseUrl``. Optional (defaults to ``https://exchange.demo.taler.net``). 661 Exchange base URL used for the payment. 662 * ``summary``. Optional (defaults to ``Test``). Summary in the contract terms of the 663 transaction.