taler-docs

Documentation for GNU Taler components, APIs and protocols
Log | Files | Refs | README | LICENSE

taler-wallet-developer.rst (29512B)


      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 3.0, 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 Additional implementation notes about request retries, cancellation, and
     29 failure reporting are documented in :doc:`../wallet/wallet-error-handling`.
     30 
     31 .. toctree::
     32   :hidden:
     33 
     34   ../wallet/wallet-error-handling
     35 
     36 
     37 WebExtension Wallet
     38 ===================
     39 
     40 Building from source
     41 --------------------
     42 
     43 .. code-block:: console
     44 
     45   $ git clone https://git.taler.net/wallet-core.git
     46   $ cd wallet-core
     47   $ ./configure
     48   $ make webex-stable
     49   # Packaged extension now available as:
     50   # dist/taler-wallet-$VERSION.zip
     51 
     52 
     53 Android Wallet
     54 ==============
     55 
     56 Please see :ref:`Build-apps-from-source` in the :doc:`taler-developer-manual`.
     57 
     58 
     59 iOS Wallet
     60 ==========
     61 
     62 Please see :ref:`Build-iOS-from-source` in the :doc:`taler-developer-manual`.
     63 
     64 .. _command-line-wallet:
     65 
     66 Command-line Wallet
     67 ===================
     68 
     69 This section describes how to use the GNU Taler wallet command line
     70 interface (CLI).
     71 
     72 The wallet CLI is targeted at developers and operators, but not meant to be
     73 used by customers.  It exposes all functionality that the more user-friendly
     74 interfaces (Android app, browser extension) offer.  However, it provides more
     75 diagnostics and advanced features as well.
     76 
     77 Building from source
     78 --------------------
     79 
     80 The easiest way to install the wallet is via NPM.  Note that a recent version of
     81 Node.JS (``>=12.20.1``) is required.
     82 
     83 We recommend to install the wallet package on a per-user basis,
     84 thus setting ``$INSTALL_PREFIX`` to a directory in ``$HOME``.
     85 
     86 .. code-block:: console
     87 
     88   $ git clone https://git.taler.net/wallet-core.git
     89   $ cd wallet-core
     90   $ ./bootstrap
     91   $ ./configure --prefix=$INSTALL_PREFIX
     92   $ make && make install
     93 
     94 The wallet command-line interface should then be available as ``taler-wallet-cli`` under ``$INSTALL_PREFIX/bin``.
     95 
     96 Installation via NPM
     97 --------------------
     98 
     99 The wallet can also obtained via NPM, the Node Package Manager.
    100 
    101 To install the wallet as a global package, run:
    102 
    103 .. code-block:: console
    104 
    105   $ npm install -g taler-wallet
    106   # check if installation was successful
    107   $ taler-wallet-cli --version
    108 
    109 To install the wallet only for your user, run:
    110 
    111 .. code-block:: console
    112 
    113   $ npm install -g --prefix=$HOME/local taler-wallet
    114   # check if installation was successful
    115   $ taler-wallet-cli --version
    116   # If this fails, make sure that $HOME/local/bin is in your $PATH
    117 
    118 To use the wallet as a library in your own project, run:
    119 
    120 .. code-block:: console
    121 
    122   $ npm install taler-wallet
    123 
    124 
    125 Getting Help
    126 ------------
    127 
    128 The wallet CLI comes with built-in help.  Invoke the wallet CLI (or any subcommand) with the ``--help`` flag to get help:
    129 
    130 .. code-block:: console
    131 
    132   $ taler-wallet-cli --help
    133   Usage: taler-wallet-cli COMMAND
    134 
    135   Command line interface for the GNU Taler wallet.
    136 
    137   Options:
    138     -h, --help             Show this message and exit.
    139     --wallet-db=VALUE      location of the wallet database file
    140     --timetravel=VALUE     modify system time by given offset in microseconds
    141     --inhibit=VALUE        Inhibit running certain operations, useful for debugging and testing.
    142     --no-throttle          Don't do any request throttling.
    143     -v, --version
    144     -V, --verbose          Enable verbose output.
    145 
    146   Commands:
    147     advanced               Subcommands for advanced operations (only use if you know what you're doing!).
    148     api                    Call the wallet-core API directly.
    149     backup                 Subcommands for backups
    150     balance                Show wallet balance.
    151     deposit                Subcommands for depositing money to payto:// accounts
    152     exchanges              Manage exchanges.
    153     handle-uri             Handle a taler:// URI.
    154     pending                Show pending operations.
    155     run-pending            Run pending operations.
    156     run-until-done         Run until no more work is left.
    157     testing                Subcommands for testing GNU Taler deployments.
    158     transactions           Show transactions.
    159 
    160 Completing operations
    161 ---------------------
    162 
    163 Note that the CLI does not run as a background daemon.  When starting
    164 operations that don't immediately finish, the wallet needs to be run explicitly
    165 to finish any pending tasks:
    166 
    167 
    168 .. code-block:: console
    169 
    170   # Do one attempt to finish all pending operations
    171   $ taler-wallet-cli run-pending
    172 
    173   # Run until all work is done
    174   $ taler-wallet-cli run-until-done
    175 
    176 Resetting the wallet
    177 --------------------
    178 
    179 The wallet can be reset by deleting its database file.  By default, the database file
    180 is ``$HOME/.talerwalletdb.sqlite3``.
    181 
    182 
    183 Handling taler:// URIs
    184 ----------------------
    185 
    186 Many interactions with the Taler wallet happen by scanning QR codes or special
    187 headers on Websites.  To emulate this with the command line interface, run the following
    188 command:
    189 
    190 .. code-block:: console
    191 
    192   $ taler-wallet-cli handle-uri $URI
    193 
    194 
    195 Manual withdrawing
    196 ------------------
    197 
    198 .. code-block:: console
    199 
    200   $ taler-wallet-cli advanced withdraw-manually \
    201       --exchange https://exchange.eurint.taler.net/ \
    202       --amount EUR:5
    203 
    204 
    205 P2P push payments
    206 -----------------
    207 
    208 The following code generates a P2P push transaction over 1 CHF
    209 with an expiration time of 30 days (assuming the wallet has a
    210 sufficient balance):
    211 
    212 .. code-block:: console
    213 
    214   $ taler-wallet-cli p2p initiate-push-debit \
    215       --purse-expiration="30 d" \
    216       --summary="The summary" \
    217       CHF:1
    218 
    219 The final URL can then be found in the transaction list:
    220 
    221 .. code-block:: console
    222 
    223   $ taler-wallet-cli transactions
    224 
    225 Background wallet
    226 -----------------
    227 
    228 A wallet can be launched in the background:
    229 
    230 .. code-block:: console
    231 
    232   $ taler-wallet-cli advanced serve &
    233 
    234 You can then run various Taler operations faster against
    235 this one persistent instance:
    236 
    237 .. code-block:: console
    238 
    239   $ taler-wallet-cli --wallet-connection=wallet-core.sock ...
    240 
    241 Here ``...`` needs to be changed to the commando to run.
    242 Make sure to run
    243 
    244 .. code-block:: console
    245 
    246   $ taler-wallet-cli --wallet-connection=wallet-core.sock \
    247       run-until-done
    248 
    249 to wait for pending transactions to complete.
    250 
    251 
    252 Testing an exchange deployment
    253 ------------------------------
    254 
    255 The following series of commands can be used to check that an exchange deployment
    256 is functional:
    257 
    258 .. code-block:: console
    259 
    260   # This will now output a payto URI that money needs to be sent to in order to allow withdrawal
    261   # of taler coins
    262   $ taler-wallet-cli advanced withdraw-manually --exchange $EXCHANGE_URL --amount EUR:10.50
    263 
    264   # Show the status of the manual withdrawal operation
    265   $ taler-wallet-cli transactions
    266 
    267   # Once the transfer has been made, try completing the withdrawal
    268   $ taler-wallet-cli run-pending
    269 
    270   # Check status of transactions and show balance
    271   $ taler-wallet-cli transactions
    272   $ taler-wallet-cli balance
    273 
    274   # Now, directly deposit coins with the exchange into a target account
    275   # (Usually, a payment is made via a merchant.  The wallet provides
    276   # this functionality for testing.)
    277   $ taler-wallet-cli deposit create EUR:5 payto://iban/$IBAN
    278 
    279   # Check if transaction was successful.
    280   # (If not, fix issue with exchange and run "run-pending" command again)
    281   $ taler-wallet-cli transactions
    282 
    283   # The wallet can also track if the exchange wired the money to the merchant account.
    284   # The "deposit group id" can be found in the output of the transactions list.
    285   $ taler-wallet-cli deposit track $DEPOSIT_GROUP_ID
    286 
    287 
    288 APIs and Data Formats
    289 =====================
    290 
    291 Envelope Format
    292 ---------------
    293 
    294 All API responses and notifications are returned in the
    295 following envelope:
    296 
    297 .. ts:def:: WalletResponseEnvelope
    298 
    299    type WalletResponseEnvelope =
    300     | WalletSuccess
    301     | WalletError
    302     | WalletNotification
    303 
    304 .. ts:def:: WalletSuccess
    305 
    306    export interface WalletSuccess {
    307      type: "response";
    308      operation: string;
    309      // ID to correlate success response to request
    310      id: string;
    311      // Result type depends on operation
    312      result: unknown;
    313    }
    314 
    315 .. ts:def:: WalletError
    316 
    317    export interface WalletError {
    318      type: "error";
    319      operation: string;
    320      // ID to correlate error response to request
    321      id: string;
    322      error: WalletErrorInfo;
    323    }
    324 
    325 .. ts:def:: WalletNotification
    326 
    327    export interface WalletSuccess {
    328      type: "notification";
    329 
    330      // actual type is WalletNotification,
    331      // to be documented here
    332      payload: any;
    333    }
    334 
    335 .. ts:def:: WalletErrorInfo
    336 
    337    export interface WalletErrorInfo {
    338      // Numeric error code defined defined in the
    339      // GANA gnu-taler-error-codes registry.
    340      talerErrorCode: number;
    341 
    342      // English description of the error code.
    343      talerErrorHint: string;
    344 
    345      // English diagnostic message that can give details
    346      // for the instance of the error.
    347      message: string;
    348 
    349      // Error details, type depends
    350      // on talerErrorCode
    351      details: unknown;
    352    }
    353 
    354 Withdrawal
    355 ----------
    356 
    357 A typical API sequence for *bank-integrated* withdrawals can for example look like this:
    358 
    359 #. ``"getWithdrawalDetailsForUri"`` returns an amount and default exchange
    360 #. ``"getWithdrawalDetailsForAmount"`` returns fee information and that ToS are not accepted
    361 
    362    #. ``"getExchangeTos"`` are shown to the user and return currentEtag
    363    #. ``"setExchangeTosAccepted"`` called with currentEtag after user accepted
    364 
    365 #. ``"acceptWithdrawal"`` after the user confirmed withdrawal with associated fees
    366 
    367 A typical API sequence for *manual* withdrawals can for example look like this:
    368 
    369 #. ``"listExchanges"`` shows a list of exchanges to the user who picks one and an amount
    370 #. ``"getWithdrawalDetailsForAmount"`` returns fee information and that ToS are not accepted
    371 
    372    #. ``"getExchangeTos"`` are shown to the user and return currentEtag
    373    #. ``"setExchangeTosAccepted"`` called with currentEtag after user accepted
    374 
    375 #. ``"acceptManualWithdrawal"`` after the user confirmed withdrawal with associated fees
    376 
    377 Integration Tests
    378 =================
    379 
    380 Integration Test Example
    381 ------------------------
    382 
    383 Integration tests can be done with the low-level wallet commands.  To select which coins and denominations
    384 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>`__).
    385 
    386 The database file for the wallet can be selected with the ``--wallet-db``
    387 option.  This option must be passed to the ``taler-wallet-cli`` command and not
    388 the subcommands.  If the database file doesn't exist, it will be created.
    389 
    390 The following example does a simple withdrawal recoup:
    391 
    392 .. code-block:: console
    393 
    394   # Withdraw digital cash
    395   $ taler-wallet-cli --wallet-db=mydb.sqlite3 testing withdraw \
    396       -b https://bank.int.taler.net/ \
    397       -e https://exchange.int.taler.net/ \
    398       -a INTKUDOS:10
    399 
    400   $ coins=$(taler-wallet-cli --wallet-db=mydb.sqlite3 advanced dump-coins)
    401 
    402   # Find coin we want to revoke
    403   $ rc=$(echo "$coins" | \
    404          jq -r '[.coins[] | select((.denom_value == "INTKUDOS:5"))][0] | .coin_pub')
    405 
    406   # Find the denom
    407   $ rd=$(echo "$coins" | \
    408          jq -r '[.coins[] | select((.denom_value == "INTKUDOS:5"))][0] | .denom_pub_hash')
    409 
    410   # Find all other coins, which will be suspended
    411   $ susp=$(echo "$coins" | \
    412            jq --arg rc "$rc" '[.coins[] | select(.coin_pub != $rc) | .coin_pub]')
    413 
    414   # The exchange revokes the denom
    415   $ taler-exchange-keyup -r $rd
    416   $ taler-deployment-restart
    417 
    418   # Now we suspend the other coins, so later we will pay with the recouped coin
    419   $ taler-wallet-cli --wallet-db=mydb.sqlite3 advanced suspend-coins "$susp"
    420 
    421   # Update exchange /keys so recoup gets scheduled
    422   $ taler-wallet-cli --wallet-db=mydb.sqlite3 exchanges update -f https://exchange.int.taler.net/
    423 
    424   # Block until scheduled operations are done
    425   $ taler-wallet-cli --wallet-db=mydb.sqlite3 run-until-done
    426 
    427   # Now we buy something, only the coins resulting from recouped will be
    428   # used, as other ones are suspended
    429   $ taler-wallet-cli --wallet-db=mydb.sqlite3 testing test-pay \
    430       -m https://backend.int.taler.net/ \
    431       -k sandbox \
    432       -a "INTKUDOS:1" \
    433       -s "foo"
    434   $ taler-wallet-cli --wallet-db=mydb.sqlite3 run-until-done
    435 
    436 
    437 To test refreshing, force a refresh:
    438 
    439 .. code-block:: console
    440 
    441   $ taler-wallet-cli --wallet-db=mydb.sqlite3 advanced force-refresh "$coin_pub"
    442 
    443 
    444 To test zombie coins, use the timetravel option. It **must** be passed to the
    445 top-level command and not the subcommand:
    446 
    447 .. code-block:: console
    448 
    449   # Update exchange /keys with time travel, value in microseconds
    450   $ taler-wallet-cli --timetravel=1000000 --wallet-db=mydb.sqlite3 \
    451       exchanges update -f https://exchange.int.taler.net/
    452 
    453 
    454 Integration Test and Fault Injection Framework
    455 ----------------------------------------------
    456 
    457 This section describes the current approach to integration testing in the wallet.
    458 
    459 It's all based on a TypeScript harness process, which itself implements
    460 the fault injection proxy (async and in-process)!
    461 
    462 The new approach consists of the following parts:
    463 
    464 1. A strongly typed, convenient helper library to easily set up and run
    465 arbitrary Taler deployments and run test cases.  These components plug
    466 together as easily as lego bricks, even with multiple
    467 exchanges/merchants/banks/etc.  Logs and clean shutdown (even on SIGINT
    468 or errors) are handled properly.  (Support for auditors is still pending
    469 but needed to fully test the wallet.)
    470 
    471 This is how a simple withdrawal and payment test case looks like:
    472 `<https://git.taler.net/taler-typescript-core.git/tree/packages/taler-harness/src/integrationtests/test-payment.ts>`__
    473 
    474 (What's particularly nice is that all our docs contain TypeScript
    475 definitions for all API request bodies.  So just copying them into the
    476 test harness gives us auto-completion and compile-time checks to avoid
    477 typos.  The wallet's JSON validation machinery is also re-used.)
    478 
    479 2. A fault injection proxy that can be plugged between the services
    480 and/or the wallet.  It runs alongside the test harness, and can thus can
    481 use arbitrary custom logic.  There's no dependency for it other than
    482 built-in Node.JS libraries.  Simple fault injections are just as easy to
    483 set up as with the twister.
    484 
    485 The following test case (a) logs all requests and responses to the test
    486 harness stdout and (b) at a certain point, starts dropping the next 10
    487 requests to the exchange (testing the wallet's retry logic):
    488 
    489 `<https://git.taler.net/taler-typescript-core.git/tree/packages/taler-harness/src/integrationtests/test-payment-fault.ts#n165>`__
    490 
    491 3. All util functionality from JS wallet-core, such as the Taler crypto,
    492 amount/date/etc. handling and JSON parsing/validation (the wallet is now
    493 more modular and easier to use as a library) can be used in the
    494 integration tests, even if a different wallet (Kotlin, whatever) is
    495 tested via the CLI.
    496 
    497 4. A bunch of test cases that use (1)-(3).  These are *significantly*
    498 more readable and hackable than other test approaches we had, while
    499 allowing for more complex scenarios.  There are still way too few tests
    500 though!
    501 
    502 5. A test runner (written in bash) that runs test cases based on a glob
    503 pattern and reports the results.
    504 
    505 Injecting a fault is as easy as:
    506 
    507 .. code:: ts
    508 
    509   // Set up test case
    510   [...]
    511 
    512   exchangeProxy.addFault({
    513     beforeResponse(ctx: FaultInjectionResponseContext) {
    514        if (cond1) { // Drop some responses
    515          ctx.dropResponse = true;
    516          return;
    517        } else if (cond2) { // modify some others
    518          ctx.responseBody = Buffer.from(`{"oops": true}`, "utf-8");
    519          return;
    520        }
    521        // Other things that can be modified:
    522        // - drop/modify the request, not just the response
    523        // - modify headers
    524        // - modify status codes
    525     }
    526   });
    527 
    528   await doSomethingWithTheWallet();
    529 
    530   exchangeProxy.clearFault();
    531 
    532   await doMoreWithTheWallet();
    533 
    534 
    535 To make the configuration easy, an ``ExchangeService`` (or ``MerchantService``,
    536 ``BankService`` etc.) can be wrapped in a ``FaultInjectedExchangeService``,
    537 which implements the ``ExchangeServiceInterface``:
    538 
    539 .. code:: ts
    540 
    541   // create exchange and two merchants
    542   const exchange = await setupExchange(...);
    543   const merchant1 = ...;
    544   const merchant2 = ...;
    545 
    546   // Add exchange to merchant-accepted exchanges.
    547   // This will adjust the config.
    548   merchant1.addExchange(exchange);
    549 
    550   // Wrap exchange in fault injection proxy
    551   const faultInjectedExchange: ExchangeServiceInterface
    552     = new FaultInjectedExchangeService(t, exchange1, 8085);
    553 
    554   // Merchant 2 talks to the exchange over fault injection,
    555   // and thus must use the "twisted" base URL.
    556   merchant2.addExchange(faultInjectedExchange);
    557 
    558 
    559 The package for the integration tests is here:
    560 
    561 `<https://git.taler.net/wallet-core.git/tree/packages/taler-harness>`__
    562 
    563 The integration tests are run via the ``taler-harness`` tool.
    564 
    565 .. code:: sh
    566 
    567   ./bootstrap && ./configure --prefix=... && make install
    568   taler-harness run-integrationtests
    569 
    570 
    571 Transaction lifecycle contract
    572 ==============================
    573 
    574 Wallet transactions expose their current state, user-visible information, and
    575 permitted ``txActions`` through the wallet API.  UIs MUST derive buttons from
    576 ``txActions`` rather than infer them from a locally duplicated transition
    577 table.  The common lifecycle classes are:
    578 
    579 * ``pending`` and ``finalizing``: wallet processing is active;
    580 * ``dialog``: an explicit user decision is required;
    581 * ``suspended``: processing was paused and may offer resume or deletion;
    582 * ``aborting``: compensating/refund work is active;
    583 * ``done``: the intended operation completed;
    584 * ``failed`` or ``aborted``: processing ended unsuccessfully, with the reason
    585   carried by the transaction response; and
    586 * ``deleted``: the history entry is hidden, while cryptographic records still
    587   needed for spend, recoup, or auditing may remain.
    588 
    589 Network retries and internal self-transitions need not change the lifecycle
    590 class.  An implementation can expose error details while remaining pending.
    591 Deletion is not cancellation: a transaction must first use an offered abort
    592 action when protocol-side compensation is necessary.
    593 
    594 
    595 KYC-gated operation retry
    596 =========================
    597 
    598 Withdrawals, deposits, and peer-to-peer operations use one common retry
    599 algorithm when an exchange denies progress for KYC or AML reasons:
    600 
    601 #. Attempt the operation unless the most recent denial is less than one hour
    602    old.  Success ends processing; HTTP 451 records the denial and whether the
    603    account authorization was invalid; unrelated failures back off.
    604 #. Query the operation's ``/kyc-check/`` endpoint.  The first request does not
    605    long-poll.  Later requests long-poll for account authorization, rule, or AML
    606    changes and include ``min_rule`` when a rule generation is known.
    607 #. Compare the HTTP status, Taler error code, and rule generation with the
    608    previous response.  An unchanged response backs off.  HTTP 200 or 204
    609    retries the operation; HTTP 202 evaluates the exposed limits; HTTP 403 can
    610    switch to a requested account key when the wallet owns it; HTTP 404 either
    611    requests new authorization or evaluates default limits.
    612 #. A ``verboten`` limit permanently fails the transaction.  A time-window
    613    limit schedules the next attempt for the first permitted time.  Otherwise
    614    the wallet immediately retries the operation.
    615 
    616 Manual review of KYC instructions, a new authorization transfer, or relevant
    617 progress in another operation resets the retry delay.  Repeated network
    618 timeouts still use exponential backoff even when the HTTP long-poll duration
    619 is shortened to fit middleware limits.
    620 
    621 
    622 Exchange base-URL migration
    623 ===========================
    624 
    625 Wallet-core maintains explicit ``old URL -> new URL`` migration plans.  A plan
    626 is applied only after the old exchange is unavailable or reports a mismatching
    627 canonical ``base_url`` and the new URL returns a valid ``/keys`` response whose
    628 ``base_url`` matches that new URL.  Applying a plan atomically replaces stored
    629 references and records the old URL, new URL, and migration timestamp.
    630 
    631 Operators must publish wallet support before moving the exchange, allow a
    632 client-update grace period, migrate the database and optional security-module
    633 keys, and keep a reverse proxy from the old URL for at least the validity of
    634 the last old ``/keys`` response.  Merchants must configure the new URL
    635 explicitly; wallet migration plans do not rewrite merchant configuration.
    636 
    637 
    638 IBAN and BBAN presentation
    639 ==========================
    640 
    641 The wallet API converts between the protocol's ``payto://iban`` representation
    642 and a user-facing account field.  ``convertIbanAccountFieldToPayto`` accepts an
    643 IBAN or the currency's supported BBAN form and returns the normalized payto
    644 URI plus the detected entry type.  ``convertIbanPaytoToAccountField`` performs
    645 the reverse conversion.  Deposit wire types advertise ``preferredEntryType``
    646 so UIs do not maintain a separate currency table.
    647 
    648 HUF accounts use BBAN entry/display for production ``iban`` wire targets.  The
    649 CHF BBAN mapping is reserved for the wallet development experiment.  A BBAN
    650 field must also accept a pasted full IBAN.  Protocol messages continue to use
    651 the normalized payto URI; BBAN is a presentation and entry convention only.
    652 
    653 
    654 Diagnostics export privacy
    655 ==========================
    656 
    657 ``getDiagnostics`` produces support information suitable for saving or
    658 sharing without exporting the wallet database.  The response MUST NOT contain
    659 private keys or other secret key material.  Account identifiers such as IBANs
    660 must be truncated to at most six characters, and user names or equivalent
    661 identifiers must be removed or truncated.  Adding fields to the response
    662 requires a privacy review against these invariants.  The diagnostics action is
    663 available outside developer mode because it is the preferred alternative to a
    664 database export.
    665 
    666 
    667 Coin selection
    668 ==============
    669 
    670 For one exchange, wallet-core selects eligible coins as follows:
    671 
    672 #. Traverse denominations from smallest to largest and add coins until their
    673    value covers the target, using the earliest-expiring coins first within a
    674    denomination.
    675 #. Traverse selected denominations from largest to smallest and remove coins
    676    whenever the remainder still covers the target.  Obtain change from the
    677    smallest indispensable coin when needed.
    678 #. When customer-paid fees remain above the merchant allowance and the wallet
    679    is not imbalanced, replace groups of small coins with equivalent larger
    680    coins until fees fall below the allowance or no useful replacement exists.
    681 
    682 A wallet is considered imbalanced for this step when it holds, on average,
    683 more than five times the denomination ratio in coins per denomination,
    684 excluding the largest denomination.  This keeps selection linear, spends old
    685 coins first, and limits small-coin accumulation.  Multiple-exchange selection
    686 is outside this algorithm and must be handled by a higher-level decision.
    687 
    688 
    689 Wallet color roles
    690 ==================
    691 
    692 Wallet UIs use semantic theme roles instead of literal colors in components.
    693 Use roles such as ``primary`` and ``onPrimary`` for an accent and its
    694 foreground, the corresponding ``Container`` pair for filled surfaces, and
    695 ``error``, ``warning``, and ``success`` roles for feedback.  Surface,
    696 background, outline, and foreground roles must likewise be paired by meaning;
    697 do not choose a role merely because its current hex value looks suitable.
    698 
    699 Every component must obtain roles from the platform theme so light and dark
    700 modes can switch without component-specific branches.  New role pairs and
    701 overrides require WCAG 2.1 AA contrast verification in both modes, including
    702 disabled, selected, focused, and error states.  Native and WebExtension
    703 implementations may encode themes differently, but should retain the same
    704 semantic names and intended hierarchy.  The evolving palette and adoption
    705 status are recorded in :doc:`../design-documents/066-wallet-color-scheme`.
    706 
    707 
    708 Wallet database migrations
    709 ==========================
    710 
    711 Wallet database upgrades are automatic and must preserve the previous data
    712 until the new representation is known to be usable.  The browser wallet uses
    713 an IndexedDB metadata database to identify the active major database and a
    714 separately named major-version database for wallet records.  This indirection
    715 allows a major migration to build a new database without destroying the old
    716 one first.
    717 
    718 IndexedDB changes use three mechanisms:
    719 
    720 * A major migration creates a new major-version database and explicitly moves
    721   data.  This is reserved for changes that cannot be expressed safely in an
    722   IndexedDB upgrade transaction; a backup export/import cycle is preferred
    723   when practical.
    724 * A minor schema migration adds or removes object stores or indexes by
    725   changing the schema declaration and its ``versionAdded`` metadata.
    726 * An ordered fixup transforms stored values, such as when a mandatory field or
    727   serialized representation changes.  Fixups can also repair data written by
    728   an already deployed buggy version.
    729 
    730 The native SQLite backend has its own ordered schema migrations.  Migration
    731 from the IndexedDB emulation to native SQLite is a separate, explicit path;
    732 backend versions must not be inferred to be interchangeable.  Every migration
    733 needs tests starting from representative old data, including interrupted and
    734 retry scenarios.  Current schema declarations and fixup registries in
    735 wallet-core are authoritative; the design history is in
    736 :doc:`../design-documents/034-wallet-db-migration`.
    737 
    738 
    739 Dev Experiments
    740 ===============
    741 
    742 Dev experiments allow simulating certain scenarios that are difficult to
    743 reproduce otherwise.  This allows more comprehensive (manual) testing of the
    744 UIs.
    745 
    746 You can enable dev experiments by putting the wallet into dev mode and then
    747 scanning the QR code for a ``taler://dev-experiment`` URI that specifies the
    748 desired dev experiment.
    749 
    750 Faking Protocol Versions
    751 ------------------------
    752 
    753 The ``start-fakeprotover`` dev experiment can be used to fake the protocol
    754 version reported by Taler components.  It mocks the ``version`` field in the
    755 response to ``/config`` or ``/keys``.
    756 
    757 Usage:
    758 
    759 .. code:: none
    760 
    761    taler://dev-experiment/start-fakeprotover?base_url=...&fake_ver=...
    762 
    763 Example:
    764 
    765 .. code:: none
    766 
    767    # Fake version 10:0:0 for https://exchange.demo.taler.net/
    768    taler://dev-experiment/start-fakeprotover?base_url=https%3A%2F%2Fexchange.demo.taler.net%2F&fake_ver=10%3A0%3A0
    769 
    770 
    771 Faking Transactions
    772 -------------------
    773 
    774 
    775 **Withdrawal Transaction:**
    776 
    777 .. code:: none
    778 
    779    taler://dev-experiment/add-fake-tx?txType=withdrawal&amountEffective=KUDOS:5
    780 
    781 Options:
    782 
    783 * ``amountEffective``: Mandatory. Effective amount of the withdrawal.
    784 * ``tRel``: Optional (defaults to ``0s``). Relative time that indicates
    785   when in the past the transaction was started (and finished).
    786 * ``exchangeBaseUrl``. Optional (defaults to ``https://exchange.demo.taler.net``).
    787   Exchange base URL used for the withdrwal.
    788 
    789 
    790 **Payment Transaction:**
    791 
    792 .. code:: none
    793 
    794    taler://dev-experiment/add-fake-tx?txType=payment&amountEffective=KUDOS:5
    795 
    796 Options:
    797 
    798 * ``amountEffective``: Mandatory. Effective amount of the withdrawal.
    799 * ``tRel``: Optional (defaults to ``0s``). Relative time that indicates
    800   when in the past the transaction was started (and finished).
    801 * ``exchangeBaseUrl``. Optional (defaults to ``https://exchange.demo.taler.net``).
    802   Exchange base URL used for the payment.
    803 * ``merchantBaseUrl``. Optional (defaults to ``https://backend.demo.taler.net/``).
    804 * ``merchantName``. Optional (defaults to ``Test Merchant``). Display name
    805   of the merchant in the contract terms of the transaction.
    806 * ``summary``. Optional (defaults to ``Test``). Summary in the contract terms of the
    807   transaction.
    808 
    809 **Peer Push Credit Transaction:**
    810 
    811 .. code:: none
    812 
    813    taler://dev-experiment/add-fake-tx?txType=peer-push-credit&amountEffective=KUDOS:5
    814 
    815 Options:
    816 
    817 * ``amountEffective``: Mandatory. Effective amount of the withdrawal.
    818 * ``tRel``: Optional (defaults to ``0s``). Relative time that indicates
    819   when in the past the transaction was started (and finished).
    820 * ``exchangeBaseUrl``. Optional (defaults to ``https://exchange.demo.taler.net``).
    821   Exchange base URL used for the payment.
    822 * ``summary``. Optional (defaults to ``Test``). Summary in the contract terms of the
    823   transaction.
    824 
    825 **Peer Push Debit Transaction:**
    826 
    827 .. code:: none
    828 
    829    taler://dev-experiment/add-fake-tx?txType=peer-push-debit&amountEffective=KUDOS:5
    830 
    831 Options:
    832 
    833 * ``amountEffective``: Mandatory. Effective amount of the withdrawal.
    834 * ``tRel``: Optional (defaults to ``0s``). Relative time that indicates
    835   when in the past the transaction was started (and finished).
    836 * ``exchangeBaseUrl``. Optional (defaults to ``https://exchange.demo.taler.net``).
    837   Exchange base URL used for the payment.
    838 * ``summary``. Optional (defaults to ``Test``). Summary in the contract terms of the
    839   transaction.