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.