From 2ec1b055a0f574e3837f2fbbb38098db4eb65f6f Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 18 Jun 2017 21:47:05 +0200 Subject: refactoring /deposit towards new transaction style (#5010) --- src/exchange/taler-exchange-httpd_db.c | 249 ++++++++------------- src/exchange/taler-exchange-httpd_db.h | 61 ++++- src/exchange/taler-exchange-httpd_deposit.c | 215 +++++++++++++++++- src/exchange/taler-exchange-httpd_responses.c | 54 ----- src/exchange/taler-exchange-httpd_responses.h | 29 --- src/exchangedb/perf_taler_exchangedb_interpreter.c | 4 +- src/exchangedb/plugin_exchangedb_postgres.c | 129 +++++------ src/exchangedb/test_exchangedb.c | 8 +- src/include/taler_exchangedb_plugin.h | 14 +- 9 files changed, 422 insertions(+), 341 deletions(-) diff --git a/src/exchange/taler-exchange-httpd_db.c b/src/exchange/taler-exchange-httpd_db.c index abbb6c3fe..2cdf61d8d 100644 --- a/src/exchange/taler-exchange-httpd_db.c +++ b/src/exchange/taler-exchange-httpd_db.c @@ -112,6 +112,79 @@ transaction_start_label: /* we will use goto for retries */ \ } while (0) +/** + * Run a database transaction for @a connection. + * Starts a transaction and calls @a cb. Upon success, + * attempts to commit the transaction. Upon soft failures, + * retries @a cb a few times. Upon hard or persistent soft + * errors, generates an error message for @a connection. + * + * @param connection MHD connection to run @a cb for + * @param[out] set to MHD response code, if transaction failed + * @param cb callback implementing transaction logic + * @param cb_cls closure for @a cb, must be read-only! + * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure + */ +int +TEH_DB_run_transaction (struct MHD_Connection *connection, + int *mhd_ret, + TEH_DB_TransactionCallback cb, + void *cb_cls) +{ + struct TALER_EXCHANGEDB_Session *session; + + *mhd_ret = -1; /* invalid value */ + if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls))) + { + GNUNET_break (0); + *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_DB_SETUP_FAILED); + return GNUNET_SYSERR; + } + for (unsigned int retries = 0;retries < MAX_TRANSACTION_COMMIT_RETRIES; retries++) + { + enum GNUNET_DB_QueryStatus qs; + + if (GNUNET_OK != + TEH_plugin->start (TEH_plugin->cls, + session)) + { + GNUNET_break (0); + *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_DB_START_FAILED); + return GNUNET_SYSERR; + } + qs = cb (cb_cls, + connection, + session, + mhd_ret); + if (0 > qs) + TEH_plugin->rollback (TEH_plugin->cls, + session); + if (GNUNET_DB_STATUS_HARD_ERROR == qs) + return GNUNET_SYSERR; + if (0 <= qs) + qs = TEH_plugin->commit (TEH_plugin->cls, + session); + if (GNUNET_DB_STATUS_HARD_ERROR == qs) + { + *mhd_ret = TEH_RESPONSE_reply_commit_error (connection, + TALER_EC_DB_COMMIT_FAILED_HARD); + return GNUNET_SYSERR; + } + /* make sure callback did not violate invariants! */ + GNUNET_assert (-1 == *mhd_ret); + if (0 <= qs) + return GNUNET_OK; + } + TALER_LOG_WARNING ("Transaction commit failed %u times\n", + MAX_TRANSACTION_COMMIT_RETRIES); + *mhd_ret = TEH_RESPONSE_reply_commit_error (connection, + TALER_EC_DB_COMMIT_FAILED_ON_RETRY); + return GNUNET_SYSERR; +} + + /** * Calculate the total value of all transactions performed. * Stores @a off plus the cost of all transactions in @a tl @@ -119,13 +192,13 @@ transaction_start_label: /* we will use goto for retries */ \ * * @param tl transaction list to process * @param off offset to use as the starting value - * @param ret where the resulting total is to be stored + * @param[out] ret where the resulting total is to be stored * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors */ -static int -calculate_transaction_list_totals (struct TALER_EXCHANGEDB_TransactionList *tl, - const struct TALER_Amount *off, - struct TALER_Amount *ret) +int +TEH_DB_calculate_transaction_list_totals (struct TALER_EXCHANGEDB_TransactionList *tl, + const struct TALER_Amount *off, + struct TALER_Amount *ret) { struct TALER_Amount spent = *off; struct TALER_EXCHANGEDB_TransactionList *pos; @@ -206,146 +279,6 @@ calculate_transaction_list_totals (struct TALER_EXCHANGEDB_TransactionList *tl, } -/** - * Execute a deposit. The validity of the coin and signature - * have already been checked. The database must now check that - * the coin is not (double or over) spent, and execute the - * transaction (record details, generate success or failure response). - * - * @param connection the MHD connection to handle - * @param deposit information about the deposit - * @return MHD result code - */ -int -TEH_DB_execute_deposit (struct MHD_Connection *connection, - const struct TALER_EXCHANGEDB_Deposit *deposit) -{ - struct TALER_EXCHANGEDB_Session *session; - struct TALER_EXCHANGEDB_TransactionList *tl; - struct TALER_Amount spent; - struct TALER_Amount value; - struct TALER_Amount amount_without_fee; - struct TEH_KS_StateHandle *mks; - struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki; - int ret; - enum GNUNET_DB_QueryStatus qs; - unsigned int retries = 0; - - if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls))) - { - GNUNET_break (0); - return TEH_RESPONSE_reply_internal_db_error (connection, - TALER_EC_DB_SETUP_FAILED); - } - again: - if (GNUNET_YES == - TEH_plugin->have_deposit (TEH_plugin->cls, - session, - deposit)) - { - GNUNET_assert (GNUNET_OK == - TALER_amount_subtract (&amount_without_fee, - &deposit->amount_with_fee, - &deposit->deposit_fee)); - return TEH_RESPONSE_reply_deposit_success (connection, - &deposit->coin.coin_pub, - &deposit->h_wire, - &deposit->h_contract_terms, - deposit->timestamp, - deposit->refund_deadline, - &deposit->merchant_pub, - &amount_without_fee); - } - - /* FIXME: move the 'mks'-logic outside of _db.c? */ - mks = TEH_KS_acquire (); - dki = TEH_KS_denomination_key_lookup (mks, - &deposit->coin.denom_pub, - TEH_KS_DKU_DEPOSIT); - if (NULL == dki) - { - TEH_KS_release (mks); - return TEH_RESPONSE_reply_internal_db_error (connection, - TALER_EC_DEPOSIT_DB_DENOMINATION_KEY_UNKNOWN); - } - TALER_amount_ntoh (&value, - &dki->issue.properties.value); - TEH_KS_release (mks); - - START_TRANSACTION (session, connection); - - /* fee for THIS transaction */ - spent = deposit->amount_with_fee; - /* add cost of all previous transactions */ - tl = TEH_plugin->get_coin_transactions (TEH_plugin->cls, - session, - &deposit->coin.coin_pub); - if (GNUNET_OK != - calculate_transaction_list_totals (tl, - &spent, - &spent)) - { - TEH_plugin->rollback (TEH_plugin->cls, - session); - TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, - tl); - return TEH_RESPONSE_reply_internal_db_error (connection, - TALER_EC_DEPOSIT_HISTORY_DB_ERROR); - } - /* Check that cost of all transactions is smaller than - the value of the coin. */ - if (0 < TALER_amount_cmp (&spent, - &value)) - { - TEH_plugin->rollback (TEH_plugin->cls, - session); - ret = TEH_RESPONSE_reply_coin_insufficient_funds (connection, - TALER_EC_DEPOSIT_INSUFFICIENT_FUNDS, - tl); - TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, - tl); - return ret; - } - TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, - tl); - qs = TEH_plugin->insert_deposit (TEH_plugin->cls, - session, - deposit); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - { - TALER_LOG_WARNING ("Failed to store /deposit information in database\n"); - TEH_plugin->rollback (TEH_plugin->cls, - session); - return TEH_RESPONSE_reply_internal_db_error (connection, - TALER_EC_DEPOSIT_STORE_DB_ERROR); - } - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - { - retries++; - TEH_plugin->rollback (TEH_plugin->cls, - session); - if (retries > 5) - return TEH_RESPONSE_reply_internal_db_error (connection, - TALER_EC_DEPOSIT_STORE_DB_ERROR); - goto again; - } - - COMMIT_TRANSACTION(session, connection); - GNUNET_assert (GNUNET_SYSERR != - TALER_amount_subtract (&amount_without_fee, - &deposit->amount_with_fee, - &deposit->deposit_fee)); - return TEH_RESPONSE_reply_deposit_success (connection, - &deposit->coin.coin_pub, - &deposit->h_wire, - &deposit->h_contract_terms, - deposit->timestamp, - deposit->refund_deadline, - &deposit->merchant_pub, - &amount_without_fee); -} - - /** * Execute a "/refund". Returns a confirmation that the refund * was successful, or a failure if we are not aware of a matching @@ -367,6 +300,7 @@ TEH_DB_execute_refund (struct MHD_Connection *connection, struct TEH_KS_StateHandle *mks; struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki; struct TALER_Amount expect_fee; + enum GNUNET_DB_QueryStatus qs; int ret; int deposit_found; int refund_found; @@ -595,10 +529,10 @@ TEH_DB_execute_refund (struct MHD_Connection *connection, tl); /* Finally, store new refund data */ - if (GNUNET_OK != - TEH_plugin->insert_refund (TEH_plugin->cls, - session, - refund)) + qs = TEH_plugin->insert_refund (TEH_plugin->cls, + session, + refund); + if (GNUNET_DB_STATUS_HARD_ERROR == qs) { TALER_LOG_WARNING ("Failed to store /refund information in database\n"); TEH_plugin->rollback (TEH_plugin->cls, @@ -606,6 +540,11 @@ TEH_DB_execute_refund (struct MHD_Connection *connection, return TEH_RESPONSE_reply_internal_db_error (connection, TALER_EC_REFUND_STORE_DB_ERROR); } + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + { + /* FIXME: #5010: retry! */ + } + COMMIT_TRANSACTION (session, connection); return TEH_RESPONSE_reply_refund_success (connection, @@ -1043,9 +982,9 @@ refresh_check_melt (struct MHD_Connection *connection, session, &coin_details->coin_info.coin_pub); if (GNUNET_OK != - calculate_transaction_list_totals (tl, - &spent, - &spent)) + TEH_DB_calculate_transaction_list_totals (tl, + &spent, + &spent)) { GNUNET_break (0); TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, @@ -2403,9 +2342,9 @@ TEH_DB_execute_payback (struct MHD_Connection *connection, TALER_amount_get_zero (value->currency, &spent); if (GNUNET_OK != - calculate_transaction_list_totals (tl, - &spent, - &spent)) + TEH_DB_calculate_transaction_list_totals (tl, + &spent, + &spent)) { GNUNET_break (0); TEH_plugin->rollback (TEH_plugin->cls, diff --git a/src/exchange/taler-exchange-httpd_db.h b/src/exchange/taler-exchange-httpd_db.h index 55faafa06..6d0b7e35a 100644 --- a/src/exchange/taler-exchange-httpd_db.h +++ b/src/exchange/taler-exchange-httpd_db.h @@ -24,20 +24,63 @@ #include #include "taler_exchangedb_plugin.h" +/** + * Function implementing a database transaction. Runs the transaction + * logic; IF it returns a non-error code, the transaction logic MUST + * NOT queue a MHD response. IF it returns an hard error, the + * transaction logic MUST queue a MHD response and set @a mhd_ret. IF + * it returns the soft error code, the function MAY be called again to + * retry and MUST not queue a MHD response. + * + * @param cls closure + * @param connection MHD request which triggered the transaction + * @param session database session to use + * @param[out] mhd_ret set to MHD response status for @a connection, + * if transaction failed (!) + * @return transaction status + */ +typedef enum GNUNET_DB_QueryStatus +(*TEH_DB_TransactionCallback)(void *cls, + struct MHD_Connection *connection, + struct TALER_EXCHANGEDB_Session *session, + int *mhd_ret); + /** - * Execute a "/deposit". The validity of the coin and signature - * have already been checked. The database must now check that - * the coin is not (double or over) spent, and execute the - * transaction (record details, generate success or failure response). + * Run a database transaction for @a connection. + * Starts a transaction and calls @a cb. Upon success, + * attempts to commit the transaction. Upon soft failures, + * retries @a cb a few times. Upon hard or persistent soft + * errors, generates an error message for @a connection. + * + * @param connection MHD connection to run @a cb for + * @param[out] set to MHD response code, if transaction failed + * @param cb callback implementing transaction logic + * @param cb_cls closure for @a cb, must be read-only! + * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure + */ +int +TEH_DB_run_transaction (struct MHD_Connection *connection, + int *mhd_ret, + TEH_DB_TransactionCallback cb, + void *cb_cls); + + +/** + * Calculate the total value of all transactions performed. + * Stores @a off plus the cost of all transactions in @a tl + * in @a ret. * - * @param connection the MHD connection to handle - * @param deposit information about the deposit - * @return MHD result code + * @param tl transaction list to process + * @param off offset to use as the starting value + * @param[out] ret where the resulting total is to be stored + * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors */ +// FIXME: maybe move to another module? int -TEH_DB_execute_deposit (struct MHD_Connection *connection, - const struct TALER_EXCHANGEDB_Deposit *deposit); +TEH_DB_calculate_transaction_list_totals (struct TALER_EXCHANGEDB_TransactionList *tl, + const struct TALER_Amount *off, + struct TALER_Amount *ret); /** diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c index 85504d8df..d732edbb6 100644 --- a/src/exchange/taler-exchange-httpd_deposit.c +++ b/src/exchange/taler-exchange-httpd_deposit.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015, 2016 Inria and GNUnet e.V. + Copyright (C) 2014-2017 Inria and GNUnet e.V. TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -36,6 +36,174 @@ #include "taler-exchange-httpd_validation.h" +/** + * Send confirmation of deposit success to client. This function + * will create a signed message affirming the given information + * and return it to the client. By this, the exchange affirms that + * the coin had sufficient (residual) value for the specified + * transaction and that it will execute the requested deposit + * operation with the given wiring details. + * + * @param connection connection to the client + * @param coin_pub public key of the coin + * @param h_wire hash of wire details + * @param h_contract_terms hash of contract details + * @param timestamp client's timestamp + * @param refund_deadline until when this deposit be refunded + * @param merchant merchant public key + * @param amount_without_fee fraction of coin value to deposit, without the fee + * @return MHD result code + */ +static int +reply_deposit_success (struct MHD_Connection *connection, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct GNUNET_HashCode *h_wire, + const struct GNUNET_HashCode *h_contract_terms, + struct GNUNET_TIME_Absolute timestamp, + struct GNUNET_TIME_Absolute refund_deadline, + const struct TALER_MerchantPublicKeyP *merchant, + const struct TALER_Amount *amount_without_fee) +{ + struct TALER_DepositConfirmationPS dc; + struct TALER_ExchangePublicKeyP pub; + struct TALER_ExchangeSignatureP sig; + + dc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT); + dc.purpose.size = htonl (sizeof (struct TALER_DepositConfirmationPS)); + dc.h_contract_terms = *h_contract_terms; + dc.h_wire = *h_wire; + dc.timestamp = GNUNET_TIME_absolute_hton (timestamp); + dc.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline); + TALER_amount_hton (&dc.amount_without_fee, + amount_without_fee); + dc.coin_pub = *coin_pub; + dc.merchant = *merchant; + TEH_KS_sign (&dc.purpose, + &pub, + &sig); + return TEH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_OK, + "{s:s, s:o, s:o}", + "status", "DEPOSIT_OK", + "sig", GNUNET_JSON_from_data_auto (&sig), + "pub", GNUNET_JSON_from_data_auto (&pub)); +} + + +/** + * Closure for #deposit_transaction. + */ +struct DepositContext +{ + /** + * Information about the deposit request. + */ + const struct TALER_EXCHANGEDB_Deposit *deposit; + + /** + * Value of the coin. + */ + struct TALER_Amount value; + +}; + + +/** + * Execute database transaction for /deposit. Runs the transaction + * logic; IF it returns a non-error code, the transaction logic MUST + * NOT queue a MHD response. IF it returns an hard error, the + * transaction logic MUST queue a MHD response and set @a mhd_ret. IF + * it returns the soft error code, the function MAY be called again to + * retry and MUST not queue a MHD response. + * + * @param cls a `struct DepositContext` + * @param connection MHD request context + * @param session database session and transaction to use + * @param[out] mhd_ret set to MHD status on error + * @return transaction status + */ +static enum GNUNET_DB_QueryStatus +deposit_transaction (void *cls, + struct MHD_Connection *connection, + struct TALER_EXCHANGEDB_Session *session, + int *mhd_ret) +{ + struct DepositContext *dc = cls; + const struct TALER_EXCHANGEDB_Deposit *deposit = dc->deposit; + struct TALER_EXCHANGEDB_TransactionList *tl; + struct TALER_Amount spent; + enum GNUNET_DB_QueryStatus qs; + + qs = TEH_plugin->have_deposit (TEH_plugin->cls, + session, + deposit); + if (qs < 0) + return qs; + if (1 == qs) + { + struct TALER_Amount amount_without_fee; + + GNUNET_assert (GNUNET_OK == + TALER_amount_subtract (&amount_without_fee, + &deposit->amount_with_fee, + &deposit->deposit_fee)); + *mhd_ret = reply_deposit_success (connection, + &deposit->coin.coin_pub, + &deposit->h_wire, + &deposit->h_contract_terms, + deposit->timestamp, + deposit->refund_deadline, + &deposit->merchant_pub, + &amount_without_fee); + /* Treat as 'hard' DB error as we want to rollback and + never try again. */ + return GNUNET_DB_STATUS_HARD_ERROR; + } + + /* Start with fee for THIS transaction */ + spent = deposit->amount_with_fee; + /* add cost of all previous transactions */ + tl = TEH_plugin->get_coin_transactions (TEH_plugin->cls, + session, + &deposit->coin.coin_pub); + if (GNUNET_OK != + TEH_DB_calculate_transaction_list_totals (tl, + &spent, + &spent)) + { + TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, + tl); + *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_DEPOSIT_HISTORY_DB_ERROR); + return GNUNET_DB_STATUS_HARD_ERROR; + } + /* Check that cost of all transactions is smaller than + the value of the coin. */ + if (0 < TALER_amount_cmp (&spent, + &dc->value)) + { + *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (connection, + TALER_EC_DEPOSIT_INSUFFICIENT_FUNDS, + tl); + TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, + tl); + return GNUNET_DB_STATUS_HARD_ERROR; + } + TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, + tl); + qs = TEH_plugin->insert_deposit (TEH_plugin->cls, + session, + deposit); + if (GNUNET_DB_STATUS_HARD_ERROR == qs) + { + TALER_LOG_WARNING ("Failed to store /deposit information in database\n"); + *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_DEPOSIT_STORE_DB_ERROR); + } + return qs; +} + + /** * We have parsed the JSON information about the deposit, do some * basic sanity checks (especially that the signature on the coin is @@ -51,7 +219,13 @@ verify_and_execute_deposit (struct MHD_Connection *connection, const struct TALER_EXCHANGEDB_Deposit *deposit) { struct TALER_DepositRequestPS dr; + int mhd_ret; + struct TALER_Amount amount_without_fee; + struct DepositContext dc; + struct TEH_KS_StateHandle *mks; + struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki; + /* check signature */ dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT); dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS)); dr.h_contract_terms = deposit->h_contract_terms; @@ -76,8 +250,43 @@ verify_and_execute_deposit (struct MHD_Connection *connection, "coin_sig"); } - return TEH_DB_execute_deposit (connection, - deposit); + /* check denomination */ + mks = TEH_KS_acquire (); + dki = TEH_KS_denomination_key_lookup (mks, + &deposit->coin.denom_pub, + TEH_KS_DKU_DEPOSIT); + if (NULL == dki) + { + TEH_KS_release (mks); + return TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_DEPOSIT_DB_DENOMINATION_KEY_UNKNOWN); + } + TALER_amount_ntoh (&dc.value, + &dki->issue.properties.value); + TEH_KS_release (mks); + + /* execute transaction */ + dc.deposit = deposit; + if (GNUNET_OK != + TEH_DB_run_transaction (connection, + &mhd_ret, + &deposit_transaction, + &dc)) + return mhd_ret; + + /* generate regular response */ + GNUNET_assert (GNUNET_SYSERR != + TALER_amount_subtract (&amount_without_fee, + &deposit->amount_with_fee, + &deposit->deposit_fee)); + return reply_deposit_success (connection, + &deposit->coin.coin_pub, + &deposit->h_wire, + &deposit->h_contract_terms, + deposit->timestamp, + deposit->refund_deadline, + &deposit->merchant_pub, + &amount_without_fee); } diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c index 01b5606a5..771b8adfe 100644 --- a/src/exchange/taler-exchange-httpd_responses.c +++ b/src/exchange/taler-exchange-httpd_responses.c @@ -461,60 +461,6 @@ TEH_RESPONSE_reply_invalid_json (struct MHD_Connection *connection) } -/** - * Send confirmation of deposit success to client. This function - * will create a signed message affirming the given information - * and return it to the client. By this, the exchange affirms that - * the coin had sufficient (residual) value for the specified - * transaction and that it will execute the requested deposit - * operation with the given wiring details. - * - * @param connection connection to the client - * @param coin_pub public key of the coin - * @param h_wire hash of wire details - * @param h_contract_terms hash of contract details - * @param timestamp client's timestamp - * @param refund_deadline until when this deposit be refunded - * @param merchant merchant public key - * @param amount_without_fee fraction of coin value to deposit, without the fee - * @return MHD result code - */ -int -TEH_RESPONSE_reply_deposit_success (struct MHD_Connection *connection, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - const struct GNUNET_HashCode *h_wire, - const struct GNUNET_HashCode *h_contract_terms, - struct GNUNET_TIME_Absolute timestamp, - struct GNUNET_TIME_Absolute refund_deadline, - const struct TALER_MerchantPublicKeyP *merchant, - const struct TALER_Amount *amount_without_fee) -{ - struct TALER_DepositConfirmationPS dc; - struct TALER_ExchangePublicKeyP pub; - struct TALER_ExchangeSignatureP sig; - - dc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT); - dc.purpose.size = htonl (sizeof (struct TALER_DepositConfirmationPS)); - dc.h_contract_terms = *h_contract_terms; - dc.h_wire = *h_wire; - dc.timestamp = GNUNET_TIME_absolute_hton (timestamp); - dc.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline); - TALER_amount_hton (&dc.amount_without_fee, - amount_without_fee); - dc.coin_pub = *coin_pub; - dc.merchant = *merchant; - TEH_KS_sign (&dc.purpose, - &pub, - &sig); - return TEH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_OK, - "{s:s, s:o, s:o}", - "status", "DEPOSIT_OK", - "sig", GNUNET_JSON_from_data_auto (&sig), - "pub", GNUNET_JSON_from_data_auto (&pub)); -} - - /** * Compile the transaction history of a coin into a JSON object. * diff --git a/src/exchange/taler-exchange-httpd_responses.h b/src/exchange/taler-exchange-httpd_responses.h index 6a33b65c7..047bb4f37 100644 --- a/src/exchange/taler-exchange-httpd_responses.h +++ b/src/exchange/taler-exchange-httpd_responses.h @@ -243,35 +243,6 @@ int TEH_RESPONSE_reply_invalid_json (struct MHD_Connection *connectionx); -/** - * Send confirmation of deposit success to client. This function - * will create a signed message affirming the given information - * and return it to the client. By this, the exchange affirms that - * the coin had sufficient (residual) value for the specified - * transaction and that it will execute the requested deposit - * operation with the given wiring details. - * - * @param connection connection to the client - * @param coin_pub public key of the coin - * @param h_wire hash of wire details - * @param h_contract_terms hash of proposal data - * @param timestamp client's timestamp - * @param refund_deadline until when this deposit be refunded - * @param merchant merchant public key - * @param amount_without_fee fraction of coin value to deposit (without fee) - * @return MHD result code - */ -int -TEH_RESPONSE_reply_deposit_success (struct MHD_Connection *connection, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - const struct GNUNET_HashCode *h_wire, - const struct GNUNET_HashCode *h_contract_terms, - struct GNUNET_TIME_Absolute timestamp, - struct GNUNET_TIME_Absolute refund_deadline, - const struct TALER_MerchantPublicKeyP *merchant, - const struct TALER_Amount *amount_without_fee); - - /** * Send proof that a request is invalid to client because of * insufficient funds. This function will create a message with all diff --git a/src/exchangedb/perf_taler_exchangedb_interpreter.c b/src/exchangedb/perf_taler_exchangedb_interpreter.c index e378fc932..054df58db 100644 --- a/src/exchangedb/perf_taler_exchangedb_interpreter.c +++ b/src/exchangedb/perf_taler_exchangedb_interpreter.c @@ -1350,7 +1350,7 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state *state) case PERF_TALER_EXCHANGEDB_CMD_GET_DEPOSIT: { unsigned int source_index; - int ret; + enum GNUNET_DB_QueryStatus ret; struct PERF_TALER_EXCHANGEDB_Data *data; source_index = state->cmd[state->i].details.get_deposit.index_deposit; @@ -1358,7 +1358,7 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state *state) ret = state->plugin->have_deposit (state->plugin->cls, state->session, data->data.deposit); - GNUNET_assert (GNUNET_SYSERR != ret); + GNUNET_assert (0 >= ret); } break; diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index d56afed35..954fca389 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -2698,11 +2698,11 @@ postgres_get_reserve_history (void *cls, * @param cls the `struct PostgresClosure` with the plugin-specific state * @param session database connection * @param deposit deposit to search for - * @return #GNUNET_YES if we know this operation, - * #GNUNET_NO if this exact deposit is unknown to us - * #GNUNET_SYSERR on DB error + * @return 1 if we know this operation, + * 0 if this exact deposit is unknown to us, + * otherwise transaction error status */ -static int +static enum GNUNET_DB_QueryStatus postgres_have_deposit (void *cls, struct TALER_EXCHANGEDB_Session *session, const struct TALER_EXCHANGEDB_Deposit *deposit) @@ -2713,75 +2713,52 @@ postgres_have_deposit (void *cls, GNUNET_PQ_query_param_auto_from_type (&deposit->merchant_pub), GNUNET_PQ_query_param_end }; - PGresult *result; - - result = GNUNET_PQ_exec_prepared (session->conn, - "get_deposit", - params); - if (PGRES_TUPLES_OK != - PQresultStatus (result)) - { - BREAK_DB_ERR (result, session->conn); - PQclear (result); - return GNUNET_SYSERR; - } - if (0 == PQntuples (result)) - { - PQclear (result); - return GNUNET_NO; - } + struct TALER_EXCHANGEDB_Deposit deposit2; + struct GNUNET_PQ_ResultSpec rs[] = { + TALER_PQ_result_spec_amount ("amount_with_fee", + &deposit2.amount_with_fee), + GNUNET_PQ_result_spec_absolute_time ("timestamp", + &deposit2.timestamp), + GNUNET_PQ_result_spec_absolute_time ("refund_deadline", + &deposit2.refund_deadline), + GNUNET_PQ_result_spec_absolute_time ("wire_deadline", + &deposit2.wire_deadline), + GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms", + &deposit2.h_contract_terms), + GNUNET_PQ_result_spec_auto_from_type ("h_wire", + &deposit2.h_wire), + GNUNET_PQ_result_spec_end + }; + enum GNUNET_DB_QueryStatus qs; + qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn, + "get_deposit", + params, + rs); + if (0 >= qs) + return qs; /* Now we check that the other information in @a deposit also matches, and if not report inconsistencies. */ - { - struct TALER_EXCHANGEDB_Deposit deposit2; - struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_result_spec_amount ("amount_with_fee", - &deposit2.amount_with_fee), - GNUNET_PQ_result_spec_absolute_time ("timestamp", - &deposit2.timestamp), - GNUNET_PQ_result_spec_absolute_time ("refund_deadline", - &deposit2.refund_deadline), - GNUNET_PQ_result_spec_absolute_time ("wire_deadline", - &deposit2.wire_deadline), - GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms", - &deposit2.h_contract_terms), - GNUNET_PQ_result_spec_auto_from_type ("h_wire", - &deposit2.h_wire), - GNUNET_PQ_result_spec_end - }; - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - 0)) - { - GNUNET_break (0); - PQclear (result); - return GNUNET_SYSERR; - } - if ( (0 != TALER_amount_cmp (&deposit->amount_with_fee, - &deposit2.amount_with_fee)) || - (deposit->timestamp.abs_value_us != - deposit2.timestamp.abs_value_us) || - (deposit->refund_deadline.abs_value_us != - deposit2.refund_deadline.abs_value_us) || - (0 != memcmp (&deposit->h_contract_terms, - &deposit2.h_contract_terms, - sizeof (struct GNUNET_HashCode))) || - (0 != memcmp (&deposit->h_wire, - &deposit2.h_wire, - sizeof (struct GNUNET_HashCode))) ) - { - /* Inconsistencies detected! Does not match! (We might want to - expand the API with a 'get_deposit' function to return the - original transaction details to be used for an error message - in the future!) #3838 */ - PQclear (result); - return GNUNET_NO; - } - } - PQclear (result); - return GNUNET_YES; + if ( (0 != TALER_amount_cmp (&deposit->amount_with_fee, + &deposit2.amount_with_fee)) || + (deposit->timestamp.abs_value_us != + deposit2.timestamp.abs_value_us) || + (deposit->refund_deadline.abs_value_us != + deposit2.refund_deadline.abs_value_us) || + (0 != memcmp (&deposit->h_contract_terms, + &deposit2.h_contract_terms, + sizeof (struct GNUNET_HashCode))) || + (0 != memcmp (&deposit->h_wire, + &deposit2.h_wire, + sizeof (struct GNUNET_HashCode))) ) + { + /* Inconsistencies detected! Does not match! (We might want to + expand the API with a 'get_deposit' function to return the + original transaction details to be used for an error message + in the future!) #3838 */ + return 0; /* Counts as if the transaction was not there */ + } + return 1; } @@ -3267,11 +3244,9 @@ postgres_insert_deposit (void *cls, * @param cls the @e cls of this struct with the plugin-specific state * @param session connection to the database * @param refund refund information to store - * @return #GNUNET_OK on success - * #GNUNET_NO on transient error - * #GNUNET_SYSERR on error + * @return query result status */ -static int +static enum GNUNET_DB_QueryStatus postgres_insert_refund (void *cls, struct TALER_EXCHANGEDB_Session *session, const struct TALER_EXCHANGEDB_Refund *refund) @@ -3289,9 +3264,9 @@ postgres_insert_refund (void *cls, GNUNET_assert (GNUNET_YES == TALER_amount_cmp_currency (&refund->refund_amount, &refund->refund_fee)); - return execute_prepared_non_select (session, - "insert_refund", - params); + return GNUNET_PQ_eval_prepared_non_select (session->conn, + "insert_refund", + params); } diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c index 319b4dc11..ba47c2d3c 100644 --- a/src/exchangedb/test_exchangedb.c +++ b/src/exchangedb/test_exchangedb.c @@ -1772,7 +1772,7 @@ run (void *cls) plugin->insert_deposit (plugin->cls, session, &deposit)); - FAILIF (GNUNET_YES != + FAILIF (1 != plugin->have_deposit (plugin->cls, session, &deposit)); @@ -1839,13 +1839,13 @@ run (void *cls) result = 10; deposit2 = deposit; RND_BLK (&deposit2.merchant_pub); /* should fail if merchant is different */ - FAILIF (GNUNET_NO != + FAILIF (0 != plugin->have_deposit (plugin->cls, session, &deposit2)); deposit2.merchant_pub = deposit.merchant_pub; RND_BLK (&deposit2.coin.coin_pub); /* should fail if coin is different */ - FAILIF (GNUNET_NO != + FAILIF (0 != plugin->have_deposit (plugin->cls, session, &deposit2)); @@ -1860,7 +1860,7 @@ run (void *cls) refund.rtransaction_id = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); refund.refund_amount = deposit.amount_with_fee; refund.refund_fee = fee_refund; - FAILIF (GNUNET_OK != + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != plugin->insert_refund (plugin->cls, session, &refund)); diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index b5eaeea72..2cf553b17 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -1293,11 +1293,11 @@ struct TALER_EXCHANGEDB_Plugin * @param cls the @e cls of this struct with the plugin-specific state * @param session database connection * @param deposit deposit to search for - * @return #GNUNET_YES if we know this operation, - * #GNUNET_NO if this exact deposit is unknown to us, - * #GNUNET_SYSERR on DB error + * @return 1 if we know this operation, + * 0 if this exact deposit is unknown to us, + * otherwise transaction error status */ - int + enum GNUNET_DB_QueryStatus (*have_deposit) (void *cls, struct TALER_EXCHANGEDB_Session *session, const struct TALER_EXCHANGEDB_Deposit *deposit); @@ -1323,11 +1323,9 @@ struct TALER_EXCHANGEDB_Plugin * @param cls the @e cls of this struct with the plugin-specific state * @param session connection to the database * @param refund refund information to store - * @return #GNUNET_OK on success, - * #GNUNET_NO on transient error - * #GNUNET_SYSERR on error + * @return query result status */ - int + enum GNUNET_DB_QueryStatus (*insert_refund) (void *cls, struct TALER_EXCHANGEDB_Session *session, const struct TALER_EXCHANGEDB_Refund *refund); -- cgit v1.2.3