aboutsummaryrefslogtreecommitdiff
path: root/src/backenddb/pg_insert_transfer_details.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backenddb/pg_insert_transfer_details.c')
-rw-r--r--src/backenddb/pg_insert_transfer_details.c290
1 files changed, 290 insertions, 0 deletions
diff --git a/src/backenddb/pg_insert_transfer_details.c b/src/backenddb/pg_insert_transfer_details.c
new file mode 100644
index 00000000..d79dbe90
--- /dev/null
+++ b/src/backenddb/pg_insert_transfer_details.c
@@ -0,0 +1,290 @@
+/*
+ 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 backenddb/pg_insert_transfer_details.c
+ * @brief Implementation of the insert_transfer_details function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_insert_transfer_details.h"
+#include "pg_helper.h"
+
+
+/**
+ * How often do we re-try if we run into a DB serialization error?
+ */
+#define MAX_RETRIES 3
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_insert_transfer_details (
+ void *cls,
+ const char *instance_id,
+ const char *exchange_url,
+ const char *payto_uri,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ const struct TALER_EXCHANGE_TransferData *td)
+{
+ struct PostgresClosure *pg = cls;
+ enum GNUNET_DB_QueryStatus qs;
+ uint64_t credit_serial;
+ unsigned int retries;
+
+ retries = 0;
+ check_connection (pg);
+
+ PREPARE (pg,
+ "lookup_credit_serial",
+ "SELECT"
+ " credit_serial"
+ " FROM merchant_transfers"
+ " WHERE exchange_url=$1"
+ " AND wtid=$4"
+ " AND account_serial="
+ " (SELECT account_serial"
+ " FROM merchant_accounts"
+ " WHERE payto_uri=$2"
+ " AND exchange_url=$1"
+ " AND merchant_serial="
+ " (SELECT merchant_serial"
+ " FROM merchant_instances"
+ " WHERE merchant_id=$3))");
+ PREPARE (pg,
+ "insert_transfer_signature",
+ "INSERT INTO merchant_transfer_signatures"
+ "(credit_serial"
+ ",signkey_serial"
+ ",credit_amount"
+ ",wire_fee"
+ ",execution_time"
+ ",exchange_sig) "
+ "SELECT $1, signkey_serial, $2, $3, $4, $5"
+ " FROM merchant_exchange_signing_keys"
+ " WHERE exchange_pub=$6"
+ " ORDER BY start_date DESC"
+ " LIMIT 1");
+ PREPARE (pg,
+ "insert_transfer_to_coin_mapping",
+ "INSERT INTO merchant_transfer_to_coin"
+ "(deposit_serial"
+ ",credit_serial"
+ ",offset_in_exchange_list"
+ ",exchange_deposit_value"
+ ",exchange_deposit_fee) "
+ "SELECT deposit_serial, $1, $2, $3, $4"
+ " FROM merchant_deposits"
+ " JOIN merchant_contract_terms USING (order_serial)"
+ " WHERE coin_pub=$5"
+ " AND h_contract_terms=$6"
+ " AND merchant_serial="
+ " (SELECT merchant_serial"
+ " FROM merchant_instances"
+ " WHERE merchant_id=$7)");
+ PREPARE (pg,
+ "update_wired_by_coin_pub",
+ "WITH os AS" /* select orders affected by the coin */
+ "(SELECT order_serial"
+ " FROM merchant_deposits"
+ " WHERE coin_pub=$1)"
+ "UPDATE merchant_contract_terms "
+ " SET wired=TRUE "
+ " WHERE order_serial IN "
+ " (SELECT order_serial FROM merchant_deposits" /* only orders for which NO un-wired coin exists*/
+ " WHERE NOT EXISTS "
+ " (SELECT order_serial FROM merchant_deposits" /* orders for which ANY un-wired coin exists */
+ " JOIN os USING (order_serial)" /* filter early */
+ " WHERE deposit_serial NOT IN"
+ " (SELECT deposit_serial " /* all coins associated with order that WERE wired */
+ " FROM merchant_deposits "
+ " JOIN os USING (order_serial)" /* filter early */
+ " JOIN merchant_deposit_to_transfer USING (deposit_serial)"
+ " JOIN merchant_transfers USING (credit_serial)"
+ " WHERE confirmed=TRUE)))");
+
+RETRY:
+ if (MAX_RETRIES < ++retries)
+ return GNUNET_DB_STATUS_SOFT_ERROR;
+ if (GNUNET_OK !=
+ TMH_PG_start_read_committed (pg,
+ "insert transfer details"))
+ {
+ GNUNET_break (0);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+
+ /* lookup credit serial */
+ {
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_string (exchange_url),
+ GNUNET_PQ_query_param_string (payto_uri),
+ GNUNET_PQ_query_param_string (instance_id),
+ GNUNET_PQ_query_param_auto_from_type (wtid),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("credit_serial",
+ &credit_serial),
+ GNUNET_PQ_result_spec_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "lookup_credit_serial",
+ params,
+ rs);
+ if (0 > qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ TMH_PG_rollback (pg);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ goto RETRY;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "'lookup_credit_serial' for account %s and amount %s failed with status %d\n",
+ payto_uri,
+ TALER_amount2s (&td->total_amount),
+ qs);
+ return qs;
+ }
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ {
+ TMH_PG_rollback (pg);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "'lookup_credit_serial' for account %s failed with transfer unknown\n",
+ payto_uri);
+ return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+ }
+ }
+
+ /* update merchant_transfer_signatures table */
+ {
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint64 (&credit_serial),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ &td->total_amount),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ &td->wire_fee),
+ GNUNET_PQ_query_param_timestamp (&td->execution_time),
+ GNUNET_PQ_query_param_auto_from_type (&td->exchange_sig),
+ GNUNET_PQ_query_param_auto_from_type (&td->exchange_pub),
+ GNUNET_PQ_query_param_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_transfer_signature",
+ params);
+ if (0 > qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ TMH_PG_rollback (pg);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ goto RETRY;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "'insert_transfer_signature' failed with status %d\n",
+ qs);
+ return qs;
+ }
+ if (0 == qs)
+ {
+ TMH_PG_rollback (pg);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "'insert_transfer_signature' failed with status %d\n",
+ qs);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ }
+
+ /* Update transfer-coin association table */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Updating transfer-coin association table\n");
+ for (unsigned int i = 0; i<td->details_length; i++)
+ {
+ const struct TALER_TrackTransferDetails *d = &td->details[i];
+ uint64_t i64 = (uint64_t) i;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint64 (&credit_serial),
+ GNUNET_PQ_query_param_uint64 (&i64),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ &d->coin_value),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ &d->coin_fee), /* deposit fee */
+ GNUNET_PQ_query_param_auto_from_type (&d->coin_pub),
+ GNUNET_PQ_query_param_auto_from_type (&d->h_contract_terms),
+ GNUNET_PQ_query_param_string (instance_id),
+ GNUNET_PQ_query_param_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_transfer_to_coin_mapping",
+ params);
+ if (0 > qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ TMH_PG_rollback (pg);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ goto RETRY;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "'insert_transfer_to_coin_mapping' failed with status %d\n",
+ qs);
+ return qs;
+ }
+ if (0 == qs)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "'insert_transfer_to_coin_mapping' failed at %u: deposit unknown\n",
+ i);
+ }
+ }
+ /* Update merchant_contract_terms 'wired' status: for all coins
+ that were wired, set the respective order's "wired" status to
+ true, *if* all other deposited coins associated with that order
+ have also been wired (this time or earlier) */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Updating contract terms 'wired' status\n");
+ for (unsigned int i = 0; i<td->details_length; i++)
+ {
+ const struct TALER_TrackTransferDetails *d = &td->details[i];
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (&d->coin_pub),
+ GNUNET_PQ_query_param_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "update_wired_by_coin_pub",
+ params);
+ if (0 > qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ TMH_PG_rollback (pg);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ goto RETRY;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "'update_wired_by_coin_pub' failed with status %d\n",
+ qs);
+ return qs;
+ }
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Committing transaction...\n");
+ qs = TMH_PG_commit (pg);
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ goto RETRY;
+ return qs;
+}