gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-exchange] 01/02: add idempotency check for batch-deposit on kyc f


From: gnunet
Subject: [taler-exchange] 01/02: add idempotency check for batch-deposit on kyc failure
Date: Sun, 01 Sep 2024 17:10:44 +0200

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository exchange.

commit 01373461c56e6561ed066c9d4b492cd9091929c1
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Sun Sep 1 16:50:30 2024 +0200

    add idempotency check for batch-deposit on kyc failure
---
 src/exchange/taler-exchange-httpd_batch-deposit.c  |  52 ++++-----
 src/exchangedb/Makefile.am                         |   1 +
 .../exchange_do_check_deposit_idempotent.sql       | 123 +++++++++++++++++++++
 src/exchangedb/perf_deposits_get_ready.c           |   3 +-
 src/exchangedb/perf_select_refunds_by_coin.c       |   2 +-
 src/exchangedb/pg_do_check_deposit_idempotent.c    | 112 +++++++++++++++++++
 src/exchangedb/pg_do_check_deposit_idempotent.h    |  45 ++++++++
 src/exchangedb/plugin_exchangedb_postgres.c        |   3 +
 src/exchangedb/procedures.sql.in                   |   1 +
 src/include/taler_exchangedb_plugin.h              |  17 +++
 10 files changed, 324 insertions(+), 35 deletions(-)

diff --git a/src/exchange/taler-exchange-httpd_batch-deposit.c 
b/src/exchange/taler-exchange-httpd_batch-deposit.c
index b18acd8aa..9cf58e4a5 100644
--- a/src/exchange/taler-exchange-httpd_batch-deposit.c
+++ b/src/exchange/taler-exchange-httpd_batch-deposit.c
@@ -451,43 +451,31 @@ static bool
 check_request_idempotent (
   struct BatchDepositContext *bdc)
 {
-#if FIXME_PLACEHOLDER
   const struct TEH_RequestContext *rc = bdc->rc;
+  enum GNUNET_DB_QueryStatus qs;
+  bool is_idempotent;
 
-  for (unsigned int i = 0; i<bwc->planchets_length; i++)
+  qs = TEH_plugin->do_check_deposit_idempotent (
+    TEH_plugin->cls,
+    &bdc->bd,
+    &bdc->exchange_timestamp,
+    &is_idempotent);
+  if (0 > qs)
   {
-    struct PlanchetContext *pc = &bwc->planchets[i];
-    enum GNUNET_DB_QueryStatus qs;
-    struct TALER_EXCHANGEDB_CollectableBlindcoin collectable;
-
-    qs = TEH_plugin->get_withdraw_info (
-      TEH_plugin->cls,
-      &pc->collectable.h_coin_envelope,
-      &collectable);
-    if (0 > qs)
-    {
-      /* FIXME: soft error not handled correctly! */
-      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
-      finish_loop (bwc,
-                   TALER_MHD_reply_with_error (
-                     rc->connection,
-                     MHD_HTTP_INTERNAL_SERVER_ERROR,
-                     TALER_EC_GENERIC_DB_FETCH_FAILED,
-                     "get_withdraw_info"));
-      return true;
-    }
-    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
-      return false;
-    pc->collectable = collectable;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    finish_loop (bdc,
+                 TALER_MHD_reply_with_error (
+                   rc->connection,
+                   MHD_HTTP_INTERNAL_SERVER_ERROR,
+                   TALER_EC_GENERIC_DB_FETCH_FAILED,
+                   "get_withdraw_info"));
+    return true;
   }
-  /* generate idempotent reply */
-  TEH_METRICS_num_requests[TEH_MT_REQUEST_IDEMPOTENT_BATCH_WITHDRAW]++;
-  bwc->phase = BDC_PHASE_GENERATE_REPLY_SUCCESS;
+  if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) ||
+       (! is_idempotent) )
+    return false;
+  bdc->phase = BDC_PHASE_REPLY_SUCCESS;
   return true;
-#else
-  GNUNET_break (0); // NOT IMPLEMENTED
-  return false;
-#endif
 }
 
 
diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am
index a81c2d722..724370efb 100644
--- a/src/exchangedb/Makefile.am
+++ b/src/exchangedb/Makefile.am
@@ -130,6 +130,7 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \
   pg_profit_drains_get_pending.h pg_profit_drains_get_pending.c \
   pg_get_drain_profit.h pg_get_drain_profit.c \
   pg_get_purse_deposit.h pg_get_purse_deposit.c \
+  pg_do_check_deposit_idempotent.h pg_do_check_deposit_idempotent.c \
   pg_insert_contract.h pg_insert_contract.c \
   pg_select_contract.h pg_select_contract.c \
   pg_select_purse_merge.h pg_select_purse_merge.c \
diff --git a/src/exchangedb/exchange_do_check_deposit_idempotent.sql 
b/src/exchangedb/exchange_do_check_deposit_idempotent.sql
new file mode 100644
index 000000000..1e7414dc0
--- /dev/null
+++ b/src/exchangedb/exchange_do_check_deposit_idempotent.sql
@@ -0,0 +1,123 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2024 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/>
+--
+CREATE OR REPLACE FUNCTION exchange_do_check_deposit_idempotent(
+  -- For batch_deposits
+  IN in_shard INT8,
+  IN in_merchant_pub BYTEA,
+  IN in_wallet_timestamp INT8,
+  IN in_exchange_timestamp INT8,
+  IN in_refund_deadline INT8,
+  IN in_wire_deadline INT8,
+  IN in_h_contract_terms BYTEA,
+  IN in_wallet_data_hash BYTEA, -- can be NULL
+  IN in_wire_salt BYTEA,
+  IN in_wire_target_h_payto BYTEA,
+  IN in_policy_details_serial_id INT8, -- can be NULL
+  IN in_policy_blocked BOOLEAN,
+  -- For wire_targets
+  IN in_receiver_wire_account TEXT,
+  -- For coin_deposits
+  IN ina_coin_pub BYTEA[],
+  IN ina_coin_sig BYTEA[],
+  IN ina_amount_with_fee taler_amount[],
+  OUT out_exchange_timestamp INT8,
+  OUT out_is_idempotent BOOL
+ )
+LANGUAGE plpgsql
+AS $$
+DECLARE
+  wtsi INT8; -- wire target serial id
+  bdsi INT8; -- batch_deposits serial id
+  i INT4;
+  ini_amount_with_fee taler_amount;
+  ini_coin_pub BYTEA;
+  ini_coin_sig BYTEA;
+BEGIN
+-- Shards:
+--         SELECT wire_targets (by h_payto);
+--         INSERT batch_deposits (by shard, merchant_pub), ON CONFLICT 
idempotency check;
+--         PERFORM[] coin_deposits (by coin_pub), ON CONFLICT idempotency 
check;
+
+out_exchange_timestamp = in_exchange_timestamp;
+
+-- First, get the 'wtsi'
+SELECT wire_target_serial_id
+  INTO wtsi
+  FROM wire_targets
+ WHERE wire_target_h_payto=in_wire_target_h_payto
+   AND payto_uri=in_receiver_wire_account;
+
+IF NOT FOUND
+THEN
+  out_is_idempotent = FALSE;
+  RETURN;
+END IF;
+
+
+-- Idempotency check: see if an identical record exists.
+-- We do select over merchant_pub, h_contract_terms and wire_target_h_payto
+-- first to maximally increase the chance of using the existing index.
+SELECT
+    exchange_timestamp
+   ,batch_deposit_serial_id
+  INTO
+    out_exchange_timestamp
+   ,bdsi
+  FROM batch_deposits
+ WHERE shard=in_shard
+   AND merchant_pub=in_merchant_pub
+   AND h_contract_terms=in_h_contract_terms
+   AND wire_target_h_payto=in_wire_target_h_payto
+   -- now check the rest, too
+   AND ( (wallet_data_hash=in_wallet_data_hash) OR
+         (wallet_data_hash IS NULL AND in_wallet_data_hash IS NULL) )
+   AND wire_salt=in_wire_salt
+   AND wallet_timestamp=in_wallet_timestamp
+   AND refund_deadline=in_refund_deadline
+   AND wire_deadline=in_wire_deadline
+   AND ( (policy_details_serial_id=in_policy_details_serial_id) OR
+         (policy_details_serial_id IS NULL AND in_policy_details_serial_id IS 
NULL) );
+
+IF NOT FOUND
+THEN
+  out_is_idempotent=FALSE;
+  RETURN;
+END IF;
+
+
+-- Check each coin
+
+FOR i IN 1..array_length(ina_coin_pub,1)
+LOOP
+  ini_coin_pub = ina_coin_pub[i];
+  ini_coin_sig = ina_coin_sig[i];
+  ini_amount_with_fee = ina_amount_with_fee[i];
+
+  PERFORM FROM coin_deposits
+    WHERE batch_deposit_serial_id=bdsi
+      AND coin_pub=ini_coin_pub
+      AND coin_sig=ini_coin_sig
+      AND amount_with_fee=ini_amount_with_fee;
+  IF NOT FOUND
+  THEN
+    out_is_idempotent=FALSE;
+    RETURN;
+  END IF;
+END LOOP; -- end FOR all coins
+
+out_is_idempotent=TRUE;
+
+END $$;
diff --git a/src/exchangedb/perf_deposits_get_ready.c 
b/src/exchangedb/perf_deposits_get_ready.c
index 005ea6843..1cb8c595a 100644
--- a/src/exchangedb/perf_deposits_get_ready.c
+++ b/src/exchangedb/perf_deposits_get_ready.c
@@ -68,8 +68,6 @@ static int result;
  */
 static struct TALER_EXCHANGEDB_Plugin *plugin;
 
-static struct TALER_DenomFeeSet fees;
-
 static struct TALER_MerchantWireHashP h_wire_wt;
 
 /**
@@ -203,6 +201,7 @@ run (void *cls)
   unsigned int *perm;
   unsigned long long duration_sq;
   struct TALER_EXCHANGEDB_RefreshRevealedCoin *ccoin;
+  struct TALER_DenomFeeSet fees;
   struct GNUNET_CRYPTO_BlindingInputValues bi = {
     .cipher = GNUNET_CRYPTO_BSA_RSA,
     .rc = 0
diff --git a/src/exchangedb/perf_select_refunds_by_coin.c 
b/src/exchangedb/perf_select_refunds_by_coin.c
index 84825d6d7..6b9592919 100644
--- a/src/exchangedb/perf_select_refunds_by_coin.c
+++ b/src/exchangedb/perf_select_refunds_by_coin.c
@@ -67,7 +67,6 @@ static int result;
  */
 static struct TALER_EXCHANGEDB_Plugin *plugin;
 
-static struct TALER_DenomFeeSet fees;
 
 static struct TALER_MerchantWireHashP h_wire_wt;
 
@@ -231,6 +230,7 @@ run (void *cls)
   unsigned long long duration_sq;
   struct TALER_EXCHANGEDB_RefreshRevealedCoin *ccoin;
   struct TALER_DenominationPublicKey *new_denom_pubs = NULL;
+  struct TALER_DenomFeeSet fees;
   unsigned int count = 0;
 
   ref = GNUNET_new_array (ROUNDS + 1,
diff --git a/src/exchangedb/pg_do_check_deposit_idempotent.c 
b/src/exchangedb/pg_do_check_deposit_idempotent.c
new file mode 100644
index 000000000..8a15200e0
--- /dev/null
+++ b/src/exchangedb/pg_do_check_deposit_idempotent.c
@@ -0,0 +1,112 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2022-2023 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 exchangedb/pg_do_deposit.c
+ * @brief Implementation of the do_deposit function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_do_check_deposit_idempotent.h"
+#include "pg_helper.h"
+#include "pg_compute_shard.h"
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_do_check_deposit_idempotent (
+  void *cls,
+  const struct TALER_EXCHANGEDB_BatchDeposit *bd,
+  struct GNUNET_TIME_Timestamp *exchange_timestamp,
+  bool *is_idempotent)
+{
+  struct PostgresClosure *pg = cls;
+  uint64_t deposit_shard = TEH_PG_compute_shard (&bd->merchant_pub);
+  const struct TALER_CoinSpendPublicKeyP *coin_pubs[GNUNET_NZL (bd->num_cdis)];
+  const struct TALER_CoinSpendSignatureP *coin_sigs[GNUNET_NZL (bd->num_cdis)];
+  struct TALER_Amount amounts_with_fee[GNUNET_NZL (bd->num_cdis)];
+  struct GNUNET_PQ_QueryParam params[] = {
+    /* data for batch_deposits */
+    GNUNET_PQ_query_param_uint64 (&deposit_shard),
+    GNUNET_PQ_query_param_auto_from_type (&bd->merchant_pub),
+    GNUNET_PQ_query_param_timestamp (&bd->wallet_timestamp),
+    GNUNET_PQ_query_param_timestamp (exchange_timestamp),
+    GNUNET_PQ_query_param_timestamp (&bd->refund_deadline),
+    GNUNET_PQ_query_param_timestamp (&bd->wire_deadline),
+    GNUNET_PQ_query_param_auto_from_type (&bd->h_contract_terms),
+    (bd->no_wallet_data_hash)
+    ? GNUNET_PQ_query_param_null ()
+    : GNUNET_PQ_query_param_auto_from_type (&bd->wallet_data_hash),
+    GNUNET_PQ_query_param_auto_from_type (&bd->wire_salt),
+    GNUNET_PQ_query_param_auto_from_type (&bd->wire_target_h_payto),
+    (0 == bd->policy_details_serial_id)
+    ? GNUNET_PQ_query_param_null ()
+    : GNUNET_PQ_query_param_uint64 (&bd->policy_details_serial_id),
+    GNUNET_PQ_query_param_bool (bd->policy_blocked),
+    /* to create entry in wire_targets */
+    GNUNET_PQ_query_param_string (bd->receiver_wire_account),
+    /* arrays for coin_deposits */
+    GNUNET_PQ_query_param_array_ptrs_auto_from_type (bd->num_cdis,
+                                                     coin_pubs,
+                                                     pg->conn),
+    GNUNET_PQ_query_param_array_ptrs_auto_from_type (bd->num_cdis,
+                                                     coin_sigs,
+                                                     pg->conn),
+    TALER_PQ_query_param_array_amount (bd->num_cdis,
+                                       amounts_with_fee,
+                                       pg->conn),
+    GNUNET_PQ_query_param_end
+  };
+  bool no_time;
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_allow_null (
+      GNUNET_PQ_result_spec_timestamp ("exchange_timestamp",
+                                       exchange_timestamp),
+      &no_time),
+    GNUNET_PQ_result_spec_bool ("is_idempotent",
+                                is_idempotent),
+    GNUNET_PQ_result_spec_end
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  for (unsigned int i = 0; i < bd->num_cdis; i++)
+  {
+    const struct TALER_EXCHANGEDB_CoinDepositInformation *cdi
+      = &bd->cdis[i];
+
+    amounts_with_fee[i] = cdi->amount_with_fee;
+    coin_pubs[i] = &cdi->coin.coin_pub;
+    coin_sigs[i] = &cdi->csig;
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Do deposit %u = %s\n",
+                i,
+                TALER_B2S (&cdi->coin.coin_pub));
+  }
+  PREPARE (pg,
+           "call_check_deposit_idempotent",
+           "SELECT "
+           " out_exchange_timestamp AS exchange_timestamp"
+           ",out_is_idempotent AS is_idempotent"
+           " FROM exchange_do_check_deposit_idempotent"
+           " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16);");
+  qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                 
"call_check_deposit_idempotent",
+                                                 params,
+                                                 rs);
+  GNUNET_PQ_cleanup_query_params_closures (params);
+  return qs;
+}
diff --git a/src/exchangedb/pg_do_check_deposit_idempotent.h 
b/src/exchangedb/pg_do_check_deposit_idempotent.h
new file mode 100644
index 000000000..8aa00bba6
--- /dev/null
+++ b/src/exchangedb/pg_do_check_deposit_idempotent.h
@@ -0,0 +1,45 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2024 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 exchangedb/pg_do_check_deposit_idempotent.h
+ * @brief implementation of the do_check_deposit_idempotent function for 
Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_DO_CHECK_DEPOSIT_IDEMPOTENT_H
+#define PG_DO_CHECK_DEPOSIT_IDEMPOTENT_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Check ifdeposit operation is idempotent to existing one.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param bd batch deposit operation details
+ * @param[in,out] exchange_timestamp time to use for the deposit (possibly 
updated)
+ * @param[out] is_idempotent set to true if the request is idempotent
+ * @return query execution status
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_do_check_deposit_idempotent (
+  void *cls,
+  const struct TALER_EXCHANGEDB_BatchDeposit *bd,
+  struct GNUNET_TIME_Timestamp *exchange_timestamp,
+  bool *is_idempotent);
+
+#endif
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c 
b/src/exchangedb/plugin_exchangedb_postgres.c
index 3915e3a46..00c4d519d 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -38,6 +38,7 @@
 #include "pg_delete_aggregation_transient.h"
 #include "pg_get_link_data.h"
 #include "pg_helper.h"
+#include "pg_do_check_deposit_idempotent.h"
 #include "pg_do_reserve_open.h"
 #include "pg_get_coin_transactions.h"
 #include "pg_get_expired_reserves.h"
@@ -744,6 +745,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
     = &TEH_PG_select_aml_decisions;
   plugin->select_deposit_amounts_for_kyc_check
     = &TEH_PG_select_deposit_amounts_for_kyc_check;
+  plugin->do_check_deposit_idempotent
+    = &TEH_PG_do_check_deposit_idempotent;
   plugin->insert_signkey_revocation
     = &TEH_PG_insert_signkey_revocation;
   plugin->select_aml_attributes
diff --git a/src/exchangedb/procedures.sql.in b/src/exchangedb/procedures.sql.in
index 5339bbb7c..b7c29ae7f 100644
--- a/src/exchangedb/procedures.sql.in
+++ b/src/exchangedb/procedures.sql.in
@@ -27,6 +27,7 @@ SET search_path TO exchange;
 #include "exchange_do_batch_withdraw_insert.sql"
 #include "exchange_do_age_withdraw.sql"
 #include "exchange_do_deposit.sql"
+#include "exchange_do_check_deposit_idempotent.sql"
 #include "exchange_do_melt.sql"
 #include "exchange_do_select_deposits_missing_wire.sql"
 #include "exchange_do_select_justification_for_missing_wire.sql"
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index 278994287..0b0e5fb0b 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -4217,6 +4217,23 @@ struct TALER_EXCHANGEDB_Plugin
     bool *ctr_conflict);
 
 
+  /**
+   * Check ifdeposit operation is idempotent to existing one.
+   *
+   * @param cls the `struct PostgresClosure` with the plugin-specific state
+   * @param bd batch deposit operation details
+   * @param[in,out] exchange_timestamp time to use for the deposit (possibly 
updated)
+   * @param[out] is_idempotent set to true if the request is idempotent
+   * @return query execution status
+   */
+  enum GNUNET_DB_QueryStatus
+    (*do_check_deposit_idempotent)(
+    void *cls,
+    const struct TALER_EXCHANGEDB_BatchDeposit *bd,
+    struct GNUNET_TIME_Timestamp *exchange_timestamp,
+    bool *is_idempotent);
+
+
   /**
    * Perform melt operation, checking for sufficient balance
    * of the coin and possibly persisting the melt details.

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]