diff options
Diffstat (limited to 'src/backenddb/pg_insert_transfer_details.c')
-rw-r--r-- | src/backenddb/pg_insert_transfer_details.c | 290 |
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; +} |