From 21d26132877990d7beac118b33e336aaed395234 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 29 Mar 2017 16:42:38 +0200 Subject: first sketch at db module implementation for #3887 --- src/exchange/taler-exchange-httpd_db.c | 115 ++++++++++++++++++++++++-- src/exchange/taler-exchange-httpd_db.h | 6 +- src/exchange/taler-exchange-httpd_payback.c | 31 ++++++- src/exchange/taler-exchange-httpd_responses.c | 65 +++++++++++++-- src/exchange/taler-exchange-httpd_responses.h | 43 ++++++++-- src/include/taler_error_codes.h | 65 ++++++++++++--- 6 files changed, 290 insertions(+), 35 deletions(-) (limited to 'src') diff --git a/src/exchange/taler-exchange-httpd_db.c b/src/exchange/taler-exchange-httpd_db.c index cac700ab8..fe92d76bb 100644 --- a/src/exchange/taler-exchange-httpd_db.c +++ b/src/exchange/taler-exchange-httpd_db.c @@ -242,6 +242,8 @@ TEH_DB_execute_deposit (struct MHD_Connection *connection, &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, @@ -250,7 +252,7 @@ TEH_DB_execute_deposit (struct MHD_Connection *connection, { TEH_KS_release (mks); return TEH_RESPONSE_reply_internal_db_error (connection, - TALER_EC_DEPOSIT_DB_DENOMINATION_KEY_UNKNOWN); + TALER_EC_DEPOSIT_DB_DENOMINATION_KEY_UNKNOWN); } TALER_amount_ntoh (&value, &dki->issue.properties.value); @@ -283,8 +285,9 @@ TEH_DB_execute_deposit (struct MHD_Connection *connection, { TEH_plugin->rollback (TEH_plugin->cls, session); - ret = TEH_RESPONSE_reply_deposit_insufficient_funds (connection, - tl); + 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; @@ -2264,18 +2267,114 @@ TEH_DB_execute_track_transaction (struct MHD_Connection *connection, * * @param connection the MHD connection to handle * @param coin information about the coin - * @param coin_bks blinding data of the coin (to be checked) - * @param coin_sig signature of the coin + * @param value how much are coins of the @a coin's denomination worth? + * @param h_blind blinded coin to use for the lookup + * @param coin_sig signature of the coin (to be stored) * @return MHD result code */ int TEH_DB_execute_payback (struct MHD_Connection *connection, const struct TALER_CoinPublicInfo *coin, - const struct TALER_DenominationBlindingKeyP *coin_bks, + const struct TALER_Amount *value, + const struct GNUNET_HashCode *h_blind, const struct TALER_CoinSpendSignatureP *coin_sig) { - GNUNET_break (0); /* not implemented (#3887) */ - return MHD_NO; + int ret; + struct TALER_EXCHANGEDB_Session *session; + struct TALER_EXCHANGEDB_TransactionList *tl; + struct TALER_EXCHANGEDB_CollectableBlindcoin collectable; + char wire_subject[42]; // FIXME: size? (#3887) + struct TALER_Amount amount; + struct TALER_Amount spent; + struct GNUNET_TIME_Absolute payback_deadline; + + 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); + } + + START_TRANSACTION (session, connection); + + /* FIXME (#3887): not _exactly_ the right call, we need to get the + reserve's incoming wire transfer data, not 'collectable' */ + ret = TEH_plugin->get_withdraw_info (TEH_plugin->cls, + session, + h_blind, + &collectable); + if (GNUNET_SYSERR == ret) + { + GNUNET_break (0); + TEH_plugin->rollback (TEH_plugin->cls, + session); + return TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_PAYBACK_DB_FETCH_FAILED); + } + if (GNUNET_NO == ret) + { + GNUNET_break_op (0); + TEH_plugin->rollback (TEH_plugin->cls, + session); + return TEH_RESPONSE_reply_payback_unknown (connection, + TALER_EC_PAYBACK_WITHDRAW_NOT_FOUND); + } + + /* Calculate remaining balance. */ + tl = TEH_plugin->get_coin_transactions (TEH_plugin->cls, + session, + &coin->coin_pub); + TALER_amount_get_zero (value->currency, + &spent); + if (GNUNET_OK != + calculate_transaction_list_totals (tl, + &spent, + &spent)) + { + GNUNET_break (0); + 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_PAYBACK_HISTORY_DB_ERROR); + } + TALER_amount_subtract (&amount, + value, + &spent); + if ( (0 == amount.fraction) && + (0 == amount.value) ) + { + GNUNET_break_op (0); + TEH_plugin->rollback (TEH_plugin->cls, + session); + ret = TEH_RESPONSE_reply_coin_insufficient_funds (connection, + TALER_EC_PAYBACK_COIN_BALANCE_ZERO, + tl); + TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, + tl); + return ret; + } + TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, + tl); + + /* FIXME: add coin to list of wire transfers for payback */ + // ret = TEH_plugin->(); // #3887 + if (GNUNET_SYSERR == ret) + { + TALER_LOG_WARNING ("Failed to store /payback information in database\n"); + TEH_plugin->rollback (TEH_plugin->cls, + session); + return TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_PAYBACK_DB_PUT_FAILED); + } + + COMMIT_TRANSACTION(session, connection); + + return TEH_RESPONSE_reply_payback_success (connection, + wire_subject, + &amount, + payback_deadline); } diff --git a/src/exchange/taler-exchange-httpd_db.h b/src/exchange/taler-exchange-httpd_db.h index 67c8665f2..520a6f59e 100644 --- a/src/exchange/taler-exchange-httpd_db.h +++ b/src/exchange/taler-exchange-httpd_db.h @@ -244,14 +244,16 @@ TEH_DB_execute_track_transaction (struct MHD_Connection *connection, * * @param connection the MHD connection to handle * @param coin information about the coin - * @param coin_bks blinding data of the coin (to be checked) + * @param value how much are coins of the @a coin's denomination worth? + * @param h_blind blinded coin to use for the lookup * @param coin_sig signature of the coin * @return MHD result code */ int TEH_DB_execute_payback (struct MHD_Connection *connection, const struct TALER_CoinPublicInfo *coin, - const struct TALER_DenominationBlindingKeyP *coin_bks, + const struct TALER_Amount *value, + const struct GNUNET_HashCode *h_blind, const struct TALER_CoinSpendSignatureP *coin_sig); diff --git a/src/exchange/taler-exchange-httpd_payback.c b/src/exchange/taler-exchange-httpd_payback.c index 2b33112d3..31235729e 100644 --- a/src/exchange/taler-exchange-httpd_payback.c +++ b/src/exchange/taler-exchange-httpd_payback.c @@ -56,7 +56,11 @@ verify_and_execute_payback (struct MHD_Connection *connection, struct TEH_KS_StateHandle *key_state; const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki; struct TALER_PaybackRequestPS pr; - + struct TALER_Amount value; + struct GNUNET_HashCode h_blind; + struct GNUNET_HashCode c_hash; + char *coin_ev; + size_t coin_ev_size; /* check denomination exists and is in payback mode */ key_state = TEH_KS_acquire (); @@ -71,6 +75,8 @@ verify_and_execute_payback (struct MHD_Connection *connection, TALER_EC_PAYBACK_DENOMINATION_KEY_UNKNOWN, "denom_pub"); } + TALER_amount_ntoh (&value, + &dki->issue.properties.value); /* check denomination signature */ if (GNUNET_YES != @@ -104,9 +110,30 @@ verify_and_execute_payback (struct MHD_Connection *connection, "coin_sig"); } + GNUNET_CRYPTO_hash (&coin->coin_pub.eddsa_pub, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey), + &c_hash); + if (GNUNET_YES != + GNUNET_CRYPTO_rsa_blind (&c_hash, + &coin_bks->bks, + coin->denom_pub.rsa_public_key, + &coin_ev, + &coin_ev_size)) + { + GNUNET_break (0); + return TEH_RESPONSE_reply_internal_error (connection, + TALER_EC_PAYBACK_BLINDING_FAILED, + "coin_bks"); + } + GNUNET_CRYPTO_hash (coin_ev, + coin_ev_size, + &h_blind); + GNUNET_free (coin_ev); + return TEH_DB_execute_payback (connection, coin, - coin_bks, + &value, + &h_blind, coin_sig); } diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c index 1caef3469..c78462532 100644 --- a/src/exchange/taler-exchange-httpd_responses.c +++ b/src/exchange/taler-exchange-httpd_responses.c @@ -436,6 +436,11 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl) const struct TALER_EXCHANGEDB_TransactionList *pos; history = json_array (); + if (NULL == history) + { + GNUNET_break (0); /* out of memory!? */ + return NULL; + } for (pos = tl; NULL != pos; pos = pos->next) { switch (pos->type) @@ -562,29 +567,33 @@ compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl) /** - * Send proof that a /deposit request is invalid to client. This - * function will create a message with all of the operations affecting - * the coin that demonstrate that the coin has insufficient value. + * Send proof that a request is invalid to client because of + * insufficient funds. This function will create a message with all + * of the operations affecting the coin that demonstrate that the coin + * has insufficient value. * * @param connection connection to the client + * @param ec error code to return * @param tl transaction list to use to build reply * @return MHD result code */ int -TEH_RESPONSE_reply_deposit_insufficient_funds (struct MHD_Connection *connection, - const struct TALER_EXCHANGEDB_TransactionList *tl) +TEH_RESPONSE_reply_coin_insufficient_funds (struct MHD_Connection *connection, + enum TALER_ErrorCode ec, + const struct TALER_EXCHANGEDB_TransactionList *tl) { json_t *history; history = compile_transaction_history (tl); if (NULL == history) - return TEH_RESPONSE_reply_internal_db_error (connection, - TALER_EC_DEPOSIT_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS); + return TEH_RESPONSE_reply_internal_error (connection, + TALER_EC_COIN_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS, + "failed to convert transaction history to JSON"); return TEH_RESPONSE_reply_json_pack (connection, MHD_HTTP_FORBIDDEN, "{s:s, s:I, s:o}", "error", "insufficient funds", - "code", (json_int_t) TALER_EC_DEPOSIT_INSUFFICIENT_FUNDS, + "code", (json_int_t) ec, "history", history); } @@ -1286,4 +1295,44 @@ TEH_RESPONSE_reply_track_transfer_details (struct MHD_Connection *connection, } + +/** + * A wallet asked for /payback, but we do not know anything + * about the original withdraw operation given. Generates a + * 404 reply. + * + * @param connection connection to the client + * @param ec Taler error code + * @return MHD result code + */ +int +TEH_RESPONSE_reply_payback_unknown (struct MHD_Connection *connection, + enum TALER_ErrorCode ec) +{ + GNUNET_break (0); /* #3887 */ + return MHD_NO; +} + + +/** + * A wallet asked for /payback, return the successful response. + * + * @param connection connection to the client + * @param wire_subject the wire subject we will use for the pay back operation + * @param amount the amount we will wire back + * @param payback_deadline deadline by which the exchange promises to pay + * @return MHD result code + */ +int +TEH_RESPONSE_reply_payback_success (struct MHD_Connection *connection, + const char *wire_subject, + const struct TALER_Amount *amount, + struct GNUNET_TIME_Absolute payback_deadline) +{ + GNUNET_break (0); /* #3887 */ + return MHD_NO; +} + + + /* end of taler-exchange-httpd_responses.c */ diff --git a/src/exchange/taler-exchange-httpd_responses.h b/src/exchange/taler-exchange-httpd_responses.h index 179ae0066..27b20d353 100644 --- a/src/exchange/taler-exchange-httpd_responses.h +++ b/src/exchange/taler-exchange-httpd_responses.h @@ -250,17 +250,20 @@ TEH_RESPONSE_reply_deposit_success (struct MHD_Connection *connection, /** - * Send proof that a /deposit request is invalid to client. This - * function will create a message with all of the operations affecting - * the coin that demonstrate that the coin has insufficient value. + * Send proof that a request is invalid to client because of + * insufficient funds. This function will create a message with all + * of the operations affecting the coin that demonstrate that the coin + * has insufficient value. * * @param connection connection to the client + * @param ec error code to return * @param tl transaction list to use to build reply * @return MHD result code */ int -TEH_RESPONSE_reply_deposit_insufficient_funds (struct MHD_Connection *connection, - const struct TALER_EXCHANGEDB_TransactionList *tl); +TEH_RESPONSE_reply_coin_insufficient_funds (struct MHD_Connection *connection, + enum TALER_ErrorCode ec, + const struct TALER_EXCHANGEDB_TransactionList *tl); /** @@ -556,4 +559,34 @@ TEH_RESPONSE_reply_refresh_link_success (struct MHD_Connection *connection, const struct TEH_RESPONSE_LinkSessionInfo *sessions); +/** + * A wallet asked for /payback, but we do not know anything + * about the original withdraw operation given. Generates a + * 404 reply. + * + * @param connection connection to the client + * @param ec Taler error code + * @return MHD result code + */ +int +TEH_RESPONSE_reply_payback_unknown (struct MHD_Connection *connection, + enum TALER_ErrorCode ec); + + +/** + * A wallet asked for /payback, return the successful response. + * + * @param connection connection to the client + * @param wire_subject the wire subject we will use for the pay back operation + * @param amount the amount we will wire back + * @param payback_deadline deadline by which the exchange promises to pay + * @return MHD result code + */ +int +TEH_RESPONSE_reply_payback_success (struct MHD_Connection *connection, + const char *wire_subject, + const struct TALER_Amount *amount, + struct GNUNET_TIME_Absolute payback_deadline); + + #endif diff --git a/src/include/taler_error_codes.h b/src/include/taler_error_codes.h index 3c48a6d44..84240c295 100644 --- a/src/include/taler_error_codes.h +++ b/src/include/taler_error_codes.h @@ -128,6 +128,17 @@ enum TALER_ErrorCode */ TALER_EC_PARAMETER_MALFORMED = 1009, + /** + * The exchange failed to obtain the transaction history of the + * given coin from the database while generating an insufficient + * funds errors. This can happen during /deposit or /payback requests. + * This response is provided with HTTP status code + * MHD_HTTP_INTERNAL_SERVER_ERROR. + */ + TALER_EC_COIN_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS = 1010, + + + /* ********** request-specific error codes ************* */ /** @@ -230,7 +241,7 @@ enum TALER_ErrorCode /** * The exchange failed to obtain the transaction history of the * given reserve from the database while generating an insufficient - * funds errors. + * funds error. * This response is provided with HTTP status code * MHD_HTTP_INTERNAL_SERVER_ERROR. */ @@ -349,15 +360,6 @@ enum TALER_ErrorCode */ TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_CONTRACT_HASH_CONFLICT = 1211, - /** - * The exchange failed to obtain the transaction history of the - * given coin from the database while generating an insufficient - * funds errors. - * This response is provided with HTTP status code - * MHD_HTTP_INTERNAL_SERVER_ERROR. - */ - TALER_EC_DEPOSIT_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS = 1212, - /** * The exchange detected that the given account number * is invalid for the selected wire format type. @@ -852,6 +854,49 @@ enum TALER_ErrorCode */ TALER_EC_PAYBACK_DENOMINATION_SIGNATURE_INVALID = 1852, + /** + * The exchange failed to access its own database about reserves. + * This response is provided with HTTP status code + * MHD_HTTP_INTERNAL_SERVER_ERROR. + */ + TALER_EC_PAYBACK_DB_FETCH_FAILED = 1853, + + /** + * The exchange could not find the corresponding withdraw operation. + * The request is denied. This response is provided with an HTTP + * status code of MHD_HTTP_NOT_FOUND. + */ + TALER_EC_PAYBACK_WITHDRAW_NOT_FOUND = 1854, + + /** + * The exchange obtained an internally inconsistent transaction + * history for the given coin. This response is provided with HTTP + * status code MHD_HTTP_INTERNAL_SERVER_ERROR. + */ + TALER_EC_PAYBACK_HISTORY_DB_ERROR = 1855, + + /** + * The exchange failed to store information about the payback to be + * performed in the database. This response is provided with HTTP + * status code MHD_HTTP_INTERNAL_SERVER_ERROR. + */ + TALER_EC_PAYBACK_DB_PUT_FAILED = 1856, + + /** + * The coin's remaining balance is zero. The request is denied. + * This response is provided with an HTTP status code of + * MHD_HTTP_FORBIDDEN. + */ + TALER_EC_PAYBACK_COIN_BALANCE_ZERO = 1857, + + /** + * The exchange failed to reproduce the coin's blinding. + * This response is provided with an HTTP status code of + * MHD_HTTP_INTERNAL_SERVER_ERROR. + */ + TALER_EC_PAYBACK_BLINDING_FAILED = 1858, + + /* *********** Merchant backend error codes ********* */ -- cgit v1.2.3