taler-docs

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

002-wallet-exchange-management.rst (14531B)


      1 XX 02: Wallet Exchange Management
      2 #################################
      3 
      4 :Design status: Superseded
      5 :Implementation status: Implemented
      6 :DD shepherd: TBD
      7 :Historical contributors: Florian Dold, Torsten Grote, Christian Grothoff
      8 :First published: 2020-04-09
      9 :Last substantive change: 2023-09-15
     10 :Implementation evidence: taler-typescript-core (2024-01-16)
     11 :Superseded by: :doc:`048-wallet-exchange-lifecycle`
     12 :Normative references: :doc:`../wallet/wallet-core`
     13 
     14 .. note::
     15 
     16   This design document is deprecated in favor of DD48.
     17 
     18   Trusted exchanges and auditors are no longer something we have.
     19 
     20   The body below documents the old trust model and is non-normative.
     21 
     22 Summary
     23 =======
     24 
     25 This document presents the requirements and proposed interface for an API that
     26 wallet-core exposes (to clients such as the CLI, WebExtension, Android Wallet)
     27 to manage exchanges known to and used by the wallet.
     28 
     29 
     30 Motivation
     31 ==========
     32 
     33 There currently is no documented API for this functionality.  The API that the
     34 WebExtension API uses doesn't support all required functionality and exposes
     35 the internal DB storage format.
     36 
     37 
     38 Background and Requirements
     39 ===========================
     40 
     41 The wallet maintains a list of known exchanges.  For each exchange in this
     42 list, the wallet regularly makes network queries to fetch updated information
     43 about the exchange's cryptographic key material and fee structure.
     44 
     45 Additionally, the wallet maintains a list of *trusted auditors*.  Auditors
     46 certify that they audit a (sub)set of denominations offered by the exchange.
     47 
     48 When an exchange is marked as *directly trusted*, the wallet can use it
     49 for withdrawals independent of how the exchange is audited.  Otherwise,
     50 a withdrawal can only proceed if an adequate set of denominations is
     51 audited by a trusted auditor.
     52 
     53 An exchange might only be known the wallet temporarily.  For example,
     54 the wallet UI may allow the user to review the fee structure of an
     55 exchange before the wallet is permanently added to the wallet.
     56 Once an exchange is either (a) marked as trusted or (b) used for a
     57 withdrawal operation, it is marked as permanent.
     58 
     59 Exchanges that are not permanent will be automatically be removed
     60 ("garbage-collected") by the wallet after some time.
     61 
     62 Exchanges also expose their terms of service (ToS) document.
     63 Before withdrawing, the wallet must ensure that the user
     64 has reviewed and accepted the current version of this ToS document.
     65 
     66 Exchange Management During Withdrawal
     67 -------------------------------------
     68 
     69 The functions to list / view exchanges can either be used in the context of
     70 some exchange management activity or in the context of a withdrawal.  In the
     71 context of a withdrawal, additional filtering must be applied, as not every
     72 exchange is compatible with every withdrawal process.  Additionally, the list
     73 of exchanges might contain additional details pertaining to this particular
     74 withdrawal process.
     75 
     76 An exchange is considered *compatible* if it accepts wire transfers with a wire
     77 method that matches the one of the withdrawal *and* the current exchange
     78 protocol version of the exchange is compatible with the exchange protocol
     79 version of the wallet.
     80 
     81 During the withdrawal process, the bank can also suggest an exchange.  Unless
     82 the exchange is already known to the wallet, this exchange will be added
     83 non-permanently to the wallet.  The bank-suggested will only be selected by
     84 default if no other trusted exchange compatible with the withdrawal process is
     85 known to the wallet.
     86 
     87 Otherwise, the exchange selected by default will be the exchange that has most
     88 recently been used for a withdrawal and is compatible with the current withdrawal.
     89 
     90 
     91 Open Questions
     92 --------------
     93 
     94 If the user reviews a **new** exchange during withdrawal
     95 but then does not decide to use it, will this exchange be permanent?
     96 
     97 Pro:
     98 
     99 * Staying permanently in the list might help when comparing multiple exchanges
    100 
    101 Con:
    102 
    103 * It clutters the list of exchanges, especially as we're not planning
    104   to have a mechanism to remove exchanges.
    105 
    106 => Maybe non-permanent exchanges can be "sticky" to some particular
    107 withdrawal session?
    108 
    109 => CG: Eh, I was expecting there to be a way to remove exchanges at least
    110    from the list of _trusted_ exchanges (if I view the full list, maybe
    111    with a trash bin or a swipe-to-remove functionality, or maybe on the
    112    "detailed view" of the exchange where I can review TOS/PP).
    113    Now, if there are coins actively withdrawn from the exchange, that would
    114    _only_ remove the exchange from the trusted list (what the user sees),
    115    and once all coins have been spent, we could stop refreshing /keys
    116    for that exchange and thus truly "deactivate" it. And once all spent coins
    117    have been "garbage collected", we can then truly forget about everything.
    118    (See above about garbage collection of exchanges.)
    119 
    120    [The auditor list view should also have a similar way to remove auditors.]
    121 
    122    So I'm not sure why you are saying that we are not planning on
    123    having a "mechanism to remove exchanges".
    124 
    125 
    126 Proposed Solution
    127 =================
    128 
    129 We will add the following functions (invoked over IPC with wallet-core).
    130 
    131 queryExchangeInfo
    132 -----------------
    133 
    134 This function will query information about an exchange based on the base URL
    135 of the exchange.  If the exchange is not known yet to the wallet, it will be
    136 added non-permanently.
    137 
    138 Request:
    139 
    140 .. code:: ts
    141 
    142   interface QueryExchangeInfoRequest {
    143     // If given, return error description if the exchange is
    144     // not compatible with this withdrawal operation.
    145     talerWithdrawUri?: string;
    146 
    147     // Exchange base URL to use for the query.
    148     exchangeBaseUrl: string;
    149 
    150     // If true, the query already returns a result even if
    151     // /wire and denomination signatures weren't processed yet
    152     partial: boolean;
    153   }
    154 
    155 Response:
    156 
    157 .. code:: ts
    158 
    159   interface QueryExchangeInfoResponse {
    160     exchangeBaseUrl: string;
    161 
    162     // Master public key
    163     exchangePub: string;
    164 
    165     trustedDirectly: boolean;
    166 
    167     // The "reasonable-ness" of the exchange's fees.
    168     feeStructureSummary: FeeStructureSummary | undefined;
    169 
    170     // Detailed info for each individual denomination
    171     denominations: ExchangeDenomination[];
    172 
    173     // Currency of the exchange.
    174     currency: string;
    175 
    176     // Last observed protocol version range of the exchange
    177     protocolVersionRange: string;
    178 
    179     // Is this exchange either trusted directly or in use?
    180     permanent: boolean;
    181 
    182     // Only present if the last exchange information update
    183     // failed.  Same error as the corresponding pending operation.
    184     lastError?: OperationError;
    185 
    186     wireInfo: ExchangeWireInfo;
    187 
    188     // Auditing state for each auditor.
    189     auditingState: ExchangeAuditingState[];
    190 
    191     // Do we trust an auditor that sufficiently audits
    192     // this exchange's denominations?
    193     trustedViaAuditor: boolean;
    194 
    195     currentTosVersion: string;
    196     acceptedTosVersion: string;
    197 
    198     // When (if so) was this exchange last used for withdrawal?
    199     lastUsedForWithdrawal: Timestamp | undefined;
    200 
    201     withdrawalRelatedInfo?: {
    202       // Can the user accept the withdrawal directly?
    203       // This field is redundant and derivable from other fields.
    204       acceptable: boolean;
    205 
    206       recommendedByBank: boolean;
    207 
    208       // Is this exchange the default exchange for this withdrawal?
    209       isDefault: boolean;
    210 
    211       withdrawalWithdrawnAmount: Amount;
    212       withdrawalCreditAmount: Amount;
    213       withdrawalFeeAmount: Amount;
    214       withdrawalOverheadAmount: Amount;
    215     };
    216   }
    217 
    218   export interface ExchangeWireInfo {
    219     feesForType: { [wireMethod: string]: WireFee[] };
    220     accounts: { paytoUri: string }[];
    221   }
    222 
    223   interface ExchangeAuditingState {
    224     auditorName: string;
    225     auditorBaseUrl: string;
    226     auditorPub: string;
    227 
    228     // Is the auditor already trusted by the wallet?
    229     trustedByWallet: boolean;
    230 
    231     // Does the auditor audit some reasonable set of
    232     // denominations of the exchange?
    233     // If this is false, at least some warning should be shown.
    234     auditedDenominationsReasonable: boolean;
    235   }
    236 
    237 
    238   interface FeeStructureSummary {
    239     // Does the fee structure fulfill our basic reasonableness
    240     // requirements?
    241     reasonable: boolean;
    242 
    243     // Lower range of amounts that this exchange can
    244     // deal with efficiently.
    245     smallAmount: Amount;
    246 
    247     // Upper range of amounts that this exchange can deal
    248     // with efficiently.
    249     bigAmount: Amount;
    250 
    251     // Rest to be specified later
    252     // [ ... ]
    253   }
    254 
    255 
    256 getExchangeTos
    257 --------------
    258 
    259 Request:
    260 
    261 .. code:: ts
    262 
    263   interface GetExchangeTosRequest {
    264     exchangeBaseUrl: string;
    265   }
    266 
    267 
    268 Response:
    269 
    270 .. code:: ts
    271 
    272   interface GetTosResponse {
    273     // Version of the exchange ToS (corresponds to tos ETag)
    274     version: string;
    275 
    276     // Text of the exchange ToS, with (optional) markdown markup.
    277     tosMarkdownText: string;
    278   }
    279 
    280 listExchanges
    281 -------------
    282 
    283 List exchanges known to the wallet.  Either lists all exchanges, or exchanges
    284 related to a withdrawal process.
    285 
    286 Request:
    287 
    288 .. code:: ts
    289 
    290   interface ExchangeListRequest {
    291     // If given, only return exchanges that
    292     // match the currency of this withdrawal
    293     // process.
    294     talerWithdrawUri?: string;
    295   }
    296 
    297 Response:
    298 
    299 .. code:: ts
    300 
    301   interface ExchangeListRespose {
    302     // Only returned in the context of withdrawals.
    303     // The base URL of the exchange that should
    304     // be considered the default for the withdrawal.
    305     withdrawalDefaultExchangeBaseUrl?: string;
    306 
    307     exchanges: {
    308       exchangeBaseUrl: string;
    309 
    310       // Incompatible exchanges are also returned,
    311       // as otherwise users might wonder why their expected
    312       // exchange is not there.
    313       compatibility: "compatible" |
    314         "incompatible-version" | "incompatible-wire";
    315 
    316       // Currency of the exchange.
    317       currency: string;
    318 
    319       // Does the wallet directly trust this exchange?
    320       trustedDirectly: boolean;
    321 
    322       // Is this exchange either trusted directly or in use?
    323       permanent: boolean;
    324 
    325       // This information is only returned if it's
    326       // already available to us, as the list query
    327       // must be fast!
    328       trustedViaAuditor: boolean | undefined;
    329 
    330       // The "reasonable-ness" of the exchange's fees.
    331       // Only provided if available (if we've already queried
    332       // and checked this exchange before).
    333       feeStructureSummary: FeeStructureSummary | undefined;
    334 
    335       // Did the user accept the current version of the exchange's ToS?
    336       currentTosAccepted: boolean;
    337 
    338       // When (if so) was this exchange last used for withdrawal?
    339       lastUsedForWithdrawal: Timestamp | undefined;
    340 
    341       withdrawalRelatedInfo?: {
    342         // Can the user accept the withdrawal directly?
    343         // This field is redundant and derivable from other fields.
    344         acceptable: boolean;
    345 
    346         recommendedByBank: boolean;
    347 
    348         // Is this exchange the default exchange for this withdrawal?
    349         isDefault: boolean;
    350 
    351         withdrawalWithdrawnAmount: Amount;
    352         withdrawalCreditAmount: Amount;
    353         withdrawalFeeAmount: Amount;
    354         withdrawalOverheadAmount: Amount;
    355       };
    356     }[];
    357   }
    358 
    359 
    360 setExchangeTrust
    361 ----------------
    362 
    363 Request:
    364 
    365 .. code:: ts
    366 
    367   interface SetExchangeTrustRequest {
    368     exchangeBaseUrl: string;
    369 
    370     trusted: boolean;
    371   }
    372 
    373 The response is an empty object or an error response.
    374 
    375 setExchangeTosAccepted
    376 ----------------------
    377 
    378 Request:
    379 
    380 .. code:: ts
    381 
    382   interface SetExchangeTosAccepted {
    383     exchangeBaseUrl: string;
    384   }
    385 
    386 The response is an empty object or an error response.
    387 
    388 
    389 Alternatives
    390 ============
    391 
    392 * The UI could directly access the wallet's DB for more flexible access to the
    393   required data.  But this would make the UI less robust against changes in wallet-core.
    394 
    395 
    396 Trust
    397 =====
    398 
    399 Ideally, exchanges come with auditors that are trusted by the wallet and
    400 therefore the user.  An exchange responsible for a three-letter currency is
    401 required to have an auditor, as these currencies are assumed to be legal
    402 tender in a nation state.
    403 
    404 If an exchange and/or an auditor are controlled by an attacker, they can steal
    405 user's funds.  Therefore, users should only use "official" auditors
    406 responsible for their currency.  As users should not be expected to know which
    407 auditors are official nor perform technical verification steps, the wallet
    408 ships with auditors pre-installed.
    409 
    410 It is assumed that -- from the user's point of view -- all auditors for a
    411 given currency are equivalent and that (modulo fees) there are no significant
    412 differences between the coins (fungibility) because most merchants will accept
    413 coins from exchanges of any auditor.  Thus, there is no need for the user
    414 interface to explicitly show the auditor for audited currencies, and we only
    415 show the currency code.  This is mandatory for three-letter currencies, but also
    416 expected to hold for other currency codes if an auditor is used.
    417 
    418 It must be possible to add a custom auditor, for example in case the wallet is
    419 outdated, someone is setting up an experimental deployment and wants to test
    420 it with the wallet, or simply to ensure that the user always has the last word
    421 about whom to trust.  Since adding custom auditors is dangerous and can be
    422 used to trick users into using malicious exchanges, this operation should be
    423 accompanied by appropriate warnings and security confirmations.
    424 
    425 Taler also supports regional currencies which are represented using currency
    426 codes between 4 and 12 letters.  These are not required to have an auditor.
    427 Regional currencies should be shown separate from real currencies in the
    428 wallet's balance sheet. If a regional currency does not have an auditor, its
    429 balance display in the user interface will be accompanied by their exchange's
    430 URL to allow for the fact that different regions or organisations may choose
    431 the same currency code, but use different and non-interoperable exchanges to
    432 handle the independent currencies.
    433 
    434 If a regional currency wants to use more than one exchange, it must use an
    435 auditor. In this case, operators must ensure that from the user's point of
    436 view, the coins of the different exchanges are interoperable.  If a regional
    437 exchange has an auditor, the regional currency code will be shown together
    438 with the URL of the auditor instead of the URL of the exchange.
    439 
    440 When withdrawing money from a regional currency exchange, the user should be
    441 made aware of the fact that the currency of the exchange is not "official".  A
    442 warning should be shown if a currency does not have an auditor or the auditor
    443 is not trusted by the users.  If the user expressed trust for a regional
    444 currency's auditor or a regional currency's exchange, no further warnings will
    445 be shown for the given currency.