diff --git a/src/include/taler_mint_service.h b/src/include/taler_mint_service.h index c9dbfb874..b228acc5e 100644 --- a/src/include/taler_mint_service.h +++ b/src/include/taler_mint_service.h @@ -216,7 +216,7 @@ struct TALER_MINT_AuditorInformation * Array of length @a denom_keys with the denomination * keys audited by this auditor. Note that the array * elements point to the same locations as the entries - * in the key's main `denom_keys` array. + * in the key's main `denom_keys` array. */ struct TALER_MINT_DenomPublicKey *const*denom_keys; }; @@ -622,9 +622,269 @@ void TALER_MINT_withdraw_sign_cancel (struct TALER_MINT_WithdrawSignHandle *sign); -/* ********************* /admin/add/incoming *********************** */ +/* ********************* /refresh/melt+reveal ***************************** */ +/** + * Melt (partially spent) coins to obtain fresh coins that are + * unlinkable to the original coin(s). Note that melting more + * than one coin in a single request will make those coins linkable, + * so the safest operation only melts one coin at a time. + * + * This API is typically used by a wallet. Note that to ensure that + * no money is lost in case of hardware failures, is operation does + * not actually initiate the request. Instead, it generates a buffer + * which the caller must store before proceeding with the actual call + * to #TALER_MINT_refresh_execute() that will generate the request. + * + * This function does verify that the given request data is internally + * consistent. However, the @a melts_sigs are only verified if @a + * check_sigs is set to #GNUNET_YES, as this may be relatively + * expensive and should be redundant. + * + * Aside from some non-trivial cryptographic operations that might + * take a bit of CPU time to complete, this function returns + * its result immediately and does not start any asynchronous + * processing. This function is also thread-safe. + * + * @param num_melts number of coins that are being melted (typically 1) + * @param melt_privs array of @a num_melts private keys of the coins to melt + * @param melt_amounts array of @a num_melts amounts specifying how much + * each coin will contribute to the melt (including fee) + * @param melt_sigs array of @a num_melts signatures affirming the + * validity of the public keys corresponding to the + * @a melt_privs private keys + * @param melt_pks array of @a num_melts denomination key information + * records corresponding to the @a melt_sigs + * validity of the keys + * @param check_sigs verify the validity of the signatures of @a melt_sigs + * @param fresh_pks_len length of the @a pks array + * @param fresh_pks array of @a pks_len denominations of fresh coins to create + * @param[OUT] res_size set to the size of the return value, or 0 on error + * @return NULL + * if the inputs are invalid (i.e. denomination key not with this mint). + * Otherwise, pointer to a buffer of @a res_size to store persistently + * before proceeding to #TALER_MINT_refresh_execute(). + * Non-null results should be freed using #GNUNET_free(). + */ +char * +TALER_MINT_refresh_prepare (unsigned int num_melts, + const struct TALER_CoinSpendPrivateKeyP *melt_privs, + const struct TALER_Amount *melt_amounts, + const struct TALER_DenominationSignature *melt_sigs, + const struct TALER_MINT_DenomPublicKey *melt_pks, + int check_sigs, + unsigned int fresh_pks_len, + const struct TALER_MINT_DenomPublicKey *fresh_pks, + size_t *res_size); + + +/* ********************* /refresh/melt ***************************** */ + +/** + * @brief A /refresh/melt Handle + */ +struct TALER_MINT_RefreshMeltHandle; + + +/** + * Callbacks of this type are used to notify the application about the + * result of the /refresh/melt stage. If successful, the @a noreveal_index + * should be committed to disk prior to proceeding #TALER_MINT_refresh_reveal(). + * + * @param cls closure + * @param http_status HTTP response code, never #MHD_HTTP_OK (200) as for successful intermediate response this callback is skipped. + * 0 if the mint's reply is bogus (fails to follow the protocol) + * @param noreveal_index choice by the mint in the cut-and-choose protocol, + * UINT16_MAX on error + * @param full_response full response from the mint (for logging, in case of errors) + */ +typedef void +(*TALER_MINT_RefreshMeltCallback) (void *cls, + unsigned int http_status, + uint16_t noreveal_index, + json_t *full_response); + + +/** + * Submit a melt request to the mint and get the mint's + * response. + * + * This API is typically used by a wallet. Note that to ensure that + * no money is lost in case of hardware failures, the provided + * argument should have been constructed using + * #TALER_MINT_refresh_prepare and committed to persistent storage + * prior to calling this function. + * + * @param mint the mint handle; the mint must be ready to operate + * @param refresh_data_length size of the @a refresh_data (returned + * in the `res_size` argument from #TALER_MINT_refresh_prepare()) + * @param refresh_data the refresh data as returned from + #TALER_MINT_refresh_prepare()) + * @param melt_cb the callback to call with the result + * @param melt_cb_cls closure for @a melt_cb + * @return a handle for this request; NULL if the argument was invalid. + * In this case, neither callback will be called. + */ +struct TALER_MINT_RefreshMeltHandle * +TALER_MINT_refresh_melt_execute (struct TALER_MINT_Handle *mint, + size_t refresh_data_length, + const char *refresh_data, + TALER_MINT_RefreshMeltCallback melt_cb, + void *melt_cb_cls); + + +/** + * Cancel a refresh melt request. This function cannot be used + * on a request handle if the callback was already invoked. + * + * @param rmh the refresh handle + */ +void +TALER_MINT_refresh_melt_cancel (struct TALER_MINT_RefreshMeltHandle *rmh); + + +/* ********************* /refresh/reveal ***************************** */ + + +/** + * Callbacks of this type are used to return the final result of + * submitting a refresh request to a mint. If the operation was + * successful, this function returns the signatures over the coins + * that were remelted. The @a coin_privs and @a sigs arrays give the + * coins in the same order (and should have the same length) in which + * the original request specified the respective denomination keys. + * + * @param cls closure + * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request + * 0 if the mint's reply is bogus (fails to follow the protocol) + * @param num_coins number of fresh coins created, length of the @a sigs and @a coin_privs arrays, 0 if the operation failed + * @param coin_privs array of @a num_coins private keys for the coins that were created, NULL on error + * @param sigs array of signature over @a num_coins coins, NULL on error + * @param full_response full response from the mint (for logging, in case of errors) + */ +typedef void +(*TALER_MINT_RefreshRevealCallback) (void *cls, + unsigned int http_status, + + unsigned int num_coins, + const struct TALER_CoinSpendPrivateKeyP *coin_privs, + const struct TALER_DenominationSignature *sigs, + json_t *full_response); + + +/** + * @brief A /refresh/reveal Handle + */ +struct TALER_MINT_RefreshRevealHandle; + + +/** + * Submit a /refresh/reval request to the mint and get the mint's + * response. + * + * This API is typically used by a wallet. Note that to ensure that + * no money is lost in case of hardware failures, the provided + * arguments should have been committed to persistent storage + * prior to calling this function. + * + * @param mint the mint handle; the mint must be ready to operate + * @param refresh_data_length size of the @a refresh_data (returned + * in the `res_size` argument from #TALER_MINT_refresh_prepare()) + * @param refresh_data the refresh data as returned from + #TALER_MINT_refresh_prepare()) + * @param noreveal_index response from the mint to the + * #TALER_MINT_refresh_melt() invocation + * @param reveal_cb the callback to call with the final result of the + * refresh operation + * @param reveal_cb_cls closure for the above callback + * @return a handle for this request; NULL if the argument was invalid. + * In this case, neither callback will be called. + */ +struct TALER_MINT_RefreshRevealHandle * +TALER_MINT_refresh_reveal (struct TALER_MINT_Handle *mint, + size_t refresh_data_length, + const char *refresh_data, + uint16_t noreveal_index, + TALER_MINT_RefreshRevealCallback reveal_cb, + void *reveal_cb_cls); + + +/** + * Cancel a refresh reveal request. This function cannot be used + * on a request handle if the callback was already invoked. + * + * @param rrh the refresh reval handle + */ +void +TALER_MINT_refresh_reveal_cancel (struct TALER_MINT_RefreshRevealHandle *rrh); + + +/* ********************* /refresh/link ***************************** */ + + +/** + * @brief A /refresh/link Handle + */ +struct TALER_MINT_RefreshLinkHandle; + + +/** + * Callbacks of this type are used to return the final result of + * submitting a /refresh/link request to a mint. If the operation was + * successful, this function returns the signatures over the coins + * that were created when the original coin was melted. + * + * @param cls closure + * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request + * 0 if the mint's reply is bogus (fails to follow the protocol) + * @param num_coins number of fresh coins created, length of the @a sigs and @a coin_privs arrays, 0 if the operation failed + * @param coin_privs array of @a num_coins private keys for the coins that were created, NULL on error + * @param sigs array of signature over @a num_coins coins, NULL on error + * @param full_response full response from the mint (for logging, in case of errors) + */ +typedef void +(*TALER_MINT_RefreshLinkCallback) (void *cls, + unsigned int http_status, + unsigned int num_coins, + const struct TALER_CoinSpendPrivateKeyP *coin_privs, + const struct TALER_DenominationSignature *sigs, + json_t *full_response); + + +/** + * Submit a link request to the mint and get the mint's response. + * + * This API is typically not used by anyone, it is more a threat + * against those trying to receive a funds transfer by abusing the + * /refresh protocol. + * + * @param mint the mint handle; the mint must be ready to operate + * @param coin_priv private key to request link data for + * @param link_cb the callback to call with the useful result of the + * refresh operation the @a coin_priv was involved in (if any) + * @param link_cb_cls closure for @a link_cb + * @return a handle for this request + */ +struct TALER_MINT_RefreshLinkHandle * +TALER_MINT_refresh_link (struct TALER_MINT_Handle *mint, + const struct TALER_CoinSpendPrivateKeyP *coin_priv, + TALER_MINT_RefreshLinkCallback link_cb, + void *link_cb_cls); + + +/** + * Cancel a refresh link request. This function cannot be used + * on a request handle if the callback was already invoked. + * + * @param rlh the refresh link handle + */ +void +TALER_MINT_refresh_link_cancel (struct TALER_MINT_RefreshLinkHandle *rlh); + + +/* ********************* /admin/add/incoming *********************** */ + /** * @brief A /admin/add/incoming Handle diff --git a/src/mint-lib/Makefile.am b/src/mint-lib/Makefile.am index e3a26be03..400fc77e0 100644 --- a/src/mint-lib/Makefile.am +++ b/src/mint-lib/Makefile.am @@ -19,6 +19,8 @@ libtalermint_la_SOURCES = \ mint_api_handle.c mint_api_handle.h \ mint_api_admin.c \ mint_api_deposit.c \ + mint_api_refresh.c \ + mint_api_refresh_link.c \ mint_api_withdraw.c libtalermint_la_LIBADD = \ diff --git a/src/mint-lib/mint_api_refresh.c b/src/mint-lib/mint_api_refresh.c new file mode 100644 index 000000000..723fe7070 --- /dev/null +++ b/src/mint-lib/mint_api_refresh.c @@ -0,0 +1,540 @@ +/* + This file is part of TALER + Copyright (C) 2015 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see + +*/ +/** + * @file mint-lib/mint_api_refresh.c + * @brief Implementation of the /refresh/melt+reveal requests of the mint's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include /* just for HTTP status codes */ +#include +#include "taler_mint_service.h" +#include "mint_api_json.h" +#include "mint_api_context.h" +#include "mint_api_handle.h" +#include "taler_signatures.h" + + +/* ********************* /refresh/ common ***************************** */ + + +/** + * Melt (partially spent) coins to obtain fresh coins that are + * unlinkable to the original coin(s). Note that melting more + * than one coin in a single request will make those coins linkable, + * so the safest operation only melts one coin at a time. + * + * This API is typically used by a wallet. Note that to ensure that + * no money is lost in case of hardware failures, is operation does + * not actually initiate the request. Instead, it generates a buffer + * which the caller must store before proceeding with the actual call + * to #TALER_MINT_refresh_execute() that will generate the request. + * + * This function does verify that the given request data is internally + * consistent. However, the @a melts_sigs are only verified if @a + * check_sigs is set to #GNUNET_YES, as this may be relatively + * expensive and should be redundant. + * + * Aside from some non-trivial cryptographic operations that might + * take a bit of CPU time to complete, this function returns + * its result immediately and does not start any asynchronous + * processing. This function is also thread-safe. + * + * @param num_melts number of coins that are being melted (typically 1) + * @param melt_privs array of @a num_melts private keys of the coins to melt + * @param melt_amounts array of @a num_melts amounts specifying how much + * each coin will contribute to the melt (including fee) + * @param melt_sigs array of @a num_melts signatures affirming the + * validity of the public keys corresponding to the + * @a melt_privs private keys + * @param melt_pks array of @a num_melts denomination key information + * records corresponding to the @a melt_sigs + * validity of the keys + * @param check_sigs verify the validity of the signatures of @a melt_sigs + * @param fresh_pks_len length of the @a pks array + * @param fresh_pks array of @a pks_len denominations of fresh coins to create + * @param[OUT] res_size set to the size of the return value, or 0 on error + * @return NULL + * if the inputs are invalid (i.e. denomination key not with this mint). + * Otherwise, pointer to a buffer of @a res_size to store persistently + * before proceeding to #TALER_MINT_refresh_execute(). + * Non-null results should be freed using #GNUNET_free(). + */ +char * +TALER_MINT_refresh_prepare (unsigned int num_melts, + const struct TALER_CoinSpendPrivateKeyP *melt_privs, + const struct TALER_Amount *melt_amounts, + const struct TALER_DenominationSignature *melt_sigs, + const struct TALER_MINT_DenomPublicKey *melt_pks, + int check_sigs, + unsigned int fresh_pks_len, + const struct TALER_MINT_DenomPublicKey *fresh_pks, + size_t *res_size) +{ + GNUNET_break (0); // FIXME: not implemented + *res_size = 0; + return NULL; +} + + +/* ********************* /refresh/melt ***************************** */ + + +/** + * @brief A /refresh/melt Handle + */ +struct TALER_MINT_RefreshMeltHandle +{ + + /** + * The connection to mint this request handle will use + */ + struct TALER_MINT_Handle *mint; + + /** + * The url for this request. + */ + char *url; + + /** + * JSON encoding of the request to POST. + */ + char *json_enc; + + /** + * Handle for the request. + */ + struct MAC_Job *job; + + /** + * Function to call with refresh melt failure results. + */ + TALER_MINT_RefreshMeltCallback melt_cb; + + /** + * Closure for @e result_cb and @e melt_failure_cb. + */ + void *melt_cb_cls; + + /** + * Download buffer + */ + struct MAC_DownloadBuffer db; + +}; + + +/** + * Function called when we're done processing the + * HTTP /refresh/melt request. + * + * @param cls the `struct TALER_MINT_RefreshMeltHandle` + * @param eh the curl request handle + */ +static void +handle_refresh_melt_finished (void *cls, + CURL *eh) +{ + struct TALER_MINT_RefreshMeltHandle *rmh = cls; + long response_code; + json_t *json; + + rmh->job = NULL; + json = MAC_download_get_result (&rmh->db, + eh, + &response_code); + switch (response_code) + { + case 0: + break; + case MHD_HTTP_OK: + GNUNET_break (0); // FIXME: NOT implemented! (parse, check sig!) + + break; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the mint is buggy + (or API version conflict); just pass JSON reply to the application */ + break; + case MHD_HTTP_FORBIDDEN: + /* Double spending; check signatures on transaction history */ + GNUNET_break (0); // FIXME: NOT implemented! + break; + case MHD_HTTP_UNAUTHORIZED: + /* Nothing really to verify, mint says one of the signatures is + invalid; assuming we checked them, this should never happen, we + should pass the JSON reply to the application */ + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the application */ + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + default: + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u\n", + response_code); + GNUNET_break (0); + response_code = 0; + break; + } + if (NULL != rmh->melt_cb) + rmh->melt_cb (rmh->melt_cb_cls, + response_code, + UINT16_MAX, + json); + json_decref (json); + TALER_MINT_refresh_melt_cancel (rmh); +} + + +/** + * Submit a melt request to the mint and get the mint's + * response. + * + * This API is typically used by a wallet. Note that to ensure that + * no money is lost in case of hardware failures, the provided + * argument should have been constructed using + * #TALER_MINT_refresh_prepare and committed to persistent storage + * prior to calling this function. + * + * @param mint the mint handle; the mint must be ready to operate + * @param refresh_data_length size of the @a refresh_data (returned + * in the `res_size` argument from #TALER_MINT_refresh_prepare()) + * @param refresh_data the refresh data as returned from + #TALER_MINT_refresh_prepare()) + * @param melt_cb the callback to call with the result + * @param melt_cb_cls closure for @a melt_cb + * @return a handle for this request; NULL if the argument was invalid. + * In this case, neither callback will be called. + */ +struct TALER_MINT_RefreshMeltHandle * +TALER_MINT_refresh_melt (struct TALER_MINT_Handle *mint, + size_t refresh_data_length, + const char *refresh_data, + TALER_MINT_RefreshMeltCallback melt_cb, + void *melt_cb_cls) +{ + json_t *melt_obj; + struct TALER_MINT_RefreshMeltHandle *rmh; + CURL *eh; + struct TALER_MINT_Context *ctx; + + if (GNUNET_YES != + MAH_handle_is_ready (mint)) + { + GNUNET_break (0); + return NULL; + } + /* FIXME: parse "refresh_data" */ + + /* FIXME: totally bogus request building here: */ + melt_obj = json_pack ("{s:o, s:O}", /* f/wire */ + "4", 42, + "6", 62); + + + rmh = GNUNET_new (struct TALER_MINT_RefreshMeltHandle); + rmh->mint = mint; + rmh->melt_cb = melt_cb; + rmh->melt_cb_cls = melt_cb_cls; + + rmh->url = MAH_path_to_url (mint, + "/refresh/melt"); + + eh = curl_easy_init (); + GNUNET_assert (NULL != (rmh->json_enc = + json_dumps (melt_obj, + JSON_COMPACT))); + json_decref (melt_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_URL, + rmh->url)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDS, + rmh->json_enc)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDSIZE, + strlen (rmh->json_enc))); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEFUNCTION, + &MAC_download_cb)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEDATA, + &rmh->db)); + ctx = MAH_handle_to_context (mint); + rmh->job = MAC_job_add (ctx, + eh, + GNUNET_YES, + &handle_refresh_melt_finished, + rmh); + return rmh; +} + + +/** + * Cancel a refresh execute request. This function cannot be used + * on a request handle if either callback was already invoked. + * + * @param rmh the refresh melt handle + */ +void +TALER_MINT_refresh_melt_cancel (struct TALER_MINT_RefreshMeltHandle *rmh) +{ + if (NULL != rmh->job) + { + MAC_job_cancel (rmh->job); + rmh->job = NULL; + } + GNUNET_free_non_null (rmh->db.buf); + GNUNET_free (rmh->url); + GNUNET_free (rmh->json_enc); + GNUNET_free (rmh); +} + + +/* ********************* /refresh/reveal ***************************** */ + + +/** + * @brief A /refresh/reveal Handle + */ +struct TALER_MINT_RefreshRevealHandle +{ + + /** + * The connection to mint this request handle will use + */ + struct TALER_MINT_Handle *mint; + + /** + * The url for this request. + */ + char *url; + + /** + * JSON encoding of the request to POST. + */ + char *json_enc; + + /** + * Handle for the request. + */ + struct MAC_Job *job; + + /** + * Function to call with the result. + */ + TALER_MINT_RefreshRevealCallback reveal_cb; + + /** + * Closure for @e reveal_cb. + */ + void *reveal_cb_cls; + + /** + * Download buffer + */ + struct MAC_DownloadBuffer db; + + +}; + + +/** + * Function called when we're done processing the + * HTTP /refresh/reveal request. + * + * @param cls the `struct TALER_MINT_RefreshHandle` + * @param eh the curl request handle + */ +static void +handle_refresh_reveal_finished (void *cls, + CURL *eh) +{ + struct TALER_MINT_RefreshRevealHandle *rrh = cls; + long response_code; + json_t *json; + + rrh->job = NULL; + json = MAC_download_get_result (&rrh->db, + eh, + &response_code); + switch (response_code) + { + case 0: + break; + case MHD_HTTP_OK: + GNUNET_break (0); // FIXME: NOT implemented! + // rrh->reveal_cb = NULL; (call with real result, do not call again below) + break; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the mint is buggy + (or API version conflict); just pass JSON reply to the application */ + break; + case MHD_HTTP_CONFLICT: + /* Nothing really to verify, mint says our reveal is inconsitent + with our commitment, so either side is buggy; we + should pass the JSON reply to the application */ + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + default: + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u\n", + response_code); + GNUNET_break (0); + response_code = 0; + break; + } + if (NULL != rrh->reveal_cb) + rrh->reveal_cb (rrh->reveal_cb_cls, + response_code, + 0, NULL, NULL, + json); + json_decref (json); + TALER_MINT_refresh_reveal_cancel (rrh); +} + + + +/** + * Submit a /refresh/reval request to the mint and get the mint's + * response. + * + * This API is typically used by a wallet. Note that to ensure that + * no money is lost in case of hardware failures, the provided + * arguments should have been committed to persistent storage + * prior to calling this function. + * + * @param mint the mint handle; the mint must be ready to operate + * @param refresh_data_length size of the @a refresh_data (returned + * in the `res_size` argument from #TALER_MINT_refresh_prepare()) + * @param refresh_data the refresh data as returned from + #TALER_MINT_refresh_prepare()) + * @param noreveal_index response from the mint to the + * #TALER_MINT_refresh_melt() invocation + * @param reveal_cb the callback to call with the final result of the + * refresh operation + * @param reveal_cb_cls closure for the above callback + * @return a handle for this request; NULL if the argument was invalid. + * In this case, neither callback will be called. + */ +struct TALER_MINT_RefreshRevealHandle * +TALER_MINT_refresh_reveal (struct TALER_MINT_Handle *mint, + size_t refresh_data_length, + const char *refresh_data, + uint16_t noreveal_index, + TALER_MINT_RefreshRevealCallback reveal_cb, + void *reveal_cb_cls) +{ + struct TALER_MINT_RefreshRevealHandle *rrh; + json_t *reveal_obj; + CURL *eh; + struct TALER_MINT_Context *ctx; + + if (GNUNET_YES != + MAH_handle_is_ready (mint)) + { + GNUNET_break (0); + return NULL; + } + /* FIXME: parse "refresh_data" */ + + /* FIXME: totally bogus request building here: */ + reveal_obj = json_pack ("{s:o, s:O}", /* f/wire */ + "4", 42, + "6", 62); + + rrh = GNUNET_new (struct TALER_MINT_RefreshRevealHandle); + rrh->mint = mint; + rrh->reveal_cb = reveal_cb; + rrh->reveal_cb_cls = reveal_cb_cls; + + rrh->url = MAH_path_to_url (rrh->mint, + "/refresh/reveal"); + + eh = curl_easy_init (); + GNUNET_assert (NULL != (rrh->json_enc = + json_dumps (reveal_obj, + JSON_COMPACT))); + json_decref (reveal_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_URL, + rrh->url)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDS, + rrh->json_enc)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDSIZE, + strlen (rrh->json_enc))); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEFUNCTION, + &MAC_download_cb)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEDATA, + &rrh->db)); + ctx = MAH_handle_to_context (rrh->mint); + rrh->job = MAC_job_add (ctx, + eh, + GNUNET_YES, + &handle_refresh_reveal_finished, + rrh); + return rrh; +} + + +/** + * Cancel a refresh reveal request. This function cannot be used + * on a request handle if the callback was already invoked. + * + * @param rrh the refresh reval handle + */ +void +TALER_MINT_refresh_reveal_cancel (struct TALER_MINT_RefreshRevealHandle *rrh) +{ + if (NULL != rrh->job) + { + MAC_job_cancel (rrh->job); + rrh->job = NULL; + } + GNUNET_free_non_null (rrh->db.buf); + GNUNET_free (rrh->url); + GNUNET_free (rrh->json_enc); + GNUNET_free (rrh); +} + + +/* end of mint_api_refresh.c */ diff --git a/src/mint-lib/mint_api_refresh_link.c b/src/mint-lib/mint_api_refresh_link.c new file mode 100644 index 000000000..3ea6b23e4 --- /dev/null +++ b/src/mint-lib/mint_api_refresh_link.c @@ -0,0 +1,236 @@ +/* + This file is part of TALER + Copyright (C) 2015 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see + +*/ +/** + * @file mint-lib/mint_api_refresh_link.c + * @brief Implementation of the /refresh/link request of the mint's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include /* just for HTTP status codes */ +#include +#include "taler_mint_service.h" +#include "mint_api_json.h" +#include "mint_api_context.h" +#include "mint_api_handle.h" +#include "taler_signatures.h" + + +/** + * @brief A /refresh/link Handle + */ +struct TALER_MINT_RefreshLinkHandle +{ + + /** + * The connection to mint this request handle will use + */ + struct TALER_MINT_Handle *mint; + + /** + * The url for this request. + */ + char *url; + + /** + * JSON encoding of the request to POST. + */ + char *json_enc; + + /** + * Handle for the request. + */ + struct MAC_Job *job; + + /** + * Function to call with the result. + */ + TALER_MINT_RefreshLinkCallback link_cb; + + /** + * Closure for @e cb. + */ + void *link_cb_cls; + + /** + * Download buffer + */ + struct MAC_DownloadBuffer db; + +}; + + +/** + * Function called when we're done processing the + * HTTP /refresh/link request. + * + * @param cls the `struct TALER_MINT_RefreshLinkHandle` + * @param eh the curl request handle + */ +static void +handle_refresh_link_finished (void *cls, + CURL *eh) +{ + struct TALER_MINT_RefreshLinkHandle *rlh = cls; + long response_code; + json_t *json; + + rlh->job = NULL; + json = MAC_download_get_result (&rlh->db, + eh, + &response_code); + switch (response_code) + { + case 0: + break; + case MHD_HTTP_OK: + GNUNET_break (0); // FIXME: NOT implemented! + // rh->link_cb = NULL; (call with real result, do not call again below) + break; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the mint is buggy + (or API version conflict); just pass JSON reply to the application */ + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, mint says this coin was not melted; we + should pass the JSON reply to the application */ + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + default: + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u\n", + response_code); + GNUNET_break (0); + response_code = 0; + break; + } + if (NULL != rlh->link_cb) + rlh->link_cb (rlh->link_cb_cls, + response_code, + 0, NULL, NULL, + json); + json_decref (json); + TALER_MINT_refresh_link_cancel (rlh); +} + + +/** + * Submit a link request to the mint and get the mint's response. + * + * This API is typically not used by anyone, it is more a threat + * against those trying to receive a funds transfer by abusing the + * /refresh protocol. + * + * @param mint the mint handle; the mint must be ready to operate + * @param coin_priv private key to request link data for + * @param link_cb the callback to call with the useful result of the + * refresh operation the @a coin_priv was involved in (if any) + * @param link_cb_cls closure for @a link_cb + * @return a handle for this request + */ +struct TALER_MINT_RefreshLinkHandle * +TALER_MINT_refresh_link (struct TALER_MINT_Handle *mint, + const struct TALER_CoinSpendPrivateKeyP *coin_priv, + TALER_MINT_RefreshLinkCallback link_cb, + void *link_cb_cls) +{ + json_t *link_obj; + struct TALER_MINT_RefreshLinkHandle *rlh; + CURL *eh; + struct TALER_MINT_Context *ctx; + + if (GNUNET_YES != + MAH_handle_is_ready (mint)) + { + GNUNET_break (0); + return NULL; + } + /* FIXME: totally bogus request building here: */ + link_obj = json_pack ("{s:o, s:O}", /* f/wire */ + "4", 42, + "6", 62); + + + rlh = GNUNET_new (struct TALER_MINT_RefreshLinkHandle); + rlh->mint = mint; + rlh->link_cb = link_cb; + rlh->link_cb_cls = link_cb_cls; + + rlh->url = MAH_path_to_url (mint, "/refresh/link"); + + eh = curl_easy_init (); + GNUNET_assert (NULL != (rlh->json_enc = + json_dumps (link_obj, + JSON_COMPACT))); + json_decref (link_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_URL, + rlh->url)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDS, + rlh->json_enc)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDSIZE, + strlen (rlh->json_enc))); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEFUNCTION, + &MAC_download_cb)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEDATA, + &rlh->db)); + ctx = MAH_handle_to_context (mint); + rlh->job = MAC_job_add (ctx, + eh, + GNUNET_YES, + &handle_refresh_link_finished, + rlh); + return rlh; +} + + +/** + * Cancel a refresh link request. This function cannot be used + * on a request handle if the callback was already invoked. + * + * @param rlh the refresh link handle + */ +void +TALER_MINT_refresh_link_cancel (struct TALER_MINT_RefreshLinkHandle *rlh) +{ + if (NULL != rlh->job) + { + MAC_job_cancel (rlh->job); + rlh->job = NULL; + } + GNUNET_free_non_null (rlh->db.buf); + GNUNET_free (rlh->url); + GNUNET_free (rlh->json_enc); + GNUNET_free (rlh); +} + + +/* end of mint_api_refresh_link.c */