taler-docs

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

get-keys.rst (24987B)


      1 .. http:get:: /keys
      2 
      3   Get a list of all denomination keys offered by the exchange,
      4   as well as the exchange's current online signing key.
      5 
      6   **Request:**
      7 
      8   :query last_issue_date: Optional argument specifying the maximum value of any of the ``stamp_start`` members of the denomination keys of a ``/keys`` response that is already known to the client. Allows the exchange to only return keys that have changed since that timestamp.  The given value must be an unsigned 64-bit integer representing seconds after 1970.  If the timestamp does not exactly match the ``stamp_start`` of one of the denomination keys, all keys are returned.
      9   :query denom_filter: Optional denomination filter.  The value ``withdraw``
     10     requests only denominations relevant for upcoming withdrawals.  For every
     11     family identified by its cipher, value and age mask, the response contains
     12     all keys whose withdrawal validity interval includes the exchange's current
     13     time and all keys tied at the family's earliest future ``stamp_start``.
     14     Fees do not distinguish families.  Thus overlapping current keys are all
     15     included, while keys whose withdrawal validity ended and rotations after
     16     the immediate successor are omitted.  A selected key remains included with
     17     ``lost`` set even if its private key is unavailable.  Auditor metadata is
     18     unchanged, but its ``denomination_keys`` arrays contain signatures only for
     19     denominations included in the response.  This parameter is mutually
     20     exclusive with ``last_issue_date``.  If omitted, the exchange returns the
     21     existing complete or cherry-picked response.
     22 
     23   The filter does not change the response schema or signature format.  Clients
     24   using it must also accept a complete response, as older exchanges may ignore
     25   the additional query parameter.
     26 
     27   **Response:**
     28 
     29   :http:statuscode:`200 OK`:
     30     The exchange responds with a `ExchangeKeysResponse` object. This request should
     31     virtually always be successful. It only fails if the exchange is misconfigured or
     32     has not yet been provisioned with key signatures via ``taler-exchange-offline``.
     33   :http:statuscode:`400 Bad Request`:
     34     A query parameter is malformed, ``denom_filter`` has an unsupported value,
     35     or ``denom_filter`` and ``last_issue_date`` were both specified.
     36     This response comes with a standard `ErrorDetail` response with
     37     a code of ``TALER_EC_GENERIC_PARAMETER_MALFORMED``.
     38   :http:statuscode:`502 Bad Gateway`:
     39     A denomination or signing key helper is unavailable.
     40     This response comes with a standard `ErrorDetail` response with
     41     a code of ``TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE`` or
     42     ``TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE``.
     43   :http:statuscode:`500 Internal Server Error`:
     44     The server experienced an internal error.
     45     This response comes with a standard `ErrorDetail` response with
     46     a code of ``TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE``.
     47   :http:statuscode:`503 Service Unavailable`:
     48     The exchange has no valid denomination keys available.  For a request with
     49     ``denom_filter=withdraw``, this also means that no current or future
     50     withdrawal denomination is available.
     51     This response comes with a standard `ErrorDetail` response with
     52     a code of ``TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING``.
     53 
     54   **Details:**
     55 
     56   .. ts:def:: ExchangeKeysResponse
     57 
     58     interface ExchangeKeysResponse {
     59       // libtool-style representation of the Exchange protocol version, see
     60       // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
     61       // The format is "current:revision:age".
     62       version: string;
     63 
     64       // The exchange's base URL.
     65       base_url: WebURL;
     66 
     67       // The exchange's currency or asset unit.
     68       currency: string;
     69 
     70       // Shopping URL where users may find shops that accept
     71       // digital cash issued by this exchange.
     72       // @since protocol **v21**.
     73       shopping_url?: WebURL;
     74 
     75       // Instructs wallets to use certain bank-specific
     76       // language (for buttons) and/or other UI/UX customization
     77       // for compliance with the rules of that bank.
     78       // The specific customizations to apply are done on a per-wallet
     79       // basis as requested by the specific bank. They only
     80       // apply when it is clear that the wallet is using digital
     81       // cash from that bank.  This is an advisory option, not
     82       // all wallets must support all compliance languages.
     83       // @since protocol **v24**.
     84       bank_compliance_language?: string;
     85 
     86       // How wallets should render this currency.
     87       currency_specification: CurrencySpecification;
     88 
     89       // Small(est?) amount that can likely be transferred to
     90       // the exchange. Should be the default amount for KYC
     91       // authentication wire transfers to this exchange.
     92       // @since protocol **v21**. Mandatory since **v33**.
     93       tiny_amount?: Amount;
     94 
     95       // Absolute cost offset for the STEFAN curve used
     96       // to (over) approximate fees payable by amount.
     97       stefan_abs: Amount;
     98 
     99       // Factor to multiply the logarithm of the amount
    100       // with to (over) approximate fees payable by amount.
    101       // Note that the total to be paid is first to be
    102       // divided by the smallest denomination to obtain
    103       // the value that the logarithm is to be taken of.
    104       stefan_log: Amount;
    105 
    106       // Linear cost factor for the STEFAN curve used
    107       // to (over) approximate fees payable by amount.
    108       //
    109       // Note that this is a scalar, as it is multiplied
    110       // with the actual amount.
    111       stefan_lin: Float;
    112 
    113       // Type of the asset. "fiat", "crypto", "regional"
    114       // or "stock".  Wallets should adjust their UI/UX
    115       // based on this value.
    116       asset_type: string;
    117 
    118       // Array of wire accounts operated by the exchange for
    119       // incoming wire transfers.
    120       accounts: ExchangeWireAccount[];
    121 
    122       // Object mapping names of wire methods (i.e. "iban" or "x-taler-bank")
    123       // to wire fees.
    124       wire_fees: { method : AggregateTransferFee[] };
    125 
    126       // List of exchanges that this exchange is partnering
    127       // with to enable wallet-to-wallet transfers.
    128       wads: ExchangePartnerListEntry[];
    129 
    130       // Set to true if this exchange allows the use
    131       // of reserves for rewards.
    132       // @deprecated in protocol **v18**.
    133       rewards_allowed: false;
    134 
    135       // Set to true if this exchange has KYC enabled and thus
    136       // requires KYC auth wire transfers prior to a first deposit.
    137       // @since protocol **v24**.
    138       kyc_enabled: boolean;
    139 
    140       // Signal to the merchant backend that it should swap
    141       // the terms-of-service and KYC auth authentication
    142       // steps in the user experience.  Optional, defaults
    143       // to false if not given.
    144       // @since protocol **v36**.
    145       kyc_swap_tos_acceptance?: boolean;
    146 
    147       // Set to TRUE if wallets should disable the direct deposit feature
    148       // and deposits should only go via Taler merchant APIs.
    149       // Mainly used for regional currency and event currency deployments
    150       // where wallets are not eligible to deposit back into originating
    151       // bank accounts and, because KYC is not enabled, wallets are thus
    152       // likely to send money to nirvana instead of where users want it.
    153       // @since protocol **v30**.
    154       disable_direct_deposit: boolean;
    155 
    156       // EdDSA master public key of the exchange, used to sign entries
    157       // in ``denoms`` and ``signkeys``.
    158       master_public_key: EddsaPublicKey;
    159 
    160       // Relative duration until inactive reserves are closed;
    161       // not signed (!), can change without notice.
    162       reserve_closing_delay: RelativeTime;
    163 
    164       // How long should a P2P push payment be valid by default.
    165       // @since protocol **v35**.
    166       default_p2p_push_expiration: RelativeTime;
    167 
    168       // Threshold amounts beyond which wallet should
    169       // trigger the KYC process of the issuing exchange.
    170       // Optional option, if not given there is no limit.
    171       // Currency must match ``currency``.
    172       wallet_balance_limit_without_kyc?: Amount[];
    173 
    174       // Array of limits that apply to all accounts.
    175       // All of the given limits will be hard limits.
    176       // Wallets and merchants are expected to obey them
    177       // and not even allow the user to cross them.
    178       // @since protocol **v21**.
    179       hard_limits: AccountLimit[];
    180 
    181       // Array of limits with a soft threshold of zero
    182       // that apply to all accounts without KYC.
    183       // Wallets and merchants are expected to trigger
    184       // a KYC process before attempting any zero-limited
    185       // operations.
    186       // @since protocol **v21**.
    187       zero_limits: ZeroLimitedOperation[];
    188 
    189       // Denominations offered by this exchange
    190       denominations: DenomGroup[];
    191 
    192       // Compact EdDSA `signature` (binary-only) over the
    193       // contatentation of all of the master_sigs (in reverse
    194       // chronological order by group) in the arrays under
    195       // "denominations".  Signature of `TALER_ExchangeKeySetPS`
    196       exchange_sig: EddsaSignature;
    197 
    198       // Public EdDSA key of the exchange that was used to generate the signature.
    199       // Should match one of the exchange's signing keys from ``signkeys``.  It is given
    200       // explicitly as the client might otherwise be confused by clock skew as to
    201       // which signing key was used for the ``exchange_sig``.
    202       exchange_pub: EddsaPublicKey;
    203 
    204       // Denominations for which the exchange currently offers/requests recoup.
    205       recoup: RecoupDenoms[];
    206 
    207       // Array of globally applicable fees by time range.
    208       global_fees: GlobalFees[];
    209 
    210       // The date when the denomination keys were last updated.
    211       list_issue_date: Timestamp;
    212 
    213       // Auditors of the exchange.
    214       auditors: AuditorKeys[];
    215 
    216       // The exchange's signing keys.
    217       signkeys: SignKey[];
    218 
    219     }
    220 
    221   The specification for the account object is:
    222 
    223   .. ts:def:: ExchangeWireAccount
    224 
    225     interface ExchangeWireAccount {
    226       // Full ``payto://`` URI identifying the account and wire method
    227       payto_uri: string;
    228 
    229       // URI to convert amounts from or to the currency used by
    230       // this wire account of the exchange. Missing if no
    231       // conversion is applicable.
    232       conversion_url?: WebURL;
    233 
    234       // Open banking gateway base URL where wallets can
    235       // initiate wire transfers to withdraw
    236       // digital cash from this exchange.
    237       // @since protocol **v34**.
    238       open_banking_gateway?: WebURL;
    239 
    240       // Wire transfer gateway base URL where wallets and merchants can
    241       // request (short) wire transfer subjects to wire funds to this
    242       // exchange without having to encode the full public key.
    243       // @since protocol **v34**.
    244       prepared_transfer_url?: WebURL;
    245 
    246       // Restrictions that apply to bank accounts that would send
    247       // funds to the exchange (crediting this exchange bank account).
    248       // Optional, empty array for unrestricted.
    249       credit_restrictions: AccountRestriction[];
    250 
    251       // Restrictions that apply to bank accounts that would receive
    252       // funds from the exchange (debiting this exchange bank account).
    253       // Optional, empty array for unrestricted.
    254       debit_restrictions: AccountRestriction[];
    255 
    256       // Signature using the exchange's offline key over
    257       // a `TALER_MasterWireDetailsPS`
    258       // with purpose ``TALER_SIGNATURE_MASTER_WIRE_DETAILS``.
    259       master_sig: EddsaSignature;
    260 
    261       // Display label wallets should use to show this
    262       // bank account.
    263       // @since protocol **v19**.
    264       bank_label?: string;
    265 
    266       // *Signed* integer with the display priority for
    267       // this bank account. Optional, 0 if missing.
    268       // @since protocol **v19**.
    269       priority?: Integer;
    270 
    271     }
    272 
    273   .. ts:def:: AccountRestriction
    274 
    275     type AccountRestriction =
    276       | RegexAccountRestriction
    277       | DenyAllAccountRestriction
    278 
    279   .. ts:def:: DenyAllAccountRestriction
    280 
    281     // Account restriction that disables this type of
    282     // account for the indicated operation categorically.
    283     interface DenyAllAccountRestriction {
    284 
    285       type: "deny";
    286     }
    287 
    288   .. ts:def:: RegexAccountRestriction
    289 
    290     // Accounts interacting with this type of account
    291     // restriction must have a normalized payto://-URI matching
    292     // the given regex.
    293     interface RegexAccountRestriction {
    294 
    295       type: "regex";
    296 
    297       // Regular expression that the payto://-URI of the
    298       // partner account must follow.  The regular expression
    299       // should follow posix-egrep, but without support for character
    300       // classes, GNU extensions, back-references or intervals. See
    301       // https://www.gnu.org/software/findutils/manual/html_node/find_html/posix_002degrep-regular-expression-syntax.html
    302       // for a description of the posix-egrep syntax. Applications
    303       // may support regexes with additional features, but exchanges
    304       // must not use such regexes.
    305       payto_regex: string;
    306 
    307       // Hint for a human to understand the restriction
    308       // (that is hopefully easier to comprehend than the regex itself).
    309       human_hint: string;
    310 
    311       // Map from IETF BCP 47 language tags to localized
    312       // human hints.
    313       human_hint_i18n?: { [lang_tag: string]: string };
    314 
    315     }
    316 
    317   .. ts:def:: ZeroLimitedOperation
    318 
    319     interface ZeroLimitedOperation {
    320 
    321       // Operation that is limited to an amount of
    322       // zero until the client has passed some KYC check.
    323       // Must be one of "WITHDRAW", "DEPOSIT",
    324       // (p2p) "MERGE", (wallet) "BALANCE",
    325       // (reserve) "CLOSE", "AGGREGATE",
    326       // "TRANSACTION" or "REFUND".
    327       operation_type: string;
    328 
    329     }
    330 
    331 
    332   .. ts:def:: AccountLimit
    333 
    334     interface AccountLimit {
    335 
    336       // Operation that is limited.
    337       // Must be one of "WITHDRAW", "DEPOSIT",
    338       // (p2p) "MERGE", (wallet) "BALANCE",
    339       // (reserve) "CLOSE", "AGGREGATE",
    340       // "TRANSACTION" or "REFUND".
    341       operation_type: string;
    342 
    343       // Timeframe during which the limit applies.
    344       // Not applicable for all operation_types
    345       // (but always present in this object anyway).
    346       timeframe: RelativeTime;
    347 
    348       // Maximum amount allowed during the given timeframe.
    349       // Zero if the operation is simply forbidden.
    350       threshold: Amount;
    351 
    352       // True if this is a soft limit that could be raised
    353       // by passing KYC checks.  Clients *may* deliberately
    354       // try to cross limits and trigger measures resulting
    355       // in 451 responses to begin KYC processes.
    356       // Clients that are aware of hard limits *should*
    357       // inform users about the hard limit and prevent flows
    358       // in the UI that would cause violations of hard limits.
    359       // Made optional in **v21** with a default of 'false' if missing.
    360       soft_limit?: boolean;
    361     }
    362 
    363   .. ts:def:: GlobalFees
    364 
    365     interface GlobalFees {
    366 
    367       // What date (inclusive) does these fees go into effect?
    368       start_date: Timestamp;
    369 
    370       // What date (exclusive) does this fees stop going into effect?
    371       end_date: Timestamp;
    372 
    373       // Account history fee, charged when a user wants to
    374       // obtain a reserve/account history.
    375       history_fee: Amount;
    376 
    377       // Annual fee charged for having an open account at the
    378       // exchange.  Charged to the account.  If the account
    379       // balance is insufficient to cover this fee, the account
    380       // is automatically deleted/closed. (Note that the exchange
    381       // will keep the account history around for longer for
    382       // regulatory reasons.)
    383       account_fee: Amount;
    384 
    385       // Purse fee, charged only if a purse is abandoned
    386       // and was not covered by the account limit.
    387       purse_fee: Amount;
    388 
    389       // How long will the exchange preserve the account history?
    390       // After an account was deleted/closed, the exchange will
    391       // retain the account history for legal reasons until this time.
    392       history_expiration: RelativeTime;
    393 
    394       // Non-negative number of concurrent purses that any
    395       // account holder is allowed to create without having
    396       // to pay the purse_fee.
    397       purse_account_limit: Integer;
    398 
    399       // How long does an exchange keep a purse around after a purse
    400       // has expired (or been successfully merged)?  A 'GET' request
    401       // for a purse will succeed until the purse expiration time
    402       // plus this value.
    403       purse_timeout: RelativeTime;
    404 
    405       // Signature of `TALER_GlobalFeesPS`.
    406       master_sig: EddsaSignature;
    407 
    408     }
    409 
    410   .. ts:def:: DenomGroup
    411 
    412     type DenomGroup =
    413       | DenomGroupRsa
    414       | DenomGroupCs
    415       | DenomGroupRsaAgeRestricted
    416       | DenomGroupCsAgeRestricted;
    417 
    418   .. ts:def:: DenomGroupRsa
    419 
    420     interface DenomGroupRsa extends DenomGroupCommon {
    421       cipher: "RSA";
    422 
    423       denoms: ({
    424         rsa_pub: RsaPublicKey;
    425       } & DenomCommon)[];
    426     }
    427 
    428   .. ts:def:: DenomGroupCs
    429 
    430     interface DenomGroupCs extends DenomGroupCommon {
    431       cipher: "CS";
    432 
    433       denoms: ({
    434         cs_pub: Cs25519Point;
    435       } & DenomCommon)[];
    436     }
    437 
    438   .. ts:def:: DenomGroupRsaAgeRestricted
    439 
    440     interface DenomGroupRsaAgeRestricted extends DenomGroupCommon {
    441       cipher: "RSA+age_restricted";
    442       age_mask: AgeMask;
    443 
    444       denoms: ({
    445         rsa_pub: RsaPublicKey;
    446       } & DenomCommon)[];
    447     }
    448 
    449   .. ts:def:: DenomGroupCsAgeRestricted
    450 
    451     interface DenomGroupCsAgeRestricted extends DenomGroupCommon {
    452       cipher: "CS+age_restricted";
    453       age_mask: AgeMask;
    454 
    455       denoms: ({
    456         cs_pub: Cs25519Point;
    457       } & DenomCommon)[];
    458     }
    459 
    460   .. ts:def:: DenomGroupCommon
    461 
    462     // Common attributes for all denomination groups
    463     interface DenomGroupCommon {
    464       // How much are coins of this denomination worth?
    465       value: Amount;
    466 
    467       // Fee charged by the exchange for withdrawing a coin of this denomination.
    468       fee_withdraw: Amount;
    469 
    470       // Fee charged by the exchange for depositing a coin of this denomination.
    471       fee_deposit: Amount;
    472 
    473       // Fee charged by the exchange for refreshing a coin of this denomination.
    474       fee_refresh: Amount;
    475 
    476       // Fee charged by the exchange for refunding a coin of this denomination.
    477       fee_refund: Amount;
    478 
    479     }
    480 
    481   .. ts:def:: DenomCommon
    482 
    483     interface DenomCommon {
    484       // Signature of `TALER_DenominationKeyValidityPS`.
    485       master_sig: EddsaSignature;
    486 
    487       // When does the denomination key become valid?
    488       stamp_start: Timestamp;
    489 
    490       // When is it no longer possible to withdraw coins
    491       // of this denomination? Note that while this option
    492       // is given per denomination, all concurrently active
    493       // denominations (of the same cipher type)
    494       // will have exactly the same withdraw
    495       // expiration time. Thus, the wallet can be sure what
    496       // is the smallest denomination being offered at any
    497       // particular point in time, and not worry about the
    498       // exchange having merely failed to sign the key of
    499       // only the smallest denomination unit.
    500       stamp_expire_withdraw: Timestamp;
    501 
    502       // When is it no longer possible to deposit coins
    503       // of this denomination?
    504       stamp_expire_deposit: Timestamp;
    505 
    506       // Timestamp indicating by when legal disputes relating to these coins must
    507       // be settled, as the exchange will afterwards destroy its evidence relating to
    508       // transactions involving this coin.
    509       stamp_expire_legal: Timestamp;
    510 
    511       // Set to 'true' if the exchange somehow "lost"
    512       // the private key. The denomination was not
    513       // necessarily revoked, but still cannot be used
    514       // to withdraw coins at this time (theoretically,
    515       // the private key could be recovered in the
    516       // future; coins signed with the private key
    517       // remain valid).
    518       lost?: boolean;
    519     }
    520 
    521   Fees for any of the operations can be zero, but the fields must still be
    522   present. The currency of the ``fee_deposit``, ``fee_refresh`` and ``fee_refund`` must match the
    523   currency of the ``value``.  Theoretically, the ``fee_withdraw`` could be in a
    524   different currency, but this is not currently supported by the
    525   implementation.
    526 
    527   .. ts:def:: RecoupDenoms
    528 
    529     interface RecoupDenoms {
    530       // Hash of the public key of the denomination that is being revoked under
    531       // emergency protocol (see ``/recoup``).
    532       h_denom_pub: HashCode;
    533 
    534       // We do not include any signature here, as the primary use-case for
    535       // this emergency involves the exchange having lost its signing keys,
    536       // so such a signature here would be pretty worthless.  However, the
    537       // exchange will not honor ``/recoup`` requests unless they are for
    538       // denomination keys listed here.
    539     }
    540 
    541   A signing key in the ``signkeys`` list is a JSON object with the following fields:
    542 
    543   .. ts:def:: SignKey
    544 
    545     interface SignKey {
    546       // The actual exchange's EdDSA signing public key.
    547       key: EddsaPublicKey;
    548 
    549       // Initial validity date for the signing key.
    550       stamp_start: Timestamp;
    551 
    552       // Date when the exchange will stop using the signing key, allowed to overlap
    553       // slightly with the next signing key's validity to allow for clock skew.
    554       stamp_expire: Timestamp;
    555 
    556       // Date when all signatures made by the signing key expire and should
    557       // henceforth no longer be considered valid in legal disputes.
    558       stamp_end: Timestamp;
    559 
    560       // Signature over ``key`` and ``stamp_expire`` by the exchange master key.
    561       // Signature of `TALER_ExchangeSigningKeyValidityPS`.
    562       // Must have purpose ``TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY``.
    563       master_sig: EddsaSignature;
    564     }
    565 
    566   An entry in the ``auditors`` list is a JSON object with the following fields:
    567 
    568   .. ts:def:: AuditorKeys
    569 
    570     interface AuditorKeys {
    571       // The auditor's EdDSA signing public key.
    572       auditor_pub: EddsaPublicKey;
    573 
    574       // The auditor's URL.
    575       auditor_url: WebURL;
    576 
    577       // The auditor's name (for humans).
    578       auditor_name: string;
    579 
    580       // An array of denomination keys the auditor affirms with its signature.
    581       // Note that the message only includes the hash of the public key, while the
    582       // signature is actually over the expanded information including expiration
    583       // times and fees.  The exact format is described below.
    584       denomination_keys: AuditorDenominationKey[];
    585     }
    586 
    587   .. ts:def:: AuditorDenominationKey
    588 
    589     interface AuditorDenominationKey {
    590       // Hash of the public RSA key used to sign coins of the respective
    591       // denomination.  Note that the auditor's signature covers more than just
    592       // the hash, but this other information is already provided in ``denoms`` and
    593       // thus not repeated here.
    594       denom_pub_h: HashCode;
    595 
    596       // Signature of `TALER_ExchangeKeyValidityPS`.
    597       auditor_sig: EddsaSignature;
    598     }
    599 
    600   The same auditor may appear multiple times in the array for different subsets
    601   of denomination keys, and the same denomination key hash may be listed
    602   multiple times for the same or different auditors.  The wallet or merchant
    603   just should check that the denomination keys they use are in the set for at
    604   least one of the auditors that they accept.
    605 
    606   .. note::
    607 
    608     Both the individual denominations *and* the denomination list is signed,
    609     allowing customers to prove that they received an inconsistent list.
    610 
    611   Aggregate wire transfer fees representing the fees the exchange
    612   charges per wire transfer to a merchant must be specified as an
    613   array in all wire transfer response objects under ``fees``.  The
    614   respective array contains objects with the following members:
    615 
    616   .. ts:def:: AggregateTransferFee
    617 
    618     interface AggregateTransferFee {
    619       // Per transfer wire transfer fee.
    620       wire_fee: Amount;
    621 
    622       // Per transfer closing fee.
    623       closing_fee: Amount;
    624 
    625       // What date (inclusive) does this fee go into effect?
    626       // The different fees must cover the full time period in which
    627       // any of the denomination keys are valid without overlap.
    628       start_date: Timestamp;
    629 
    630       // What date (exclusive) does this fee stop going into effect?
    631       // The different fees must cover the full time period in which
    632       // any of the denomination keys are valid without overlap.
    633       end_date: Timestamp;
    634 
    635       // Signature of `TALER_MasterWireFeePS` with
    636       // purpose ``TALER_SIGNATURE_MASTER_WIRE_FEES``.
    637       sig: EddsaSignature;
    638     }
    639 
    640   .. ts:def:: ExchangePartnerListEntry
    641 
    642     interface ExchangePartnerListEntry {
    643       // Base URL of the partner exchange.
    644       partner_base_url: WebURL;
    645 
    646       // Public master key of the partner exchange.
    647       partner_master_pub: EddsaPublicKey;
    648 
    649       // Per exchange-to-exchange transfer (wad) fee.
    650       wad_fee: Amount;
    651 
    652       // Exchange-to-exchange wad (wire) transfer frequency.
    653       wad_frequency: RelativeTime;
    654 
    655       // When did this partnership begin (under these conditions)?
    656       start_date: Timestamp;
    657 
    658       // How long is this partnership expected to last?
    659       end_date: Timestamp;
    660 
    661       // Signature using the exchange's offline key over
    662       // `TALER_WadPartnerSignaturePS`
    663       // with purpose ``TALER_SIGNATURE_MASTER_PARTNER_DETAILS``.
    664       master_sig: EddsaSignature;
    665     }