diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c index e1b30272c..fd36bb4a0 100644 --- a/src/exchange/taler-exchange-httpd_deposit.c +++ b/src/exchange/taler-exchange-httpd_deposit.c @@ -250,6 +250,8 @@ verify_and_execute_deposit (struct MHD_Connection *connection, struct DepositContext dc; struct TEH_KS_StateHandle *mks; const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki; + enum TALER_ErrorCode ec; + unsigned int hc; /* check signature */ dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT); @@ -287,12 +289,15 @@ verify_and_execute_deposit (struct MHD_Connection *connection, } dki = TEH_KS_denomination_key_lookup_by_hash (mks, &deposit->coin.denom_pub_hash, - TEH_KS_DKU_DEPOSIT); + TEH_KS_DKU_DEPOSIT, + &ec, + &hc); if (NULL == dki) { TEH_KS_release (mks); - return TEH_RESPONSE_reply_internal_db_error (connection, - TALER_EC_DEPOSIT_DB_DENOMINATION_KEY_UNKNOWN); + return TEH_RESPONSE_reply_with_error (connection, + ec, + hc); } TALER_amount_ntoh (&dc.value, &dki->issue.properties.value); @@ -388,6 +393,7 @@ TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh, json_t *wire; char *emsg; enum TALER_ErrorCode ec; + unsigned int hc; struct TALER_EXCHANGEDB_Deposit deposit; struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki; struct TEH_KS_StateHandle *key_state; @@ -497,16 +503,17 @@ TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh, } dki = TEH_KS_denomination_key_lookup_by_hash (key_state, &deposit.coin.denom_pub_hash, - TEH_KS_DKU_DEPOSIT); + TEH_KS_DKU_DEPOSIT, + &ec, + &hc); if (NULL == dki) { - /* FIXME: #3887: if DK was revoked, we might want to give a 403 and not a 404! */ TEH_KS_release (key_state); TALER_LOG_WARNING ("Unknown denomination key in /deposit request\n"); GNUNET_JSON_parse_free (spec); - return TEH_RESPONSE_reply_arg_unknown (connection, - TALER_EC_DEPOSIT_DENOMINATION_KEY_UNKNOWN, - "denom_pub"); + return TEH_RESPONSE_reply_with_error (connection, + ec, + hc); } TALER_amount_ntoh (&deposit.deposit_fee, &dki->issue.properties.fee_deposit); diff --git a/src/exchange/taler-exchange-httpd_keystate.c b/src/exchange/taler-exchange-httpd_keystate.c index ce6a856d9..bde5c7d7e 100644 --- a/src/exchange/taler-exchange-httpd_keystate.c +++ b/src/exchange/taler-exchange-httpd_keystate.c @@ -1954,6 +1954,8 @@ TEH_KS_acquire_ (struct GNUNET_TIME_Absolute now, * @param key_state state to look in * @param denom_pub_hash hash of denomination public key * @param use purpose for which the key is being located + * @param ec[out] set to the error code, in case the operation failed + * @param hc[out] set to the HTTP status code to use * @return the denomination key issue, * or NULL if denom_pub could not be found (or is not valid at this time for the given @a use) */ @@ -1962,7 +1964,9 @@ TEH_KS_denomination_key_lookup_by_hash (const struct TEH_KS_StateHandle *key_state, const struct GNUNET_HashCode *denom_pub_hash, - enum TEH_KS_DenominationKeyUse use) + enum TEH_KS_DenominationKeyUse use, + enum TALER_ErrorCode *ec, + unsigned int *hc) { struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki; struct GNUNET_TIME_Absolute now; @@ -1976,7 +1980,25 @@ TEH_KS_denomination_key_lookup_by_hash (const struct dki = GNUNET_CONTAINER_multihashmap_get (key_state->revoked_map, denom_pub_hash); if (NULL == dki) + { + *hc = MHD_HTTP_NOT_FOUND; + switch (use) + { + case TEH_KS_DKU_PAYBACK: + *ec = TALER_EC_PAYBACK_DENOMINATION_KEY_UNKNOWN; + break; + case TEH_KS_DKU_ZOMBIE: + *ec = TALER_EC_REFRESH_PAYBACK_DENOMINATION_KEY_NOT_FOUND; + break; + case TEH_KS_DKU_WITHDRAW: + *ec = TALER_EC_WITHDRAW_DENOMINATION_KEY_NOT_FOUND; + break; + case TEH_KS_DKU_DEPOSIT: + *ec = TALER_EC_DEPOSIT_DENOMINATION_KEY_UNKNOWN; + break; + } return NULL; + } now = GNUNET_TIME_absolute_get (); if (now.abs_value_us < GNUNET_TIME_absolute_ntoh (dki->issue.properties.start).abs_value_us) @@ -1984,6 +2006,22 @@ TEH_KS_denomination_key_lookup_by_hash (const struct GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Not returning DKI for %s, as start time is in the future\n", GNUNET_h2s (denom_pub_hash)); + *hc = MHD_HTTP_PRECONDITION_FAILED; + switch (use) + { + case TEH_KS_DKU_PAYBACK: + *ec = TALER_EC_PAYBACK_DENOMINATION_VALIDITY_IN_FUTURE; + break; + case TEH_KS_DKU_ZOMBIE: + *ec = TALER_EC_REFRESH_PAYBACK_DENOMINATION_VALIDITY_IN_FUTURE; + break; + case TEH_KS_DKU_WITHDRAW: + *ec = TALER_EC_WITHDRAW_VALIDITY_IN_FUTURE; + break; + case TEH_KS_DKU_DEPOSIT: + *ec = TALER_EC_DEPOSIT_DENOMINATION_VALIDITY_IN_FUTURE; + break; + } return NULL; } now = GNUNET_TIME_absolute_get (); @@ -1997,6 +2035,8 @@ TEH_KS_denomination_key_lookup_by_hash (const struct GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Not returning DKI for %s, as time to create coins has passed\n", GNUNET_h2s (denom_pub_hash)); + *ec = TALER_EC_WITHDRAW_VALIDITY_IN_PAST; + *hc = MHD_HTTP_GONE; return NULL; } if (NULL == dki->denom_priv.rsa_private_key) @@ -2004,6 +2044,8 @@ TEH_KS_denomination_key_lookup_by_hash (const struct GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Not returning DKI of %s for WITHDRAW operation as we lack the private key, even though the withdraw period did not yet expire!\n", GNUNET_h2s (denom_pub_hash)); + *ec = TALER_EC_DENOMINATION_KEY_LOST; + *hc = MHD_HTTP_SERVICE_UNAVAILABLE; return NULL; } break; @@ -2015,6 +2057,8 @@ TEH_KS_denomination_key_lookup_by_hash (const struct GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Not returning DKI for %s, as time to spend coin has passed\n", GNUNET_h2s (denom_pub_hash)); + *ec = TALER_EC_DEPOSIT_DENOMINATION_EXPIRED; + *hc = MHD_HTTP_GONE; return NULL; } break; @@ -2026,6 +2070,8 @@ TEH_KS_denomination_key_lookup_by_hash (const struct GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Not returning DKI for %s, as time to payback coin has passed\n", GNUNET_h2s (denom_pub_hash)); + *ec = TALER_EC_REFRESH_PAYBACK_DENOMINATION_EXPIRED; + *hc = MHD_HTTP_GONE; return NULL; } break; @@ -2037,6 +2083,8 @@ TEH_KS_denomination_key_lookup_by_hash (const struct GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Not returning DKI for %s, as legal expiration of coin has passed\n", GNUNET_h2s (denom_pub_hash)); + *ec = TALER_EC_REFRESH_ZOMBIE_DENOMINATION_EXPIRED; + *hc = MHD_HTTP_GONE; return NULL; } break; diff --git a/src/exchange/taler-exchange-httpd_keystate.h b/src/exchange/taler-exchange-httpd_keystate.h index 7e2a659ef..708b043b5 100644 --- a/src/exchange/taler-exchange-httpd_keystate.h +++ b/src/exchange/taler-exchange-httpd_keystate.h @@ -129,6 +129,8 @@ enum TEH_KS_DenominationKeyUse * @param key_state state to look in * @param denom_pub_hash hash of denomination public key * @param use purpose for which the key is being located + * @param ec[out] set to the error code, in case the operation failed + * @param hc[out] set to the HTTP status code to use * @return the denomination key issue, * or NULL if denom_pub could not be found (or is not valid at this time for the given @a use) */ @@ -137,7 +139,9 @@ TEH_KS_denomination_key_lookup_by_hash (const struct TEH_KS_StateHandle *key_state, const struct GNUNET_HashCode *denom_pub_hash, - enum TEH_KS_DenominationKeyUse use); + enum TEH_KS_DenominationKeyUse use, + enum TALER_ErrorCode *ec, + unsigned int *hc); /** diff --git a/src/exchange/taler-exchange-httpd_payback.c b/src/exchange/taler-exchange-httpd_payback.c index 8a881ffff..05a2355a3 100644 --- a/src/exchange/taler-exchange-httpd_payback.c +++ b/src/exchange/taler-exchange-httpd_payback.c @@ -429,6 +429,8 @@ verify_and_execute_payback (struct MHD_Connection *connection, struct GNUNET_HashCode c_hash; char *coin_ev; size_t coin_ev_size; + enum TALER_ErrorCode ec; + unsigned int hc; /* check denomination exists and is in payback mode */ key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ()); @@ -441,15 +443,17 @@ verify_and_execute_payback (struct MHD_Connection *connection, } dki = TEH_KS_denomination_key_lookup_by_hash (key_state, &coin->denom_pub_hash, - TEH_KS_DKU_PAYBACK); + TEH_KS_DKU_PAYBACK, + &ec, + &hc); if (NULL == dki) { TEH_KS_release (key_state); TALER_LOG_WARNING ( "Denomination key in /payback request not in payback mode\n"); - return TEH_RESPONSE_reply_arg_unknown (connection, - TALER_EC_PAYBACK_DENOMINATION_KEY_UNKNOWN, - "denom_pub"); + return TEH_RESPONSE_reply_with_error (connection, + ec, + hc); } TALER_amount_ntoh (&pc.value, &dki->issue.properties.value); diff --git a/src/exchange/taler-exchange-httpd_refresh_melt.c b/src/exchange/taler-exchange-httpd_refresh_melt.c index 538d8afe2..a00fda14f 100644 --- a/src/exchange/taler-exchange-httpd_refresh_melt.c +++ b/src/exchange/taler-exchange-httpd_refresh_melt.c @@ -447,6 +447,8 @@ TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh, json_t *root; struct RefreshMeltContext rmc; int res; + unsigned int hc; + enum TALER_ErrorCode ec; struct TEH_KS_StateHandle *key_state; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("coin_pub", @@ -501,7 +503,9 @@ TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh, rmc.dki = TEH_KS_denomination_key_lookup_by_hash (key_state, &rmc.refresh_session.coin. denom_pub_hash, - TEH_KS_DKU_DEPOSIT); + TEH_KS_DKU_DEPOSIT, + &ec, + &hc); /* Consider case that denomination was revoked but this coin was already seen and thus refresh is OK. */ if (NULL == rmc.dki) @@ -511,7 +515,9 @@ TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh, dki = TEH_KS_denomination_key_lookup_by_hash (key_state, &rmc.refresh_session.coin. denom_pub_hash, - TEH_KS_DKU_PAYBACK); + TEH_KS_DKU_PAYBACK, + &ec, + &hc); if (NULL != dki) { struct TALER_CoinPublicInfo coin_info; @@ -547,7 +553,9 @@ TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh, dki = TEH_KS_denomination_key_lookup_by_hash (key_state, &rmc.refresh_session.coin. denom_pub_hash, - TEH_KS_DKU_ZOMBIE); + TEH_KS_DKU_ZOMBIE, + &ec, + &hc); if (NULL != dki) { rmc.dki = dki; @@ -558,9 +566,9 @@ TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh, if (NULL == rmc.dki) { TALER_LOG_WARNING ("Unknown denomination key in /refresh/melt request\n"); - res = TEH_RESPONSE_reply_arg_unknown (connection, - TALER_EC_REFRESH_MELT_DENOMINATION_KEY_NOT_FOUND, - "denom_pub"); + res = TEH_RESPONSE_reply_with_error (connection, + ec, + hc); goto cleanup; } diff --git a/src/exchange/taler-exchange-httpd_refresh_reveal.c b/src/exchange/taler-exchange-httpd_refresh_reveal.c index c1a7b6392..be5228dd9 100644 --- a/src/exchange/taler-exchange-httpd_refresh_reveal.c +++ b/src/exchange/taler-exchange-httpd_refresh_reveal.c @@ -610,6 +610,8 @@ handle_refresh_reveal_json (struct MHD_Connection *connection, &dki_h[i]), GNUNET_JSON_spec_end () }; + unsigned int hc; + enum TALER_ErrorCode ec; res = TEH_PARSE_json_array (connection, new_denoms_h_json, @@ -623,13 +625,15 @@ handle_refresh_reveal_json (struct MHD_Connection *connection, } dkis[i] = TEH_KS_denomination_key_lookup_by_hash (key_state, &dki_h[i], - TEH_KS_DKU_WITHDRAW); + TEH_KS_DKU_WITHDRAW, + &ec, + &hc); if (NULL == dkis[i]) { TEH_KS_release (key_state); - return TEH_RESPONSE_reply_arg_invalid (connection, - TALER_EC_REFRESH_REVEAL_FRESH_DENOMINATION_KEY_NOT_FOUND, - "new_denoms_h"); + return TEH_RESPONSE_reply_with_error (connection, + ec, + hc); } GNUNET_assert (NULL != dkis[i]->denom_priv.rsa_private_key); } diff --git a/src/exchange/taler-exchange-httpd_refund.c b/src/exchange/taler-exchange-httpd_refund.c index 93df54c8a..48532a2ce 100644 --- a/src/exchange/taler-exchange-httpd_refund.c +++ b/src/exchange/taler-exchange-httpd_refund.c @@ -161,6 +161,8 @@ refund_transaction (void *cls, int deposit_found; int refund_found; int fee_cmp; + unsigned int hc; + enum TALER_ErrorCode ec; dep = NULL; ref = NULL; @@ -354,7 +356,9 @@ refund_transaction (void *cls, } dki = TEH_KS_denomination_key_lookup_by_hash (mks, &dep->coin.denom_pub_hash, - TEH_KS_DKU_DEPOSIT); + TEH_KS_DKU_DEPOSIT, + &ec, + &hc); if (NULL == dki) { /* DKI not found, but we do have a coin with this DK in our database; @@ -363,9 +367,9 @@ refund_transaction (void *cls, TEH_KS_release (mks); TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, tl); - *mhd_ret = TEH_RESPONSE_reply_internal_error (connection, - TALER_EC_REFUND_DENOMINATION_KEY_NOT_FOUND, - "denomination key not found"); + *mhd_ret = TEH_RESPONSE_reply_with_error (connection, + ec, + hc); return GNUNET_DB_STATUS_HARD_ERROR; } TALER_amount_ntoh (&expect_fee, diff --git a/src/exchange/taler-exchange-httpd_reserve_withdraw.c b/src/exchange/taler-exchange-httpd_reserve_withdraw.c index 1199ed67d..578aace31 100644 --- a/src/exchange/taler-exchange-httpd_reserve_withdraw.c +++ b/src/exchange/taler-exchange-httpd_reserve_withdraw.c @@ -365,6 +365,8 @@ TEH_RESERVE_handler_reserve_withdraw (struct TEH_RequestHandler *rh, json_t *root; int res; int mhd_ret; + unsigned int hc; + enum TALER_ErrorCode ec; struct TALER_Amount amount; struct TALER_Amount fee_withdraw; struct GNUNET_JSON_Specification spec[] = { @@ -407,14 +409,16 @@ TEH_RESERVE_handler_reserve_withdraw (struct TEH_RequestHandler *rh, } wc.dki = TEH_KS_denomination_key_lookup_by_hash (wc.key_state, &wc.denom_pub_hash, - TEH_KS_DKU_WITHDRAW); + TEH_KS_DKU_WITHDRAW, + &ec, + &hc); if (NULL == wc.dki) { GNUNET_JSON_parse_free (spec); TEH_KS_release (wc.key_state); - return TEH_RESPONSE_reply_arg_unknown (connection, - TALER_EC_WITHDRAW_DENOMINATION_KEY_NOT_FOUND, - "denom_pub"); + return TEH_RESPONSE_reply_with_error (connection, + ec, + hc); } GNUNET_assert (NULL != wc.dki->denom_priv.rsa_private_key); TALER_amount_ntoh (&amount, diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c index 2e1835845..048a8bef0 100644 --- a/src/exchange/taler-exchange-httpd_responses.c +++ b/src/exchange/taler-exchange-httpd_responses.c @@ -361,6 +361,26 @@ TEH_RESPONSE_reply_internal_error (struct MHD_Connection *connection, } +/** + * Send a response indicating an error. + * + * @param connection the MHD connection to use + * @param ec error code uniquely identifying the error + * @param http_status HTTP status code to use + * @return a MHD result code + */ +int +TEH_RESPONSE_reply_with_error (struct MHD_Connection *connection, + enum TALER_ErrorCode ec, + unsigned int http_status) +{ + return TEH_RESPONSE_reply_json_pack (connection, + http_status, + "{s:I}", + "code", (json_int_t) ec); +} + + /** * Send a response indicating an external error. * diff --git a/src/exchange/taler-exchange-httpd_responses.h b/src/exchange/taler-exchange-httpd_responses.h index 177eaa43e..9b6a78859 100644 --- a/src/exchange/taler-exchange-httpd_responses.h +++ b/src/exchange/taler-exchange-httpd_responses.h @@ -170,6 +170,20 @@ TEH_RESPONSE_reply_internal_error (struct MHD_Connection *connection, const char *hint); +/** + * Send a response indicating an error. + * + * @param connection the MHD connection to use + * @param ec error code uniquely identifying the error + * @param http_status HTTP status code to use + * @return a MHD result code + */ +int +TEH_RESPONSE_reply_with_error (struct MHD_Connection *connection, + enum TALER_ErrorCode ec, + unsigned int http_status); + + /** * Send a response indicating an external error. * diff --git a/src/include/taler_error_codes.h b/src/include/taler_error_codes.h index 4962d5fe5..57dbd2db9 100644 --- a/src/include/taler_error_codes.h +++ b/src/include/taler_error_codes.h @@ -297,6 +297,29 @@ enum TALER_ErrorCode */ TALER_EC_WITHDRAW_RESERVE_HISTORY_IMPOSSIBLE = 1113, + /** + * Validity period of the coin to be withdrawn + * is in the future. Returned with an HTTP + * status of #MHD_HTTP_PRECONDITION_FAILED. + */ + TALER_EC_WITHDRAW_VALIDITY_IN_FUTURE = 1114, + + /** + * Withdraw period of the coin to be withdrawn + * is in the past. Returned with an HTTP + * status of #MHD_HTTP_GONE. + */ + TALER_EC_WITHDRAW_VALIDITY_IN_PAST = 1115, + + /** + * The private key associated with the denomination + * key is unknown to the server, possibly because + * the key was revoked. Returned with an HTTP + * status of #MHD_HTTP_SERVICE_UNAVAILABLE. + */ + TALER_EC_DENOMINATION_KEY_LOST = 1116, + + /** * The exchange failed to obtain the transaction history of the * given reserve from the database. @@ -453,6 +476,20 @@ enum TALER_ErrorCode */ TALER_EC_DEPOSIT_INVALID_TIMESTAMP = 1218, + /** + * Validity period of the denomination key + * is in the future. Returned with an HTTP + * status of #MHD_HTTP_PRECONDITION_FAILED. + */ + TALER_EC_DEPOSIT_DENOMINATION_VALIDITY_IN_FUTURE = 1219, + + /** + * Denomination key of the coin is past the + * deposit deadline. Returned with an HTTP + * status of #MHD_HTTP_GONE. + */ + TALER_EC_DEPOSIT_DENOMINATION_EXPIRED = 1220, + /** * The respective coin did not have sufficient residual value @@ -531,6 +568,35 @@ enum TALER_ErrorCode */ TALER_EC_REFRESH_MELT_COIN_EXPIRED_NO_ZOMBIE = 1309, + /** + * The exchange is unaware of the denomination key that was + * used to sign the melted zombie coin. This response is provided + * with HTTP status code MHD_HTTP_NOT_FOUND. + */ + TALER_EC_REFRESH_PAYBACK_DENOMINATION_KEY_NOT_FOUND = 1301, + + /** + * Validity period of the denomination key + * is in the future. Returned with an HTTP + * status of #MHD_HTTP_PRECONDITION_FAILED. + */ + TALER_EC_REFRESH_PAYBACK_DENOMINATION_VALIDITY_IN_FUTURE = 1301, + + /** + * Denomination key of the coin is past the + * deposit deadline. Returned with an HTTP + * status of #MHD_HTTP_GONE. + */ + TALER_EC_REFRESH_PAYBACK_DENOMINATION_EXPIRED = 1302, + + /** + * Denomination key of the coin is past the + * deposit deadline. Returned with an HTTP + * status of #MHD_HTTP_GONE. + */ + TALER_EC_REFRESH_ZOMBIE_DENOMINATION_EXPIRED = 1303, + + /** * The provided transfer keys do not match up with the * original commitment. Information about the original @@ -921,7 +987,15 @@ enum TALER_ErrorCode * This response is provided with an HTTP status code of * MHD_HTTP_INTERNAL_SERVER_ERROR */ - TALER_EC_PAYBACK_COIN_BALANCE_NEGATIVE = 1857, + TALER_EC_PAYBACK_COIN_BALANCE_NEGATIVE = 1859, + + /** + * Validity period of the denomination key + * is in the future. Returned with an HTTP + * status of #MHD_HTTP_PRECONDITION_FAILED. + */ + TALER_EC_PAYBACK_DENOMINATION_VALIDITY_IN_FUTURE = 1860, + /** * The "have" parameter was not a natural number.