summaryrefslogtreecommitdiff
path: root/design-documents/002-wallet-exchange-management.rst
blob: 9d10045a69b8b8e3a2a47cba99fa65aea93f2703 (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
422
423
424
425
426
427
428
429
430
431
432
Design Doc 002: Wallet Exchange Management
##########################################

.. note::

  This design document is currently a draft, it
  does not reflect any implementation decisions yet.

Summary
=======

This document presents the requirements and proposed interface for an API that
wallet-core exposes (to clients such as the CLI, WebExtension, Android Wallet)
to manage exchanges known to and used by the wallet.


Motivation
==========

There currently is no documented API for this functionality.  The API that the
WebExtension API uses doesn't support all required functionality and exposes
the internal DB storage format.


Background and Requirements
===========================

The wallet maintains a list of known exchanges.  For each exchange in this
list, the wallet regularly makes network queries to fetch updated information
about the exchange's cryptographic key material and fee structure.

Additionally, the wallet maintains a list of *trusted auditors*.  Auditors
certify that they audit a (sub)set of denominations offered by the exchange.

When an exchange is marked as *directly trusted*, the wallet can use it
for withdrawals independent of how the exchange is audited.  Otherwise,
a withdrawal can only proceed if an adequate set of denominations is
audited by a trusted auditor.

An exchange might only be known the wallet temporarily.  For example,
the wallet UI may allow the user to review the fee structure of an
exchange before the wallet is permanently added to the wallet.
Once a an exchange is either (a) marked as trusted or (b) used for a
withdrawal operation, it is marked as permanent.

Exchanges that are not permanent will be automatically be removed
("garbage-collected") by the wallet after some time.

Exchanges also expose their terms of service (ToS) document.
Before withdrawing, the wallet must ensure that the user
has reviewed and accepted the current version of this ToS document.

Exchange Management During Withdrawal
-------------------------------------

The functions to list / view exchanges can either be used in the context of
some exchange management activity or in the context of a withdrawal.  In the
context of a withdrawal, additional filtering must be applied, as not every
exchange is compatible with every withdrawal process.  Additionally, the list
of exchanges might contain additional details pertaining to this particular
withdrawal process.

An exchange is considered *compatible* if it accepts wire transfers with a wire
method that matches the one of the withdrawal *and* the current exchange
protocol version of the exchange is compatible with the exchange protocol
version of the wallet.

During the withdrawal process, the bank can also suggest an exchange.  Unless
the exchange is already known to the wallet, this exchange will be added
non-permanently to the wallet.  The bank-suggested will only be selected by
default if no other trusted exchange compatible with the withdrawal process is
known to the wallet.

Otherwise, the exchange selected by default will be the exchange that has most
recently been used for a withdrawal and is compatible with the current withdrawal.


Open Questions
--------------

If the user reviews a **new** exchange during withdrawal
but then does not decide to use it, will this exchange be permanent?

Pro:

* Staying permanently in the list might help when comparing multiple exchanges

Con:

* It clutters the list of exchanges, especially as we're not planning
  to have a mechanism to remove exchanges.

=> Maybe non-permanent exchanges can be "sticky" to some particular
withdrawal session?

=> CG: Eh, I was expecting there to be a way to remove exchanges at least
   from the list of _trusted_ exchanges (if I view the full list, maybe
   with a trash bin or a swipe-to-remove functionality, or maybe on the
   "detailed view" of the exchange where I can review TOS/PP).
   Now, if there are coins actively withdrawn from the exchange, that would
   _only_ remove the exchange from the trusted list (what the user sees),
   and once all coins have been spent, we could stop refreshing /keys
   for that exchange and thus truly "deactivate" it. And once all spent coins
   have been "garbage collected", we can then truly forget about everything.
   (See above about garbage collection of exchanges.)

   [The auditor list view should also have a similar way to remove auditors.]

   So I'm not sure why you are saying that we are not planning on
   having a "mechanism to remove exchanges".


Proposed Solution
=================

We will add the following functions (invoked over IPC with wallet-core).

queryExchangeInfo
-----------------

This function will query information about an exchange based on the base URL
of the exchange.  If the exchange is not known yet to the wallet, it will be
added non-permanently.

Request:

.. code:: ts

  interface QueryExchangeInfoRequest {
    // If given, return error description if the exchange is
    // not compatible with this withdrawal operation.
    talerWithdrawUri?: string;

    // Exchange base URL to use for the query.
    exchangeBaseUrl: string;

    // If true, the query already returns a result even if
    // /wire and denomination signatures weren't processed yet
    partial: boolean;
  }

Response:

.. code:: ts

  interface QueryExchangeInfoResponse {
    exchangeBaseUrl: string;

    // Master public key
    exchangePub: string;

    trustedDirectly: boolean;

    // The "reasonable-ness" of the exchange's fees.
    feeStructureSummary: FeeStructureSummary | undefined;

    // Detailed info for each individual denomination
    denominations: ExchangeDenomination[];

    // Currency of the exchange.
    currency: string;

    // Last observed protocol version range of the exchange
    protocolVersionRange: string;

    // Is this exchange either trusted directly or in use?
    permanent: boolean;

    // Only present if the last exchange information update
    // failed.  Same error as the corresponding pending operation.
    lastError?: OperationError;

    wireInfo: ExchangeWireInfo;

    // Auditing state for each auditor.
    auditingState: ExchangeAuditingState[];

    // Do we trust an auditor that sufficiently audits
    // this exchange's denominations?
    trustedViaAuditor: boolean;

    currentTosVersion: string;
    acceptedTosVersion: string;

    // When (if so) was this exchange last used for withdrawal?
    lastUsedForWithdrawal: Timestamp | undefined;

    withdrawalRelatedInfo?: {
      // Can the user accept the withdrawal directly?
      // This field is redundant and derivable from other fields.
      acceptable: boolean;

      recommendedByBank: boolean;

      // Is this exchange the default exchange for this withdrawal?
      isDefault: boolean;

      withdrawalWithdrawnAmount: Amount;
      withdrawalCreditAmount: Amount;
      withdrawalFeeAmount: Amount;
      withdrawalOverheadAmount: Amount;
    };
  }

  export interface ExchangeWireInfo {
    feesForType: { [wireMethod: string]: WireFee[] };
    accounts: { paytoUri: string }[];
  }

  interface ExchangeAuditingState {
    auditorName: string;
    auditorBaseUrl: string;
    auditorPub: string;

    // Is the auditor already trusted by the wallet?
    trustedByWallet: boolean;

    // Does the auditor audit some reasonable set of
    // denominations of the exchange?
    // If this is false, at least some warning should be shown.
    auditedDenominationsReasonable: boolean;
  }


  interface FeeStructureSummary {
    // Does the fee structure fulfill our basic reasonableness
    // requirements?
    reasonable: boolean;

    // Lower range of amounts that this exchange can
    // deal with efficiently.
    smallAmount: Amount;

    // Upper range of amounts that this exchange can deal
    // with efficiently.
    bigAmount: Amount;

    // Rest to be specified later
    // [ ... ]
  }


getExchangeTos
--------------

Request:

.. code:: ts

  interface GetExchangeTosRequest {
    exchangeBaseUrl: string;
  }


Response:

.. code:: ts

  interface GetTosResponse {
    // Version of the exchange ToS (corresponds to tos ETag)
    version: string;

    // Text of the exchange ToS, with (optional) markdown markup.
    tosMarkdownText: string;
  }

listExchanges
-------------

List exchanges known to the wallet.  Either lists all exchanges, or exchanges
related to a withdrawal process.

Request:

.. code:: ts

  interface ExchangeListRequest {
    // If given, only return exchanges that
    // match the currency of this withdrawal
    // process.
    talerWithdrawUri?: string;
  }

Response:

.. code:: ts

  interface ExchangeListRespose {
    // Only returned in the context of withdrawals.
    // The base URL of the exchange that should
    // be considered the default for the withdrawal.
    withdrawalDefaultExchangeBaseUrl?: string;

    exchanges: {
      exchangeBaseUrl: string;

      // Incompatible exchanges are also returned,
      // as otherwise users might wonder why their expected
      // exchange is not there.
      compatibility: "compatible" |
        "incompatible-version" | "incompatible-wire";

      // Currency of the exchange.
      currency: string;

      // Does the wallet directly trust this exchange?
      trustedDirectly: boolean;

      // Is this exchange either trusted directly or in use?
      permanent: boolean;

      // This information is only returned if it's
      // already available to us, as the list query
      // must be fast!
      trustedViaAuditor: boolean | undefined;

      // The "reasonable-ness" of the exchange's fees.
      // Only provided if available (if we've already queried
      // and checked this exchange before).
      feeStructureSummary: FeeStructureSummary | undefined;

      // Did the user accept the current version of the exchange's ToS?
      currentTosAccepted: boolean;

      // When (if so) was this exchange last used for withdrawal?
      lastUsedForWithdrawal: Timestamp | undefined;

      withdrawalRelatedInfo?: {
        // Can the user accept the withdrawal directly?
        // This field is redundant and derivable from other fields.
        acceptable: boolean;

        recommendedByBank: boolean;

        // Is this exchange the default exchange for this withdrawal?
        isDefault: boolean;

        withdrawalWithdrawnAmount: Amount;
        withdrawalCreditAmount: Amount;
        withdrawalFeeAmount: Amount;
        withdrawalOverheadAmount: Amount;
      };
    }[];
  }


setExchangeTrust
----------------

Request:

.. code:: ts

  interface SetExchangeTrustRequest {
    exchangeBaseUrl: string;

    trusted: boolean;
  }

The response is an empty object or an error response.

setExchangeTosAccepted
----------------------

Request:

.. code:: ts

  interface SetExchangeTosAccepted {
    exchangeBaseUrl: string;
  }

The response is an empty object or an error response.


Alternatives
============

* The UI could directly access the wallet's DB for more flexible access to the
  required data.  But this would make the UI less robust against changes in wallet-core.


Trust
=====

Ideally, exchanges come with auditors that are trusted by the wallet and
therefore the user.  An exchange responsible for a three-letter currency is
required to have an auditor, as these currencies are assumed to be legal
tender in a nation state.

If an exchange and/or an auditor are controlled by an attacker, they can steal
user's funds.  Therefore, users should only use "official" auditors
responsible for their currency.  As users should not be expected to know which
auditors are official nor perform technical verification steps, the wallet
ships with auditors pre-installed.

It is assumed that -- from the user's point of view -- all auditors for a
given currency are equivalent and that (modulo fees) there are no significant
differences between the coins (fungibility) because most merchants will accept
coins from exchanges of any auditor.  Thus, there is no need for the user
interface to explicitly show the auditor for audited currencies, and we only
show the currency code.  This is mandatory for three-letter currencies, but also
expected to hold for other currency codes if an auditor is used.

It must be possible to add a custom auditor, for example in case the wallet is
outdated, someone is setting up an experimental deployment and wants to test
it with the wallet, or simply to ensure that the user always has the last word
about whom to trust.  Since adding custom auditors is dangerous and can be
used to trick users into using malicious exchanges, this operation should be
accompanied by appropriate warnings and security confirmations.

Taler also supports regional currencies which are represented using currency
codes between 4 and 12 letters.  These are not required to have an auditor.
Regional currencies should be shown separate from real currencies in the
wallet's balance sheet. If a regional currency does not have an auditor, its
balance display in the user interface will be accompanied by their exchange's
URL to allow for the fact that different regions or organisations may choose
the same currency code, but use different and non-interoperable exchanges to
handle the independent currencies.

If a regional currency wants to use more than one exchange, it must use an
auditor. In this case, operators must ensure that from the user's point of
view, the coins of the different exchanges are interoperable.  If a regional
exchange has an auditor, the regional currency code will be shown together
with the URL of the auditor instead of the URL of the exchange.

When withdrawing money from a regional currency exchange, the user should be
made aware of the fact that the currency of the exchange is not "official".  A
warning should be shown if a currency does not have an auditor or the auditor
is not trusted by the users.  If the user expressed trust for a regional
currency's auditor or a regional currency's exchange, no further warnings will
be shown for the given currency.