diff options
| author | Christian Grothoff <christian@grothoff.org> | 2022-03-05 17:14:32 +0100 | 
|---|---|---|
| committer | Christian Grothoff <christian@grothoff.org> | 2022-03-05 17:14:32 +0100 | 
| commit | a080f118904525034777686e294f9ad5f703c71c (patch) | |
| tree | 02ef0f76c86f46e1afaa6d64fa411b5c1c349e9e | |
| parent | 57470e4c0891e2a98fd33fec0ed9b49da4997c2a (diff) | |
add more logic for new global fees
| m--------- | contrib/gana | 0 | ||||
| -rw-r--r-- | src/exchange/Makefile.am | 1 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd.c | 12 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd_management.h | 13 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd_management_global_fees.c | 268 | ||||
| -rw-r--r-- | src/exchangedb/plugin_exchangedb_postgres.c | 429 | ||||
| -rw-r--r-- | src/include/taler_exchangedb_plugin.h | 27 | 
7 files changed, 747 insertions, 3 deletions
| diff --git a/contrib/gana b/contrib/gana -Subproject 24eb905bac48869b4184801571c0728c772b299 +Subproject 2b8a5d1376cc20acb2d67251f636b5a9d1cf7a6 diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am index e4526f1c..3a07b6f4 100644 --- a/src/exchange/Makefile.am +++ b/src/exchange/Makefile.am @@ -94,6 +94,7 @@ taler_exchange_httpd_SOURCES = \    taler-exchange-httpd_management_auditors_AP_disable.c \    taler-exchange-httpd_management_denominations_HDP_revoke.c \    taler-exchange-httpd_management_extensions.c \ +  taler-exchange-httpd_management_global_fees.c \    taler-exchange-httpd_management_post_keys.c \    taler-exchange-httpd_management_signkey_EP_revoke.c \    taler-exchange-httpd_management_wire_enable.c \ diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index 909c8544..f0dc365a 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -733,6 +733,18 @@ handle_post_management (struct TEH_RequestContext *rc,                                                    root);    }    if (0 == strcmp (args[0], +                   "global-fee")) +  { +    if (NULL != args[1]) +    { +      GNUNET_break_op (0); +      return r404 (rc->connection, +                   "/management/global-fee/*"); +    } +    return TEH_handler_management_post_global_fees (rc->connection, +                                                    root); +  } +  if (0 == strcmp (args[0],                     "extensions"))    {      return TEH_handler_management_post_extensions (rc->connection, diff --git a/src/exchange/taler-exchange-httpd_management.h b/src/exchange/taler-exchange-httpd_management.h index 67302c96..1bae4901 100644 --- a/src/exchange/taler-exchange-httpd_management.h +++ b/src/exchange/taler-exchange-httpd_management.h @@ -136,6 +136,19 @@ TEH_handler_management_post_wire_fees (  /** + * Handle a POST "/management/global-fees" request. + * + * @param connection the MHD connection to handle + * @param root uploaded JSON data + * @return MHD result code + */ +MHD_RESULT +TEH_handler_management_post_global_fees ( +  struct MHD_Connection *connection, +  const json_t *root); + + +/**   * Handle a POST "/management/extensions" request.   *   * @param connection the MHD connection to handle diff --git a/src/exchange/taler-exchange-httpd_management_global_fees.c b/src/exchange/taler-exchange-httpd_management_global_fees.c new file mode 100644 index 00000000..0753da84 --- /dev/null +++ b/src/exchange/taler-exchange-httpd_management_global_fees.c @@ -0,0 +1,268 @@ +/* +  This file is part of TALER +  Copyright (C) 2020, 2021, 2022 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 taler-exchange-httpd_management_global_fees.c + * @brief Handle request to add global fee details + * @author Christian Grothoff + */ +#include "platform.h" +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_json_lib.h> +#include <jansson.h> +#include <microhttpd.h> +#include <pthread.h> +#include "taler_json_lib.h" +#include "taler_mhd_lib.h" +#include "taler_signatures.h" +#include "taler-exchange-httpd_management.h" +#include "taler-exchange-httpd_responses.h" + + +/** + * Closure for the #add_fee transaction. + */ +struct AddFeeContext +{ +  /** +   * Fee's signature affirming the #TALER_SIGNATURE_MASTER_GLOBAL_FEES operation. +   */ +  struct TALER_MasterSignatureP master_sig; + +  /** +   * Starting period. +   */ +  struct GNUNET_TIME_Timestamp start_time; + +  /** +   * End of period. +   */ +  struct GNUNET_TIME_Timestamp end_time; + +  /** +   * Global fee amounts. +   */ +  struct TALER_GlobalFeeSet fees; + +  /** +   * When does an unmerged purse expire? +   */ +  struct GNUNET_TIME_Relative purse_timeout; + +  /** +   * When does an account without KYC expire? +   */ +  struct GNUNET_TIME_Relative kyc_timeout; + +  /** +   * When does an account history expire? +   */ +  struct GNUNET_TIME_Relative history_expiration; + +  /** +   * Number of free purses per account. +   */ +  uint32_t purse_account_limit; + +}; + + +/** + * Function implementing database transaction to add a fee.  Runs the + * transaction logic; IF it returns a non-error code, the transaction logic + * MUST NOT queue a MHD response.  IF it returns an hard error, the + * transaction logic MUST queue a MHD response and set @a mhd_ret.  IF it + * returns the soft error code, the function MAY be called again to retry and + * MUST not queue a MHD response. + * + * @param cls closure with a `struct AddFeeContext` + * @param connection MHD request which triggered the transaction + * @param[out] mhd_ret set to MHD response status for @a connection, + *             if transaction failed (!) + * @return transaction status + */ +static enum GNUNET_DB_QueryStatus +add_fee (void *cls, +         struct MHD_Connection *connection, +         MHD_RESULT *mhd_ret) +{ +  struct AddFeeContext *afc = cls; +  enum GNUNET_DB_QueryStatus qs; +  struct TALER_GlobalFeeSet fees; +  struct GNUNET_TIME_Relative purse_timeout; +  struct GNUNET_TIME_Relative kyc_timeout; +  struct GNUNET_TIME_Relative history_expiration; +  uint32_t purse_account_limit; + +  qs = TEH_plugin->lookup_global_fee_by_time ( +    TEH_plugin->cls, +    afc->start_time, +    afc->end_time, +    &fees, +    &purse_timeout, +    &kyc_timeout, +    &history_expiration, +    &purse_account_limit); +  if (qs < 0) +  { +    if (GNUNET_DB_STATUS_SOFT_ERROR == qs) +      return qs; +    GNUNET_break (0); +    *mhd_ret = TALER_MHD_reply_with_error (connection, +                                           MHD_HTTP_INTERNAL_SERVER_ERROR, +                                           TALER_EC_GENERIC_DB_FETCH_FAILED, +                                           "lookup global fee"); +    return qs; +  } +  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs) +  { +    if ( (GNUNET_OK == +          TALER_amount_is_valid (&fees.history)) && +         (0 == +          TALER_global_fee_set_cmp (&fees, +                                    &afc->fees)) ) +    { +      /* this will trigger the 'success' response */ +      return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; +    } +    else +    { +      *mhd_ret = TALER_MHD_reply_with_error ( +        connection, +        MHD_HTTP_CONFLICT, +        TALER_EC_EXCHANGE_MANAGEMENT_GLOBAL_FEE_MISMATCH, +        NULL); +    } +    return GNUNET_DB_STATUS_HARD_ERROR; +  } + +  qs = TEH_plugin->insert_global_fee ( +    TEH_plugin->cls, +    afc->start_time, +    afc->end_time, +    &afc->fees, +    afc->purse_timeout, +    afc->kyc_timeout, +    afc->history_expiration, +    afc->purse_account_limit, +    &afc->master_sig); +  if (qs < 0) +  { +    if (GNUNET_DB_STATUS_SOFT_ERROR == qs) +      return qs; +    GNUNET_break (0); +    *mhd_ret = TALER_MHD_reply_with_error (connection, +                                           MHD_HTTP_INTERNAL_SERVER_ERROR, +                                           TALER_EC_GENERIC_DB_STORE_FAILED, +                                           "insert fee"); +    return qs; +  } +  return qs; +} + + +MHD_RESULT +TEH_handler_management_post_global_fees ( +  struct MHD_Connection *connection, +  const json_t *root) +{ +  struct AddFeeContext afc; +  struct GNUNET_JSON_Specification spec[] = { +    GNUNET_JSON_spec_fixed_auto ("master_sig", +                                 &afc.master_sig), +    GNUNET_JSON_spec_timestamp ("fee_start", +                                &afc.start_time), +    GNUNET_JSON_spec_timestamp ("fee_end", +                                &afc.end_time), +    TALER_JSON_spec_amount ("history_fee", +                            TEH_currency, +                            &afc.fees.history), +    TALER_JSON_spec_amount ("kyc_fee", +                            TEH_currency, +                            &afc.fees.kyc), +    TALER_JSON_spec_amount ("account_fee", +                            TEH_currency, +                            &afc.fees.account), +    TALER_JSON_spec_amount ("purse_fee", +                            TEH_currency, +                            &afc.fees.purse), +    GNUNET_JSON_spec_relative_time ("purse_timeout", +                                    &afc.purse_timeout), +    GNUNET_JSON_spec_relative_time ("kyc_timeout", +                                    &afc.kyc_timeout), +    GNUNET_JSON_spec_relative_time ("history_expiration", +                                    &afc.history_expiration), +    GNUNET_JSON_spec_uint32 ("purse_account_limit", +                             &afc.purse_account_limit), +    GNUNET_JSON_spec_end () +  }; + +  { +    enum GNUNET_GenericReturnValue res; + +    res = TALER_MHD_parse_json_data (connection, +                                     root, +                                     spec); +    if (GNUNET_SYSERR == res) +      return MHD_NO; /* hard failure */ +    if (GNUNET_NO == res) +      return MHD_YES; /* failure */ +  } + +  if (GNUNET_OK != +      TALER_exchange_offline_global_fee_verify ( +        afc.start_time, +        afc.end_time, +        &afc.fees, +        afc.purse_timeout, +        afc.kyc_timeout, +        afc.history_expiration, +        afc.purse_account_limit, +        &TEH_master_public_key, +        &afc.master_sig)) +  { +    /* signature invalid */ +    GNUNET_break_op (0); +    return TALER_MHD_reply_with_error ( +      connection, +      MHD_HTTP_FORBIDDEN, +      TALER_EC_EXCHANGE_MANAGEMENT_GLOBAL_FEE_SIGNATURE_INVALID, +      NULL); +  } + +  { +    enum GNUNET_GenericReturnValue res; +    MHD_RESULT ret; + +    res = TEH_DB_run_transaction (connection, +                                  "add global fee", +                                  TEH_MT_OTHER, +                                  &ret, +                                  &add_fee, +                                  &afc); +    if (GNUNET_SYSERR == res) +      return ret; +  } +  //  TEH_global_update_state (); // FIXME: trigger! +  return TALER_MHD_reply_static ( +    connection, +    MHD_HTTP_NO_CONTENT, +    NULL, +    NULL, +    0); +} + + +/* end of taler-exchange-httpd_management_global_fees.c */ diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index fe06634f..09fa0562 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -1355,6 +1355,29 @@ prepare_statements (struct PostgresClosure *pg)        "   AND start_date <= $2"        "   AND end_date > $2;",        2), +    /* Used in #postgres_get_global_fee() */ +    GNUNET_PQ_make_prepare ( +      "get_global_fee", +      "SELECT " +      " start_date" +      ",end_date" +      ",history_fee_val" +      ",history_fee_frac" +      ",kyc_fee_val" +      ",kyc_fee_frac" +      ",account_fee_val" +      ",account_fee_frac" +      ",purse_fee_val" +      ",purse_fee_frac" +      ",purse_timeout" +      ",kyc_timeout" +      ",history_expiration" +      ",purse_account_limit" +      ",master_sig" +      " FROM global_fee" +      " WHERE start_date <= $1" +      "   AND end_date > $1;", +      1),      /* Used in #postgres_insert_wire_fee */      GNUNET_PQ_make_prepare (        "insert_wire_fee", @@ -1372,6 +1395,28 @@ prepare_statements (struct PostgresClosure *pg)        ") VALUES "        "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);",        10), +    /* Used in #postgres_insert_global_fee */ +    GNUNET_PQ_make_prepare ( +      "insert_global_fee", +      "INSERT INTO global_fee " +      "(start_date" +      ",end_date" +      ",history_fee_val" +      ",history_fee_frac" +      ",kyc_fee_val" +      ",kyc_fee_frac" +      ",account_fee_val" +      ",account_fee_frac" +      ",purse_fee_val" +      ",purse_fee_frac" +      ",purse_timeout" +      ",kyc_timeout" +      ",history_expiration" +      ",purse_account_limit" +      ",master_sig" +      ") VALUES " +      "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15);", +      15),      /* Used in #postgres_store_wire_transfer_out */      GNUNET_PQ_make_prepare (        "insert_wire_out", @@ -1949,6 +1994,26 @@ prepare_statements (struct PostgresClosure *pg)        " AND end_date > $2"        " AND start_date < $3;",        1), +    /* used in #postgres_lookup_wire_fee_by_time() */ +    GNUNET_PQ_make_prepare ( +      "lookup_global_fee_by_time", +      "SELECT" +      " history_fee_val" +      ",history_fee_frac" +      ",kyc_fee_val" +      ",kyc_fee_frac" +      ",account_fee_val" +      ",account_fee_frac" +      ",purse_fee_val" +      ",purse_fee_frac" +      ",purse_timeout" +      ",kyc_timeout" +      ",history_expiration" +      ",purse_account_limit" +      " FROM global_fee" +      " WHERE end_date > $1" +      "   AND start_date < $2;", +      1),      /* used in #postgres_commit */      GNUNET_PQ_make_prepare (        "do_commit", @@ -7662,6 +7727,71 @@ postgres_get_wire_fee (void *cls,  /** + * Obtain global fees from database. + * + * @param cls closure + * @param date for which date do we want the fee? + * @param[out] start_date when does the fee go into effect + * @param[out] end_date when does the fee end being valid + * @param[out] fees how high are the wire fees + * @param[out] purse_timeout set to how long we keep unmerged purses + * @param[out] kyc_timeout set to how long we keep accounts without KYC + * @param[out] history_expiration set to how long we keep account histories + * @param[out] purse_account_limit set to the number of free purses per account + * @param[out] master_sig signature over the above by the exchange master key + * @return status of the transaction + */ +static enum GNUNET_DB_QueryStatus +postgres_get_global_fee (void *cls, +                         struct GNUNET_TIME_Timestamp date, +                         struct GNUNET_TIME_Timestamp *start_date, +                         struct GNUNET_TIME_Timestamp *end_date, +                         struct TALER_GlobalFeeSet *fees, +                         struct GNUNET_TIME_Relative *purse_timeout, +                         struct GNUNET_TIME_Relative *kyc_timeout, +                         struct GNUNET_TIME_Relative *history_expiration, +                         uint32_t *purse_account_limit, +                         struct TALER_MasterSignatureP *master_sig) +{ +  struct PostgresClosure *pg = cls; +  struct GNUNET_PQ_QueryParam params[] = { +    GNUNET_PQ_query_param_timestamp (&date), +    GNUNET_PQ_query_param_end +  }; +  struct GNUNET_PQ_ResultSpec rs[] = { +    GNUNET_PQ_result_spec_timestamp ("start_date", +                                     start_date), +    GNUNET_PQ_result_spec_timestamp ("end_date", +                                     end_date), +    TALER_PQ_RESULT_SPEC_AMOUNT ("history_fee", +                                 &fees->history), +    TALER_PQ_RESULT_SPEC_AMOUNT ("kyc_fee", +                                 &fees->kyc), +    TALER_PQ_RESULT_SPEC_AMOUNT ("account_fee", +                                 &fees->account), +    TALER_PQ_RESULT_SPEC_AMOUNT ("purse_fee", +                                 &fees->purse), +    GNUNET_PQ_result_spec_relative_time ("purse_timeout", +                                         purse_timeout), +    GNUNET_PQ_result_spec_relative_time ("kyc_timeout", +                                         kyc_timeout), +    GNUNET_PQ_result_spec_relative_time ("history_expiration", +                                         history_expiration), +    GNUNET_PQ_result_spec_uint32 ("purse_account_limit", +                                  purse_account_limit), +    GNUNET_PQ_result_spec_auto_from_type ("master_sig", +                                          master_sig), +    GNUNET_PQ_result_spec_end +  }; + +  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, +                                                   "get_global_fee", +                                                   params, +                                                   rs); +} + + +/**   * Insert wire transfer fee into database.   *   * @param cls closure @@ -7742,6 +7872,119 @@ postgres_insert_wire_fee (void *cls,  /** + * Insert global fee data into database. + * + * @param cls closure + * @param start_date when does the fee go into effect + * @param fees how high is are the global fees + * @param purse_timeout when do purses time out + * @param kyc_timeout when do reserves without KYC time out + * @param history_expiration how long are account histories preserved + * @param purse_account_limit how many purses are free per account * @param master_sig signature over the above by the exchange master key + * @return transaction status code + */ +static enum GNUNET_DB_QueryStatus +postgres_insert_global_fee (void *cls, +                            struct GNUNET_TIME_Timestamp start_date, +                            struct GNUNET_TIME_Timestamp end_date, +                            const struct TALER_GlobalFeeSet *fees, +                            struct GNUNET_TIME_Relative purse_timeout, +                            struct GNUNET_TIME_Relative kyc_timeout, +                            struct GNUNET_TIME_Relative history_expiration, +                            uint32_t purse_account_limit, +                            const struct TALER_MasterSignatureP *master_sig) +{ +  struct PostgresClosure *pg = cls; +  struct GNUNET_PQ_QueryParam params[] = { +    GNUNET_PQ_query_param_timestamp (&start_date), +    GNUNET_PQ_query_param_timestamp (&end_date), +    TALER_PQ_query_param_amount (&fees->history), +    TALER_PQ_query_param_amount (&fees->kyc), +    TALER_PQ_query_param_amount (&fees->account), +    TALER_PQ_query_param_amount (&fees->purse), +    GNUNET_PQ_query_param_relative_time (&purse_timeout), +    GNUNET_PQ_query_param_relative_time (&kyc_timeout), +    GNUNET_PQ_query_param_relative_time (&history_expiration), +    GNUNET_PQ_query_param_uint32 (&purse_account_limit), +    GNUNET_PQ_query_param_auto_from_type (master_sig), +    GNUNET_PQ_query_param_end +  }; +  struct TALER_GlobalFeeSet wx; +  struct TALER_MasterSignatureP sig; +  struct GNUNET_TIME_Timestamp sd; +  struct GNUNET_TIME_Timestamp ed; +  enum GNUNET_DB_QueryStatus qs; +  struct GNUNET_TIME_Relative pt; +  struct GNUNET_TIME_Relative kt; +  struct GNUNET_TIME_Relative he; +  uint32_t pal; + +  qs = postgres_get_global_fee (pg, +                                start_date, +                                &sd, +                                &ed, +                                &wx, +                                &pt, +                                &kt, +                                &he, +                                &pal, +                                &sig); +  if (qs < 0) +    return qs; +  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) +  { +    if (0 != GNUNET_memcmp (&sig, +                            master_sig)) +    { +      GNUNET_break (0); +      return GNUNET_DB_STATUS_HARD_ERROR; +    } +    if (0 != +        TALER_global_fee_set_cmp (fees, +                                  &wx)) +    { +      GNUNET_break (0); +      return GNUNET_DB_STATUS_HARD_ERROR; +    } +    if ( (GNUNET_TIME_timestamp_cmp (sd, +                                     !=, +                                     start_date)) || +         (GNUNET_TIME_timestamp_cmp (ed, +                                     !=, +                                     end_date)) ) +    { +      GNUNET_break (0); +      return GNUNET_DB_STATUS_HARD_ERROR; +    } +    if ( (GNUNET_TIME_relative_cmp (purse_timeout, +                                    !=, +                                    pt)) || +         (GNUNET_TIME_relative_cmp (kyc_timeout, +                                    !=, +                                    kt)) || +         (GNUNET_TIME_relative_cmp (history_expiration, +                                    !=, +                                    he)) ) +    { +      GNUNET_break (0); +      return GNUNET_DB_STATUS_HARD_ERROR; +    } +    if (purse_account_limit != pal) +    { +      GNUNET_break (0); +      return GNUNET_DB_STATUS_HARD_ERROR; +    } +    /* equal record already exists */ +    return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; +  } + +  return GNUNET_PQ_eval_prepared_non_select (pg->conn, +                                             "insert_global_fee", +                                             params); +} + + +/**   * Closure for #reserve_expired_cb().   */  struct ExpiredReserveContext @@ -10679,10 +10922,10 @@ struct WireFeeLookupContext  /** - * Helper function for #postgres_iterate_denomination_info(). - * Calls the callback with each denomination key. + * Helper function for #postgres_lookup_wire_fee_by_time(). + * Calls the callback with the wire fee structure.   * - * @param cls a `struct DenomIteratorContext` + * @param cls a `struct WireFeeLookupContext`   * @param result db results   * @param num_results number of results in @a result   */ @@ -10783,6 +11026,182 @@ postgres_lookup_wire_fee_by_time (  /** + * Closure for #global_fee_by_time_helper() + */ +struct GlobalFeeLookupContext +{ + +  /** +   * Set to the wire fees. Set to invalid if fees conflict over +   * the given time period. +   */ +  struct TALER_GlobalFeeSet *fees; + +  /** +   * Set to timeout of unmerged purses +   */ +  struct GNUNET_TIME_Relative *purse_timeout; + +  /** +   * Set to timeout of accounts without kyc. +   */ +  struct GNUNET_TIME_Relative *kyc_timeout; + +  /** +   * Set to history expiration for reserves. +   */ +  struct GNUNET_TIME_Relative *history_expiration; + +  /** +   * Set to number of free purses per account. +   */ +  uint32_t *purse_account_limit; + +  /** +   * Plugin context. +   */ +  struct PostgresClosure *pg; +}; + + +/** + * Helper function for #postgres_lookup_global_fee_by_time(). + * Calls the callback with each denomination key. + * + * @param cls a `struct GlobalFeeLookupContext` + * @param result db results + * @param num_results number of results in @a result + */ +static void +global_fee_by_time_helper (void *cls, +                           PGresult *result, +                           unsigned int num_results) +{ +  struct GlobalFeeLookupContext *wlc = cls; +  struct PostgresClosure *pg = wlc->pg; + +  for (unsigned int i = 0; i<num_results; i++) +  { +    struct TALER_GlobalFeeSet fs; +    struct GNUNET_TIME_Relative purse_timeout; +    struct GNUNET_TIME_Relative kyc_timeout; +    struct GNUNET_TIME_Relative history_expiration; +    uint32_t purse_account_limit; +    struct GNUNET_PQ_ResultSpec rs[] = { +      TALER_PQ_RESULT_SPEC_AMOUNT ("history_fee", +                                   &fs.history), +      TALER_PQ_RESULT_SPEC_AMOUNT ("kyc_fee", +                                   &fs.kyc), +      TALER_PQ_RESULT_SPEC_AMOUNT ("account_fee", +                                   &fs.account), +      TALER_PQ_RESULT_SPEC_AMOUNT ("purse_fee", +                                   &fs.purse), +      GNUNET_PQ_result_spec_relative_time ("purse_timeout", +                                           &purse_timeout), +      GNUNET_PQ_result_spec_relative_time ("kyc_timeout", +                                           &kyc_timeout), +      GNUNET_PQ_result_spec_relative_time ("history_expiration", +                                           &history_expiration), +      GNUNET_PQ_result_spec_uint32 ("purse_account_limit", +                                    &purse_account_limit), +      GNUNET_PQ_result_spec_end +    }; + +    if (GNUNET_OK != +        GNUNET_PQ_extract_result (result, +                                  rs, +                                  i)) +    { +      GNUNET_break (0); +      /* invalidate */ +      memset (wlc->fees, +              0, +              sizeof (struct TALER_GlobalFeeSet)); +      return; +    } +    if (0 == i) +    { +      *wlc->fees = fs; +      *wlc->purse_timeout = purse_timeout; +      *wlc->kyc_timeout = kyc_timeout; +      *wlc->history_expiration = history_expiration; +      *wlc->purse_account_limit = purse_account_limit; +      continue; +    } +    if ( (0 != +          TALER_global_fee_set_cmp (&fs, +                                    wlc->fees)) || +         (purse_account_limit != *wlc->purse_account_limit) || +         (GNUNET_TIME_relative_cmp (purse_timeout, +                                    !=, +                                    *wlc->purse_timeout)) || +         (GNUNET_TIME_relative_cmp (kyc_timeout, +                                    !=, +                                    *wlc->kyc_timeout)) || +         (GNUNET_TIME_relative_cmp (history_expiration, +                                    !=, +                                    *wlc->history_expiration)) ) +    { +      /* invalidate */ +      memset (wlc->fees, +              0, +              sizeof (struct TALER_GlobalFeeSet)); +      return; +    } +  } +} + + +/** + * Lookup information about known global fees. + * + * @param cls closure + * @param start_time starting time of fee + * @param end_time end time of fee + * @param[out] fees set to wire fees for that time period; if + *             different global fee exists within this time + *             period, an 'invalid' amount is returned. + * @param[out] purse_timeout set to when unmerged purses expire + * @param[out] kyc_timeout set to when reserves without kyc expire + * @param[out] history_expiration set to when we expire reserve histories + * @param[out] purse_account_limit set to number of free purses + * @return transaction status code + */ +static enum GNUNET_DB_QueryStatus +postgres_lookup_global_fee_by_time ( +  void *cls, +  struct GNUNET_TIME_Timestamp start_time, +  struct GNUNET_TIME_Timestamp end_time, +  struct TALER_GlobalFeeSet *fees, +  struct GNUNET_TIME_Relative *purse_timeout, +  struct GNUNET_TIME_Relative *kyc_timeout, +  struct GNUNET_TIME_Relative *history_expiration, +  uint32_t *purse_account_limit) +{ +  struct PostgresClosure *pg = cls; +  struct GNUNET_PQ_QueryParam params[] = { +    GNUNET_PQ_query_param_timestamp (&start_time), +    GNUNET_PQ_query_param_timestamp (&end_time), +    GNUNET_PQ_query_param_end +  }; +  struct GlobalFeeLookupContext wlc = { +    .fees = fees, +    .purse_timeout = purse_timeout, +    .kyc_timeout = kyc_timeout, +    .history_expiration = history_expiration, +    .purse_account_limit = purse_account_limit, +    .pg = pg +  }; + +  return GNUNET_PQ_eval_prepared_multi_select (pg->conn, +                                               "lookup_global_fee_by_time", +                                               params, +                                               &global_fee_by_time_helper, +                                               &wlc); +} + + +/**   * Lookup the latest serial number of @a table.  Used in   * exchange-auditor database replication.   * @@ -11914,7 +12333,9 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)    plugin->lookup_transfer_by_deposit = &postgres_lookup_transfer_by_deposit;    plugin->insert_aggregation_tracking = &postgres_insert_aggregation_tracking;    plugin->insert_wire_fee = &postgres_insert_wire_fee; +  plugin->insert_global_fee = &postgres_insert_global_fee;    plugin->get_wire_fee = &postgres_get_wire_fee; +  plugin->get_global_fee = &postgres_get_global_fee;    plugin->get_expired_reserves = &postgres_get_expired_reserves;    plugin->insert_reserve_closed = &postgres_insert_reserve_closed;    plugin->wire_prepare_data_insert = &postgres_wire_prepare_data_insert; @@ -11988,6 +12409,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)      = &postgres_select_auditor_denom_sig;    plugin->lookup_wire_fee_by_time      = &postgres_lookup_wire_fee_by_time; +  plugin->lookup_global_fee_by_time +    = &postgres_lookup_global_fee_by_time;    plugin->add_denomination_key      = &postgres_add_denomination_key;    plugin->activate_signing_key diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 683d4f57..7440a8aa 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -4129,6 +4129,33 @@ struct TALER_EXCHANGEDB_Plugin    /** +   * Lookup information about known global fees. +   * +   * @param cls closure +   * @param start_time starting time of fee +   * @param end_time end time of fee +   * @param[out] fees set to wire fees for that time period; if +   *             different global fee exists within this time +   *             period, an 'invalid' amount is returned. +   * @param[out] purse_timeout set to when unmerged purses expire +   * @param[out] kyc_timeout set to when reserves without kyc expire +   * @param[out] history_expiration set to when we expire reserve histories +   * @param[out] purse_account_limit set to number of free purses +   * @return transaction status code +   */ +  enum GNUNET_DB_QueryStatus +  (*lookup_global_fee_by_time)( +    void *cls, +    struct GNUNET_TIME_Timestamp start_time, +    struct GNUNET_TIME_Timestamp end_time, +    struct TALER_GlobalFeeSet *fees, +    struct GNUNET_TIME_Relative *purse_timeout, +    struct GNUNET_TIME_Relative *kyc_timeout, +    struct GNUNET_TIME_Relative *history_expiration, +    uint32_t *purse_account_limit); + + +  /**     * Lookup the latest serial number of @a table.  Used in     * exchange-auditor database replication.     * | 
