diff --git a/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c b/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c index 65bbb4326..10b5d873b 100644 --- a/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c +++ b/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c @@ -52,26 +52,32 @@ struct AgeRevealContext uint32_t num_coins; /** - * TODO:oec num_coins denoms + * #num_coins hashes of the denominations to withdraw from. + * Those must support age restriction. */ struct TALER_DenominationHashP *denoms_h; /** - * TODO:oec num_coins blinded coins + * #num_coins hashes of blinded coins. */ struct TALER_BlindedCoinHashP *coin_evs; /** - * TODO:oec num_coins*(kappa - 1) disclosed coins + * #num_coins*(kappa - 1) disclosed coins. */ struct GNUNET_CRYPTO_EddsaPrivateKey *disclosed_coins; + /** + * The data from the original age-withraw request. Will be retrieved from + * the DB via @a ach. + */ + struct TALER_EXCHANGEDB_AgeWithdrawCommitment age_withdraw_request; }; /** * Helper function to free resources in the context */ -void +static void age_reveal_context_free (struct AgeRevealContext *actx) { GNUNET_free (actx->denoms_h); @@ -81,24 +87,29 @@ age_reveal_context_free (struct AgeRevealContext *actx) /** - * Handle a "/age-withdraw/$ACH/reveal request. Parses the given JSON - * ... TODO:oec:description + * Parse the json body of a "/age-withdraw/$ACH/reveal request. It extracts + * the denomination hashes, blinded coins and disclosed coins and allocates + * memory for those. * * @param connection The MHD connection to handle - * @param actx The context of the operation, only partially built at call time * @param j_denoms_h Array of hashes of the denominations for the withdrawal, in JSON format * @param j_coin_evs The blinded envelopes in JSON format for the coins that are not revealed and will be signed on success * @param j_disclosed_coins The n*(kappa-1) disclosed coins' private keys in JSON format, from which all other attributes (age restriction, blinding, nonce) will be derived from + * @param[out] actx The context of the operation, only partially built at call time + * @param[out] mhd_mret The result if a reply is queued for MHD + * @return true on success, false on failure, with a reply already queued for MHD. */ -MHD_RESULT -handle_age_withdraw_reveal_json ( +static bool +parse_age_withdraw_reveal_json ( struct MHD_Connection *connection, - struct AgeRevealContext *actx, const json_t *j_denoms_h, const json_t *j_coin_evs, - const json_t *j_disclosed_coins) + const json_t *j_disclosed_coins, + struct AgeRevealContext *actx, + MHD_RESULT *mhd_ret) { - MHD_RESULT mhd_ret = MHD_NO; + + bool result = false; /* Verify JSON-structure consistency */ { @@ -121,18 +132,18 @@ handle_age_withdraw_reveal_json ( error = "the size of array disclosed_coins must be " TALER_CNC_KAPPA_MINUS_ONE_STR " times of the size of denoms_h"; else if (actx->num_coins > TALER_MAX_FRESH_COINS) - /** - * FIXME?: If the user had commited to more than the maximum coins allowed, - * the reserve has been charged, but now the user can not withdraw any money - * from it. How can the user get their money back? - **/ + /* In violation of the protocol, the wallet committed to more than the + * maximum coins allowed */ error = "maximum number of coins that can be withdrawn has been exceeded"; if (NULL != error) - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - error); + { + *mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + error); + return result; + } } /* Parse denomination keys */ @@ -157,13 +168,12 @@ handle_age_withdraw_reveal_json ( sizeof(msg), "couldn't parse entry no. %d in array denoms_h", idx + 1); - mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - msg); - goto EXIT; + *mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + msg); + goto CLEANUP; } - }; } @@ -189,11 +199,11 @@ handle_age_withdraw_reveal_json ( sizeof(msg), "couldn't parse entry no. %d in array coin_evs", idx + 1); - mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - msg); - goto EXIT; + *mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + msg); + goto CLEANUP; } }; } @@ -221,30 +231,81 @@ handle_age_withdraw_reveal_json ( sizeof(msg), "couldn't parse entry no. %d in array disclosed_coins", idx + 1); - mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - msg); - goto EXIT; + *mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + msg); + goto CLEANUP; } }; } - /* TODO:oec: find commitment */ - /* TODO:oec: check validity of denoms */ - /* TODO:oec: check amount total against denoms */ - /* TODO:oec: compute the disclosed blinded coins */ - /* TODO:oec: generate h_commitment_comp */ - /* TODO:oec: compare h_commitment_comp against h_commitment */ - /* TODO:oec: sign the coins */ - /* TODO:oec: send response */ + result = true; + *mhd_ret = MHD_YES; - - /* TODO */ -EXIT: +CLEANUP: age_reveal_context_free (actx); - return mhd_ret; + return result; +} + + +/** + * Check if the request belongs to an existing age-withdraw request. + * If so, sets the age_withdraw object with the request data. + * Otherwise, it queues an appropriate MHD response. + * + * @param rc Request context + * @param ach Original commitment value sent with the age-withdraw request + * @param rpub Reserve public key used in the original age-withdraw request + * @param[out] age_withdraw Data from the original age-withdraw request + * @param[out] mhd The result if a reply is queued for MHD + * @return true if the withdraw request has been found, + * false if we did not find the request in the DB + */ +static bool +retrieve_original_commitment ( + struct MHD_Connection *connection, + const struct TALER_AgeWithdrawCommitmentHashP *h_commitment, + const struct TALER_ReservePublicKeyP *reserve_pub, + struct TALER_EXCHANGEDB_AgeWithdrawCommitment *age_withdraw_request, + MHD_RESULT *mhd) +{ + enum GNUNET_DB_QueryStatus qs; + + qs = TEH_plugin->get_age_withdraw_info (TEH_plugin->cls, + reserve_pub, + h_commitment, + age_withdraw_request); + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) + { + + switch (qs) + { + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + *mhd = TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_AGE_WITHDRAW_COMMITMENT_UNKNOWN, + NULL); + break; + case GNUNET_DB_STATUS_HARD_ERROR: + *mhd = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "get_age_withdraw_info"); + break; + case GNUNET_DB_STATUS_SOFT_ERROR: + default: + GNUNET_break (0); /* should be impossible */ + *mhd = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + NULL); + } + return false; + } + + return true; } @@ -254,6 +315,7 @@ TEH_handler_age_withdraw_reveal ( const struct TALER_AgeWithdrawCommitmentHashP *ach, const json_t *root) { + MHD_RESULT res = MHD_NO; struct AgeRevealContext actx = {0}; json_t *j_denoms_h; json_t *j_coin_evs; @@ -270,33 +332,55 @@ TEH_handler_age_withdraw_reveal ( /* Parse JSON body*/ { - enum GNUNET_GenericReturnValue res; + enum GNUNET_GenericReturnValue ret; - res = TALER_MHD_parse_json_data (rc->connection, + ret = TALER_MHD_parse_json_data (rc->connection, root, spec); - if (GNUNET_OK != res) + if (GNUNET_OK != ret) { GNUNET_break_op (0); - return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; + return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES; } } + do { + /* Extract information from json for denominations, blinded and disclosed coins */ + if (! parse_age_withdraw_reveal_json (rc->connection, + j_denoms_h, + j_coin_evs, + j_disclosed_coins, + &actx, + &res)) + break; - /* handle reveal request */ - { - MHD_RESULT res; + /* Find original commitment */ + if (! retrieve_original_commitment (rc->connection, + &actx.ach, + &actx.reserve_pub, + &actx.age_withdraw_request, + &res)) + break; - res = handle_age_withdraw_reveal_json (rc->connection, - &actx, - j_denoms_h, - j_coin_evs, - j_disclosed_coins); + /* Ensure validity of denoms */ + if (! all_donimations_valid (rc->connection, + &actx.denoms_h, + /* Arg for denoms */ + &res)) + break; - GNUNET_JSON_parse_free (spec); - return res; - } + /* TODO:oec: check amount total against denoms */ + /* TODO:oec: compute the disclosed blinded coins */ + /* TODO:oec: generate h_commitment_comp */ + /* TODO:oec: compare h_commitment_comp against h_commitment */ + /* TODO:oec: sign the coins */ + /* TODO:oec: send response */ + } while(0); + + age_reveal_context_free (&actx); + GNUNET_JSON_parse_free (spec); + return res; } diff --git a/src/exchange/taler-exchange-httpd_refreshes_reveal.c b/src/exchange/taler-exchange-httpd_refreshes_reveal.c index d2e498351..d672521dd 100644 --- a/src/exchange/taler-exchange-httpd_refreshes_reveal.c +++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c @@ -511,11 +511,11 @@ resolve_refreshes_reveal_denominations ( } } - old_dk = TEH_keys_denomination_by_hash2 ( - ksh, - &rctx->melt.session.coin.denom_pub_hash, - connection, - &ret); + old_dk = TEH_keys_denomination_by_hash2 (ksh, + &rctx->melt.session.coin. + denom_pub_hash, + connection, + &ret); if (NULL == old_dk) return ret;