summaryrefslogtreecommitdiff
path: root/taler-wallet.rst
blob: 2fa41d9eead6c5f19eac8cc872ece5afa0012474 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
GNU Taler Wallet Developer Manual
#################################

The GNU Taler wallet allows customers to withdraw and spend digital cash.

.. _command-line-wallet:

Command-line Wallet
===================

The command-line wallet is used primarily for testing by developers.

Building from source
--------------------

.. code-block:: console

  $ git clone https://git.taler.net/wallet-core.git
  $ cd wallet-core
  $ ./bootstrap
  $ ./configure --prefix=$INSTALL_PREFIX
  $ make && make install

The wallet command-line interface should then be available as ``taler-wallet-cli`` under ``$INSTALL_PREFIX/bin``.

Installation via NPM
--------------------

The wallet can also obtained via NPM, the Node Package Manager.

To install the wallet as a global package, run:

.. code-block:: console

  $ npm install -g taler-wallet
  # check if installation was successful
  $ taler-wallet-cli --version

To install the wallet only for your user, run:

.. code-block:: console

  $ npm install -g --prefix=$HOME/local taler-wallet
  # check if installation was successful
  $ taler-wallet-cli --version
  # If this fails, make sure that $HOME/local/bin is in your $PATH

To use the wallet as a library in your own project, run:

.. code-block:: console

  $ npm install taler-wallet


Manual withdrawing
==================

.. code-block:: console

  $ taler-wallet-cli advanced withdraw-manually \
      --exchange https://exchange.eurint.taler.net/ \
      --amount EUR:5

WebExtension Wallet
===================

Building from source
--------------------

.. code-block:: console

  $ git clone https://git.taler.net/wallet-core.git
  $ cd wallet-core
  $ ./configure
  $ make webex-stable
  # Packaged extension now available as:
  # dist/taler-wallet-$VERSION.zip


Android Wallet
==============

Please see :ref:`Build-apps-from-source` in the :doc:`taler-developer-manual`.


APIs and Data Formats
=====================

Envelope Format
---------------

All API responses and notifications are returned in the
following envelope:

.. ts:def:: WalletResponseEnvelope

   type WalletResponseEnvelope =
    | WalletSuccess
    | WalletError
    | WalletNotification

.. ts:def:: WalletSuccess

   export interface WalletSuccess {
     type: "response";
     operation: string;
     // ID to correlate success response to request
     id: string;
     // Result type depends on operation
     result: unknown;
   }

.. ts:def:: WalletError

   export interface WalletError {
     type: "error";
     operation: string;
     // ID to correlate error response to request
     id: string;
     error: WalletErrorInfo;
   }

.. ts:def:: WalletNotification

   export interface WalletSuccess {
     type: "notification";

     // actual type is WalletNotification,
     // to be documented here
     payload: any;
   }

.. ts:def:: WalletErrorInfo

   export interface WalletErrorInfo {
     // Numeric error code defined defined in the
     // GANA gnu-taler-error-codes registry.
     talerErrorCode: number;

     // English description of the error code.
     talerErrorHint: string;

     // English diagnostic message that can give details
     // for the instance of the error.
     message: string;

     // Error details, type depends
     // on talerErrorCode
     details: unknown;
   }

Withdrawal
----------

A typical API sequence for *bank-integrated* withdrawals can for example look like this:

#. ``"getWithdrawalDetailsForUri"`` returns an amount and default exchange
#. ``"getWithdrawalDetailsForAmount"`` returns fee information and that ToS are not accepted

   #. ``"getExchangeTos"`` are shown to the user and return currentEtag
   #. ``"setExchangeTosAccepted"`` called with currentEtag after user accepted

#. ``"acceptWithdrawal"`` after the user confirmed withdrawal with associated fees

A typical API sequence for *manual* withdrawals can for example look like this:

#. ``"listExchanges"`` shows a list of exchanges to the user who picks one and an amount
#. ``"getWithdrawalDetailsForAmount"`` returns fee information and that ToS are not accepted

   #. ``"getExchangeTos"`` are shown to the user and return currentEtag
   #. ``"setExchangeTosAccepted"`` called with currentEtag after user accepted

#. ``"acceptManualWithdrawal"`` after the user confirmed withdrawal with associated fees

Integration Tests
=================

Integration Test Example
------------------------

Integration tests can be done with the low-level wallet commands.  To select which coins and denominations
to use, the wallet can dump the coins in an easy-to-process format (`CoinDumpJson <https://git.taler.net/wallet-core.git/tree/src/types/talerTypes.ts#n734>`__).

The database file for the wallet can be selected with the ``--wallet-db``
option.  This option must be passed to the ``taler-wallet-cli`` command and not
the subcommands.  If the database file doesn't exist, it will be created.

The following example does a simple withdrawal recoup:

.. code-block:: console

  # Withdraw digital cash
  $ taler-wallet-cli --wallet-db=mydb.json testing withdraw \
      -b https://bank.int.taler.net/ \
      -e https://exchange.int.taler.net/ \
      -a INTKUDOS:10

  $ coins=$(taler-wallet-cli --wallet-db=mydb.json advanced dump-coins)

  # Find coin we want to revoke
  $ rc=$(echo "$coins" | \
         jq -r '[.coins[] | select((.denom_value == "INTKUDOS:5"))][0] | .coin_pub')

  # Find the denom
  $ rd=$(echo "$coins" | \
         jq -r '[.coins[] | select((.denom_value == "INTKUDOS:5"))][0] | .denom_pub_hash')

  # Find all other coins, which will be suspended
  $ susp=$(echo "$coins" | \
           jq --arg rc "$rc" '[.coins[] | select(.coin_pub != $rc) | .coin_pub]')

  # The exchange revokes the denom
  $ taler-exchange-keyup -r $rd
  $ taler-deployment-restart

  # Now we suspend the other coins, so later we will pay with the recouped coin
  $ taler-wallet-cli --wallet-db=mydb.json advanced suspend-coins "$susp"

  # Update exchange /keys so recoup gets scheduled
  $ taler-wallet-cli --wallet-db=mydb.json exchanges update -f https://exchange.int.taler.net/

  # Block until scheduled operations are done
  $ taler-wallet-cli --wallet-db=mydb.json run-until-done

  # Now we buy something, only the coins resulting from recouped will be
  # used, as other ones are suspended
  $ taler-wallet-cli --wallet-db=mydb.json testing test-pay \
      -m https://backend.int.taler.net/ \
      -k sandbox \
      -a "INTKUDOS:1" \
      -s "foo"
  $ taler-wallet-cli --wallet-db=mydb.json run-until-done


To test refreshing, force a refresh:

.. code-block:: console

  $ taler-wallet-cli --wallet-db=mydb.json advanced force-refresh "$coin_pub"


To test zombie coins, use the timetravel option. It **must** be passed to the
top-level command and not the subcommand:

.. code-block:: console

  # Update exchange /keys with time travel, value in microseconds
  $ taler-wallet-cli --timetravel=1000000 --wallet-db=mydb.json \
      exchanges update -f https://exchange.int.taler.net/

Test Cases
----------

Things we already have tests for:

* Can the wallet recoup coins and spend them?
  [`link <https://git.taler.net/wallet-core.git/tree/integrationtests/test-recoup.sh>`__]

Things we still need tests for:

* Does the wallet do retries correctly when the exchange is not reachable?
  Or when the merchant is not reachable?  Or the bank?
  This can be tested by temporarily killing those services.
* How does the wallet deal with processing the same ``taler://(pay|withdraw)`` URI twice?
* Test tipping (accepting/refusing a tip)
* Test refunds
* Test for :ref:`session-based payments <repurchase>`
* Test case for auto-refunds
  (scenario where the vending machine finds out that its motor is broken,
  so it automatically gives a refund)
* Does the wallet report "insufficient balance" correctly
  (as opposed to, say, crashing)?
* Perf tests:  How does the wallet handle withdrawing a *LOT* of coins?
* Are the transaction history and pending operations reported correctly?

Tests for things the wallet doesn't handle correctly yet:

* What happens if the wallet double-spends a coin?
  (Easy to test by copying the wallet DB before spending
  and then running a spend again with the old DB).
* What happens when a reserve is changed between accepting withdrawal
  and actually withdrawing coins?
  (This is harder to test.  Might not be possible with the current CLI.
  The idea would be be to have some ``--inhibit=withdraw`` flag
  that tells the wallet to not actually withdraw,
  so we can change the reserve state and then resume the wallet.)
* What happens if the exchange suddenly has a completely new list of denominations on offer?
* What happens if the exchange changes its master public key?
  The wallet *should* handle this gracefully
  even if we have coins with that exchange,
  provided that the old denominations can be recouped.
  (That one is pretty difficult!)
* Does the wallet handle :ref:`payment aborts <order-abort>` correctly?

There are test cases that require us to modify the communication between the wallet and exchange.

* What does the wallet do when the exchange/merchant announce an incompatible protocol version?
* What happens if some signature made by the exchange/merchant is garbage?
* What if the exchange reports a double-spend and the proof it gives us is invalid?



Integration Test and Fault Injection Framework
----------------------------------------------

This section describes the current approach to integration testing in the wallet.

It's all based on a TypeScript harness process, which itself implements
the fault injection proxy (async and in-process)!

The new approach consists of the following parts:

1. A strongly typed, convenient helper library to easily set up and run
arbitrary Taler deployments and run test cases.  These components plug
together as easily as lego bricks, even with multiple
exchanges/merchants/banks/etc.  Logs and clean shutdown (even on SIGINT
or errors) are handled properly.  (Support for auditors is still pending
but needed to fully test the wallet.)

This is how a simple withdrawal and payment test case looks like:
`<https://git.taler.net/wallet-core.git/tree/packages/taler-integrationtests/src/test-payment.ts>`__

(What's particularly nice is that all our docs contain TypeScript
definitions for all API request bodies.  So just copying them into the
test harness gives us auto-completion and compile-time checks to avoid
typos.  The wallet's JSON validation machinery is also re-used.)

2. A fault injection proxy that can be plugged between the services
and/or the wallet.  It runs alongside the test harness, and can thus can
use arbitrary custom logic.  There's no dependency for it other than
built-in Node.JS libraries.  Simple fault injections are just as easy to
set up as with the twister.

The following test case (a) logs all requests and responses to the test
harness stdout and (b) at a certain point, starts dropping the next 10
requests to the exchange (testing the wallet's retry logic):

`<https://git.taler.net/wallet-core.git/tree/packages/taler-integrationtests/src/test-payment-fault.ts#n165>`__

3. All util functionality from JS wallet-core, such as the Taler crypto,
amount/date/etc. handling and JSON parsing/validation (the wallet is now
more modular and easier to use as a library) can be used in the
integration tests, even if a different wallet (Kotlin, whatever) is
tested via the CLI.

4. A bunch of test cases that use (1)-(3).  These are *significantly*
more readable and hackable than other test approaches we had, while
allowing for more complex scenarios.  There are still way too few tests
though!

5. A test runner (written in bash) that runs test cases based on a glob
pattern and reports the results.

Injecting a fault is as easy as:

.. code:: ts

  // Set up test case
  [...]

  exchangeProxy.addFault({
    beforeResponse(ctx: FaultInjectionResponseContext) {
       if (cond1) { // Drop some responses
         ctx.dropResponse = true;
         return;
       } else if (cond2) { // modify some others
         ctx.responseBody = Buffer.from(`{"oops": true}`, "utf-8");
         return;
       }
       // Other things that can be modified:
       // - drop/modify the request, not just the response
       // - modify headers
       // - modify status codes
    }
  });

  await doSomethingWithTheWallet();

  exchangeProxy.clearFault();

  await doMoreWithTheWallet();


To make the configuration easy, an ``ExchangeService`` (or ``MerchantService``,
``BankService`` etc.) can be wrapped in a ``FaultInjectedExchangeService``,
which implements the ``ExchangeServiceInterface``:

.. code:: ts

  // create exchange and two merchants
  const exchange = await setupExchange(...);
  const merchant1 = ...;
  const merchant2 = ...;

  // Add exchange to merchant-accepted exchanges.
  // This will adjust the config.
  merchant1.addExchange(exchange);

  // Wrap exchange in fault injection proxy
  const faultInjectedExchange: ExchangeServiceInterface
    = new FaultInjectedExchangeService(t, exchange1, 8085);

  // Merchant 2 talks to the exchange over fault injection,
  // and thus must use the "twisted" base URL.
  merchant2.addExchange(faultInjectedExchange);


The package for the integration tests is here:

`<https://git.taler.net/wallet-core.git/tree/packages/taler-integrationtests>`__

The shortcut to run all integration tests is

.. code:: sh

  ./bootstrap && ./configure --prefix=... \
     && make install integrationtests

(From the root of the whole repo.  If you're developing tests, it's way
faster to just run "make compile install" once and then use
"./testrunner" from the ``taler-integrationtests`` package.)