diff options
| author | Christian Grothoff <christian@grothoff.org> | 2020-12-09 12:31:10 +0100 | 
|---|---|---|
| committer | Christian Grothoff <christian@grothoff.org> | 2020-12-09 12:31:10 +0100 | 
| commit | fa1914b26781a9409a35fd6f8bfb54aae09fdd79 (patch) | |
| tree | 626be333f8fb26f7a0d58fe334fc7cd41952af08 | |
| parent | 9911b327ac299ec7eeae81b98cb520f4153071f2 (diff) | |
work on new logic to generate /wire response
| m--------- | contrib/gana | 0 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd_wire2.c | 315 | ||||
| -rw-r--r-- | src/exchangedb/plugin_exchangedb_postgres.c | 259 | ||||
| -rw-r--r-- | src/include/taler_exchangedb_plugin.h | 67 | 
4 files changed, 641 insertions, 0 deletions
| diff --git a/contrib/gana b/contrib/gana -Subproject 9d38f712c153727dbb895673d6d9841be57c12c +Subproject 3501eb7b857d573258c1ab1c42d7e827c36cec9 diff --git a/src/exchange/taler-exchange-httpd_wire2.c b/src/exchange/taler-exchange-httpd_wire2.c new file mode 100644 index 00000000..b2f8a2a1 --- /dev/null +++ b/src/exchange/taler-exchange-httpd_wire2.c @@ -0,0 +1,315 @@ +/* +  This file is part of TALER +  Copyright (C) 2015-2020 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_wire.c + * @brief Handle /wire requests + * @author Christian Grothoff + */ +#include "platform.h" +#include <gnunet/gnunet_json_lib.h> +#include "taler-exchange-httpd_keystate.h" +#include "taler-exchange-httpd_responses.h" +#include "taler-exchange-httpd_wire.h" +#include "taler_json_lib.h" +#include "taler_mhd_lib.h" +#include <jansson.h> + + +/** + * Thread-local.  Contains a pointer to `struct WireStateHandle` or NULL. + * Stores the per-thread latest generation of our wire response. + */ +static pthread_key_t wire_state; + +/** + * Counter incremented whenever we have a reason to re-build the #wire_state + * because something external changed (in another thread).  The counter is + * manipulated using an atomic update, and thus to ensure that threads notice + * when it changes, the variable MUST be volatile.  See #get_wire_state() + * and #TEH_wire_update_state() for uses of this variable. + */ +static volatile uint64_t wire_generation; + + +/** + * State we keep per thread to cache the /wire response. + */ +struct WireStateHandle +{ +  /** +   * Cached JSON for /wire response. +   */ +  json_t *wire_reply; + +  /** +   * For which (global) wire_generation was this data structure created? +   * Used to check when we are outdated and need to be re-generated. +   */ +  uint64_t wire_generation; + +}; + + +/** + * Free memory assciated with @a wsh + * + * @param[in] wsh wire state to destroy + */ +static void +destroy_wire_state (struct WireStateHandle *wsh) +{ +  json_decref (wsh->wire_reply); +  GNUNET_free (wsh); +} + + +/** + * Add information about a wire account to @a cls. + * + * @param cls a `json_t *` object to expand with wire account details + * @param payto_uri the exchange bank account URI to add + * @param master_sig master key signature affirming that this is a bank + *                   account of the exchange (of purpose #TALER_SIGNATURE_MASTER_WIRE_DETAILS) + */ +static void +add_wire_account (void *cls, +                  const char *payto_uri, +                  const struct TALER_MasterSignatureP *master_sig) +{ +  json_t *a = cls; + +  if (0 != +      json_array_append_new ( +        a, +        json_pack ("{s:s, s:o}", +                   "url", /* "payto_uri" would be better, but this is the name in the spec */ +                   payto_uri, +                   "sig", +                   GNUNET_JSON_from_data_auto (master_sig)))) +  { +    GNUNET_break (0);   /* out of memory!? */ +    return; +  } +} + + +/** + * Add information about a wire account to @a cls. + * + * @param cls a `json_t *` array to expand with wire account details + * @param wire_fee the wire fee we charge + * @param closing_fee the closing fee we charge + * @param start_date from when are these fees valid (start date) + * @param end_date until when are these fees valid (end date, exclusive) + * @param master_sig master key signature affirming that this is the corrrect + *                   fee (of purpose #TALER_SIGNATURE_MASTER_WIRE_FEES) + */ +static void +add_wire_fee (void *cls, +              const struct TALER_Amount *wire_fee, +              const struct TALER_Amount *closing_fee, +              struct GNUNET_TIME_Absolute start_date, +              struct GNUNET_TIME_Absolute end_date, +              const struct TALER_MasterSignatureP *master_sig) +{ +  json_t *a = cls; + +  if (0 != +      json_array_append_new ( +        a, +        json_pack ("{s:o, s:o, s:o, s:o, s:o}", +                   "wire_fee", +                   TALER_JSON_from_amount (wire_fee), +                   "closing_fee", +                   TALER_JSON_from_amount (closing_fee), +                   "start_date", +                   GNUNET_JSON_from_time_abs (start_date), +                   "end_date", +                   GNUNET_JSON_from_time_abs (end_date), +                   "sig", +                   GNUNET_JSON_from_data_auto (master_sig)))) +  { +    GNUNET_break (0);   /* out of memory!? */ +    return; +  } +} + + +/** + * Create the /wire response from our database state. + * + * @return NULL on error + */ +static struct WireStateHandle * +build_wire_state (void) +{ +  json_t *wire_accounts_array; +  json_t *wire_fee_object; +  json_t *wire_reply; +  uint64_t wg = wire_generation; /* must be obtained FIRST */ +  enum GNUNET_DB_QueryStatus qs; + +  wire_accounts_array = json_array (); +  GNUNET_assert (NULL != wire_accounts_array); +  qs = TEH_plugin->get_wire_accounts (TEH_plugin->cls, +                                      &add_wire_account, +                                      wire_accounts_array); +  if (0 > qs) +  { +    GNUNET_break (0); +    json_decref (wire_accounts_array); +    return NULL; +  } +  wire_fee_object = json_object (); +  GNUNET_assert (NULL != wire_fee_object); +  { +    json_t *account; +    size_t index; + +    json_array_foreach (wire_accounts_array, index, account) { +      char *wire_method; +      const char *payto_uri = json_string_value (json_object_get (account, +                                                                  "url")); +      GNUNET_assert (NULL != payto_uri); +      wire_method = TALER_payto_get_method (payto_uri); +      if (NULL == json_object_get (wire_fee_object, +                                   wire_method)) +      { +        json_t *a = json_array (); + +        GNUNET_assert (NULL != a); +        qs = TEH_plugin->get_wire_fees (TEH_plugin->cls, +                                        wire_method, +                                        &add_wire_fee, +                                        a); +        if (0 > qs) +        { +          GNUNET_break (0); +          json_decref (a); +          json_decref (wire_fee_object); +          json_decref (wire_accounts_array); +          return NULL; +        } +        GNUNET_assert (0 == +                       json_object_set_new (wire_fee_object, +                                            wire_method, +                                            a)); +      } +      GNUNET_free (wire_method); + +    } +  } +  wire_reply = json_pack ( +    "{s:O, s:O, s:o}", +    "accounts", wire_accounts_array, +    "fees", wire_fee_object, +    "master_public_key", +    GNUNET_JSON_from_data_auto (&TEH_master_public_key)); +  GNUNET_assert (NULL != wire_reply); +  { +    struct WireStateHandle *wsh; + +    wsh = GNUNET_new (struct WireStateHandle); +    wsh->wire_reply = wire_reply; +    wsh->wire_generation = wg; +    return wsh; +  } +} + + +/** + * Something changed in the database. Rebuild the wire replies.  This function + * should be called if the exchange learns about a new signature from our + * master key. + * + * (We do not do so immediately, but merely signal to all threads that they + * need to rebuild their wire state upon the next call to + * #wire_get_state()). + */ +void +TEH_wire_update_state () +{ +  __sync_fetch_and_add (&key_generation, +                        1); +} + + +/** + * Return the current key state for this thread.  Possibly + * re-builds the key state if we have reason to believe + * that something changed. + * + * @return NULL on error + */ +struct WireStateHandle * +get_wire_state (void) +{ +  struct WireStateHandle *old_wsh; +  struct WireStateHandle *wsh; + +  old_wsh = pthread_getspecific (wire_state); +  if ( (NULL == old_wsh) || +       (old_wsh->key_generation < key_generation) ) +  { +    wsh = build_wire_state (); +    if (NULL == wsh) +      return NULL; +    if (0 != pthread_setspecific (wire_state, +                                  wsh)) +    { +      GNUNET_break (0); +      destroy_wire_state (wsh); +      return NULL; +    } +    if (NULL != old_wsh) +      destroy_key_state (old_wsh, +                         false); +    return wsh; +  } +  return old_wsh; +} + + +/** + * Handle a "/wire" request. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param args array of additional options (must be empty for this function) + * @return MHD result code +  */ +MHD_RESULT +TEH_handler_wire (const struct TEH_RequestHandler *rh, +                  struct MHD_Connection *connection, +                  const char *const args[]) +{ +  struct WireStateHandle *wsh; + +  (void) rh; +  (void) args; +  wsh = get_wire_state (); +  if (NULL == wsh) +    TALER_MHD_reply_error (connection, +                           MHD_HTTP_INTERNAL_SERVER_ERROR, +                           TALER_EC_WTF, +                           NULL); +  return TALER_MHD_reply_json (connection, +                               json_incref (wsh->wire_reply), +                               MHD_HTTP_OK); +} + + +/* end of taler-exchange-httpd_wire.c */ diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 2fcb0d01..24ae5b2e 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -1519,6 +1519,27 @@ postgres_get_session (void *cls)                                " ,last_change=$3"                                " WHERE payto_uri=$1",                                3), +      /* used in #postgres_update_wire() */ +      GNUNET_PQ_make_prepare ("get_wire_accounts", +                              "SELECT" +                              " payto_uri" +                              ",master_sig" +                              " FROM wire_accounts" +                              " WHERE is_active", +                              0), +      /* used in #postgres_update_wire() */ +      GNUNET_PQ_make_prepare ("get_wire_fees", +                              "SELECT" +                              " wire_fee_val" +                              ",wire_fee_frac" +                              ",closing_fee_val" +                              ",closing_fee_frac" +                              ",start_date" +                              ",end_date" +                              ",master_sig" +                              " FROM wire_fee" +                              " WHERE wire_method=$1", +                              1),        /* used in #postgres_insert_signkey_revocation() */        GNUNET_PQ_make_prepare ("insert_signkey_revocation",                                "INSERT INTO signkey_revocations " @@ -8212,6 +8233,240 @@ postgres_update_wire (void *cls,  /** + * Closure for #get_wire_accounts_cb(). + */ +struct GetWireAccountsContext +{ +  /** +   * Function to call per result. +   */ +  TALER_EXCHANGEDB_WireAccountCallback cb; + +  /** +   * Closure for @e cb. +   */ +  void *cb_cls; + +  /** +   * Flag set to #GNUNET_OK as long as everything is fine. +   */ +  int status; + +}; + + +/** + * Invoke the callback for each result. + * + * @param cls a `struct MissingWireContext *` + * @param result SQL result + * @param num_results number of rows in @a result + */ +static void +get_wire_accounts_cb (void *cls, +                      PGresult *result, +                      unsigned int num_results) +{ +  struct GetWireAccountsContext *ctx = cls; + +  for (unsigned int i = 0; i < num_results; i++) +  { +    char *payto_uri; +    struct TALER_MasterSignatureP master_sig; +    struct GNUNET_PQ_ResultSpec rs[] = { +      GNUNET_PQ_result_spec_string ("payto_uri", +                                    &payto_uri), +      GNUNET_PQ_result_spec_auto_from_type ("master_sig", +                                            &master_sig), +      GNUNET_PQ_result_spec_end +    }; + +    if (GNUNET_OK != +        GNUNET_PQ_extract_result (result, +                                  rs, +                                  i)) +    { +      GNUNET_break (0); +      ctx->status = GNUNET_SYSERR; +      return; +    } +    ctx->cb (ctx->cb_cls, +             payto_uri, +             &master_sig); +    GNUNET_PQ_cleanup_result (rs); +  } +} + + +/** + * Obtain information about the enabled wire accounts of the exchange. + * + * @param cls closure + * @param cb function to call on each account + * @param cb_cls closure for @a cb + * @return transaction status code + */ +static enum GNUNET_DB_QueryStatus +postgres_get_wire_accounts (void *cls, +                            TALER_EXCHANGEDB_WireAccountCallback cb, +                            void *cb_cls) +{ +  struct PostgresClosure *pg = cls; +  struct GetWireAccountsContext ctx = { +    .cb = cb, +    .cb_cls = cb_cls, +    .status = GNUNET_OK +  }; +  struct GNUNET_PQ_QueryParam params[] = { +    GNUNET_PQ_query_param_end +  }; +  enum GNUNET_DB_QueryStatus qs; +  struct TALER_EXCHANGEDB_Session *session; + +  session = postgres_get_session (pg); +  if (NULL == session) +    return GNUNET_DB_STATUS_HARD_ERROR; +  qs = GNUNET_PQ_eval_prepared_multi_select (session->conn, +                                             "get_wire_accounts", +                                             params, +                                             &get_wire_accounts_cb, +                                             &ctx); +  if (GNUNET_OK != ctx.status) +    return GNUNET_DB_STATUS_HARD_ERROR; +  return qs; + +} + + +/** + * Closure for #get_wire_fees_cb(). + */ +struct GetWireFeesContext +{ +  /** +   * Function to call per result. +   */ +  TALER_EXCHANGEDB_WireFeeCallback cb; + +  /** +   * Closure for @e cb. +   */ +  void *cb_cls; + +  /** +   * Plugin context. +   */ +  struct PostgresClosure *pg; + +  /** +   * Flag set to #GNUNET_OK as long as everything is fine. +   */ +  int status; + +}; + + +/** + * Invoke the callback for each result. + * + * @param cls a `struct MissingWireContext *` + * @param result SQL result + * @param num_results number of rows in @a result + */ +static void +get_wire_fees_cb (void *cls, +                  PGresult *result, +                  unsigned int num_results) +{ +  struct GetWireFeesContext *ctx = cls; +  struct PostgresClosure *pg = ctx->pg; + +  for (unsigned int i = 0; i < num_results; i++) +  { +    struct TALER_MasterSignatureP master_sig; +    struct TALER_Amount wire_fee; +    struct TALER_Amount closing_fee; +    struct GNUNET_TIME_Absolute start_date; +    struct GNUNET_TIME_Absolute end_date; +    struct GNUNET_PQ_ResultSpec rs[] = { +      TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee", +                                   &wire_fee), +      TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee", +                                   &closing_fee), +      TALER_PQ_result_spec_absolute_time ("start_date", +                                          &start_date), +      TALER_PQ_result_spec_absolute_time ("end_date", +                                          &end_date), +      GNUNET_PQ_result_spec_auto_from_type ("master_sig", +                                            &master_sig), +      GNUNET_PQ_result_spec_end +    }; + +    if (GNUNET_OK != +        GNUNET_PQ_extract_result (result, +                                  rs, +                                  i)) +    { +      GNUNET_break (0); +      ctx->status = GNUNET_SYSERR; +      return; +    } +    ctx->cb (ctx->cb_cls, +             &wire_fee, +             &closing_fee, +             start_date, +             end_date, +             &master_sig); +    GNUNET_PQ_cleanup_result (rs); +  } +} + + +/** + * Obtain information about the fee structure of the exchange for + * a given @a wire_method + * + * @param cls closure + * @param wire_method which wire method to obtain fees for + * @param cb function to call on each account + * @param cb_cls closure for @a cb + * @return transaction status code + */ +static enum GNUNET_DB_QueryStatus +postgres_get_wire_fees (void *cls, +                        const char *wire_method, +                        TALER_EXCHANGEDB_WireFeeCallback cb, +                        void *cb_cls) +{ +  struct PostgresClosure *pg = cls; +  struct GNUNET_PQ_QueryParam params[] = { +    GNUNET_PQ_query_param_string (wire_method), +    GNUNET_PQ_query_param_end +  }; +  struct GetWireFeesContext ctx = { +    .cb = cb, +    .cb_cls = cb_cls, +    .pg = pg, +    .status = GNUNET_OK +  }; +  enum GNUNET_DB_QueryStatus qs; +  struct TALER_EXCHANGEDB_Session *session; + +  session = postgres_get_session (pg); +  if (NULL == session) +    return GNUNET_DB_STATUS_HARD_ERROR; +  qs = GNUNET_PQ_eval_prepared_multi_select (session->conn, +                                             "get_wire_fees", +                                             params, +                                             &get_wire_fees_cb, +                                             &ctx); +  if (GNUNET_OK != ctx.status) +    return GNUNET_DB_STATUS_HARD_ERROR; +  return qs; +} + + +/**   * Store information about a revoked online signing key.   *   * @param cls closure @@ -8810,6 +9065,10 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)      = &postgres_insert_wire;    plugin->update_wire      = &postgres_update_wire; +  plugin->get_wire_accounts +    = &postgres_get_wire_accounts; +  plugin->get_wire_fees +    = &postgres_get_wire_fees;    plugin->insert_signkey_revocation      = &postgres_insert_signkey_revocation;    plugin->lookup_future_denomination_key diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 57254787..65c31726 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -1428,6 +1428,42 @@ typedef int  /** + * Provide information about a wire account. + * + * @param cls closure + * @param payto_uri the exchange bank account URI + * @param master_sig master key signature affirming that this is a bank + *                   account of the exchange (of purpose #TALER_SIGNATURE_MASTER_WIRE_DETAILS) + */ +typedef void +(*TALER_EXCHANGEDB_WireAccountCallback)( +  void *cls, +  const char *payto_uri, +  const struct TALER_MasterSignatureP *master_sig); + + +/** + * Provide information about wire fees. + * + * @param cls closure + * @param wire_fee the wire fee we charge + * @param closing_fee the closing fee we charge + * @param start_date from when are these fees valid (start date) + * @param end_date until when are these fees valid (end date, exclusive) + * @param master_sig master key signature affirming that this is the corrrect + *                   fee (of purpose #TALER_SIGNATURE_MASTER_WIRE_FEES) + */ +typedef void +(*TALER_EXCHANGEDB_WireFeeCallback)( +  void *cls, +  const struct TALER_Amount *wire_fee, +  const struct TALER_Amount *closing_fee, +  struct GNUNET_TIME_Absolute start_date, +  struct GNUNET_TIME_Absolute end_date, +  const struct TALER_MasterSignatureP *master_sig); + + +/**   * Function called with details about withdraw operations.   *   * @param cls closure @@ -3255,6 +3291,37 @@ struct TALER_EXCHANGEDB_Plugin    /** +   * Obtain information about the enabled wire accounts of the exchange. +   * +   * @param cls closure +   * @param cb function to call on each account +   * @param cb_cls closure for @a cb +   * @return transaction status code +   */ +  enum GNUNET_DB_QueryStatus +  (*get_wire_accounts)(void *cls, +                       TALER_EXCHANGEDB_WireAccountCallback cb, +                       void *cb_cls); + + +  /** +   * Obtain information about the fee structure of the exchange for +   * a given @a wire_method +   * +   * @param cls closure +   * @param wire_method which wire method to obtain fees for +   * @param cb function to call on each account +   * @param cb_cls closure for @a cb +   * @return transaction status code +   */ +  enum GNUNET_DB_QueryStatus +  (*get_wire_fees)(void *cls, +                   const char *wire_method, +                   TALER_EXCHANGEDB_WireFeeCallback cb, +                   void *cb_cls); + + +  /**     * Store information about a revoked online signing key.     *     * @param cls closure | 
