diff options
| m--------- | contrib/gana | 0 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd_responses.c | 56 | ||||
| -rw-r--r-- | src/exchangedb/Makefile.am | 1 | ||||
| -rw-r--r-- | src/exchangedb/pg_get_reserve_history.c | 1018 | ||||
| -rw-r--r-- | src/exchangedb/pg_get_reserve_history.h | 67 | ||||
| -rw-r--r-- | src/exchangedb/plugin_exchangedb_common.c | 16 | ||||
| -rw-r--r-- | src/exchangedb/plugin_exchangedb_postgres.c | 1033 | ||||
| -rw-r--r-- | src/include/taler_crypto_lib.h | 49 | ||||
| -rw-r--r-- | src/include/taler_exchangedb_plugin.h | 91 | ||||
| -rw-r--r-- | src/util/exchange_signatures.c | 94 | ||||
| -rw-r--r-- | src/util/wallet_signatures.c | 2 | 
11 files changed, 1395 insertions, 1032 deletions
| diff --git a/contrib/gana b/contrib/gana -Subproject affa835c8ee4789134bdb5b49928dd3788d847d +Subproject 9657bf77de05c0ac17ff39629306a604066b21d diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c index 77f01de6..2cef1719 100644 --- a/src/exchange/taler-exchange-httpd_responses.c +++ b/src/exchange/taler-exchange-httpd_responses.c @@ -425,6 +425,62 @@ TEH_RESPONSE_compile_transaction_history (          break;        } +    case TALER_EXCHANGEDB_TT_PURSE_REFUND: +      { +        const struct TALER_EXCHANGEDB_PurseRefundListEntry *prefund = +          pos->details.purse_refund; +        struct TALER_Amount value; +        enum TALER_ErrorCode ec; +        struct TALER_ExchangePublicKeyP epub; +        struct TALER_ExchangeSignatureP esig; + +        if (0 > +            TALER_amount_subtract (&value, +                                   &prefund->refund_amount, +                                   &prefund->refund_fee)) +        { +          GNUNET_break (0); +          json_decref (history); +          return NULL; +        } +        ec = TALER_exchange_online_purse_refund_sign ( +          &TEH_keys_exchange_sign_, +          &value, +          &prefund->refund_fee, +          coin_pub, +          &prefund->purse_pub, +          &epub, +          &esig); +        if (TALER_EC_NONE != ec) +        { +          GNUNET_break (0); +          json_decref (history); +          return NULL; +        } +        if (0 != +            json_array_append_new ( +              history, +              GNUNET_JSON_PACK ( +                GNUNET_JSON_pack_string ("type", +                                         "PURSE-REFUND"), +                TALER_JSON_pack_amount ("amount", +                                        &value), +                TALER_JSON_pack_amount ("refund_fee", +                                        &prefund->refund_fee), +                GNUNET_JSON_pack_data_auto ("exchange_sig", +                                            &esig), +                GNUNET_JSON_pack_data_auto ("exchange_pub", +                                            &epub), +                GNUNET_JSON_pack_data_auto ("purse_pub", +                                            &prefund->purse_pub)))) +        { +          GNUNET_break (0); +          json_decref (history); +          return NULL; +        } +      } +      break; +      case TALER_EXCHANGEDB_TT_RESERVE_OPEN:        {          struct TALER_EXCHANGEDB_ReserveOpenListEntry *role diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am index 059c0136..61b18d13 100644 --- a/src/exchangedb/Makefile.am +++ b/src/exchangedb/Makefile.am @@ -73,6 +73,7 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \    pg_do_reserve_open.c pg_do_reserve_open.h \    pg_get_coin_transactions.c pg_get_coin_transactions.h \    pg_get_expired_reserves.c pg_get_expired_reserves.h \ +  pg_get_reserve_history.c pg_get_reserve_history.h \    pg_get_unfinished_close_requests.c pg_get_unfinished_close_requests.h \    pg_insert_close_request.c pg_insert_close_request.h \    pg_insert_records_by_table.c pg_insert_records_by_table.h \ diff --git a/src/exchangedb/pg_get_reserve_history.c b/src/exchangedb/pg_get_reserve_history.c new file mode 100644 index 00000000..c3ccab1f --- /dev/null +++ b/src/exchangedb/pg_get_reserve_history.c @@ -0,0 +1,1018 @@ +/* +   This file is part of TALER +   Copyright (C) 2022 Taler Systems SA + +   TALER is free software; you can redistribute it and/or modify it under the +   terms of the GNU 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 General Public License for more details. + +   You should have received a copy of the GNU General Public License along with +   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file pg_get_reserve_history.c + * @brief Low-level (statement-level) Postgres database access for the exchange + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_error_codes.h" +#include "taler_dbevents.h" +#include "taler_pq_lib.h" +#include "pg_get_reserve_history.h" +#include "plugin_exchangedb_common.h" +#include "pg_helper.h" + +/** + * Closure for callbacks invoked via #postgres_get_reserve_history. + */ +struct ReserveHistoryContext +{ + +  /** +   * Which reserve are we building the history for? +   */ +  const struct TALER_ReservePublicKeyP *reserve_pub; + +  /** +   * Where we build the history. +   */ +  struct TALER_EXCHANGEDB_ReserveHistory *rh; + +  /** +   * Tail of @e rh list. +   */ +  struct TALER_EXCHANGEDB_ReserveHistory *rh_tail; + +  /** +   * Plugin context. +   */ +  struct PostgresClosure *pg; + +  /** +   * Sum of all credit transactions. +   */ +  struct TALER_Amount balance_in; + +  /** +   * Sum of all debit transactions. +   */ +  struct TALER_Amount balance_out; + +  /** +   * Set to #GNUNET_SYSERR on serious internal errors during +   * the callbacks. +   */ +  enum GNUNET_GenericReturnValue status; +}; + + +/** + * Append and return a fresh element to the reserve + * history kept in @a rhc. + * + * @param rhc where the history is kept + * @return the fresh element that was added + */ +static struct TALER_EXCHANGEDB_ReserveHistory * +append_rh (struct ReserveHistoryContext *rhc) +{ +  struct TALER_EXCHANGEDB_ReserveHistory *tail; + +  tail = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory); +  if (NULL != rhc->rh_tail) +  { +    rhc->rh_tail->next = tail; +    rhc->rh_tail = tail; +  } +  else +  { +    rhc->rh_tail = tail; +    rhc->rh = tail; +  } +  return tail; +} + + +/** + * Add bank transfers to result set for #postgres_get_reserve_history. + * + * @param cls a `struct ReserveHistoryContext *` + * @param result SQL result + * @param num_results number of rows in @a result + */ +static void +add_bank_to_exchange (void *cls, +                      PGresult *result, +                      unsigned int num_results) +{ +  struct ReserveHistoryContext *rhc = cls; +  struct PostgresClosure *pg = rhc->pg; + +  while (0 < num_results) +  { +    struct TALER_EXCHANGEDB_BankTransfer *bt; +    struct TALER_EXCHANGEDB_ReserveHistory *tail; + +    bt = GNUNET_new (struct TALER_EXCHANGEDB_BankTransfer); +    { +      struct GNUNET_PQ_ResultSpec rs[] = { +        GNUNET_PQ_result_spec_uint64 ("wire_reference", +                                      &bt->wire_reference), +        TALER_PQ_RESULT_SPEC_AMOUNT ("credit", +                                     &bt->amount), +        GNUNET_PQ_result_spec_timestamp ("execution_date", +                                         &bt->execution_date), +        GNUNET_PQ_result_spec_string ("sender_account_details", +                                      &bt->sender_account_details), +        GNUNET_PQ_result_spec_end +      }; + +      if (GNUNET_OK != +          GNUNET_PQ_extract_result (result, +                                    rs, +                                    --num_results)) +      { +        GNUNET_break (0); +        GNUNET_free (bt); +        rhc->status = GNUNET_SYSERR; +        return; +      } +    } +    GNUNET_assert (0 <= +                   TALER_amount_add (&rhc->balance_in, +                                     &rhc->balance_in, +                                     &bt->amount)); +    bt->reserve_pub = *rhc->reserve_pub; +    tail = append_rh (rhc); +    tail->type = TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE; +    tail->details.bank = bt; +  } /* end of 'while (0 < rows)' */ +} + + +/** + * Add coin withdrawals to result set for #postgres_get_reserve_history. + * + * @param cls a `struct ReserveHistoryContext *` + * @param result SQL result + * @param num_results number of rows in @a result + */ +static void +add_withdraw_coin (void *cls, +                   PGresult *result, +                   unsigned int num_results) +{ +  struct ReserveHistoryContext *rhc = cls; +  struct PostgresClosure *pg = rhc->pg; + +  while (0 < num_results) +  { +    struct TALER_EXCHANGEDB_CollectableBlindcoin *cbc; +    struct TALER_EXCHANGEDB_ReserveHistory *tail; + +    cbc = GNUNET_new (struct TALER_EXCHANGEDB_CollectableBlindcoin); +    { +      struct GNUNET_PQ_ResultSpec rs[] = { +        GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev", +                                              &cbc->h_coin_envelope), +        GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash", +                                              &cbc->denom_pub_hash), +        TALER_PQ_result_spec_blinded_denom_sig ("denom_sig", +                                                &cbc->sig), +        GNUNET_PQ_result_spec_auto_from_type ("reserve_sig", +                                              &cbc->reserve_sig), +        TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee", +                                     &cbc->amount_with_fee), +        TALER_PQ_RESULT_SPEC_AMOUNT ("fee_withdraw", +                                     &cbc->withdraw_fee), +        GNUNET_PQ_result_spec_end +      }; + +      if (GNUNET_OK != +          GNUNET_PQ_extract_result (result, +                                    rs, +                                    --num_results)) +      { +        GNUNET_break (0); +        GNUNET_free (cbc); +        rhc->status = GNUNET_SYSERR; +        return; +      } +    } +    GNUNET_assert (0 <= +                   TALER_amount_add (&rhc->balance_out, +                                     &rhc->balance_out, +                                     &cbc->amount_with_fee)); +    cbc->reserve_pub = *rhc->reserve_pub; +    tail = append_rh (rhc); +    tail->type = TALER_EXCHANGEDB_RO_WITHDRAW_COIN; +    tail->details.withdraw = cbc; +  } +} + + +/** + * Add recoups to result set for #postgres_get_reserve_history. + * + * @param cls a `struct ReserveHistoryContext *` + * @param result SQL result + * @param num_results number of rows in @a result + */ +static void +add_recoup (void *cls, +            PGresult *result, +            unsigned int num_results) +{ +  struct ReserveHistoryContext *rhc = cls; +  struct PostgresClosure *pg = rhc->pg; + +  while (0 < num_results) +  { +    struct TALER_EXCHANGEDB_Recoup *recoup; +    struct TALER_EXCHANGEDB_ReserveHistory *tail; + +    recoup = GNUNET_new (struct TALER_EXCHANGEDB_Recoup); +    { +      struct GNUNET_PQ_ResultSpec rs[] = { +        TALER_PQ_RESULT_SPEC_AMOUNT ("amount", +                                     &recoup->value), +        GNUNET_PQ_result_spec_auto_from_type ("coin_pub", +                                              &recoup->coin.coin_pub), +        GNUNET_PQ_result_spec_auto_from_type ("coin_blind", +                                              &recoup->coin_blind), +        GNUNET_PQ_result_spec_auto_from_type ("coin_sig", +                                              &recoup->coin_sig), +        GNUNET_PQ_result_spec_timestamp ("recoup_timestamp", +                                         &recoup->timestamp), +        GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash", +                                              &recoup->coin.denom_pub_hash), +        TALER_PQ_result_spec_denom_sig ( +          "denom_sig", +          &recoup->coin.denom_sig), +        GNUNET_PQ_result_spec_end +      }; + +      if (GNUNET_OK != +          GNUNET_PQ_extract_result (result, +                                    rs, +                                    --num_results)) +      { +        GNUNET_break (0); +        GNUNET_free (recoup); +        rhc->status = GNUNET_SYSERR; +        return; +      } +    } +    GNUNET_assert (0 <= +                   TALER_amount_add (&rhc->balance_in, +                                     &rhc->balance_in, +                                     &recoup->value)); +    recoup->reserve_pub = *rhc->reserve_pub; +    tail = append_rh (rhc); +    tail->type = TALER_EXCHANGEDB_RO_RECOUP_COIN; +    tail->details.recoup = recoup; +  } /* end of 'while (0 < rows)' */ +} + + +/** + * Add exchange-to-bank transfers to result set for + * #postgres_get_reserve_history. + * + * @param cls a `struct ReserveHistoryContext *` + * @param result SQL result + * @param num_results number of rows in @a result + */ +static void +add_exchange_to_bank (void *cls, +                      PGresult *result, +                      unsigned int num_results) +{ +  struct ReserveHistoryContext *rhc = cls; +  struct PostgresClosure *pg = rhc->pg; + +  while (0 < num_results) +  { +    struct TALER_EXCHANGEDB_ClosingTransfer *closing; +    struct TALER_EXCHANGEDB_ReserveHistory *tail; + +    closing = GNUNET_new (struct TALER_EXCHANGEDB_ClosingTransfer); +    { +      struct GNUNET_PQ_ResultSpec rs[] = { +        TALER_PQ_RESULT_SPEC_AMOUNT ("amount", +                                     &closing->amount), +        TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee", +                                     &closing->closing_fee), +        GNUNET_PQ_result_spec_timestamp ("execution_date", +                                         &closing->execution_date), +        GNUNET_PQ_result_spec_string ("receiver_account", +                                      &closing->receiver_account_details), +        GNUNET_PQ_result_spec_auto_from_type ("wtid", +                                              &closing->wtid), +        GNUNET_PQ_result_spec_end +      }; + +      if (GNUNET_OK != +          GNUNET_PQ_extract_result (result, +                                    rs, +                                    --num_results)) +      { +        GNUNET_break (0); +        GNUNET_free (closing); +        rhc->status = GNUNET_SYSERR; +        return; +      } +    } +    GNUNET_assert (0 <= +                   TALER_amount_add (&rhc->balance_out, +                                     &rhc->balance_out, +                                     &closing->amount)); +    closing->reserve_pub = *rhc->reserve_pub; +    tail = append_rh (rhc); +    tail->type = TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK; +    tail->details.closing = closing; +  } /* end of 'while (0 < rows)' */ +} + + +/** + * Add purse merge transfers to result set for + * #postgres_get_reserve_history. + * + * @param cls a `struct ReserveHistoryContext *` + * @param result SQL result + * @param num_results number of rows in @a result + */ +static void +add_p2p_merge (void *cls, +               PGresult *result, +               unsigned int num_results) +{ +  struct ReserveHistoryContext *rhc = cls; +  struct PostgresClosure *pg = rhc->pg; + +  while (0 < num_results) +  { +    struct TALER_EXCHANGEDB_PurseMerge *merge; +    struct TALER_EXCHANGEDB_ReserveHistory *tail; + +    merge = GNUNET_new (struct TALER_EXCHANGEDB_PurseMerge); +    { +      uint32_t flags32; +      struct TALER_Amount balance; +      struct GNUNET_PQ_ResultSpec rs[] = { +        TALER_PQ_RESULT_SPEC_AMOUNT ("purse_fee", +                                     &merge->purse_fee), +        TALER_PQ_RESULT_SPEC_AMOUNT ("balance", +                                     &balance), +        TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee", +                                     &merge->amount_with_fee), +        GNUNET_PQ_result_spec_timestamp ("merge_timestamp", +                                         &merge->merge_timestamp), +        GNUNET_PQ_result_spec_timestamp ("purse_expiration", +                                         &merge->purse_expiration), +        GNUNET_PQ_result_spec_uint32 ("age_limit", +                                      &merge->min_age), +        GNUNET_PQ_result_spec_uint32 ("flags", +                                      &flags32), +        GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms", +                                              &merge->h_contract_terms), +        GNUNET_PQ_result_spec_auto_from_type ("merge_pub", +                                              &merge->merge_pub), +        GNUNET_PQ_result_spec_auto_from_type ("purse_pub", +                                              &merge->purse_pub), +        GNUNET_PQ_result_spec_auto_from_type ("reserve_sig", +                                              &merge->reserve_sig), +        GNUNET_PQ_result_spec_end +      }; + +      if (GNUNET_OK != +          GNUNET_PQ_extract_result (result, +                                    rs, +                                    --num_results)) +      { +        GNUNET_break (0); +        GNUNET_free (merge); +        rhc->status = GNUNET_SYSERR; +        return; +      } +      merge->flags = (enum TALER_WalletAccountMergeFlags) flags32; +      if ( (! GNUNET_TIME_absolute_is_future ( +              merge->merge_timestamp.abs_time)) && +           (-1 != TALER_amount_cmp (&balance, +                                    &merge->amount_with_fee)) ) +        merge->merged = true; +    } +    if (merge->merged) +      GNUNET_assert (0 <= +                     TALER_amount_add (&rhc->balance_in, +                                       &rhc->balance_in, +                                       &merge->amount_with_fee)); +    GNUNET_assert (0 <= +                   TALER_amount_add (&rhc->balance_out, +                                     &rhc->balance_out, +                                     &merge->purse_fee)); +    merge->reserve_pub = *rhc->reserve_pub; +    tail = append_rh (rhc); +    tail->type = TALER_EXCHANGEDB_RO_PURSE_MERGE; +    tail->details.merge = merge; +  } +} + + +/** + * Add paid for history requests to result set for + * #postgres_get_reserve_history. + * + * @param cls a `struct ReserveHistoryContext *` + * @param result SQL result + * @param num_results number of rows in @a result + */ +static void +add_history_requests (void *cls, +                      PGresult *result, +                      unsigned int num_results) +{ +  struct ReserveHistoryContext *rhc = cls; +  struct PostgresClosure *pg = rhc->pg; + +  while (0 < num_results) +  { +    struct TALER_EXCHANGEDB_HistoryRequest *history; +    struct TALER_EXCHANGEDB_ReserveHistory *tail; + +    history = GNUNET_new (struct TALER_EXCHANGEDB_HistoryRequest); +    { +      struct GNUNET_PQ_ResultSpec rs[] = { +        TALER_PQ_RESULT_SPEC_AMOUNT ("history_fee", +                                     &history->history_fee), +        GNUNET_PQ_result_spec_timestamp ("request_timestamp", +                                         &history->request_timestamp), +        GNUNET_PQ_result_spec_auto_from_type ("reserve_sig", +                                              &history->reserve_sig), +        GNUNET_PQ_result_spec_end +      }; + +      if (GNUNET_OK != +          GNUNET_PQ_extract_result (result, +                                    rs, +                                    --num_results)) +      { +        GNUNET_break (0); +        GNUNET_free (history); +        rhc->status = GNUNET_SYSERR; +        return; +      } +    } +    GNUNET_assert (0 <= +                   TALER_amount_add (&rhc->balance_out, +                                     &rhc->balance_out, +                                     &history->history_fee)); +    history->reserve_pub = *rhc->reserve_pub; +    tail = append_rh (rhc); +    tail->type = TALER_EXCHANGEDB_RO_HISTORY_REQUEST; +    tail->details.history = history; +  } +} + + +enum GNUNET_DB_QueryStatus +TEH_PG_get_reserve_history (void *cls, +                            const struct TALER_ReservePublicKeyP *reserve_pub, +                            struct TALER_Amount *balance, +                            struct TALER_EXCHANGEDB_ReserveHistory **rhp) +{ +  struct PostgresClosure *pg = cls; +  struct ReserveHistoryContext rhc; +  struct +  { +    /** +     * Name of the prepared statement to run. +     */ +    const char *statement; +    /** +     * Function to use to process the results. +     */ +    GNUNET_PQ_PostgresResultHandler cb; +  } work[] = { +    /** #TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE */ +    { "reserves_in_get_transactions", +      add_bank_to_exchange }, +    /** #TALER_EXCHANGEDB_RO_WITHDRAW_COIN */ +    { "get_reserves_out", +      &add_withdraw_coin }, +    /** #TALER_EXCHANGEDB_RO_RECOUP_COIN */ +    { "recoup_by_reserve", +      &add_recoup }, +    /** #TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK */ +    { "close_by_reserve", +      &add_exchange_to_bank }, +    /** #TALER_EXCHANGEDB_RO_PURSE_MERGE */ +    { "merge_by_reserve", +      &add_p2p_merge }, +    /** #TALER_EXCHANGEDB_RO_HISTORY_REQUEST */ +    { "history_by_reserve", +      &add_history_requests }, +    /* List terminator */ +    { NULL, +      NULL } +  }; +  enum GNUNET_DB_QueryStatus qs; +  struct GNUNET_PQ_QueryParam params[] = { +    GNUNET_PQ_query_param_auto_from_type (reserve_pub), +    GNUNET_PQ_query_param_end +  }; + +  PREPARE (pg, +           "reserves_in_get_transactions", +           /* +           "SELECT" +           " wire_reference" +           ",credit_val" +           ",credit_frac" +           ",execution_date" +           ",payto_uri AS sender_account_details" +           " FROM reserves_in" +           " JOIN wire_targets" +           "   ON (wire_source_h_payto = wire_target_h_payto)" +           " WHERE reserve_pub=$1;", +           */ +           "WITH ri AS MATERIALIZED ( " +           "  SELECT * " +           "  FROM reserves_in " +           "  WHERE reserve_pub = $1 " +           ") " +           "SELECT  " +           "  wire_reference " +           "  ,credit_val " +           "  ,credit_frac " +           "  ,execution_date " +           "  ,payto_uri AS sender_account_details " +           "FROM wire_targets " +           "JOIN ri  " +           "  ON (wire_target_h_payto = wire_source_h_payto) " +           "WHERE wire_target_h_payto = ( " +           "  SELECT wire_source_h_payto FROM ri " +           "); "); + +  PREPARE (pg, +           "get_reserves_out", +           /* +           "SELECT" +           " ro.h_blind_ev" +           ",denom.denom_pub_hash" +           ",ro.denom_sig" +           ",ro.reserve_sig" +           ",ro.execution_date" +           ",ro.amount_with_fee_val" +           ",ro.amount_with_fee_frac" +           ",denom.fee_withdraw_val" +           ",denom.fee_withdraw_frac" +           " FROM reserves res" +           " JOIN reserves_out_by_reserve ror" +           "   ON (res.reserve_uuid = ror.reserve_uuid)" +           " JOIN reserves_out ro" +           "   ON (ro.h_blind_ev = ror.h_blind_ev)" +           " JOIN denominations denom" +           "   ON (ro.denominations_serial = denom.denominations_serial)" +           " WHERE res.reserve_pub=$1;", +           */ +           "WITH robr AS MATERIALIZED ( " +           "  SELECT h_blind_ev " +           "  FROM reserves_out_by_reserve " +           "  WHERE reserve_uuid= ( " +           "    SELECT reserve_uuid " +           "    FROM reserves " +           "    WHERE reserve_pub = $1 " +           "  ) " +           ") SELECT " +           "  ro.h_blind_ev " +           "  ,denom.denom_pub_hash " +           "  ,ro.denom_sig " +           "  ,ro.reserve_sig " +           "  ,ro.execution_date " +           "  ,ro.amount_with_fee_val " +           "  ,ro.amount_with_fee_frac " +           "  ,denom.fee_withdraw_val " +           "  ,denom.fee_withdraw_frac " +           "FROM robr " +           "JOIN reserves_out ro " +           "  ON (ro.h_blind_ev = robr.h_blind_ev) " +           "JOIN denominations denom " +           "  ON (ro.denominations_serial = denom.denominations_serial);"); +  PREPARE (pg, +           "recoup_by_reserve", +           /* +           "SELECT" +           " recoup.coin_pub" +           ",recoup.coin_sig" +           ",recoup.coin_blind" +           ",recoup.amount_val" +           ",recoup.amount_frac" +           ",recoup.recoup_timestamp" +           ",denominations.denom_pub_hash" +           ",known_coins.denom_sig" +           " FROM denominations" +           " JOIN (known_coins" +           "   JOIN recoup " +           "   ON (recoup.coin_pub = known_coins.coin_pub))" +           "  ON (known_coins.denominations_serial = denominations.denominations_serial)" +           " WHERE recoup.coin_pub" +           " IN (SELECT coin_pub" +           "     FROM recoup_by_reserve" +           "     JOIN (reserves_out" +           "       JOIN (reserves_out_by_reserve" +           "         JOIN reserves" +           "           ON (reserves.reserve_uuid = reserves_out_by_reserve.reserve_uuid))" +           "       ON (reserves_out_by_reserve.h_blind_ev = reserves_out.h_blind_ev))" +           "     ON (recoup_by_reserve.reserve_out_serial_id = reserves_out.reserve_out_serial_id)" +           "     WHERE reserves.reserve_pub=$1);", +           */ +           "SELECT robr.coin_pub " +           "  ,robr.coin_sig " +           "  ,robr.coin_blind " +           "  ,robr.amount_val " +           "  ,robr.amount_frac " +           "  ,robr.recoup_timestamp " +           "  ,denominations.denom_pub_hash " +           "  ,robr.denom_sig " +           "FROM denominations " +           "  JOIN exchange_do_recoup_by_reserve($1) robr" +           " USING (denominations_serial);"); + +  PREPARE (pg, +           "close_by_reserve", +           "SELECT" +           " amount_val" +           ",amount_frac" +           ",closing_fee_val" +           ",closing_fee_frac" +           ",execution_date" +           ",payto_uri AS receiver_account" +           ",wtid" +           " FROM reserves_close" +           "   JOIN wire_targets" +           "     USING (wire_target_h_payto)" +           " WHERE reserve_pub=$1;"); + +  PREPARE (pg, +           "merge_by_reserve", +           "SELECT" +           " pr.amount_with_fee_val" +           ",pr.amount_with_fee_frac" +           ",pr.balance_val" +           ",pr.balance_frac" +           ",pr.purse_fee_val" +           ",pr.purse_fee_frac" +           ",pr.h_contract_terms" +           ",pr.merge_pub" +           ",am.reserve_sig" +           ",pm.purse_pub" +           ",pm.merge_timestamp" +           ",pr.purse_expiration" +           ",pr.age_limit" +           ",pr.flags" +           " FROM purse_merges pm" +           "   JOIN purse_requests pr" +           "     USING (purse_pub)" +           "   JOIN account_merges am" +           "     ON (am.purse_pub = pm.purse_pub AND" +           "         am.reserve_pub = pm.reserve_pub)" +           " WHERE pm.reserve_pub=$1" +           "  AND pm.partner_serial_id=0" /* must be local! */ +           "  AND pr.finished" +           "  AND NOT pr.refunded;"); + +  PREPARE (pg, +           "history_by_reserve", +           "SELECT" +           " history_fee_val" +           ",history_fee_frac" +           ",request_timestamp" +           ",reserve_sig" +           " FROM history_requests" +           " WHERE reserve_pub=$1;"); + +  rhc.reserve_pub = reserve_pub; +  rhc.rh = NULL; +  rhc.rh_tail = NULL; +  rhc.pg = pg; +  rhc.status = GNUNET_OK; +  GNUNET_assert (GNUNET_OK == +                 TALER_amount_set_zero (pg->currency, +                                        &rhc.balance_in)); +  GNUNET_assert (GNUNET_OK == +                 TALER_amount_set_zero (pg->currency, +                                        &rhc.balance_out)); +  qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; /* make static analysis happy */ +  for (unsigned int i = 0; NULL != work[i].cb; i++) +  { +    qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, +                                               work[i].statement, +                                               params, +                                               work[i].cb, +                                               &rhc); +    if ( (0 > qs) || +         (GNUNET_OK != rhc.status) ) +      break; +  } +  if ( (qs < 0) || +       (rhc.status != GNUNET_OK) ) +  { +    TEH_COMMON_free_reserve_history (cls, +                                     rhc.rh); +    rhc.rh = NULL; +    if (qs >= 0) +    { +      /* status == SYSERR is a very hard error... */ +      qs = GNUNET_DB_STATUS_HARD_ERROR; +    } +  } +  *rhp = rhc.rh; +  GNUNET_assert (0 <= +                 TALER_amount_subtract (balance, +                                        &rhc.balance_in, +                                        &rhc.balance_out)); +  return qs; +} + + +enum GNUNET_DB_QueryStatus +TEH_PG_get_reserve_status (void *cls, +                           const struct TALER_ReservePublicKeyP *reserve_pub, +                           struct TALER_Amount *balance_in, +                           struct TALER_Amount *balance_out, +                           struct TALER_EXCHANGEDB_ReserveHistory **rhp) +{ +  struct PostgresClosure *pg = cls; +  struct ReserveHistoryContext rhc; +  struct +  { +    /** +     * Name of the prepared statement to run. +     */ +    const char *statement; +    /** +     * Function to use to process the results. +     */ +    GNUNET_PQ_PostgresResultHandler cb; +  } work[] = { +    /** #TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE */ +    { "reserves_in_get_transactions_truncated", +      add_bank_to_exchange }, +    /** #TALER_EXCHANGEDB_RO_WITHDRAW_COIN */ +    { "get_reserves_out_truncated", +      &add_withdraw_coin }, +    /** #TALER_EXCHANGEDB_RO_RECOUP_COIN */ +    { "recoup_by_reserve_truncated", +      &add_recoup }, +    /** #TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK */ +    { "close_by_reserve_truncated", +      &add_exchange_to_bank }, +    /** #TALER_EXCHANGEDB_RO_PURSE_MERGE */ +    { "merge_by_reserve_truncated", +      &add_p2p_merge }, +    /** #TALER_EXCHANGEDB_RO_HISTORY_REQUEST */ +    { "history_by_reserve_truncated", +      &add_history_requests }, +    /* List terminator */ +    { NULL, +      NULL } +  }; +  enum GNUNET_DB_QueryStatus qs; +  struct GNUNET_TIME_Absolute timelimit; +  struct GNUNET_PQ_QueryParam params[] = { +    GNUNET_PQ_query_param_auto_from_type (reserve_pub), +    GNUNET_PQ_query_param_absolute_time (&timelimit), +    GNUNET_PQ_query_param_end +  }; + +  PREPARE (pg, +           "reserves_in_get_transactions_truncated", +           /* +           "SELECT" +           " wire_reference" +           ",credit_val" +           ",credit_frac" +           ",execution_date" +           ",payto_uri AS sender_account_details" +           " FROM reserves_in" +           " JOIN wire_targets" +           "   ON (wire_source_h_payto = wire_target_h_payto)" +           " WHERE reserve_pub=$1" +           "   AND execution_date>=$2;", +           */ +           "WITH ri AS MATERIALIZED ( " +           "  SELECT * " +           "  FROM reserves_in " +           "  WHERE reserve_pub = $1 " +           ") " +           "SELECT  " +           "  wire_reference " +           "  ,credit_val " +           "  ,credit_frac " +           "  ,execution_date " +           "  ,payto_uri AS sender_account_details " +           "FROM wire_targets " +           "JOIN ri  " +           "  ON (wire_target_h_payto = wire_source_h_payto) " +           "WHERE execution_date >= $2" +           "  AND wire_target_h_payto = ( " +           "  SELECT wire_source_h_payto FROM ri " +           "); "); +  PREPARE (pg, +           "get_reserves_out_truncated", +           /* +           "SELECT" +           " ro.h_blind_ev" +           ",denom.denom_pub_hash" +           ",ro.denom_sig" +           ",ro.reserve_sig" +           ",ro.execution_date" +           ",ro.amount_with_fee_val" +           ",ro.amount_with_fee_frac" +           ",denom.fee_withdraw_val" +           ",denom.fee_withdraw_frac" +           " FROM reserves res" +           " JOIN reserves_out_by_reserve ror" +           "   ON (res.reserve_uuid = ror.reserve_uuid)" +           " JOIN reserves_out ro" +           "   ON (ro.h_blind_ev = ror.h_blind_ev)" +           " JOIN denominations denom" +           "   ON (ro.denominations_serial = denom.denominations_serial)" +           " WHERE res.reserve_pub=$1" +           "   AND execution_date>=$2;", +           */ +           "WITH robr AS MATERIALIZED ( " +           "  SELECT h_blind_ev " +           "  FROM reserves_out_by_reserve " +           "  WHERE reserve_uuid= ( " +           "    SELECT reserve_uuid " +           "    FROM reserves " +           "    WHERE reserve_pub = $1 " +           "  ) " +           ") SELECT " +           "  ro.h_blind_ev " +           "  ,denom.denom_pub_hash " +           "  ,ro.denom_sig " +           "  ,ro.reserve_sig " +           "  ,ro.execution_date " +           "  ,ro.amount_with_fee_val " +           "  ,ro.amount_with_fee_frac " +           "  ,denom.fee_withdraw_val " +           "  ,denom.fee_withdraw_frac " +           "FROM robr " +           "JOIN reserves_out ro " +           "  ON (ro.h_blind_ev = robr.h_blind_ev) " +           "JOIN denominations denom " +           "  ON (ro.denominations_serial = denom.denominations_serial)" +           " WHERE ro.execution_date>=$2;"); + +  PREPARE (pg, +           "recoup_by_reserve_truncated", +           /* +           "SELECT" +           " recoup.coin_pub" +           ",recoup.coin_sig" +           ",recoup.coin_blind" +           ",recoup.amount_val" +           ",recoup.amount_frac" +           ",recoup.recoup_timestamp" +           ",denominations.denom_pub_hash" +           ",known_coins.denom_sig" +           " FROM denominations" +           " JOIN (known_coins" +           "   JOIN recoup " +           "   ON (recoup.coin_pub = known_coins.coin_pub))" +           "  ON (known_coins.denominations_serial = denominations.denominations_serial)" +           " WHERE recoup_timestamp>=$2" +           " AND recoup.coin_pub" +           "  IN (SELECT coin_pub" +           "     FROM recoup_by_reserve" +           "     JOIN (reserves_out" +           "       JOIN (reserves_out_by_reserve" +           "         JOIN reserves" +           "           ON (reserves.reserve_uuid = reserves_out_by_reserve.reserve_uuid))" +           "       ON (reserves_out_by_reserve.h_blind_ev = reserves_out.h_blind_ev))" +           "     ON (recoup_by_reserve.reserve_out_serial_id = reserves_out.reserve_out_serial_id)" +           "     WHERE reserves.reserve_pub=$1);", +           */ +           "SELECT robr.coin_pub " +           "  ,robr.coin_sig " +           "  ,robr.coin_blind " +           "  ,robr.amount_val " +           "  ,robr.amount_frac " +           "  ,robr.recoup_timestamp " +           "  ,denominations.denom_pub_hash " +           "  ,robr.denom_sig " +           "FROM denominations " +           "  JOIN exchange_do_recoup_by_reserve($1) robr" +           "    USING (denominations_serial)" +           " WHERE recoup_timestamp>=$2;"); +  /* Used in #postgres_get_reserve_status() */ +  PREPARE (pg, +           "close_by_reserve_truncated", +           "SELECT" +           " amount_val" +           ",amount_frac" +           ",closing_fee_val" +           ",closing_fee_frac" +           ",execution_date" +           ",payto_uri AS receiver_account" +           ",wtid" +           " FROM reserves_close" +           "   JOIN wire_targets" +           "     USING (wire_target_h_payto)" +           " WHERE reserve_pub=$1" +           "   AND execution_date>=$2;"); + +  /* Used in #postgres_get_reserve_status() */ +  PREPARE (pg, +           "merge_by_reserve_truncated", +           "SELECT" +           " pr.amount_with_fee_val" +           ",pr.amount_with_fee_frac" +           ",pr.balance_val" +           ",pr.balance_frac" +           ",pr.purse_fee_val" +           ",pr.purse_fee_frac" +           ",pr.h_contract_terms" +           ",pr.merge_pub" +           ",am.reserve_sig" +           ",pm.purse_pub" +           ",pm.merge_timestamp" +           ",pr.purse_expiration" +           ",pr.age_limit" +           ",pr.flags" +           " FROM purse_merges pm" +           "   JOIN purse_requests pr" +           "     USING (purse_pub)" +           "   JOIN account_merges am" +           "     ON (am.purse_pub = pm.purse_pub AND" +           "         am.reserve_pub = pm.reserve_pub)" +           " WHERE pm.reserve_pub=$1" +           "  AND pm.merge_timestamp >= $2" +           "  AND pm.partner_serial_id=0" /* must be local! */ +           "  AND pr.finished" +           "  AND NOT pr.refunded;"); + +  /* Used in #postgres_get_reserve_status() */ +  PREPARE (pg, +           "history_by_reserve_truncated", +           "SELECT" +           " history_fee_val" +           ",history_fee_frac" +           ",request_timestamp" +           ",reserve_sig" +           " FROM history_requests" +           " WHERE reserve_pub=$1" +           "  AND request_timestamp>=$2;"); + +  timelimit = GNUNET_TIME_absolute_subtract ( +    GNUNET_TIME_absolute_get (), +    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_WEEKS, +                                   5)); +  rhc.reserve_pub = reserve_pub; +  rhc.rh = NULL; +  rhc.rh_tail = NULL; +  rhc.pg = pg; +  rhc.status = GNUNET_OK; +  GNUNET_assert (GNUNET_OK == +                 TALER_amount_set_zero (pg->currency, +                                        &rhc.balance_in)); +  GNUNET_assert (GNUNET_OK == +                 TALER_amount_set_zero (pg->currency, +                                        &rhc.balance_out)); +  qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; /* make static analysis happy */ +  for (unsigned int i = 0; NULL != work[i].cb; i++) +  { +    qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, +                                               work[i].statement, +                                               params, +                                               work[i].cb, +                                               &rhc); +    if ( (0 > qs) || +         (GNUNET_OK != rhc.status) ) +      break; +  } +  if ( (qs < 0) || +       (rhc.status != GNUNET_OK) ) +  { +    TEH_COMMON_free_reserve_history (cls, +                                     rhc.rh); +    rhc.rh = NULL; +    if (qs >= 0) +    { +      /* status == SYSERR is a very hard error... */ +      qs = GNUNET_DB_STATUS_HARD_ERROR; +    } +  } +  *rhp = rhc.rh; +  *balance_in = rhc.balance_in; +  *balance_out = rhc.balance_out; +  return qs; +} diff --git a/src/exchangedb/pg_get_reserve_history.h b/src/exchangedb/pg_get_reserve_history.h new file mode 100644 index 00000000..ee47996b --- /dev/null +++ b/src/exchangedb/pg_get_reserve_history.h @@ -0,0 +1,67 @@ +/* +   This file is part of TALER +   Copyright (C) 2022 Taler Systems SA + +   TALER is free software; you can redistribute it and/or modify it under the +   terms of the GNU 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 General Public License for more details. + +   You should have received a copy of the GNU General Public License along with +   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file pg_get_reserve_history.h + * @brief implementation of the get_reserve_history function + * @author Christian Grothoff + */ +#ifndef PG_GET_RESERVE_HISTORY_H +#define PG_GET_RESERVE_HISTORY_H + +#include "taler_util.h" +#include "taler_json_lib.h" +#include "taler_exchangedb_plugin.h" + + +/** + * Get all of the transaction history associated with the specified + * reserve. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param reserve_pub public key of the reserve + * @param[out] balance set to the reserve balance + * @param[out] rhp set to known transaction history (NULL if reserve is unknown) + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +TEH_PG_get_reserve_history (void *cls, +                            const struct TALER_ReservePublicKeyP *reserve_pub, +                            struct TALER_Amount *balance, +                            struct TALER_EXCHANGEDB_ReserveHistory **rhp); + + +/** + * Get a truncated transaction history associated with the specified + * reserve. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param reserve_pub public key of the reserve + * @param[out] balance_in set to the total of inbound + *             transactions in the returned history + * @param[out] balance_out set to the total of outbound + *             transactions in the returned history + * @param[out] rhp set to known transaction history (NULL if reserve is unknown) + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +TEH_PG_get_reserve_status (void *cls, +                           const struct TALER_ReservePublicKeyP *reserve_pub, +                           struct TALER_Amount *balance_in, +                           struct TALER_Amount *balance_out, +                           struct TALER_EXCHANGEDB_ReserveHistory **rhp); + + +#endif diff --git a/src/exchangedb/plugin_exchangedb_common.c b/src/exchangedb/plugin_exchangedb_common.c index a0159fb8..562710ea 100644 --- a/src/exchangedb/plugin_exchangedb_common.c +++ b/src/exchangedb/plugin_exchangedb_common.c @@ -85,6 +85,22 @@ TEH_COMMON_free_reserve_history (          GNUNET_free (history);          break;        } +    case TALER_EXCHANGEDB_RO_OPEN_REQUEST: +      { +        struct TALER_EXCHANGEDB_OpenRequest *or; + +        or = rh->details.open_request; +        GNUNET_free (or); +        break; +      } +    case TALER_EXCHANGEDB_RO_CLOSE_REQUEST: +      { +        struct TALER_EXCHANGEDB_CloseRequest *cr; + +        cr = rh->details.close_request; +        GNUNET_free (cr); +        break; +      }      }      {        struct TALER_EXCHANGEDB_ReserveHistory *next; diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 940523b7..af441d95 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -34,6 +34,7 @@  #include "pg_do_reserve_open.h"  #include "pg_get_coin_transactions.h"  #include "pg_get_expired_reserves.h" +#include "pg_get_reserve_history.h"  #include "pg_get_unfinished_close_requests.h"  #include "pg_insert_close_request.h"  #include "pg_insert_records_by_table.h" @@ -684,72 +685,8 @@ prepare_statements (struct PostgresClosure *pg)        " ORDER BY reserve_in_serial_id;"),      /* Used in #postgres_get_reserve_history() to obtain inbound transactions         for a reserve */ -    GNUNET_PQ_make_prepare ( -      "reserves_in_get_transactions", -      /* -      "SELECT" -      " wire_reference" -      ",credit_val" -      ",credit_frac" -      ",execution_date" -      ",payto_uri AS sender_account_details" -      " FROM reserves_in" -      " JOIN wire_targets" -      "   ON (wire_source_h_payto = wire_target_h_payto)" -      " WHERE reserve_pub=$1;", -      */ -      "WITH ri AS MATERIALIZED ( " -      "  SELECT * " -      "  FROM reserves_in " -      "  WHERE reserve_pub = $1 " -      ") " -      "SELECT  " -      "  wire_reference " -      "  ,credit_val " -      "  ,credit_frac " -      "  ,execution_date " -      "  ,payto_uri AS sender_account_details " -      "FROM wire_targets " -      "JOIN ri  " -      "  ON (wire_target_h_payto = wire_source_h_payto) " -      "WHERE wire_target_h_payto = ( " -      "  SELECT wire_source_h_payto FROM ri " -      "); "),      /* Used in #postgres_get_reserve_status() to obtain inbound transactions         for a reserve */ -    GNUNET_PQ_make_prepare ( -      "reserves_in_get_transactions_truncated", -      /* -      "SELECT" -      " wire_reference" -      ",credit_val" -      ",credit_frac" -      ",execution_date" -      ",payto_uri AS sender_account_details" -      " FROM reserves_in" -      " JOIN wire_targets" -      "   ON (wire_source_h_payto = wire_target_h_payto)" -      " WHERE reserve_pub=$1" -      "   AND execution_date>=$2;", -      */ -      "WITH ri AS MATERIALIZED ( " -      "  SELECT * " -      "  FROM reserves_in " -      "  WHERE reserve_pub = $1 " -      ") " -      "SELECT  " -      "  wire_reference " -      "  ,credit_val " -      "  ,credit_frac " -      "  ,execution_date " -      "  ,payto_uri AS sender_account_details " -      "FROM wire_targets " -      "JOIN ri  " -      "  ON (wire_target_h_payto = wire_source_h_payto) " -      "WHERE execution_date >= $2" -      "  AND wire_target_h_payto = ( " -      "  SELECT wire_source_h_payto FROM ri " -      "); "),      /* Used in #postgres_do_withdraw() to store         the signature of a blinded coin with the blinded coin's         details before returning it during /reserve/withdraw. We store @@ -879,108 +816,7 @@ prepare_statements (struct PostgresClosure *pg)        "    JOIN denominations denom"        "      USING (denominations_serial)"        " WHERE h_blind_ev=$1;"), -    /* Used during #postgres_get_reserve_history() to -       obtain all of the /reserve/withdraw operations that -       have been performed on a given reserve. (i.e. to -       demonstrate double-spending) */ -    GNUNET_PQ_make_prepare ( -      "get_reserves_out", -      /* -      "SELECT" -      " ro.h_blind_ev" -      ",denom.denom_pub_hash" -      ",ro.denom_sig" -      ",ro.reserve_sig" -      ",ro.execution_date" -      ",ro.amount_with_fee_val" -      ",ro.amount_with_fee_frac" -      ",denom.fee_withdraw_val" -      ",denom.fee_withdraw_frac" -      " FROM reserves res" -      " JOIN reserves_out_by_reserve ror" -      "   ON (res.reserve_uuid = ror.reserve_uuid)" -      " JOIN reserves_out ro" -      "   ON (ro.h_blind_ev = ror.h_blind_ev)" -      " JOIN denominations denom" -      "   ON (ro.denominations_serial = denom.denominations_serial)" -      " WHERE res.reserve_pub=$1;", -      */ -      "WITH robr AS MATERIALIZED ( " -      "  SELECT h_blind_ev " -      "  FROM reserves_out_by_reserve " -      "  WHERE reserve_uuid= ( " -      "    SELECT reserve_uuid " -      "    FROM reserves " -      "    WHERE reserve_pub = $1 " -      "  ) " -      ") SELECT " -      "  ro.h_blind_ev " -      "  ,denom.denom_pub_hash " -      "  ,ro.denom_sig " -      "  ,ro.reserve_sig " -      "  ,ro.execution_date " -      "  ,ro.amount_with_fee_val " -      "  ,ro.amount_with_fee_frac " -      "  ,denom.fee_withdraw_val " -      "  ,denom.fee_withdraw_frac " -      "FROM robr " -      "JOIN reserves_out ro " -      "  ON (ro.h_blind_ev = robr.h_blind_ev) " -      "JOIN denominations denom " -      "  ON (ro.denominations_serial = denom.denominations_serial);"), -    /* Used during #postgres_get_reserve_status() to -       obtain all of the /reserve/withdraw operations that -       have been performed on a given reserve. (i.e. to -       demonstrate double-spending) */ -    GNUNET_PQ_make_prepare ( -      "get_reserves_out_truncated", -      /* -      "SELECT" -      " ro.h_blind_ev" -      ",denom.denom_pub_hash" -      ",ro.denom_sig" -      ",ro.reserve_sig" -      ",ro.execution_date" -      ",ro.amount_with_fee_val" -      ",ro.amount_with_fee_frac" -      ",denom.fee_withdraw_val" -      ",denom.fee_withdraw_frac" -      " FROM reserves res" -      " JOIN reserves_out_by_reserve ror" -      "   ON (res.reserve_uuid = ror.reserve_uuid)" -      " JOIN reserves_out ro" -      "   ON (ro.h_blind_ev = ror.h_blind_ev)" -      " JOIN denominations denom" -      "   ON (ro.denominations_serial = denom.denominations_serial)" -      " WHERE res.reserve_pub=$1" -      "   AND execution_date>=$2;", -      */ -      "WITH robr AS MATERIALIZED ( " -      "  SELECT h_blind_ev " -      "  FROM reserves_out_by_reserve " -      "  WHERE reserve_uuid= ( " -      "    SELECT reserve_uuid " -      "    FROM reserves " -      "    WHERE reserve_pub = $1 " -      "  ) " -      ") SELECT " -      "  ro.h_blind_ev " -      "  ,denom.denom_pub_hash " -      "  ,ro.denom_sig " -      "  ,ro.reserve_sig " -      "  ,ro.execution_date " -      "  ,ro.amount_with_fee_val " -      "  ,ro.amount_with_fee_frac " -      "  ,denom.fee_withdraw_val " -      "  ,denom.fee_withdraw_frac " -      "FROM robr " -      "JOIN reserves_out ro " -      "  ON (ro.h_blind_ev = robr.h_blind_ev) " -      "JOIN denominations denom " -      "  ON (ro.denominations_serial = denom.denominations_serial)" -      " WHERE ro.execution_date>=$2;"),      /* Used in #postgres_select_withdrawals_above_serial_id() */ -      GNUNET_PQ_make_prepare (        "get_reserve_balance",        "SELECT" @@ -2030,203 +1866,6 @@ prepare_statements (struct PostgresClosure *pg)        "     USING (reserve_pub)"        " WHERE close_uuid>=$1"        " ORDER BY close_uuid ASC;"), -    /* Used in #postgres_get_reserve_history() to obtain recoup transactions -       for a reserve - query optimization should be disabled i.e. -       BEGIN; SET LOCAL join_collapse_limit=1; query; COMMIT; */ -    GNUNET_PQ_make_prepare ( -      "recoup_by_reserve", -      /* -      "SELECT" -      " recoup.coin_pub" -      ",recoup.coin_sig" -      ",recoup.coin_blind" -      ",recoup.amount_val" -      ",recoup.amount_frac" -      ",recoup.recoup_timestamp" -      ",denominations.denom_pub_hash" -      ",known_coins.denom_sig" -      " FROM denominations" -      " JOIN (known_coins" -      "   JOIN recoup " -      "   ON (recoup.coin_pub = known_coins.coin_pub))" -      "  ON (known_coins.denominations_serial = denominations.denominations_serial)" -      " WHERE recoup.coin_pub" -      " IN (SELECT coin_pub" -      "     FROM recoup_by_reserve" -      "     JOIN (reserves_out" -      "       JOIN (reserves_out_by_reserve" -      "         JOIN reserves" -      "           ON (reserves.reserve_uuid = reserves_out_by_reserve.reserve_uuid))" -      "       ON (reserves_out_by_reserve.h_blind_ev = reserves_out.h_blind_ev))" -      "     ON (recoup_by_reserve.reserve_out_serial_id = reserves_out.reserve_out_serial_id)" -      "     WHERE reserves.reserve_pub=$1);", -      */ -      "SELECT robr.coin_pub " -      "  ,robr.coin_sig " -      "  ,robr.coin_blind " -      "  ,robr.amount_val " -      "  ,robr.amount_frac " -      "  ,robr.recoup_timestamp " -      "  ,denominations.denom_pub_hash " -      "  ,robr.denom_sig " -      "FROM denominations " -      "  JOIN exchange_do_recoup_by_reserve($1) robr" -      " USING (denominations_serial);"), -    /* Used in #postgres_get_reserve_status() to obtain recoup transactions -       for a reserve - query optimization should be disabled i.e. -       BEGIN; SET LOCAL join_collapse_limit=1; query; COMMIT; */ -    GNUNET_PQ_make_prepare ( -      "recoup_by_reserve_truncated", -      /* -      "SELECT" -      " recoup.coin_pub" -      ",recoup.coin_sig" -      ",recoup.coin_blind" -      ",recoup.amount_val" -      ",recoup.amount_frac" -      ",recoup.recoup_timestamp" -      ",denominations.denom_pub_hash" -      ",known_coins.denom_sig" -      " FROM denominations" -      " JOIN (known_coins" -      "   JOIN recoup " -      "   ON (recoup.coin_pub = known_coins.coin_pub))" -      "  ON (known_coins.denominations_serial = denominations.denominations_serial)" -      " WHERE recoup_timestamp>=$2" -      " AND recoup.coin_pub" -      "  IN (SELECT coin_pub" -      "     FROM recoup_by_reserve" -      "     JOIN (reserves_out" -      "       JOIN (reserves_out_by_reserve" -      "         JOIN reserves" -      "           ON (reserves.reserve_uuid = reserves_out_by_reserve.reserve_uuid))" -      "       ON (reserves_out_by_reserve.h_blind_ev = reserves_out.h_blind_ev))" -      "     ON (recoup_by_reserve.reserve_out_serial_id = reserves_out.reserve_out_serial_id)" -      "     WHERE reserves.reserve_pub=$1);", -      */ -      "SELECT robr.coin_pub " -      "  ,robr.coin_sig " -      "  ,robr.coin_blind " -      "  ,robr.amount_val " -      "  ,robr.amount_frac " -      "  ,robr.recoup_timestamp " -      "  ,denominations.denom_pub_hash " -      "  ,robr.denom_sig " -      "FROM denominations " -      "  JOIN exchange_do_recoup_by_reserve($1) robr" -      "    USING (denominations_serial)" -      " WHERE recoup_timestamp>=$2;"), -    /* Used in #postgres_get_reserve_history() */ -    GNUNET_PQ_make_prepare ( -      "close_by_reserve", -      "SELECT" -      " amount_val" -      ",amount_frac" -      ",closing_fee_val" -      ",closing_fee_frac" -      ",execution_date" -      ",payto_uri AS receiver_account" -      ",wtid" -      " FROM reserves_close" -      "   JOIN wire_targets" -      "     USING (wire_target_h_payto)" -      " WHERE reserve_pub=$1;"), -    /* Used in #postgres_get_reserve_status() */ -    GNUNET_PQ_make_prepare ( -      "close_by_reserve_truncated", -      "SELECT" -      " amount_val" -      ",amount_frac" -      ",closing_fee_val" -      ",closing_fee_frac" -      ",execution_date" -      ",payto_uri AS receiver_account" -      ",wtid" -      " FROM reserves_close" -      "   JOIN wire_targets" -      "     USING (wire_target_h_payto)" -      " WHERE reserve_pub=$1" -      "   AND execution_date>=$2;"), -    /* Used in #postgres_get_reserve_history() */ -    GNUNET_PQ_make_prepare ( -      "merge_by_reserve", -      "SELECT" -      " pr.amount_with_fee_val" -      ",pr.amount_with_fee_frac" -      ",pr.balance_val" -      ",pr.balance_frac" -      ",pr.purse_fee_val" -      ",pr.purse_fee_frac" -      ",pr.h_contract_terms" -      ",pr.merge_pub" -      ",am.reserve_sig" -      ",pm.purse_pub" -      ",pm.merge_timestamp" -      ",pr.purse_expiration" -      ",pr.age_limit" -      ",pr.flags" -      " FROM purse_merges pm" -      "   JOIN purse_requests pr" -      "     USING (purse_pub)" -      "   JOIN account_merges am" -      "     ON (am.purse_pub = pm.purse_pub AND" -      "         am.reserve_pub = pm.reserve_pub)" -      " WHERE pm.reserve_pub=$1" -      "  AND pm.partner_serial_id=0" /* must be local! */ -      "  AND pr.finished" -      "  AND NOT pr.refunded;"), -    /* Used in #postgres_get_reserve_status() */ -    GNUNET_PQ_make_prepare ( -      "merge_by_reserve_truncated", -      "SELECT" -      " pr.amount_with_fee_val" -      ",pr.amount_with_fee_frac" -      ",pr.balance_val" -      ",pr.balance_frac" -      ",pr.purse_fee_val" -      ",pr.purse_fee_frac" -      ",pr.h_contract_terms" -      ",pr.merge_pub" -      ",am.reserve_sig" -      ",pm.purse_pub" -      ",pm.merge_timestamp" -      ",pr.purse_expiration" -      ",pr.age_limit" -      ",pr.flags" -      " FROM purse_merges pm" -      "   JOIN purse_requests pr" -      "     USING (purse_pub)" -      "   JOIN account_merges am" -      "     ON (am.purse_pub = pm.purse_pub AND" -      "         am.reserve_pub = pm.reserve_pub)" -      " WHERE pm.reserve_pub=$1" -      "  AND pm.merge_timestamp >= $2" -      "  AND pm.partner_serial_id=0" /* must be local! */ -      "  AND pr.finished" -      "  AND NOT pr.refunded;"), -    /* Used in #postgres_get_reserve_history() */ -    GNUNET_PQ_make_prepare ( -      "history_by_reserve", -      "SELECT" -      " history_fee_val" -      ",history_fee_frac" -      ",request_timestamp" -      ",reserve_sig" -      " FROM history_requests" -      " WHERE reserve_pub=$1;"), -    /* Used in #postgres_get_reserve_status() */ -    GNUNET_PQ_make_prepare ( -      "history_by_reserve_truncated", -      "SELECT" -      " history_fee_val" -      ",history_fee_frac" -      ",request_timestamp" -      ",reserve_sig" -      " FROM history_requests" -      " WHERE reserve_pub=$1" -      "  AND request_timestamp>=$2;"), -    /* Used in #postgres_get_coin_transactions() to obtain recoup transactions -       for a coin */      /* Used in #postgres_get_reserve_by_h_blind() */      GNUNET_PQ_make_prepare (        "reserve_by_h_blind", @@ -4872,670 +4511,6 @@ postgres_do_recoup_refresh (  /** - * Closure for callbacks invoked via #postgres_get_reserve_history. - */ -struct ReserveHistoryContext -{ - -  /** -   * Which reserve are we building the history for? -   */ -  const struct TALER_ReservePublicKeyP *reserve_pub; - -  /** -   * Where we build the history. -   */ -  struct TALER_EXCHANGEDB_ReserveHistory *rh; - -  /** -   * Tail of @e rh list. -   */ -  struct TALER_EXCHANGEDB_ReserveHistory *rh_tail; - -  /** -   * Plugin context. -   */ -  struct PostgresClosure *pg; - -  /** -   * Sum of all credit transactions. -   */ -  struct TALER_Amount balance_in; - -  /** -   * Sum of all debit transactions. -   */ -  struct TALER_Amount balance_out; - -  /** -   * Set to #GNUNET_SYSERR on serious internal errors during -   * the callbacks. -   */ -  enum GNUNET_GenericReturnValue status; -}; - - -/** - * Append and return a fresh element to the reserve - * history kept in @a rhc. - * - * @param rhc where the history is kept - * @return the fresh element that was added - */ -static struct TALER_EXCHANGEDB_ReserveHistory * -append_rh (struct ReserveHistoryContext *rhc) -{ -  struct TALER_EXCHANGEDB_ReserveHistory *tail; - -  tail = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory); -  if (NULL != rhc->rh_tail) -  { -    rhc->rh_tail->next = tail; -    rhc->rh_tail = tail; -  } -  else -  { -    rhc->rh_tail = tail; -    rhc->rh = tail; -  } -  return tail; -} - - -/** - * Add bank transfers to result set for #postgres_get_reserve_history. - * - * @param cls a `struct ReserveHistoryContext *` - * @param result SQL result - * @param num_results number of rows in @a result - */ -static void -add_bank_to_exchange (void *cls, -                      PGresult *result, -                      unsigned int num_results) -{ -  struct ReserveHistoryContext *rhc = cls; -  struct PostgresClosure *pg = rhc->pg; - -  while (0 < num_results) -  { -    struct TALER_EXCHANGEDB_BankTransfer *bt; -    struct TALER_EXCHANGEDB_ReserveHistory *tail; - -    bt = GNUNET_new (struct TALER_EXCHANGEDB_BankTransfer); -    { -      struct GNUNET_PQ_ResultSpec rs[] = { -        GNUNET_PQ_result_spec_uint64 ("wire_reference", -                                      &bt->wire_reference), -        TALER_PQ_RESULT_SPEC_AMOUNT ("credit", -                                     &bt->amount), -        GNUNET_PQ_result_spec_timestamp ("execution_date", -                                         &bt->execution_date), -        GNUNET_PQ_result_spec_string ("sender_account_details", -                                      &bt->sender_account_details), -        GNUNET_PQ_result_spec_end -      }; - -      if (GNUNET_OK != -          GNUNET_PQ_extract_result (result, -                                    rs, -                                    --num_results)) -      { -        GNUNET_break (0); -        GNUNET_free (bt); -        rhc->status = GNUNET_SYSERR; -        return; -      } -    } -    GNUNET_assert (0 <= -                   TALER_amount_add (&rhc->balance_in, -                                     &rhc->balance_in, -                                     &bt->amount)); -    bt->reserve_pub = *rhc->reserve_pub; -    tail = append_rh (rhc); -    tail->type = TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE; -    tail->details.bank = bt; -  } /* end of 'while (0 < rows)' */ -} - - -/** - * Add coin withdrawals to result set for #postgres_get_reserve_history. - * - * @param cls a `struct ReserveHistoryContext *` - * @param result SQL result - * @param num_results number of rows in @a result - */ -static void -add_withdraw_coin (void *cls, -                   PGresult *result, -                   unsigned int num_results) -{ -  struct ReserveHistoryContext *rhc = cls; -  struct PostgresClosure *pg = rhc->pg; - -  while (0 < num_results) -  { -    struct TALER_EXCHANGEDB_CollectableBlindcoin *cbc; -    struct TALER_EXCHANGEDB_ReserveHistory *tail; - -    cbc = GNUNET_new (struct TALER_EXCHANGEDB_CollectableBlindcoin); -    { -      struct GNUNET_PQ_ResultSpec rs[] = { -        GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev", -                                              &cbc->h_coin_envelope), -        GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash", -                                              &cbc->denom_pub_hash), -        TALER_PQ_result_spec_blinded_denom_sig ("denom_sig", -                                                &cbc->sig), -        GNUNET_PQ_result_spec_auto_from_type ("reserve_sig", -                                              &cbc->reserve_sig), -        TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee", -                                     &cbc->amount_with_fee), -        TALER_PQ_RESULT_SPEC_AMOUNT ("fee_withdraw", -                                     &cbc->withdraw_fee), -        GNUNET_PQ_result_spec_end -      }; - -      if (GNUNET_OK != -          GNUNET_PQ_extract_result (result, -                                    rs, -                                    --num_results)) -      { -        GNUNET_break (0); -        GNUNET_free (cbc); -        rhc->status = GNUNET_SYSERR; -        return; -      } -    } -    GNUNET_assert (0 <= -                   TALER_amount_add (&rhc->balance_out, -                                     &rhc->balance_out, -                                     &cbc->amount_with_fee)); -    cbc->reserve_pub = *rhc->reserve_pub; -    tail = append_rh (rhc); -    tail->type = TALER_EXCHANGEDB_RO_WITHDRAW_COIN; -    tail->details.withdraw = cbc; -  } -} - - -/** - * Add recoups to result set for #postgres_get_reserve_history. - * - * @param cls a `struct ReserveHistoryContext *` - * @param result SQL result - * @param num_results number of rows in @a result - */ -static void -add_recoup (void *cls, -            PGresult *result, -            unsigned int num_results) -{ -  struct ReserveHistoryContext *rhc = cls; -  struct PostgresClosure *pg = rhc->pg; - -  while (0 < num_results) -  { -    struct TALER_EXCHANGEDB_Recoup *recoup; -    struct TALER_EXCHANGEDB_ReserveHistory *tail; - -    recoup = GNUNET_new (struct TALER_EXCHANGEDB_Recoup); -    { -      struct GNUNET_PQ_ResultSpec rs[] = { -        TALER_PQ_RESULT_SPEC_AMOUNT ("amount", -                                     &recoup->value), -        GNUNET_PQ_result_spec_auto_from_type ("coin_pub", -                                              &recoup->coin.coin_pub), -        GNUNET_PQ_result_spec_auto_from_type ("coin_blind", -                                              &recoup->coin_blind), -        GNUNET_PQ_result_spec_auto_from_type ("coin_sig", -                                              &recoup->coin_sig), -        GNUNET_PQ_result_spec_timestamp ("recoup_timestamp", -                                         &recoup->timestamp), -        GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash", -                                              &recoup->coin.denom_pub_hash), -        TALER_PQ_result_spec_denom_sig ( -          "denom_sig", -          &recoup->coin.denom_sig), -        GNUNET_PQ_result_spec_end -      }; - -      if (GNUNET_OK != -          GNUNET_PQ_extract_result (result, -                                    rs, -                                    --num_results)) -      { -        GNUNET_break (0); -        GNUNET_free (recoup); -        rhc->status = GNUNET_SYSERR; -        return; -      } -    } -    GNUNET_assert (0 <= -                   TALER_amount_add (&rhc->balance_in, -                                     &rhc->balance_in, -                                     &recoup->value)); -    recoup->reserve_pub = *rhc->reserve_pub; -    tail = append_rh (rhc); -    tail->type = TALER_EXCHANGEDB_RO_RECOUP_COIN; -    tail->details.recoup = recoup; -  } /* end of 'while (0 < rows)' */ -} - - -/** - * Add exchange-to-bank transfers to result set for - * #postgres_get_reserve_history. - * - * @param cls a `struct ReserveHistoryContext *` - * @param result SQL result - * @param num_results number of rows in @a result - */ -static void -add_exchange_to_bank (void *cls, -                      PGresult *result, -                      unsigned int num_results) -{ -  struct ReserveHistoryContext *rhc = cls; -  struct PostgresClosure *pg = rhc->pg; - -  while (0 < num_results) -  { -    struct TALER_EXCHANGEDB_ClosingTransfer *closing; -    struct TALER_EXCHANGEDB_ReserveHistory *tail; - -    closing = GNUNET_new (struct TALER_EXCHANGEDB_ClosingTransfer); -    { -      struct GNUNET_PQ_ResultSpec rs[] = { -        TALER_PQ_RESULT_SPEC_AMOUNT ("amount", -                                     &closing->amount), -        TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee", -                                     &closing->closing_fee), -        GNUNET_PQ_result_spec_timestamp ("execution_date", -                                         &closing->execution_date), -        GNUNET_PQ_result_spec_string ("receiver_account", -                                      &closing->receiver_account_details), -        GNUNET_PQ_result_spec_auto_from_type ("wtid", -                                              &closing->wtid), -        GNUNET_PQ_result_spec_end -      }; - -      if (GNUNET_OK != -          GNUNET_PQ_extract_result (result, -                                    rs, -                                    --num_results)) -      { -        GNUNET_break (0); -        GNUNET_free (closing); -        rhc->status = GNUNET_SYSERR; -        return; -      } -    } -    GNUNET_assert (0 <= -                   TALER_amount_add (&rhc->balance_out, -                                     &rhc->balance_out, -                                     &closing->amount)); -    closing->reserve_pub = *rhc->reserve_pub; -    tail = append_rh (rhc); -    tail->type = TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK; -    tail->details.closing = closing; -  } /* end of 'while (0 < rows)' */ -} - - -/** - * Add purse merge transfers to result set for - * #postgres_get_reserve_history. - * - * @param cls a `struct ReserveHistoryContext *` - * @param result SQL result - * @param num_results number of rows in @a result - */ -static void -add_p2p_merge (void *cls, -               PGresult *result, -               unsigned int num_results) -{ -  struct ReserveHistoryContext *rhc = cls; -  struct PostgresClosure *pg = rhc->pg; - -  while (0 < num_results) -  { -    struct TALER_EXCHANGEDB_PurseMerge *merge; -    struct TALER_EXCHANGEDB_ReserveHistory *tail; - -    merge = GNUNET_new (struct TALER_EXCHANGEDB_PurseMerge); -    { -      uint32_t flags32; -      struct TALER_Amount balance; -      struct GNUNET_PQ_ResultSpec rs[] = { -        TALER_PQ_RESULT_SPEC_AMOUNT ("purse_fee", -                                     &merge->purse_fee), -        TALER_PQ_RESULT_SPEC_AMOUNT ("balance", -                                     &balance), -        TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee", -                                     &merge->amount_with_fee), -        GNUNET_PQ_result_spec_timestamp ("merge_timestamp", -                                         &merge->merge_timestamp), -        GNUNET_PQ_result_spec_timestamp ("purse_expiration", -                                         &merge->purse_expiration), -        GNUNET_PQ_result_spec_uint32 ("age_limit", -                                      &merge->min_age), -        GNUNET_PQ_result_spec_uint32 ("flags", -                                      &flags32), -        GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms", -                                              &merge->h_contract_terms), -        GNUNET_PQ_result_spec_auto_from_type ("merge_pub", -                                              &merge->merge_pub), -        GNUNET_PQ_result_spec_auto_from_type ("purse_pub", -                                              &merge->purse_pub), -        GNUNET_PQ_result_spec_auto_from_type ("reserve_sig", -                                              &merge->reserve_sig), -        GNUNET_PQ_result_spec_end -      }; - -      if (GNUNET_OK != -          GNUNET_PQ_extract_result (result, -                                    rs, -                                    --num_results)) -      { -        GNUNET_break (0); -        GNUNET_free (merge); -        rhc->status = GNUNET_SYSERR; -        return; -      } -      merge->flags = (enum TALER_WalletAccountMergeFlags) flags32; -      if ( (! GNUNET_TIME_absolute_is_future ( -              merge->merge_timestamp.abs_time)) && -           (-1 != TALER_amount_cmp (&balance, -                                    &merge->amount_with_fee)) ) -        merge->merged = true; -    } -    if (merge->merged) -      GNUNET_assert (0 <= -                     TALER_amount_add (&rhc->balance_in, -                                       &rhc->balance_in, -                                       &merge->amount_with_fee)); -    GNUNET_assert (0 <= -                   TALER_amount_add (&rhc->balance_out, -                                     &rhc->balance_out, -                                     &merge->purse_fee)); -    merge->reserve_pub = *rhc->reserve_pub; -    tail = append_rh (rhc); -    tail->type = TALER_EXCHANGEDB_RO_PURSE_MERGE; -    tail->details.merge = merge; -  } -} - - -/** - * Add paid for history requests to result set for - * #postgres_get_reserve_history. - * - * @param cls a `struct ReserveHistoryContext *` - * @param result SQL result - * @param num_results number of rows in @a result - */ -static void -add_history_requests (void *cls, -                      PGresult *result, -                      unsigned int num_results) -{ -  struct ReserveHistoryContext *rhc = cls; -  struct PostgresClosure *pg = rhc->pg; - -  while (0 < num_results) -  { -    struct TALER_EXCHANGEDB_HistoryRequest *history; -    struct TALER_EXCHANGEDB_ReserveHistory *tail; - -    history = GNUNET_new (struct TALER_EXCHANGEDB_HistoryRequest); -    { -      struct GNUNET_PQ_ResultSpec rs[] = { -        TALER_PQ_RESULT_SPEC_AMOUNT ("history_fee", -                                     &history->history_fee), -        GNUNET_PQ_result_spec_timestamp ("request_timestamp", -                                         &history->request_timestamp), -        GNUNET_PQ_result_spec_auto_from_type ("reserve_sig", -                                              &history->reserve_sig), -        GNUNET_PQ_result_spec_end -      }; - -      if (GNUNET_OK != -          GNUNET_PQ_extract_result (result, -                                    rs, -                                    --num_results)) -      { -        GNUNET_break (0); -        GNUNET_free (history); -        rhc->status = GNUNET_SYSERR; -        return; -      } -    } -    GNUNET_assert (0 <= -                   TALER_amount_add (&rhc->balance_out, -                                     &rhc->balance_out, -                                     &history->history_fee)); -    history->reserve_pub = *rhc->reserve_pub; -    tail = append_rh (rhc); -    tail->type = TALER_EXCHANGEDB_RO_HISTORY_REQUEST; -    tail->details.history = history; -  } -} - - -/** - * Get all of the transaction history associated with the specified - * reserve. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param reserve_pub public key of the reserve - * @param[out] balance set to the reserve balance - * @param[out] rhp set to known transaction history (NULL if reserve is unknown) - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_get_reserve_history (void *cls, -                              const struct TALER_ReservePublicKeyP *reserve_pub, -                              struct TALER_Amount *balance, -                              struct TALER_EXCHANGEDB_ReserveHistory **rhp) -{ -  struct PostgresClosure *pg = cls; -  struct ReserveHistoryContext rhc; -  struct -  { -    /** -     * Name of the prepared statement to run. -     */ -    const char *statement; -    /** -     * Function to use to process the results. -     */ -    GNUNET_PQ_PostgresResultHandler cb; -  } work[] = { -    /** #TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE */ -    { "reserves_in_get_transactions", -      add_bank_to_exchange }, -    /** #TALER_EXCHANGEDB_RO_WITHDRAW_COIN */ -    { "get_reserves_out", -      &add_withdraw_coin }, -    /** #TALER_EXCHANGEDB_RO_RECOUP_COIN */ -    { "recoup_by_reserve", -      &add_recoup }, -    /** #TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK */ -    { "close_by_reserve", -      &add_exchange_to_bank }, -    /** #TALER_EXCHANGEDB_RO_PURSE_MERGE */ -    { "merge_by_reserve", -      &add_p2p_merge }, -    /** #TALER_EXCHANGEDB_RO_HISTORY_REQUEST */ -    { "history_by_reserve", -      &add_history_requests }, -    /* List terminator */ -    { NULL, -      NULL } -  }; -  enum GNUNET_DB_QueryStatus qs; -  struct GNUNET_PQ_QueryParam params[] = { -    GNUNET_PQ_query_param_auto_from_type (reserve_pub), -    GNUNET_PQ_query_param_end -  }; - -  rhc.reserve_pub = reserve_pub; -  rhc.rh = NULL; -  rhc.rh_tail = NULL; -  rhc.pg = pg; -  rhc.status = GNUNET_OK; -  GNUNET_assert (GNUNET_OK == -                 TALER_amount_set_zero (pg->currency, -                                        &rhc.balance_in)); -  GNUNET_assert (GNUNET_OK == -                 TALER_amount_set_zero (pg->currency, -                                        &rhc.balance_out)); -  qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; /* make static analysis happy */ -  for (unsigned int i = 0; NULL != work[i].cb; i++) -  { -    qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, -                                               work[i].statement, -                                               params, -                                               work[i].cb, -                                               &rhc); -    if ( (0 > qs) || -         (GNUNET_OK != rhc.status) ) -      break; -  } -  if ( (qs < 0) || -       (rhc.status != GNUNET_OK) ) -  { -    TEH_COMMON_free_reserve_history (cls, -                                     rhc.rh); -    rhc.rh = NULL; -    if (qs >= 0) -    { -      /* status == SYSERR is a very hard error... */ -      qs = GNUNET_DB_STATUS_HARD_ERROR; -    } -  } -  *rhp = rhc.rh; -  GNUNET_assert (0 <= -                 TALER_amount_subtract (balance, -                                        &rhc.balance_in, -                                        &rhc.balance_out)); -  return qs; -} - - -/** - * Get a truncated transaction history associated with the specified - * reserve. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param reserve_pub public key of the reserve - * @param[out] balance_in set to the total of inbound - *             transactions in the returned history - * @param[out] balance_out set to the total of outbound - *             transactions in the returned history - * @param[out] rhp set to known transaction history (NULL if reserve is unknown) - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_get_reserve_status (void *cls, -                             const struct TALER_ReservePublicKeyP *reserve_pub, -                             struct TALER_Amount *balance_in, -                             struct TALER_Amount *balance_out, -                             struct TALER_EXCHANGEDB_ReserveHistory **rhp) -{ -  struct PostgresClosure *pg = cls; -  struct ReserveHistoryContext rhc; -  struct -  { -    /** -     * Name of the prepared statement to run. -     */ -    const char *statement; -    /** -     * Function to use to process the results. -     */ -    GNUNET_PQ_PostgresResultHandler cb; -  } work[] = { -    /** #TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE */ -    { "reserves_in_get_transactions_truncated", -      add_bank_to_exchange }, -    /** #TALER_EXCHANGEDB_RO_WITHDRAW_COIN */ -    { "get_reserves_out_truncated", -      &add_withdraw_coin }, -    /** #TALER_EXCHANGEDB_RO_RECOUP_COIN */ -    { "recoup_by_reserve_truncated", -      &add_recoup }, -    /** #TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK */ -    { "close_by_reserve_truncated", -      &add_exchange_to_bank }, -    /** #TALER_EXCHANGEDB_RO_PURSE_MERGE */ -    { "merge_by_reserve_truncated", -      &add_p2p_merge }, -    /** #TALER_EXCHANGEDB_RO_HISTORY_REQUEST */ -    { "history_by_reserve_truncated", -      &add_history_requests }, -    /* List terminator */ -    { NULL, -      NULL } -  }; -  enum GNUNET_DB_QueryStatus qs; -  struct GNUNET_TIME_Absolute timelimit; -  struct GNUNET_PQ_QueryParam params[] = { -    GNUNET_PQ_query_param_auto_from_type (reserve_pub), -    GNUNET_PQ_query_param_absolute_time (&timelimit), -    GNUNET_PQ_query_param_end -  }; - -  timelimit = GNUNET_TIME_absolute_subtract ( -    GNUNET_TIME_absolute_get (), -    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_WEEKS, -                                   5)); -  rhc.reserve_pub = reserve_pub; -  rhc.rh = NULL; -  rhc.rh_tail = NULL; -  rhc.pg = pg; -  rhc.status = GNUNET_OK; -  GNUNET_assert (GNUNET_OK == -                 TALER_amount_set_zero (pg->currency, -                                        &rhc.balance_in)); -  GNUNET_assert (GNUNET_OK == -                 TALER_amount_set_zero (pg->currency, -                                        &rhc.balance_out)); -  qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; /* make static analysis happy */ -  for (unsigned int i = 0; NULL != work[i].cb; i++) -  { -    qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, -                                               work[i].statement, -                                               params, -                                               work[i].cb, -                                               &rhc); -    if ( (0 > qs) || -         (GNUNET_OK != rhc.status) ) -      break; -  } -  if ( (qs < 0) || -       (rhc.status != GNUNET_OK) ) -  { -    TEH_COMMON_free_reserve_history (cls, -                                     rhc.rh); -    rhc.rh = NULL; -    if (qs >= 0) -    { -      /* status == SYSERR is a very hard error... */ -      qs = GNUNET_DB_STATUS_HARD_ERROR; -    } -  } -  *rhp = rhc.rh; -  *balance_in = rhc.balance_in; -  *balance_out = rhc.balance_out; -  return qs; -} - - -/**   * Get the balance of the specified reserve.   *   * @param cls the `struct PostgresClosure` with the plugin-specific state @@ -14139,8 +13114,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)    plugin->do_recoup = &postgres_do_recoup;    plugin->do_recoup_refresh = &postgres_do_recoup_refresh;    plugin->get_reserve_balance = &postgres_get_reserve_balance; -  plugin->get_reserve_history = &postgres_get_reserve_history; -  plugin->get_reserve_status = &postgres_get_reserve_status;    plugin->count_known_coins = &postgres_count_known_coins;    plugin->ensure_coin_known = &postgres_ensure_coin_known;    plugin->get_known_coin = &postgres_get_known_coin; @@ -14352,6 +13325,10 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)      = &TEH_PG_get_coin_transactions;    plugin->get_expired_reserves      = &TEH_PG_get_expired_reserves; +  plugin->get_reserve_history +    = &TEH_PG_get_reserve_history; +  plugin->get_reserve_status +    = &TEH_PG_get_reserve_status;    plugin->get_unfinished_close_requests      = &TEH_PG_get_unfinished_close_requests;    plugin->insert_records_by_table diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h index a861563b..df992c6e 100644 --- a/src/include/taler_crypto_lib.h +++ b/src/include/taler_crypto_lib.h @@ -3835,6 +3835,51 @@ TALER_exchange_online_melt_confirmation_verify (  /** + * Create exchange purse refund confirmation signature. + * + * @param scb function to call to create the signature + * @param amount_without_fee refunded amount + * @param refund_fee refund fee charged + * @param coin_pub coin that was refunded + * @param purse_pub public key of the expired purse + * @param[out] pub where to write the public key + * @param[out] sig where to write the signature + * @return #TALER_EC_NONE on success + */ +enum TALER_ErrorCode +TALER_exchange_online_purse_refund_sign ( +  TALER_ExchangeSignCallback scb, +  const struct TALER_Amount *amount_without_fee, +  const struct TALER_Amount *refund_fee, +  const struct TALER_CoinSpendPublicKeyP *coin_pub, +  const struct TALER_PurseContractPublicKeyP *purse_pub, +  struct TALER_ExchangePublicKeyP *pub, +  struct TALER_ExchangeSignatureP *sig); + + +/** + * Verify signature of exchange affirming purse refund + * from purse expiration. + * + * @param amount_without_fee refunded amount + * @param refund_fee refund fee charged + * @param coin_pub coin that was refunded + * @param purse_pub public key of the expired purse + * @param pub public key to verify signature against + * @param sig signature to verify + * @return #GNUNET_OK if the signature is valid + */ +enum GNUNET_GenericReturnValue +TALER_exchange_online_purse_refund_verify ( +  const struct TALER_Amount *amount_without_fee, +  const struct TALER_Amount *refund_fee, +  const struct TALER_CoinSpendPublicKeyP *coin_pub, +  const struct TALER_PurseContractPublicKeyP *purse_pub, +  const struct TALER_ExchangePublicKeyP *pub, +  const struct TALER_ExchangeSignatureP *sig); + + +/**   * Create exchange key set signature.   *   * @param scb function to call to create the signature @@ -3860,8 +3905,8 @@ TALER_exchange_online_key_set_sign (   *   * @param timestamp time when the key set was issued   * @param hc hash over all the keys - * @param pub where to write the public key - * @param sig where to write the signature + * @param pub public key to verify signature against + * @param sig signature to verify   * @return #GNUNET_OK if the signature is valid   */  enum GNUNET_GenericReturnValue diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index a12c2ac3..7a0d5c55 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -1271,6 +1271,75 @@ struct TALER_EXCHANGEDB_HistoryRequest  /** + * Details about a (paid for) reserve open request. + */ +struct TALER_EXCHANGEDB_OpenRequest +{ +  /** +   * Public key of the reserve the open request was for. +   */ +  struct TALER_ReservePublicKeyP reserve_pub; + +  /** +   * Fee paid for the request from the reserve. +   */ +  struct TALER_Amount open_fee; + +  /** +   * When was the request made. +   */ +  struct GNUNET_TIME_Timestamp request_timestamp; + +  /** +   * How long was the reserve supposed to be open. +   */ +  struct GNUNET_TIME_Timestamp reserve_expiration; + +  /** +   * Signature by the reserve approving the open request. +   */ +  struct TALER_ReserveSignatureP reserve_sig; + +  /** +   * How many open purses should be included with the +   * open reserve? +   */ +  uint32_t purse_limit; + +}; + + +/** + * Details about an (explicit) reserve close request. + */ +struct TALER_EXCHANGEDB_CloseRequest +{ +  /** +   * Public key of the reserve the history request was for. +   */ +  struct TALER_ReservePublicKeyP reserve_pub; + +  /** +   * When was the request made. +   */ +  struct GNUNET_TIME_Timestamp request_timestamp; + +  /** +   * Hash of the payto://-URI of the target account +   * for the closure, or all zeros for the reserve +   * origin account. +   */ +  struct TALER_PaytoHashP target_account_h_payto; + +  /** +   * Signature by the reserve approving the history request. +   */ +  struct TALER_ReserveSignatureP reserve_sig; + +}; + + +/**   * @brief Types of operations on a reserve.   */  enum TALER_EXCHANGEDB_ReserveOperation @@ -1306,7 +1375,17 @@ enum TALER_EXCHANGEDB_ReserveOperation    /**     * Event where a wallet paid for a full reserve history.     */ -  TALER_EXCHANGEDB_RO_HISTORY_REQUEST = 5 +  TALER_EXCHANGEDB_RO_HISTORY_REQUEST = 5, + +  /** +   * Event where a wallet paid to open a reserve for longer. +   */ +  TALER_EXCHANGEDB_RO_OPEN_REQUEST = 6, + +  /** +   * Event where a wallet requested a reserve to be closed. +   */ +  TALER_EXCHANGEDB_RO_CLOSE_REQUEST = 7  }; @@ -1367,6 +1446,16 @@ struct TALER_EXCHANGEDB_ReserveHistory       */      struct TALER_EXCHANGEDB_HistoryRequest *history; +    /** +     * Details about a (paid for) open reserve request. +     */ +    struct TALER_EXCHANGEDB_OpenRequest *open_request; + +    /** +     * Details about an (explicit) reserve close request. +     */ +    struct TALER_EXCHANGEDB_CloseRequest *close_request; +    } details;  }; diff --git a/src/util/exchange_signatures.c b/src/util/exchange_signatures.c index d42f70d6..8ecb3caf 100644 --- a/src/util/exchange_signatures.c +++ b/src/util/exchange_signatures.c @@ -1383,6 +1383,100 @@ GNUNET_NETWORK_STRUCT_BEGIN  /**   * Response by which the exchange affirms that it has + * received funds deposited into a purse. + */ +struct TALER_CoinPurseRefundConfirmationPS +{ + +  /** +   * Purpose is #TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_REFUND +   */ +  struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + +  /** +   * Public key of the purse. +   */ +  struct TALER_PurseContractPublicKeyP purse_pub; + +  /** +   * Public key of the coin. +   */ +  struct TALER_CoinSpendPublicKeyP coin_pub; + +  /** +   * How much will be refunded to the purse. +   */ +  struct TALER_AmountNBO refunded_amount; + +  /** +   * How much was the refund fee. +   */ +  struct TALER_AmountNBO refund_fee; + +}; + +GNUNET_NETWORK_STRUCT_END + + +enum TALER_ErrorCode +TALER_exchange_online_purse_refund_sign ( +  TALER_ExchangeSignCallback scb, +  const struct TALER_Amount *amount_without_fee, +  const struct TALER_Amount *refund_fee, +  const struct TALER_CoinSpendPublicKeyP *coin_pub, +  const struct TALER_PurseContractPublicKeyP *purse_pub, +  struct TALER_ExchangePublicKeyP *pub, +  struct TALER_ExchangeSignatureP *sig) +{ +  struct TALER_CoinPurseRefundConfirmationPS dc = { +    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_REFUND), +    .purpose.size = htonl (sizeof (dc)), +    .coin_pub = *coin_pub, +    .purse_pub = *purse_pub, +  }; + +  TALER_amount_hton (&dc.refunded_amount, +                     amount_without_fee); +  TALER_amount_hton (&dc.refund_fee, +                     refund_fee); +  return scb (&dc.purpose, +              pub, +              sig); +} + + +enum GNUNET_GenericReturnValue +TALER_exchange_online_purse_refund_verify ( +  const struct TALER_Amount *amount_without_fee, +  const struct TALER_Amount *refund_fee, +  const struct TALER_CoinSpendPublicKeyP *coin_pub, +  const struct TALER_PurseContractPublicKeyP *purse_pub, +  const struct TALER_ExchangePublicKeyP *pub, +  const struct TALER_ExchangeSignatureP *sig) +{ +  struct TALER_CoinPurseRefundConfirmationPS dc = { +    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_REFUND), +    .purpose.size = htonl (sizeof (dc)), +    .coin_pub = *coin_pub, +    .purse_pub = *purse_pub, +  }; + +  TALER_amount_hton (&dc.refunded_amount, +                     amount_without_fee); +  TALER_amount_hton (&dc.refund_fee, +                     refund_fee); +  return +    GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_REFUND, +                                &dc, +                                &sig->eddsa_signature, +                                &pub->eddsa_pub); +} + + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Response by which the exchange affirms that it has   * merged a purse into a reserve.   */  struct TALER_PurseMergedConfirmationPS diff --git a/src/util/wallet_signatures.c b/src/util/wallet_signatures.c index c57506bd..1209bb69 100644 --- a/src/util/wallet_signatures.c +++ b/src/util/wallet_signatures.c @@ -1422,7 +1422,7 @@ struct TALER_ReserveClosePS    /**     * Hash of the payto://-URI of the target account     * for the closure, or all zeros for the reserve -   * origina ccount. +   * origin account.     */    struct TALER_PaytoHashP target_account_h_payto; | 
