diff options
Diffstat (limited to 'src/exchange')
-rw-r--r-- | src/exchange/taler-exchange-httpd_db.c | 143 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_payback.c | 276 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_responses.c | 63 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_responses.h | 31 |
4 files changed, 266 insertions, 247 deletions
diff --git a/src/exchange/taler-exchange-httpd_db.c b/src/exchange/taler-exchange-httpd_db.c index 9e95ff40a..bfe2112c4 100644 --- a/src/exchange/taler-exchange-httpd_db.c +++ b/src/exchange/taler-exchange-httpd_db.c @@ -1622,147 +1622,4 @@ TEH_DB_execute_track_transaction (struct MHD_Connection *connection, } -/** - * Execute a "/payback". The validity of the coin and signature have - * already been checked. The database must now check that the coin is - * not (double) spent, and execute the transaction (record details, - * generate success or failure response). - * - * @param connection the MHD connection to handle - * @param coin information about 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_blind blinding factor used (for later verification by the auditor) - * @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_Amount *value, - const struct GNUNET_HashCode *h_blind, - const struct TALER_DenominationBlindingKeyP *coin_blind, - const struct TALER_CoinSpendSignatureP *coin_sig) -{ - int ret; - struct TALER_EXCHANGEDB_Session *session; - struct TALER_EXCHANGEDB_TransactionList *tl; - struct TALER_ReservePublicKeyP reserve_pub; - struct TALER_Amount amount; - struct TALER_Amount spent; - struct GNUNET_TIME_Absolute now; - enum GNUNET_DB_QueryStatus qs; - - 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); - - /* Check whether a payback is allowed, and if so, to which - reserve / account the money should go */ - ret = TEH_plugin->get_reserve_by_h_blind (TEH_plugin->cls, - session, - h_blind, - &reserve_pub); - 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. */ - qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls, - session, - &coin->coin_pub, - &tl); - (void) qs; /* FIXME #5010 */ - TALER_amount_get_zero (value->currency, - &spent); - if (GNUNET_OK != - TEH_DB_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); - } - if (GNUNET_SYSERR == - TALER_amount_subtract (&amount, - value, - &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_COIN_BALANCE_NEGATIVE); - } - if ( (0 == amount.fraction) && - (0 == amount.value) ) - { - 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); - now = GNUNET_TIME_absolute_get (); - (void) GNUNET_TIME_round_abs (&now); - - /* add coin to list of wire transfers for payback */ - ret = TEH_plugin->insert_payback_request (TEH_plugin->cls, - session, - &reserve_pub, - coin, - coin_sig, - coin_blind, - &amount, - h_blind, - now); - 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, - &coin->coin_pub, - &reserve_pub, - &amount, - now); -} - - /* end of taler-exchange-httpd_db.c */ diff --git a/src/exchange/taler-exchange-httpd_payback.c b/src/exchange/taler-exchange-httpd_payback.c index 7c5230d50..8b4051cb7 100644 --- a/src/exchange/taler-exchange-httpd_payback.c +++ b/src/exchange/taler-exchange-httpd_payback.c @@ -35,6 +35,253 @@ /** + * A wallet asked for /payback, but we do not know anything about the + * original withdraw operation specified. Generates a 404 reply. + * + * @param connection connection to the client + * @param ec Taler error code + * @return MHD result code + */ +static int +reply_payback_unknown (struct MHD_Connection *connection, + enum TALER_ErrorCode ec) +{ + return TEH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_NOT_FOUND, + "{s:s, s:I}", + "error", "blinded coin unknown", + "code", (json_int_t) ec); +} + + +/** + * A wallet asked for /payback, return the successful response. + * + * @param connection connection to the client + * @param coin_pub coin for which we are processing the payback request + * @param reserve_pub public key of the reserve that will receive the payback + * @param amount the amount we will wire back + * @param timestamp when did the exchange receive the /payback request + * @return MHD result code + */ +static int +reply_payback_success (struct MHD_Connection *connection, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_ReservePublicKeyP *reserve_pub, + const struct TALER_Amount *amount, + struct GNUNET_TIME_Absolute timestamp) +{ + struct TALER_PaybackConfirmationPS pc; + struct TALER_ExchangePublicKeyP pub; + struct TALER_ExchangeSignatureP sig; + + pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK); + pc.purpose.size = htonl (sizeof (struct TALER_PaybackConfirmationPS)); + pc.timestamp = GNUNET_TIME_absolute_hton (timestamp); + TALER_amount_hton (&pc.payback_amount, + amount); + pc.coin_pub = *coin_pub; + pc.reserve_pub = *reserve_pub; + TEH_KS_sign (&pc.purpose, + &pub, + &sig); + return TEH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_OK, + "{s:o, s:o, s:o, s:o, s:o}", + "reserve_pub", GNUNET_JSON_from_data_auto (reserve_pub), + "timestamp", GNUNET_JSON_from_time_abs (timestamp), + "amount", TALER_JSON_from_amount (amount), + "exchange_sig", GNUNET_JSON_from_data_auto (&sig), + "exchange_pub", GNUNET_JSON_from_data_auto (&pub)); +} + + +/** + * Closure for #payback_transaction. + */ +struct PaybackContext +{ + /** + * Hash of the blinded coin. + */ + struct GNUNET_HashCode h_blind; + + /** + * Full value of the coin. + */ + struct TALER_Amount value; + + /** + * Details about the coin. + */ + const struct TALER_CoinPublicInfo *coin; + + /** + * Key used to blind the coin. + */ + const struct TALER_DenominationBlindingKeyP *coin_bks; + + /** + * Signature of the coin requesting payback. + */ + const struct TALER_CoinSpendSignatureP *coin_sig; + + /** + * Set by #payback_transaction() to the reserve that will + * receive the payback. + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * Set by #payback_transaction() to the amount that will be paid back + */ + struct TALER_Amount amount; + + /** + * Set by #payback_transaction to the timestamp when the payback + * was accepted. + */ + struct GNUNET_TIME_Absolute now; + +}; + + +/** + * Execute a "/payback". The validity of the coin and signature have + * already been checked. The database must now check that the coin is + * not (double) spent, and execute the transaction. + * + * 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 the `struct PaybackContext *` + * @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 code + */ +static enum GNUNET_DB_QueryStatus +payback_transaction (void *cls, + struct MHD_Connection *connection, + struct TALER_EXCHANGEDB_Session *session, + int *mhd_ret) +{ + struct PaybackContext *pc = cls; + struct TALER_EXCHANGEDB_TransactionList *tl; + struct TALER_Amount spent; + enum GNUNET_DB_QueryStatus qs; + + /* Check whether a payback is allowed, and if so, to which + reserve / account the money should go */ + qs = TEH_plugin->get_reserve_by_h_blind (TEH_plugin->cls, + session, + &pc->h_blind, + &pc->reserve_pub); + if (0 > qs) + { + if (GNUNET_DB_STATUS_HARD_ERROR == qs) + { + GNUNET_break (0); + *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_PAYBACK_DB_FETCH_FAILED); + } + return qs; + } + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + GNUNET_break_op (0); + *mhd_ret = reply_payback_unknown (connection, + TALER_EC_PAYBACK_WITHDRAW_NOT_FOUND); + return GNUNET_DB_STATUS_HARD_ERROR; + } + + /* Calculate remaining balance. */ + qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls, + session, + &pc->coin->coin_pub, + &tl); + if (0 > qs) + { + if (GNUNET_DB_STATUS_HARD_ERROR == qs) + { + GNUNET_break (0); + *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_PAYBACK_DB_FETCH_FAILED); + } + return qs; + } + TALER_amount_get_zero (pc->value.currency, + &spent); + if (GNUNET_OK != + TEH_DB_calculate_transaction_list_totals (tl, + &spent, + &spent)) + { + GNUNET_break (0); + TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, + tl); + *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_PAYBACK_HISTORY_DB_ERROR); + return GNUNET_DB_STATUS_HARD_ERROR; + } + if (GNUNET_SYSERR == + TALER_amount_subtract (&pc->amount, + &pc->value, + &spent)) + { + GNUNET_break (0); + TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, + tl); + *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_PAYBACK_COIN_BALANCE_NEGATIVE); + return GNUNET_DB_STATUS_HARD_ERROR; + } + if ( (0 == pc->amount.fraction) && + (0 == pc->amount.value) ) + { + TEH_plugin->rollback (TEH_plugin->cls, + session); + *mhd_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 GNUNET_DB_STATUS_HARD_ERROR; + } + TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, + tl); + pc->now = GNUNET_TIME_absolute_get (); + (void) GNUNET_TIME_round_abs (&pc->now); + + /* add coin to list of wire transfers for payback */ + qs = TEH_plugin->insert_payback_request (TEH_plugin->cls, + session, + &pc->reserve_pub, + pc->coin, + pc->coin_sig, + pc->coin_bks, + &pc->amount, + &pc->h_blind, + pc->now); + if (0 > qs) + { + if (GNUNET_DB_STATUS_HARD_ERROR == qs) + { + TALER_LOG_WARNING ("Failed to store /payback information in database\n"); + *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_PAYBACK_DB_PUT_FAILED); + } + return qs; + } + return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; +} + + +/** * We have parsed the JSON information about the payback request. Do * some basic sanity checks (especially that the signature on the * request and coin is valid) and then execute the payback operation. @@ -53,14 +300,14 @@ verify_and_execute_payback (struct MHD_Connection *connection, const struct TALER_DenominationBlindingKeyP *coin_bks, const struct TALER_CoinSpendSignatureP *coin_sig) { + struct PaybackContext pc; 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; + int mhd_ret; /* check denomination exists and is in payback mode */ key_state = TEH_KS_acquire (); @@ -75,7 +322,7 @@ verify_and_execute_payback (struct MHD_Connection *connection, TALER_EC_PAYBACK_DENOMINATION_KEY_UNKNOWN, "denom_pub"); } - TALER_amount_ntoh (&value, + TALER_amount_ntoh (&pc.value, &dki->issue.properties.value); /* check denomination signature */ @@ -127,15 +374,24 @@ verify_and_execute_payback (struct MHD_Connection *connection, } GNUNET_CRYPTO_hash (coin_ev, coin_ev_size, - &h_blind); + &pc.h_blind); GNUNET_free (coin_ev); - return TEH_DB_execute_payback (connection, - coin, - &value, - &h_blind, - coin_bks, - coin_sig); + pc.coin_sig = coin_sig; + pc.coin_bks = coin_bks; + pc.coin = coin; + if (GNUNET_OK != + TEH_DB_run_transaction (connection, + &mhd_ret, + &payback_transaction, + &pc)) + return mhd_ret; + + return reply_payback_success (connection, + &coin->coin_pub, + &pc.reserve_pub, + &pc.amount, + pc.now); } diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c index 3f3f57215..7dab61ed6 100644 --- a/src/exchange/taler-exchange-httpd_responses.c +++ b/src/exchange/taler-exchange-httpd_responses.c @@ -1294,67 +1294,4 @@ 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 specified. 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) -{ - return TEH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_NOT_FOUND, - "{s:s, s:I}", - "error", "blinded coin unknown", - "code", (json_int_t) ec); -} - - -/** - * A wallet asked for /payback, return the successful response. - * - * @param connection connection to the client - * @param coin_pub coin for which we are processing the payback request - * @param reserve_pub public key of the reserve that will receive the payback - * @param amount the amount we will wire back - * @param timestamp when did the exchange receive the /payback request - * @return MHD result code - */ -int -TEH_RESPONSE_reply_payback_success (struct MHD_Connection *connection, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - const struct TALER_ReservePublicKeyP *reserve_pub, - const struct TALER_Amount *amount, - struct GNUNET_TIME_Absolute timestamp) -{ - struct TALER_PaybackConfirmationPS pc; - struct TALER_ExchangePublicKeyP pub; - struct TALER_ExchangeSignatureP sig; - - pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK); - pc.purpose.size = htonl (sizeof (struct TALER_PaybackConfirmationPS)); - pc.timestamp = GNUNET_TIME_absolute_hton (timestamp); - TALER_amount_hton (&pc.payback_amount, - amount); - pc.coin_pub = *coin_pub; - pc.reserve_pub = *reserve_pub; - TEH_KS_sign (&pc.purpose, - &pub, - &sig); - return TEH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_OK, - "{s:o, s:o, s:o, s:o, s:o}", - "reserve_pub", GNUNET_JSON_from_data_auto (reserve_pub), - "timestamp", GNUNET_JSON_from_time_abs (timestamp), - "amount", TALER_JSON_from_amount (amount), - "exchange_sig", GNUNET_JSON_from_data_auto (&sig), - "exchange_pub", GNUNET_JSON_from_data_auto (&pub)); -} - - - /* 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 27f253b78..8a51d8d53 100644 --- a/src/exchange/taler-exchange-httpd_responses.h +++ b/src/exchange/taler-exchange-httpd_responses.h @@ -488,37 +488,6 @@ 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 specified. 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 coin_pub coin for which we are processing the payback request - * @param reserve_pub public key of the reserve that will receive the payback - * @param amount the amount we will wire back - * @param timestamp when did the exchange receive the /payback request - * @return MHD result code - */ -int -TEH_RESPONSE_reply_payback_success (struct MHD_Connection *connection, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - const struct TALER_ReservePublicKeyP *reserve_pub, - - const struct TALER_Amount *amount, - struct GNUNET_TIME_Absolute timestamp); - /** * Compile the transaction history of a coin into a JSON object. |