/*
   This file is part of TALER
   Copyright (C) 2014-2026 Taler Systems SA

   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU Affero General Public License as published by the Free Software
   Foundation; either version 3, or (at your option) any later version.

   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License along with
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
/**
 * @file include/taler/exchange/get-keys.h
 * @brief C interface for GET /keys
 * @author Christian Grothoff
 */
#ifndef _TALER_EXCHANGE__GET_KEYS_H
#define _TALER_EXCHANGE__GET_KEYS_H

#include <taler/exchange/common.h>

/* *********************  /keys *********************** */


/**
 * @brief Exchange's signature key
 */
struct TALER_EXCHANGE_SigningPublicKey
{
  /**
   * The signing public key
   */
  struct TALER_ExchangePublicKeyP key;

  /**
   * Signature over this signing key by the exchange's master signature.
   */
  struct TALER_MasterSignatureP master_sig;

  /**
   * Validity start time
   */
  struct GNUNET_TIME_Timestamp valid_from;

  /**
   * Validity expiration time (how long the exchange may use it).
   */
  struct GNUNET_TIME_Timestamp valid_until;

  /**
   * Validity expiration time for legal disputes.
   */
  struct GNUNET_TIME_Timestamp valid_legal;
};


/**
 * @brief Public information about a exchange's denomination key
 */
struct TALER_EXCHANGE_DenomPublicKey
{
  /**
   * The public key
   */
  struct TALER_DenominationPublicKey key;

  /**
   * The hash of the public key.
   */
  struct TALER_DenominationHashP h_key;

  /**
   * Exchange's master signature over this denomination record.
   */
  struct TALER_MasterSignatureP master_sig;

  /**
   * Timestamp indicating when the denomination key becomes valid
   */
  struct GNUNET_TIME_Timestamp valid_from;

  /**
   * Timestamp indicating when the denomination key can’t be used anymore to
   * withdraw new coins.
   */
  struct GNUNET_TIME_Timestamp withdraw_valid_until;

  /**
   * Timestamp indicating when coins of this denomination become invalid.
   */
  struct GNUNET_TIME_Timestamp expire_deposit;

  /**
   * When do signatures with this denomination key become invalid?
   * After this point, these signatures cannot be used in (legal)
   * disputes anymore, as the Exchange is then allowed to destroy its side
   * of the evidence.  @e expire_legal is expected to be significantly
   * larger than @e expire_deposit (by a year or more).
   */
  struct GNUNET_TIME_Timestamp expire_legal;

  /**
   * The value of this denomination
   */
  struct TALER_Amount value;

  /**
   * The applicable fees for this denomination
   */
  struct TALER_DenomFeeSet fees;

  /**
   * Set to true if the private denomination key has been
   * lost by the exchange and thus the key cannot be
   * used for withdrawing at this time.
   */
  bool lost;

  /**
   * Set to true if this denomination key has been
   * revoked by the exchange.
   */
  bool revoked;

};


/**
 * Information we track per denomination audited by the auditor.
 */
struct TALER_EXCHANGE_AuditorDenominationInfo
{

  /**
   * Signature by the auditor affirming that it is monitoring this
   * denomination.
   */
  struct TALER_AuditorSignatureP auditor_sig;

  /**
   * Offsets into the key's main `denom_keys` array identifying the
   * denomination being audited by this auditor.
   */
  unsigned int denom_key_offset;

};


/**
 * @brief Information we get from the exchange about auditors.
 */
struct TALER_EXCHANGE_AuditorInformation
{
  /**
   * Public key of the auditing institution.  Wallets and merchants
   * are expected to be configured with a set of public keys of
   * auditors that they deem acceptable.  These public keys are
   * the roots of the Taler PKI.
   */
  struct TALER_AuditorPublicKeyP auditor_pub;

  /**
   * URL of the auditing institution.  Signed by the auditor's public
   * key, this URL is a place where applications can direct users for
   * additional information about the auditor.  In the future, there
   * should also be an auditor API for automated submission about
   * claims of misbehaving exchange providers.
   */
  char *auditor_url;

  /**
   * Name of the auditing institution (human-readable).
   */
  char *auditor_name;

  /**
   * Array of length @a num_denom_keys with the denomination
   * keys audited by this auditor.
   */
  struct TALER_EXCHANGE_AuditorDenominationInfo *denom_keys;

  /**
   * Number of denomination keys audited by this auditor.
   */
  unsigned int num_denom_keys;
};


/**
 * Global fees and options of an exchange for a given time period.
 */
struct TALER_EXCHANGE_GlobalFee
{

  /**
   * Signature affirming all of the data.
   */
  struct TALER_MasterSignatureP master_sig;

  /**
   * Starting time of the validity period (inclusive).
   */
  struct GNUNET_TIME_Timestamp start_date;

  /**
   * End time of the validity period (exclusive).
   */
  struct GNUNET_TIME_Timestamp end_date;

  /**
   * Unmerged purses will be timed out after at most this time.
   */
  struct GNUNET_TIME_Relative purse_timeout;

  /**
   * Account history is limited to this timeframe.
   */
  struct GNUNET_TIME_Relative history_expiration;

  /**
   * Fees that apply globally, independent of denomination
   * and wire method.
   */
  struct TALER_GlobalFeeSet fees;

  /**
   * Number of free purses per account.
   */
  uint32_t purse_account_limit;
};


/**
 * List sorted by @a start_date with fees to be paid for aggregate wire transfers.
 */
struct TALER_EXCHANGE_WireAggregateFees
{
  /**
   * This is a linked list.
   */
  struct TALER_EXCHANGE_WireAggregateFees *next;

  /**
   * Fee to be paid whenever the exchange wires funds to the merchant.
   */
  struct TALER_WireFeeSet fees;

  /**
   * Time when this fee goes into effect (inclusive)
   */
  struct GNUNET_TIME_Timestamp start_date;

  /**
   * Time when this fee stops being in effect (exclusive).
   */
  struct GNUNET_TIME_Timestamp end_date;

  /**
   * Signature affirming the above fee structure.
   */
  struct TALER_MasterSignatureP master_sig;
};


/**
 * Information about wire fees by wire method.
 */
struct TALER_EXCHANGE_WireFeesByMethod
{
  /**
   * Wire method with the given @e fees.
   */
  char *method;

  /**
   * Linked list of wire fees the exchange charges for
   * accounts of the wire @e method.
   */
  struct TALER_EXCHANGE_WireAggregateFees *fees_head;

};


/**
 * Information about a partner exchange for wallet-to-wallet transfers.
 */
struct TALER_EXCHANGE_WadPartner
{
  /**
   * Base URL of the partner exchange.
   */
  char *partner_base_url;

  /**
   * Public master key of the partner exchange.
   */
  struct TALER_MasterPublicKeyP partner_master_pub;

  /**
   * Per exchange-to-exchange transfer (wad) fee.
   */
  struct TALER_Amount wad_fee;

  /**
   * Exchange-to-exchange wad (wire) transfer frequency.
   */
  struct GNUNET_TIME_Relative wad_frequency;

  /**
   * When did this partnership begin (under these conditions)?
   */
  struct GNUNET_TIME_Timestamp start_date;

  /**
   * How long is this partnership expected to last?
   */
  struct GNUNET_TIME_Timestamp end_date;

  /**
   * Signature using the exchange's offline master key over
   * TALER_WadPartnerSignaturePS with purpose
   * TALER_SIGNATURE_MASTER_PARTNER_DETAILS.
   */
  struct TALER_MasterSignatureP master_sig;
};


/**
 * Type of an account restriction.
 */
enum TALER_EXCHANGE_AccountRestrictionType
{
  /**
   * Invalid restriction.
   */
  TALER_EXCHANGE_AR_INVALID = 0,

  /**
   * Account must not be used for this operation.
   */
  TALER_EXCHANGE_AR_DENY = 1,

  /**
   * Other account must match given regular expression.
   */
  TALER_EXCHANGE_AR_REGEX = 2
};

/**
 * Restrictions that apply to using a given exchange bank account.
 */
struct TALER_EXCHANGE_AccountRestriction
{

  /**
   * Type of the account restriction.
   */
  enum TALER_EXCHANGE_AccountRestrictionType type;

  /**
   * Restriction details depending on @e type.
   */
  union
  {
    /**
     * Details if type is #TALER_EXCHANGE_AR_REGEX.
     */
    struct
    {
      /**
       * Regular expression that the normalized payto://-URI of the partner
       * account must follow.  The regular expression should follow
       * posix-egrep, but without support for character classes, GNU
       * extensions, back-references or intervals. See
       * https://www.gnu.org/software/findutils/manual/html_node/find_html/posix_002degrep-regular-expression-syntax.html
       * for a description of the posix-egrep syntax. Applications may support
       * regexes with additional features, but exchanges must not use such
       * regexes.
       */
      char *posix_egrep;

      /**
       * Hint for a human to understand the restriction.
       */
      char *human_hint;

      /**
       * Internationalizations for the @e human_hint.  Map from IETF BCP 47
       * language tax to localized human hints.
       */
      json_t *human_hint_i18n;
    } regex;
  } details;

};


/**
 * Information about a wire account of the exchange.
 */
struct TALER_EXCHANGE_WireAccount
{
  /**
   * payto://-URI of the exchange.
   */
  struct TALER_FullPayto fpayto_uri;

  /**
   * URL of a conversion service in case using this account is subject to
   * currency conversion.  NULL for no conversion needed.
   */
  char *conversion_url;

  /**
   * Open banking gateway base URL for wallet-initiated wire
   * transfers.  NULL if not configured.
   * @since protocol v33.
   */
  char *open_banking_gateway;

  /**
   * Wire transfer gateway base URL for short wire transfer
   * subjects.  NULL if not configured.
   * @since protocol v33.
   */
  char *prepared_transfer_url;

  /**
   * Array of restrictions that apply when crediting
   * this account.
   */
  struct TALER_EXCHANGE_AccountRestriction *credit_restrictions;

  /**
   * Array of restrictions that apply when debiting
   * this account.
   */
  struct TALER_EXCHANGE_AccountRestriction *debit_restrictions;

  /**
   * Length of the @e credit_restrictions array.
   */
  unsigned int credit_restrictions_length;

  /**
   * Length of the @e debit_restrictions array.
   */
  unsigned int debit_restrictions_length;

  /**
   * Signature of the exchange over the account (was checked by the API).
   */
  struct TALER_MasterSignatureP master_sig;

  /**
   * Display label for the account, can be NULL.
   */
  char *bank_label;

  /**
   * Priority for ordering the account in the display.
   */
  int64_t priority;

};


/**
 * Applicable soft limits of zero for an account (or wallet).
 * Clients should begin a KYC process before attempting
 * these operations.
 */
struct TALER_EXCHANGE_ZeroLimitedOperation
{

  /**
   * Operation type for which the restriction applies.
   */
  enum TALER_KYCLOGIC_KycTriggerEvent operation_type;

};


/**
 * Applicable limits for an account (or wallet). Exceeding these limits may
 * trigger additional KYC requirements or be categorically verboten.
 */
struct TALER_EXCHANGE_AccountLimit
{

  /**
   * Operation type for which the restriction applies.
   */
  enum TALER_KYCLOGIC_KycTriggerEvent operation_type;

  /**
   * Timeframe over which the @e threshold is computed.
   */
  struct GNUNET_TIME_Relative timeframe;

  /**
   * The maximum amount transacted within the given @e timeframe for the
   * specified @e operation_type.
   */
  struct TALER_Amount threshold;

  /**
   * True if this is a soft limit and passing KYC checks
   * or AML reviews may raise this limit. False if this
   * is a hard limit that the exchange will not permit
   * the client to exceed.
   */
  bool soft_limit;
};


/**
 * @brief Information about keys from the exchange.
 */
struct TALER_EXCHANGE_Keys
{

  /**
   * Long-term offline signing key of the exchange.
   */
  struct TALER_MasterPublicKeyP master_pub;

  /**
   * Array of the exchange's online signing keys.
   */
  struct TALER_EXCHANGE_SigningPublicKey *sign_keys;

  /**
   * Array of the exchange's denomination keys.
   */
  struct TALER_EXCHANGE_DenomPublicKey *denom_keys;

  /**
   * Array of the keys of the auditors of the exchange.
   */
  struct TALER_EXCHANGE_AuditorInformation *auditors;

  /**
   * Array with the global fees of the exchange.
   */
  struct TALER_EXCHANGE_GlobalFee *global_fees;

  /**
   * Supported Taler protocol version by the exchange.
   * String in the format current:revision:age using the
   * semantics of GNU libtool.  See
   * https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
   */
  char *version;

  /**
   * Supported currency of the exchange.
   */
  char *currency;

  /**
   * What is the base URL of the exchange that returned
   * these keys?
   */
  char *exchange_url;

  /**
   * Asset type used by the exchange. Typical values
   * are "fiat" or "crypto" or "regional" or "stock".
   * Wallets should adjust their UI/UX based on this
   * value.
   */
  char *asset_type;

  /**
   * Shopping URL where users may find shops that accept
   * digital cash from this exchange.  NULL if not configured.
   * @since protocol v21.
   */
  char *shopping_url;

  /**
   * Bank-specific compliance language hint for wallets.
   * NULL if not configured.
   * @since protocol v24.
   */
  char *bank_compliance_language;

  /**
   * Array of amounts a wallet is allowed to hold from
   * this exchange before it must undergo further KYC checks.
   * Length is given in @e wblwk_length.
   */
  struct TALER_Amount *wallet_balance_limit_without_kyc;

  /**
   * Smallest amount that can likely be transferred to the exchange,
   * used as the default for KYC authentication wire transfers.
   * Only valid when @e tiny_amount_available is true.
   * @since protocol v21.
   */
  struct TALER_Amount tiny_amount;

  /**
   * Array of partner exchanges for wallet-to-wallet transfers.
   * Length is given in @e num_wad_partners.
   */
  struct TALER_EXCHANGE_WadPartner *wad_partners;

  /**
   * Array of accounts of the exchange.
   */
  struct TALER_EXCHANGE_WireAccount *accounts;

  /**
   * Array of hard limits that apply at this exchange.
   * All limits in this array will be hard limits.
   */
  struct TALER_EXCHANGE_AccountLimit *hard_limits;

  /**
   * Array of operations with a default soft limit of zero
   * that apply at this exchange.
   * Clients should begin a KYC process before attempting
   * these operations.
   */
  struct TALER_EXCHANGE_ZeroLimitedOperation *zero_limits;

  /**
   * Array of wire fees by wire method.
   */
  struct TALER_EXCHANGE_WireFeesByMethod *fees;

  /**
   * Currency rendering specification for this exchange.
   */
  struct TALER_CurrencySpecification cspec;

  /**
   * How long after a reserve went idle will the exchange close it?
   * This is an approximate number, not cryptographically signed by
   * the exchange (advisory-only, may change anytime).
   */
  struct GNUNET_TIME_Relative reserve_closing_delay;

  /**
   * Timestamp indicating the /keys generation.
   */
  struct GNUNET_TIME_Timestamp list_issue_date;

  /**
   * When does this keys data expire?
   */
  struct GNUNET_TIME_Timestamp key_data_expiration;

  /**
   * Timestamp indicating the creation time of the last
   * denomination key in /keys.
   * Used to fetch /keys incrementally.
   */
  struct GNUNET_TIME_Timestamp last_denom_issue_date;

  /**
   * If age restriction is enabled on the exchange, we get an non-zero age_mask
   */
  struct TALER_AgeMask age_mask;

  /**
   * Absolute STEFAN parameter.
   */
  struct TALER_Amount stefan_abs;

  /**
   * Logarithmic STEFAN parameter.
   */
  struct TALER_Amount stefan_log;

  /**
   * Linear STEFAN parameter.
   */
  double stefan_lin;

  /**
   * Length of @e accounts array.
   */
  unsigned int accounts_len;

  /**
   * Length of @e fees array.
   */
  unsigned int fees_len;

  /**
   * Length of @e hard_limits array.
   */
  unsigned int hard_limits_length;

  /**
   * Length of @e zero_limits array.
   */
  unsigned int zero_limits_length;

  /**
   * Length of the @e wallet_balance_limit_without_kyc
   * array.
   */
  unsigned int wblwk_length;

  /**
   * Length of the @e global_fees array.
   */
  unsigned int num_global_fees;

  /**
   * Length of the @e sign_keys array (number of valid entries).
   */
  unsigned int num_sign_keys;

  /**
   * Length of the @e denom_keys array.
   */
  unsigned int num_denom_keys;

  /**
   * Length of the @e auditors array.
   */
  unsigned int num_auditors;

  /**
   * Actual length of the @e auditors array (size of allocation).
   */
  unsigned int auditors_size;

  /**
   * Actual length of the @e denom_keys array (size of allocation).
   */
  unsigned int denom_keys_size;

  /**
   * Reference counter for this structure.
   * Freed when it reaches 0.
   */
  unsigned int rc;

  /**
   * Length of the @e wad_partners array.
   */
  unsigned int num_wad_partners;

  /**
   * Set to true if KYC is enabled at this exchange.
   */
  bool kyc_enabled;

  /**
   * Set to true to signal to the merchant backend that
   * it should swap the terms-of-service and KYC auth
   * authentication steps in the user experience.
   * Defaults to false if not provided by the exchange.
   */
  bool kyc_swap_tos_acceptance;

  /**
   * Set to true if the @e tiny_amount field is valid.
   * @since protocol v21.
   */
  bool tiny_amount_available;

  /**
   * Set to true if wallets should disable the direct deposit
   * feature.  Mainly used for regional/event currency deployments.
   * @since protocol v30.
   */
  bool disable_direct_deposit;

  /**
   * How long should a P2P push payment be valid by default.
   */
  struct GNUNET_TIME_Relative default_p2p_push_expiration;


};


/**
 * How compatible are the protocol version of the exchange and this
 * client?  The bits (1,2,4) can be used to test if the exchange's
 * version is incompatible, older or newer respectively.
 */
enum TALER_EXCHANGE_VersionCompatibility
{

  /**
   * The exchange runs exactly the same protocol version.
   */
  TALER_EXCHANGE_VC_MATCH = 0,

  /**
   * The exchange is too old or too new to be compatible with this
   * implementation (bit)
   */
  TALER_EXCHANGE_VC_INCOMPATIBLE = 1,

  /**
   * The exchange is older than this implementation (bit)
   */
  TALER_EXCHANGE_VC_OLDER = 2,

  /**
   * The exchange is too old to be compatible with
   * this implementation.
   */
  TALER_EXCHANGE_VC_INCOMPATIBLE_OUTDATED
    = TALER_EXCHANGE_VC_INCOMPATIBLE
      | TALER_EXCHANGE_VC_OLDER,

  /**
   * The exchange is more recent than this implementation (bit).
   */
  TALER_EXCHANGE_VC_NEWER = 4,

  /**
   * The exchange is too recent for this implementation.
   */
  TALER_EXCHANGE_VC_INCOMPATIBLE_NEWER
    = TALER_EXCHANGE_VC_INCOMPATIBLE
      | TALER_EXCHANGE_VC_NEWER,

  /**
   * We could not even parse the version data.
   */
  TALER_EXCHANGE_VC_PROTOCOL_ERROR = 8

};


/**
 * Response from /keys.
 */
struct TALER_EXCHANGE_KeysResponse
{
  /**
   * HTTP response data
   */
  struct TALER_EXCHANGE_HttpResponse hr;

  /**
   * Details depending on the HTTP status code.
   */
  union
  {

    /**
     * Details on #MHD_HTTP_OK.
     */
    struct
    {
      /**
       * Information about the various keys used by the exchange.
       */
      const struct TALER_EXCHANGE_Keys *keys;

      /**
       * Protocol compatibility information
       */
      enum TALER_EXCHANGE_VersionCompatibility compat;
    } ok;
  } details;

};


/**
 * Possible options we can set for the GET /keys request.
 */
enum TALER_EXCHANGE_GetKeysOption
{
  /**
   * End of list of options.
   */
  TALER_EXCHANGE_GET_KEYS_OPTION_END = 0,

  /**
   * Perform incremental fetch using the given previous keys object.
   * Defaults to NULL (no incremental fetch).
   */
  TALER_EXCHANGE_GET_KEYS_OPTION_LAST_KEYS

};


/**
 * Value for an option for the GET /keys request.
 */
struct TALER_EXCHANGE_GetKeysOptionValue
{
  /**
   * Type of the option being set.
   */
  enum TALER_EXCHANGE_GetKeysOption option;

  /**
   * Specific option value.
   */
  union
  {
    /**
     * Value if @e option is TALER_EXCHANGE_GET_KEYS_OPTION_LAST_KEYS.
     * Previous keys object for incremental fetch.
     */
    struct TALER_EXCHANGE_Keys *last_keys;

  } details;

};


/**
 * @brief Handle for a GET /keys request.
 */
struct TALER_EXCHANGE_GetKeysHandle;


/**
 * Terminate the list of options.
 *
 * @return the terminating object of struct TALER_EXCHANGE_GetKeysOptionValue
 */
#define TALER_EXCHANGE_get_keys_option_end_()                    \
        (const struct TALER_EXCHANGE_GetKeysOptionValue)         \
        {                                                        \
          .option = TALER_EXCHANGE_GET_KEYS_OPTION_END           \
        }

/**
 * Set previous keys for incremental fetch.
 *
 * @param k previous keys object (may be NULL to request full fetch)
 * @return representation of the option as a struct TALER_EXCHANGE_GetKeysOptionValue
 */
#define TALER_EXCHANGE_get_keys_option_last_keys(k)              \
        (const struct TALER_EXCHANGE_GetKeysOptionValue)         \
        {                                                        \
          .option = TALER_EXCHANGE_GET_KEYS_OPTION_LAST_KEYS,   \
          .details.last_keys = (k)                               \
        }


#ifndef TALER_EXCHANGE_GET_KEYS_RESULT_CLOSURE
/**
 * Type of the closure used by
 * the #TALER_EXCHANGE_GetKeysCallback.
 */
#define TALER_EXCHANGE_GET_KEYS_RESULT_CLOSURE void
#endif /* TALER_EXCHANGE_GET_KEYS_RESULT_CLOSURE */

/**
 * Function called with information about who is auditing
 * a particular exchange and what keys the exchange is using.
 * The ownership over the @a keys object is passed to
 * the callee, thus it is given explicitly and not
 * (only) via @a kr.
 *
 * @param cls closure
 * @param kr response from /keys
 * @param[in] keys keys object passed to callback with
 *  reference counter of 1. Must be freed by callee
 *  using #TALER_EXCHANGE_keys_decref(). NULL on failure.
 */
typedef void
(*TALER_EXCHANGE_GetKeysCallback) (
  TALER_EXCHANGE_GET_KEYS_RESULT_CLOSURE *cls,
  const struct TALER_EXCHANGE_KeysResponse *kr,
  struct TALER_EXCHANGE_Keys *keys);


/**
 * Set up GET /keys operation.
 * Note that you must explicitly start the operation after
 * possibly setting options.
 *
 * @param ctx the context
 * @param url HTTP base URL for the exchange
 * @return handle to operation, NULL on error
 */
struct TALER_EXCHANGE_GetKeysHandle *
TALER_EXCHANGE_get_keys_create (
  struct GNUNET_CURL_Context *ctx,
  const char *url);


/**
 * Set the requested options for the operation.
 *
 * If any option fails, other options may or may not be applied.
 *
 * @param gkh the request to set the options for
 * @param num_options length of the @a options array
 * @param options an array of options
 * @return #GNUNET_OK on success,
 *         #GNUNET_NO on failure,
 *         #GNUNET_SYSERR on internal error
 */
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_get_keys_set_options_ (
  struct TALER_EXCHANGE_GetKeysHandle *gkh,
  unsigned int num_options,
  const struct TALER_EXCHANGE_GetKeysOptionValue options[]);


/**
 * Set the requested options for the operation.
 *
 * If any option fails, other options may or may not be applied.
 *
 * It should be used with helpers that create required options, for example:
 *
 * TALER_EXCHANGE_get_keys_set_options (
 *   gkh,
 *   TALER_EXCHANGE_get_keys_option_last_keys (prev_keys));
 *
 * @param gkh the request to set the options for
 * @param ... the list of options, each created by a
 *            TALER_EXCHANGE_get_keys_option_NAME(VALUE) helper
 * @return #GNUNET_OK on success,
 *         #GNUNET_NO on failure,
 *         #GNUNET_SYSERR on internal error
 */
#define TALER_EXCHANGE_get_keys_set_options(gkh,...)              \
        TALER_EXCHANGE_get_keys_set_options_ (                    \
          gkh,                                                    \
          TALER_EXCHANGE_COMMON_OPTIONS_ARRAY_MAX_SIZE,           \
          ((const struct TALER_EXCHANGE_GetKeysOptionValue[])     \
           {__VA_ARGS__, TALER_EXCHANGE_get_keys_option_end_ () } \
          ))


/**
 * Start GET /keys operation.
 *
 * @param[in,out] gkh operation to start
 * @param cert_cb function to call with the exchange's certification information
 * @param cert_cb_cls closure for @a cert_cb
 * @return status code, #TALER_EC_NONE on success
 */
enum TALER_ErrorCode
TALER_EXCHANGE_get_keys_start (
  struct TALER_EXCHANGE_GetKeysHandle *gkh,
  TALER_EXCHANGE_GetKeysCallback cert_cb,
  TALER_EXCHANGE_GET_KEYS_RESULT_CLOSURE *cert_cb_cls);


/**
 * Serialize the latest data from @a keys to be persisted
 * (for example, to be used as @a last_keys later).
 *
 * @param kd the key data to serialize
 * @return NULL on error; otherwise JSON object owned by the caller
 */
json_t *
TALER_EXCHANGE_keys_to_json (const struct TALER_EXCHANGE_Keys *kd);


/**
 * Deserialize keys data stored in @a j.
 *
 * @param j JSON keys data previously returned from #TALER_EXCHANGE_keys_to_json()
 * @return NULL on error (i.e. invalid JSON); otherwise
 *         keys object with reference counter 1 owned by the caller
 */
struct TALER_EXCHANGE_Keys *
TALER_EXCHANGE_keys_from_json (const json_t *j);


/**
 * Cancel GET /keys operation.
 *
 * @param[in] gkh the GET /keys handle
 */
void
TALER_EXCHANGE_get_keys_cancel (struct TALER_EXCHANGE_GetKeysHandle *gkh);


/**
 * Increment reference counter for @a keys
 *
 * @param[in,out] keys object to increment reference counter for
 * @return keys, with incremented reference counter
 */
struct TALER_EXCHANGE_Keys *
TALER_EXCHANGE_keys_incref (struct TALER_EXCHANGE_Keys *keys);


/**
 * Decrement reference counter for @a keys.
 * Frees @a keys if reference counter becomes zero.
 *
 * @param[in,out] keys object to decrement reference counter for
 */
void
TALER_EXCHANGE_keys_decref (struct TALER_EXCHANGE_Keys *keys);


/**
 * Use STEFAN curve in @a keys to convert @a brut to @a net.  Computes the
 * expected minimum (!) @a net amount that should for sure arrive in the
 * target amount at cost of @a brut to the wallet. Note that STEFAN curves by
 * design over-estimate actual fees and a wallet may be able to achieve the
 * same @a net amount with less fees --- or if the available coins are
 * abnormal in structure, it may take more.
 *
 * @param keys exchange key data
 * @param brut gross amount (actual cost including fees)
 * @param[out] net net amount (effective amount)
 * @return #GNUNET_OK on success, #GNUNET_NO if the
 *   resulting @a net is zero (or lower)
 */
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_keys_stefan_b2n (
  const struct TALER_EXCHANGE_Keys *keys,
  const struct TALER_Amount *brut,
  struct TALER_Amount *net);


/**
 * Use STEFAN curve in @a keys to convert @a net to @a brut.  Computes the
 * expected maximum (!) @a brut amount that should be needed in the wallet to
 * transfer @a net amount to the target account.  Note that STEFAN curves by
 * design over-estimate actual fees and a wallet may be able to achieve the
 * same @a net amount with less fees --- or if the available coins are
 * abnormal in structure, it may take more.
 *
 * @param keys exchange key data
 * @param net net amount (effective amount)
 * @param[out] brut gross amount (actual cost including fees)
 * @return #GNUNET_OK on success, #GNUNET_NO if the
 *   resulting @a brut is zero (only if @a net was zero)
 */
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_keys_stefan_n2b (
  const struct TALER_EXCHANGE_Keys *keys,
  const struct TALER_Amount *net,
  struct TALER_Amount *brut);


/**
 * Round brutto or netto value computed via STEFAN
 * curve to decimal places commonly used at the exchange.
 *
 * @param keys exchange keys response data
 * @param[in,out] val value to round
 */
void
TALER_EXCHANGE_keys_stefan_round (
  const struct TALER_EXCHANGE_Keys *keys,
  struct TALER_Amount *val);


/**
 * Test if the given @a pub is a the current signing key from the exchange
 * according to @a keys.
 *
 * @param keys the exchange's key set
 * @param pub claimed current online signing key for the exchange
 * @return #GNUNET_OK if @a pub is (according to /keys) a current signing key
 */
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_test_signing_key (
  const struct TALER_EXCHANGE_Keys *keys,
  const struct TALER_ExchangePublicKeyP *pub);


/**
 * Check if a wire transfer is allowed between
 * @a account if the exchange and @a payto_uri.
 *
 * @param account exchange account to check
 * @param check_credit true for credit (sending money
 *   to the exchange), false for debit (receiving money
 *   from the exchange)
 * @param payto_uri other bank account (merchant, customer)
 * @return
 *   #GNUNET_YES if the exchange would allow this
 *   #GNUNET_NO if this is not allowed
 *   #GNUNET_SYSERR if data in @a account is malformed
 *       or we experienced internal errors
 */
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_test_account_allowed (
  const struct TALER_EXCHANGE_WireAccount *account,
  bool check_credit,
  const struct TALER_NormalizedPayto payto_uri);


/**
 * Check if a wire transfer is allowed between the exchange
 * and an account identified by @a payto_uri.
 *
 * @param keys exchange /keys response to check against
 * @param check_credit true for credit (sending money
 *   to the exchange), false for debit (receiving money
 *   from the exchange)
 * @param payto_uri other bank account (merchant, customer)
 * @return
 *   #GNUNET_YES if the exchange would allow this
 *   #GNUNET_NO if this is not allowed
 *   #GNUNET_SYSERR if data in @a account is malformed
 *       or we experienced internal errors
 */
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_keys_test_account_allowed (
  const struct TALER_EXCHANGE_Keys *keys,
  bool check_credit,
  const struct TALER_NormalizedPayto payto_uri);


/**
 * Check the hard limits in @a keys for the given
 * @a event and lower @a limit to the lowest applicable
 * limit independent (!) of the timeframe.  Useful
 * to determine the absolute transaction limit.
 *
 * @param keys exchange keys to evaluate
 * @param event trigger type to evaluate
 * @param[in,out] limit to lower to the minimum limit
 *    that applies to @a event
 */
void
TALER_EXCHANGE_keys_evaluate_hard_limits (
  const struct TALER_EXCHANGE_Keys *keys,
  enum TALER_KYCLOGIC_KycTriggerEvent event,
  struct TALER_Amount *limit);


/**
 * Check if a (soft) limit of zero applies for the
 * given @a event under @a keys.
 *
 * @param keys exchange keys to evaluate
 * @param event trigger type to evaluate
 * @return true if the operation is soft-limited and
 *   thus KYC is required before the operation may be
 *   accepted at the exchange
 */
bool
TALER_EXCHANGE_keys_evaluate_zero_limits (
  const struct TALER_EXCHANGE_Keys *keys,
  enum TALER_KYCLOGIC_KycTriggerEvent event);


/**
 * Obtain the denomination key details from the exchange.
 *
 * @param keys the exchange's key set
 * @param pk public key of the denomination to lookup
 * @return details about the given denomination key, NULL if the key is not
 * found
 */
const struct TALER_EXCHANGE_DenomPublicKey *
TALER_EXCHANGE_get_denomination_key (
  const struct TALER_EXCHANGE_Keys *keys,
  const struct TALER_DenominationPublicKey *pk);


/**
 * Obtain the global fee details from the exchange.
 *
 * @param keys the exchange's key set
 * @param ts time for when to fetch the fees
 * @return details about the fees, NULL if no fees are known at @a ts
 */
const struct TALER_EXCHANGE_GlobalFee *
TALER_EXCHANGE_get_global_fee (
  const struct TALER_EXCHANGE_Keys *keys,
  struct GNUNET_TIME_Timestamp ts);


/**
 * Create a copy of a denomination public key.
 *
 * @param key key to copy
 * @returns a copy, must be freed with #TALER_EXCHANGE_destroy_denomination_key()
 * @deprecated
 */
struct TALER_EXCHANGE_DenomPublicKey *
TALER_EXCHANGE_copy_denomination_key (
  const struct TALER_EXCHANGE_DenomPublicKey *key);


/**
 * Destroy a denomination public key.
 * Should only be called with keys created by #TALER_EXCHANGE_copy_denomination_key().
 *
 * @param key key to destroy.
 * @deprecated
 */
void
TALER_EXCHANGE_destroy_denomination_key (
  struct TALER_EXCHANGE_DenomPublicKey *key);


/**
 * Obtain the denomination key details from the exchange.
 *
 * @param keys the exchange's key set
 * @param hc hash of the public key of the denomination to lookup
 * @return details about the given denomination key
 */
const struct TALER_EXCHANGE_DenomPublicKey *
TALER_EXCHANGE_get_denomination_key_by_hash (
  const struct TALER_EXCHANGE_Keys *keys,
  const struct TALER_DenominationHashP *hc);


/**
 * Obtain meta data about an exchange (online) signing
 * key.
 *
 * @param keys from where to obtain the meta data
 * @param exchange_pub public key to lookup
 * @return NULL on error (@a exchange_pub not known)
 */
const struct TALER_EXCHANGE_SigningPublicKey *
TALER_EXCHANGE_get_signing_key_info (
  const struct TALER_EXCHANGE_Keys *keys,
  const struct TALER_ExchangePublicKeyP *exchange_pub);


/* *********************  wire helpers *********************** */


/**
 * Parse array of @a accounts of the exchange into @a was.
 *
 * @param master_pub master public key of the exchange, NULL to not verify signatures
 * @param accounts array of accounts to parse
 * @param[out] was where to write the result (already allocated)
 * @param was_length length of the @a was array, must match the length of @a accounts
 * @return #GNUNET_OK if parsing @a accounts succeeded
 */
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_parse_accounts (
  const struct TALER_MasterPublicKeyP *master_pub,
  const json_t *accounts,
  unsigned int was_length,
  struct TALER_EXCHANGE_WireAccount was[static was_length]);


/**
 * Free data within @a was, but not @a was itself.
 *
 * @param was array of wire account data
 * @param was_len length of the @a was array
 */
void
TALER_EXCHANGE_free_accounts (
  unsigned int was_len,
  struct TALER_EXCHANGE_WireAccount was[static was_len]);

#endif
